studio.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824
  1. // set superglobal js var by default 100 for glyph
  2. window.size = 'medium';
  3. var SVG_NS = "http://www.w3.org/2000/svg";
  4. var svgCache = {};
  5. var options = {};
  6. var $badge;
  7. var $badgeRaster;
  8. var $studio = document.getElementById('studio');
  9. var $template = document.getElementById('studio-template');
  10. var $palette = document.getElementById('studio-palette');
  11. var $mask = document.getElementById('studio-mask');
  12. var $glyph = document.getElementById('studio-glyph');
  13. var $glyphSelector;
  14. var $glyphSelectorButton;
  15. window.addEventListener('load', function init() {
  16. $badgeRaster = new Image();
  17. $badgeRaster.id = 'raster';
  18. document.getElementById('output').appendChild($badgeRaster);
  19. $template.addEventListener('change', updateTemplate);
  20. $palette.addEventListener('change', updatePalette);
  21. $mask.addEventListener('change', updateMask);
  22. $glyph.addEventListener('change', updateGlyph);
  23. initStudio();
  24. initPalettes();
  25. initOptions();
  26. updateTemplate();
  27. });
  28. // ==[ General Methods ]======================================================
  29. /**
  30. *
  31. */
  32. function showError(err) {
  33. // TO DO - show errors :)
  34. console.err(err);
  35. }
  36. // ==[ Studio ]===============================================================
  37. /**
  38. *
  39. */
  40. function initStudio() {
  41. initGlyphSelector();
  42. document.addEventListener('keydown', function (event) {
  43. if (event.keyCode === 27) { // Escape
  44. if ($glyphSelector && $glyphSelector.offsetWidth)
  45. closeGlyphSelector();
  46. }
  47. }, true);
  48. document.addEventListener('focus', function (event) {
  49. [$glyphSelector].forEach(function ($overlay) {
  50. if ($overlay && $overlay.offsetWidth && !$overlay.contains(event.target)) {
  51. event.stopPropagation();
  52. $overlay.focus();
  53. }
  54. });
  55. }, true);
  56. }
  57. // ==[ Glyph Selector ]=======================================================
  58. /**
  59. *
  60. */
  61. function initGlyphSelector() {
  62. if ($glyphSelector) {
  63. return false;
  64. }
  65. var glyphLog = [];
  66. $glyphSelectorButton = document.createElement('button');
  67. $glyphSelectorButton.className = 'btn btn-default pull-right';
  68. $glyphSelectorButton.id = 'search-glyphs';
  69. $glyphSelectorButton.type = 'button';
  70. $glyphSelectorButton.innerHTML = '<em class="fa fa-search"></em> Search';
  71. $glyphSelectorButton.addEventListener('click', openGlyphSelector);
  72. $glyph.parentNode.insertBefore($glyphSelectorButton, $glyph.nextSibling);
  73. var $$options = $glyph.querySelectorAll('option');
  74. $glyphSelector = importTemplate('glyph-selector', function ($template) {
  75. var $list = $template.querySelector('ul');
  76. for (var i = 0, l = $$options.length; i < l; i++) {
  77. (function ($option, index) {
  78. var value = $option.value;
  79. var id = 'glyph-selector-item-' + value;
  80. var $node = importTemplate('glyph-selector-item', function ($template) {
  81. var $input = $template.querySelector('input');
  82. $input.id = id;
  83. $input.value = index;
  84. var checked = $glyph.selectedIndex === index;
  85. $input[checked ? 'setAttribute' : 'removeAttribute']('checked', 'checked');
  86. var $label = $template.querySelector('label');
  87. $label.id = id + '-label';
  88. $label.className = 'fa fa-' + value;
  89. $label.setAttribute('for', id);
  90. $label.setAttribute('title', $option.text);
  91. }).querySelector('li');
  92. $list.appendChild($node);
  93. glyphLog.push({
  94. id: id,
  95. value: value
  96. });
  97. })($$options[i], i);
  98. }
  99. }).querySelector('#glyph-selector');
  100. $glyphSelector.querySelector('.header').appendChild(makeCloseButton(closeGlyphSelector));
  101. $studio.appendChild($glyphSelector);
  102. $glyphSelector.addEventListener('change', function (event) {
  103. event.stopPropagation();
  104. var index = event.target.value;
  105. $glyph.selectedIndex = index;
  106. updateGlyph();
  107. });
  108. $glyphSelector.addEventListener('click', function (event) {
  109. if (event.target.nodeName.toLowerCase() !== 'label') {
  110. return;
  111. }
  112. event.stopPropagation();
  113. closeGlyphSelector();
  114. });
  115. $glyphSelector.addEventListener('keydown', function (event) {
  116. if (event.keyCode === 13) { // Enter
  117. if (event.target.name)
  118. $glyph.selectedIndex = event.target.value;
  119. return updateGlyph(closeGlyphSelector);
  120. }
  121. if (event.keyCode === 38 || event.keyCode === 40) { // Up / Down
  122. event.preventDefault();
  123. var $container = event.target.parentNode.parentNode;
  124. var itemSize = event.target.parentNode.offsetWidth;
  125. var containerSize = $container.offsetWidth;
  126. var rowCount = Math.floor(containerSize / itemSize);
  127. var currentIndex = parseInt(event.target.value);
  128. var newIndex = currentIndex;
  129. var altFinder;
  130. if (event.keyCode === 38) {
  131. // Move up a row
  132. newIndex = currentIndex - rowCount;
  133. altFinder = 'firstElementChild';
  134. } else {
  135. // Move down a row
  136. newIndex = currentIndex + rowCount;
  137. altFinder = 'lastElementChild';
  138. }
  139. var newItem = $container.querySelector('input[value="' + newIndex + '"]') ||
  140. $container[altFinder].querySelector('input');
  141. $glyph.selectedIndex = newItem.value;
  142. newItem.checked = true;
  143. newItem.focus();
  144. rasterize();
  145. }
  146. });
  147. $glyphSelector.addEventListener('focus', function (event) {
  148. if (event.target !== $glyphSelector)
  149. return;
  150. event.stopPropagation();
  151. }, true);
  152. $glyph.addEventListener('change', function (event) {
  153. var $selectorItem = document.getElementById('glyph-selector-item-' + this.value);
  154. if ($selectorItem) {
  155. $selectorItem.click();
  156. }
  157. });
  158. }
  159. /**
  160. *
  161. */
  162. function openGlyphSelector() {
  163. if (!$glyphSelector)
  164. initGlyphSelector();
  165. $glyphSelector.classList.remove('hidden');
  166. if ($glyph.value)
  167. document.getElementById('glyph-selector-item-' + $glyph.value + '-label').focus();
  168. $glyphSelector.focus();
  169. }
  170. /**
  171. *
  172. */
  173. function closeGlyphSelector() {
  174. if (!$glyphSelector)
  175. return;
  176. $glyphSelector.classList.add('hidden');
  177. $glyphSelectorButton.focus();
  178. }
  179. // ==[ Templates ]============================================================
  180. /**
  181. *
  182. */
  183. function getCurrentTemplate() {
  184. return $template.value;
  185. }
  186. /**
  187. *
  188. */
  189. function updateTemplate(callback) {
  190. callback = cb(callback);
  191. var path = $template.dataset.path;
  192. var shape = getCurrentTemplate();
  193. loadSVG(path + '/' + shape + '.svg', function (err, $svg) {
  194. if (err)
  195. return showError(err);
  196. $badge = $svg;
  197. extractOptions();
  198. setCustomPalette($svg);
  199. updatePalette(function () {
  200. updateMask(callback);
  201. });
  202. });
  203. }
  204. // ==[ Palettes ]=============================================================
  205. function Palette(colors) {
  206. this._colors = {};
  207. if (colors) {
  208. for (var color in colors) {
  209. if (colors.hasOwnProperty(color)) {
  210. this._colors[color] = Palette.parseColor(colors[color]);
  211. }
  212. }
  213. }
  214. if (!this._colors.hasOwnProperty('glyph'))
  215. this._colors['glyph'] = '#000000';
  216. }
  217. Palette.prototype.toNode = function (id) {
  218. var content = [];
  219. for (var color in this._colors) {
  220. if (this._colors.hasOwnProperty(color)) {
  221. content.push('.color-' + color + ' { fill: ' + this._colors[color] + '; }');
  222. }
  223. }
  224. var $node = document.createElement('style');
  225. $node.type = 'text/css';
  226. $node.id = id || 'palette';
  227. $node.textContent = content.join('\n');
  228. return $node;
  229. }
  230. Palette.prototype.colors = function () {
  231. return Object.keys(this._colors);
  232. }
  233. Palette.prototype.color = function (name) {
  234. return this._colors[name] || '#000';
  235. }
  236. Palette.parseColor = function (str) {
  237. // Should probably be a bit more robust about this!
  238. if (!/^#[a-f0-9]{3}$/i.test(str))
  239. return str.toLowerCase();
  240. return '#' + str.charAt(1) + str.charAt(1)
  241. + str.charAt(2) + str.charAt(2)
  242. + str.charAt(3) + str.charAt(3);
  243. }
  244. Palette.fromDataset = function (dataset) {
  245. var colors = {};
  246. for (var item in dataset) {
  247. if (/^color\w+/i.test(item)) {
  248. var color = item
  249. .replace(/^color(\w)/i, function (m, c) {
  250. return c.toLowerCase();
  251. })
  252. .replace(/[A-Z]/, function (m) {
  253. return '-' + m.toLowerCase();
  254. });
  255. colors[color] = dataset[item];
  256. }
  257. }
  258. return new Palette(colors);
  259. }
  260. Palette.fromSVG = function ($svg) {
  261. var colors = {};
  262. var $node = $svg.getElementById('palette');
  263. if (!$node || $node.nodeName !== 'style')
  264. return new Palette();
  265. var $stylesheet = document.createElement('style');
  266. $stylesheet.setAttribute('media', 'print');
  267. $stylesheet.appendChild(document.createTextNode($node.textContent));
  268. document.head.appendChild($stylesheet);
  269. var sheet = $stylesheet.sheet;
  270. document.head.removeChild($stylesheet);
  271. var rules = sheet.rules || sheet.cssRules;
  272. for (var i = 0, l = rules.length; i < l; i++) {
  273. var rule = rules[i];
  274. var selector = rule.selectorText;
  275. if (/^\.color-/.test(selector)) {
  276. var key = selector.replace(/^\.color-/, '');
  277. var value = rule.style.fill || '#000';
  278. colors[key] = value;
  279. }
  280. }
  281. return new Palette(colors);
  282. }
  283. /**
  284. *
  285. */
  286. function initPalettes() {
  287. var $custom = document.createElement('option');
  288. $custom.disabled = true;
  289. $custom.value = 'custom';
  290. $custom.text = 'Custom';
  291. $custom.id = 'custom-color-option';
  292. $palette.options.add($custom);
  293. var $container = document.getElementById('custom-palette');
  294. $palette.addEventListener('change', function () {
  295. var isCustom = (this.options[this.selectedIndex] === $custom);
  296. $custom.disabled = !isCustom;
  297. setCustomColors();
  298. updatePalette();
  299. });
  300. var changeTimer;
  301. $container.addEventListener('change', function (event) {
  302. var $input = event.target;
  303. $custom.setAttribute('data-color-' + $input.name, $input.value);
  304. $custom.disabled = false;
  305. $palette.selectedIndex = $palette.options.length - 1;
  306. updatePalette();
  307. });
  308. setCustomColors();
  309. }
  310. /**
  311. *
  312. */
  313. function getCurrentPalette() {
  314. var $option = $palette.options[$palette.selectedIndex];
  315. return Palette.fromDataset($option.dataset);
  316. }
  317. /**
  318. *
  319. */
  320. function updatePalette(callback) {
  321. callback = cb(callback);
  322. var $oldPalette = $badge.getElementById('palette');
  323. var $newPalette = getCurrentPalette().toNode();
  324. if ($oldPalette) {
  325. $oldPalette.parentNode.insertBefore($newPalette, $oldPalette);
  326. $oldPalette.parentNode.removeChild($oldPalette);
  327. } else {
  328. var $defs = $badge.querySelector('defs') || document.createElement('defs');
  329. if (!$defs.parentNode)
  330. $badge.insertBefore($defs, $badge.childNodes[0]);
  331. $defs.appendChild($newPalette)
  332. }
  333. updateGlyph(callback);
  334. }
  335. /**
  336. *
  337. */
  338. function setCustomPalette($svg, callback) {
  339. callback = cb(callback);
  340. var colors = Palette.fromSVG($svg).colors();
  341. var $container = document.getElementById('custom-palette');
  342. var display = $container.style.display;
  343. $container.innerHTML = '';
  344. $container.style.display = 'none';
  345. $container.className = 'item';
  346. for (var i = 0, l = colors.length; i < l; i++) {
  347. var name = colors[i];
  348. var label = name.replace(/(^|-)(\w)/g, function (m, x, c) {
  349. return (x ? ' ' : '') + c.toUpperCase();
  350. });
  351. $container.appendChild(importTemplate('custom-color', function ($template) {
  352. var $label = $template.querySelector('span');
  353. $label.textContent = label;
  354. var $input = $template.querySelector('input');
  355. $input.name = name;
  356. $input.id = 'custom-color-picker-' + name;
  357. }));
  358. }
  359. if (colors.length)
  360. $container.style.display = display;
  361. setCustomColors();
  362. }
  363. /**
  364. *
  365. */
  366. function setCustomColors() {
  367. var $custom = document.getElementById('custom-color-option');
  368. var $option = $palette.options[$palette.selectedIndex];
  369. var palette = Palette.fromDataset($option.dataset);
  370. var colors = palette.colors();
  371. for (var i = 0, l = colors.length; i < l; i++) {
  372. var colorName = colors[i];
  373. var colorValue = palette.color(colorName);
  374. $custom.setAttribute('data-color-' + colorName, colorValue);
  375. var $input = document.getElementById('custom-color-picker-' + colorName);
  376. if ($input) {
  377. $input.value = colorValue;
  378. }
  379. }
  380. }
  381. // ==[ Masks ]================================================================
  382. function getCurrentMask() {
  383. return $mask.value;
  384. }
  385. /**
  386. *
  387. */
  388. function updateMask(callback) {
  389. callback = cb(callback);
  390. var path = $mask.dataset.path;
  391. var mask = getCurrentMask();
  392. if (!mask) {
  393. var $svg = document.createElementNS(SVG_NS, 'svg');
  394. var $g = document.createElementNS(SVG_NS, 'g');
  395. $g.id = 'mask';
  396. $svg.appendChild($g);
  397. return done(null, $svg);
  398. }
  399. loadSVG(path + '/' + mask + '.svg', done);
  400. function done(err, $svg) {
  401. if (err)
  402. return showError(err);
  403. var $oldMask = $badge.querySelector('#mask');
  404. var $newMask = $svg.querySelector('#mask');
  405. $oldMask.parentNode.insertBefore($newMask, $oldMask);
  406. $oldMask.parentNode.removeChild($oldMask);
  407. rasterize(callback);
  408. }
  409. }
  410. // ==[ Options ]==============================================================
  411. /**
  412. *
  413. */
  414. function initOptions() {
  415. if ($badge)
  416. extractOptions();
  417. var $options = document.getElementById('options');
  418. $options.addEventListener('change', function (event) {
  419. event.stopPropagation();
  420. var option = event.target.name;
  421. if (!options.hasOwnProperty(option))
  422. return;
  423. options[option] = !!event.target.checked;
  424. setOptions();
  425. });
  426. }
  427. /**
  428. *
  429. */
  430. function extractOptions() {
  431. var $options = document.getElementById('options');
  432. $options.innerHTML = '';
  433. var $optional = $badge.querySelectorAll('.optional');
  434. if (!$optional.length) {
  435. $options.innerHTML = '<i>None</l>';
  436. return;
  437. }
  438. for (var i = 0, l = $optional.length; i < l; i++) {
  439. var $option = $optional[i];
  440. var label = $option.getAttribute('title');
  441. var name = $option.id;
  442. var enabled = ($option.getAttribute('display') !== 'none');
  443. if (!options.hasOwnProperty(name))
  444. options[name] = enabled;
  445. $option[!!options[name] ? 'removeAttribute' : 'setAttribute']('display', 'none');
  446. $options.appendChild(importTemplate('option', function ($template) {
  447. var $checkbox = $template.querySelector('input');
  448. $checkbox.name = name;
  449. $checkbox.checked = !!options[name];
  450. var $label = $template.querySelector('span');
  451. $label.textContent = label;
  452. }));
  453. }
  454. }
  455. /**
  456. *
  457. */
  458. function setOptions(callback) {
  459. callback = cb(callback);
  460. if (!$badge)
  461. return callback();
  462. for (var option in options) {
  463. if (options.hasOwnProperty(option)) {
  464. var $node = $badge.getElementById(option);
  465. var visible = !!options[option];
  466. $node && ($node[visible ? 'removeAttribute' : 'setAttribute']('display', 'none'));
  467. }
  468. }
  469. rasterize(callback)
  470. }
  471. // ==[ Glyphs ]===============================================================
  472. /**
  473. *
  474. */
  475. function getCurrentGlyph() {
  476. return $glyph.value;
  477. }
  478. /**
  479. *
  480. */
  481. function getCurrentGlyphValue() {
  482. if (!$glyph.value)
  483. return '';
  484. var $i = document.createElement('i');
  485. $i.className = 'fa fa-' + getCurrentGlyph();
  486. document.body.appendChild($i);
  487. var chr = window.getComputedStyle($i, ':before').content;
  488. document.body.removeChild($i);
  489. chr = chr.replace(/["']/g, '');
  490. return chr;
  491. }
  492. /**
  493. *
  494. */
  495. function updateGlyph(callback) {
  496. var glyph = getCurrentGlyphValue();
  497. if (!glyph)
  498. return setGlyphImage(null, callback);
  499. var $canvas = document.createElement('canvas');
  500. $canvas.width = parseInt($badgeRaster.offsetWidth);
  501. $canvas.height = parseInt($badgeRaster.offsetHeight);
  502. var ctx = $canvas.getContext('2d');
  503. ctx.font = parseInt($canvas.width / 3) + "px FontAwesome";
  504. ctx.fillStyle = getCurrentPalette().color('glyph');
  505. ctx.textAlign = "center";
  506. ctx.textBaseline = "middle";
  507. ctx.shadowColor = "rgba(0,0,0,0.5)";
  508. ctx.shadowOffsetX = 0;
  509. ctx.shadowOffsetY = 0;
  510. ctx.shadowBlur = 5;
  511. ctx.fillText(glyph, $canvas.width / 2, $canvas.height / 2);
  512. var $image = new Image();
  513. $image.onload = function () {
  514. setGlyphImage($image, callback);
  515. }
  516. $image.src = $canvas.toDataURL("image/png");
  517. // $image.src = "./media/images/cheese.jpg";
  518. }
  519. /**
  520. *
  521. */
  522. function setGlyphImage($image, callback) {
  523. callback = cb(callback);
  524. var $newGlyph = document.createElementNS(SVG_NS, 'g');
  525. $newGlyph.id = 'glyph';
  526. if (!$image)
  527. return done();
  528. var gxyAdd = 0;
  529. var whAdd = 0;
  530. if (window.size == 'big') {
  531. gxyAdd = 40;
  532. whAdd = 80
  533. } else if (window.size == 'small') {
  534. gxyAdd = -40;
  535. whAdd = -80
  536. }
  537. var iWidth = $image.width;
  538. var iHeight = $image.height;
  539. var rWidth = $badgeRaster.width;
  540. var rHeight = $badgeRaster.height;
  541. var box = $badge.getAttribute('viewBox').split(' ');
  542. var bWidth = parseInt(box[2]);
  543. var bHeight = parseInt(box[3]);
  544. var cx = bWidth / 2 + parseInt(box[0]);
  545. var cy = bHeight / 2 + parseInt(box[1]);
  546. var gWidth = iWidth / (rWidth / bWidth);
  547. var gHeight = iHeight / (rHeight / bHeight);
  548. var gx = cx - (gWidth / 2);
  549. var gy = cy - (gHeight / 2);
  550. var $glyph = document.createElementNS(SVG_NS, 'image');
  551. $glyph.setAttribute('x', gx - gxyAdd);
  552. $glyph.setAttribute('y', gy - gxyAdd);
  553. $glyph.setAttribute('width', gWidth + whAdd);
  554. $glyph.setAttribute('height', gHeight + whAdd);
  555. $glyph.setAttribute('xlink:href', $image.src);
  556. $newGlyph.appendChild($glyph);
  557. done();
  558. function done() {
  559. var $oldGlyph = $badge.getElementById('glyph');
  560. $oldGlyph.parentNode.insertBefore($newGlyph, $oldGlyph);
  561. $oldGlyph.parentNode.removeChild($oldGlyph);
  562. rasterize(callback);
  563. }
  564. }
  565. // ==[ Helpers ]==============================================================
  566. /**
  567. *
  568. */
  569. function rasterize(callback) {
  570. callback = cb(callback);
  571. var $svg = $badge.cloneNode(true);
  572. var $canvas = document.createElement('canvas');
  573. $canvas.width = parseInt($svg.getAttribute('width'));
  574. $canvas.height = parseInt($svg.getAttribute('height'));
  575. var ctx = $canvas.getContext('2d');
  576. var svg_xml = (new XMLSerializer()).serializeToString($svg);
  577. /*
  578. // This is the 'official' way of doing this. However, Firefox seems to have
  579. // an issue referencing relative fragment URIs created by `createObjectURL`.
  580. // So we're using a base64 encoding hack instead :( Worth noting that if
  581. // there are non-standard unicode characters in the XML, it'll die a death.
  582. var DOMURL = window.URL || window.webkitURL || window;
  583. var blob = new Blob([svg_xml], {type: 'image/svg+xml;charset=utf-8'});
  584. var url = DOMURL.createObjectURL(blob);
  585. */
  586. var url = 'data:image/svg+xml;base64,' + btoa(svg_xml);
  587. var $img = new Image();
  588. $img.onload = function () {
  589. ctx.drawImage($img, 0, 0);
  590. $badgeRaster.src = $canvas.toDataURL("image/png");
  591. callback();
  592. }
  593. $img.src = url;
  594. }
  595. /**
  596. *
  597. */
  598. function cb(fn) {
  599. if (typeof fn === 'function')
  600. return fn;
  601. return function () {
  602. };
  603. }
  604. /**
  605. *
  606. */
  607. function load(url, method, callback) {
  608. var request = new XMLHttpRequest();
  609. request.onload = function () {
  610. callback(null, request.responseXML || request.responseText, request);
  611. }
  612. request.onerror = function (err) {
  613. callback(err, null, request);
  614. }
  615. request.open(method, url, true);
  616. request.send();
  617. }
  618. /**
  619. *
  620. */
  621. function loadSVG(path, callback) {
  622. if (svgCache[path])
  623. return callback(null, svgCache[path].cloneNode(true));
  624. load(path, 'GET', function (err, $doc, request) {
  625. if (err)
  626. return callback(err);
  627. if (!$doc || typeof $doc === 'string')
  628. return callback(new Error('Not valid SVG'));
  629. svgCache[path] = $doc.getElementsByTagName('svg')[0];
  630. callback(null, svgCache[path].cloneNode(true));
  631. })
  632. }
  633. /**
  634. *
  635. */
  636. function importTemplate(name, builder) {
  637. var $template = document.getElementById(name + '-template');
  638. if (typeof builder === 'function')
  639. builder($template.content);
  640. return document.importNode($template.content, true);
  641. }
  642. /**
  643. *
  644. */
  645. function makeCloseButton(callback) {
  646. var $template = importTemplate('close-button');
  647. $template.querySelector('button').addEventListener('click', callback);
  648. return $template;
  649. }