index.cjs.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. function ensureImported(element, target) {
  4. if (element.ownerDocument !== target.ownerDocument) {
  5. try {
  6. // may fail on webkit
  7. return target.ownerDocument.importNode(element, true);
  8. } catch (e) {
  9. // ignore
  10. }
  11. }
  12. return element;
  13. }
  14. /**
  15. * appendTo utility
  16. */
  17. /**
  18. * Append a node to a target element and return the appended node.
  19. *
  20. * @param {SVGElement} element
  21. * @param {SVGElement} target
  22. *
  23. * @return {SVGElement} the appended node
  24. */
  25. function appendTo(element, target) {
  26. return target.appendChild(ensureImported(element, target));
  27. }
  28. /**
  29. * append utility
  30. */
  31. /**
  32. * Append a node to an element
  33. *
  34. * @param {SVGElement} element
  35. * @param {SVGElement} node
  36. *
  37. * @return {SVGElement} the element
  38. */
  39. function append(target, node) {
  40. appendTo(node, target);
  41. return target;
  42. }
  43. /**
  44. * attribute accessor utility
  45. */
  46. var LENGTH_ATTR = 2;
  47. var CSS_PROPERTIES = {
  48. 'alignment-baseline': 1,
  49. 'baseline-shift': 1,
  50. 'clip': 1,
  51. 'clip-path': 1,
  52. 'clip-rule': 1,
  53. 'color': 1,
  54. 'color-interpolation': 1,
  55. 'color-interpolation-filters': 1,
  56. 'color-profile': 1,
  57. 'color-rendering': 1,
  58. 'cursor': 1,
  59. 'direction': 1,
  60. 'display': 1,
  61. 'dominant-baseline': 1,
  62. 'enable-background': 1,
  63. 'fill': 1,
  64. 'fill-opacity': 1,
  65. 'fill-rule': 1,
  66. 'filter': 1,
  67. 'flood-color': 1,
  68. 'flood-opacity': 1,
  69. 'font': 1,
  70. 'font-family': 1,
  71. 'font-size': LENGTH_ATTR,
  72. 'font-size-adjust': 1,
  73. 'font-stretch': 1,
  74. 'font-style': 1,
  75. 'font-variant': 1,
  76. 'font-weight': 1,
  77. 'glyph-orientation-horizontal': 1,
  78. 'glyph-orientation-vertical': 1,
  79. 'image-rendering': 1,
  80. 'kerning': 1,
  81. 'letter-spacing': 1,
  82. 'lighting-color': 1,
  83. 'marker': 1,
  84. 'marker-end': 1,
  85. 'marker-mid': 1,
  86. 'marker-start': 1,
  87. 'mask': 1,
  88. 'opacity': 1,
  89. 'overflow': 1,
  90. 'pointer-events': 1,
  91. 'shape-rendering': 1,
  92. 'stop-color': 1,
  93. 'stop-opacity': 1,
  94. 'stroke': 1,
  95. 'stroke-dasharray': 1,
  96. 'stroke-dashoffset': 1,
  97. 'stroke-linecap': 1,
  98. 'stroke-linejoin': 1,
  99. 'stroke-miterlimit': 1,
  100. 'stroke-opacity': 1,
  101. 'stroke-width': LENGTH_ATTR,
  102. 'text-anchor': 1,
  103. 'text-decoration': 1,
  104. 'text-rendering': 1,
  105. 'unicode-bidi': 1,
  106. 'visibility': 1,
  107. 'word-spacing': 1,
  108. 'writing-mode': 1
  109. };
  110. function getAttribute(node, name) {
  111. if (CSS_PROPERTIES[name]) {
  112. return node.style[name];
  113. } else {
  114. return node.getAttributeNS(null, name);
  115. }
  116. }
  117. function setAttribute(node, name, value) {
  118. var hyphenated = name.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
  119. var type = CSS_PROPERTIES[hyphenated];
  120. if (type) {
  121. // append pixel unit, unless present
  122. if (type === LENGTH_ATTR && typeof value === 'number') {
  123. value = String(value) + 'px';
  124. }
  125. node.style[hyphenated] = value;
  126. } else {
  127. node.setAttributeNS(null, name, value);
  128. }
  129. }
  130. function setAttributes(node, attrs) {
  131. var names = Object.keys(attrs), i, name;
  132. for (i = 0, name; (name = names[i]); i++) {
  133. setAttribute(node, name, attrs[name]);
  134. }
  135. }
  136. /**
  137. * Gets or sets raw attributes on a node.
  138. *
  139. * @param {SVGElement} node
  140. * @param {Object} [attrs]
  141. * @param {String} [name]
  142. * @param {String} [value]
  143. *
  144. * @return {String}
  145. */
  146. function attr(node, name, value) {
  147. if (typeof name === 'string') {
  148. if (value !== undefined) {
  149. setAttribute(node, name, value);
  150. } else {
  151. return getAttribute(node, name);
  152. }
  153. } else {
  154. setAttributes(node, name);
  155. }
  156. return node;
  157. }
  158. /**
  159. * Clear utility
  160. */
  161. function index(arr, obj) {
  162. if (arr.indexOf) {
  163. return arr.indexOf(obj);
  164. }
  165. for (var i = 0; i < arr.length; ++i) {
  166. if (arr[i] === obj) {
  167. return i;
  168. }
  169. }
  170. return -1;
  171. }
  172. var re = /\s+/;
  173. var toString = Object.prototype.toString;
  174. function defined(o) {
  175. return typeof o !== 'undefined';
  176. }
  177. /**
  178. * Wrap `el` in a `ClassList`.
  179. *
  180. * @param {Element} el
  181. * @return {ClassList}
  182. * @api public
  183. */
  184. function classes(el) {
  185. return new ClassList(el);
  186. }
  187. function ClassList(el) {
  188. if (!el || !el.nodeType) {
  189. throw new Error('A DOM element reference is required');
  190. }
  191. this.el = el;
  192. this.list = el.classList;
  193. }
  194. /**
  195. * Add class `name` if not already present.
  196. *
  197. * @param {String} name
  198. * @return {ClassList}
  199. * @api public
  200. */
  201. ClassList.prototype.add = function(name) {
  202. // classList
  203. if (this.list) {
  204. this.list.add(name);
  205. return this;
  206. }
  207. // fallback
  208. var arr = this.array();
  209. var i = index(arr, name);
  210. if (!~i) {
  211. arr.push(name);
  212. }
  213. if (defined(this.el.className.baseVal)) {
  214. this.el.className.baseVal = arr.join(' ');
  215. } else {
  216. this.el.className = arr.join(' ');
  217. }
  218. return this;
  219. };
  220. /**
  221. * Remove class `name` when present, or
  222. * pass a regular expression to remove
  223. * any which match.
  224. *
  225. * @param {String|RegExp} name
  226. * @return {ClassList}
  227. * @api public
  228. */
  229. ClassList.prototype.remove = function(name) {
  230. if ('[object RegExp]' === toString.call(name)) {
  231. return this.removeMatching(name);
  232. }
  233. // classList
  234. if (this.list) {
  235. this.list.remove(name);
  236. return this;
  237. }
  238. // fallback
  239. var arr = this.array();
  240. var i = index(arr, name);
  241. if (~i) {
  242. arr.splice(i, 1);
  243. }
  244. this.el.className.baseVal = arr.join(' ');
  245. return this;
  246. };
  247. /**
  248. * Remove all classes matching `re`.
  249. *
  250. * @param {RegExp} re
  251. * @return {ClassList}
  252. * @api private
  253. */
  254. ClassList.prototype.removeMatching = function(re) {
  255. var arr = this.array();
  256. for (var i = 0; i < arr.length; i++) {
  257. if (re.test(arr[i])) {
  258. this.remove(arr[i]);
  259. }
  260. }
  261. return this;
  262. };
  263. /**
  264. * Toggle class `name`, can force state via `force`.
  265. *
  266. * For browsers that support classList, but do not support `force` yet,
  267. * the mistake will be detected and corrected.
  268. *
  269. * @param {String} name
  270. * @param {Boolean} force
  271. * @return {ClassList}
  272. * @api public
  273. */
  274. ClassList.prototype.toggle = function(name, force) {
  275. // classList
  276. if (this.list) {
  277. if (defined(force)) {
  278. if (force !== this.list.toggle(name, force)) {
  279. this.list.toggle(name); // toggle again to correct
  280. }
  281. } else {
  282. this.list.toggle(name);
  283. }
  284. return this;
  285. }
  286. // fallback
  287. if (defined(force)) {
  288. if (!force) {
  289. this.remove(name);
  290. } else {
  291. this.add(name);
  292. }
  293. } else {
  294. if (this.has(name)) {
  295. this.remove(name);
  296. } else {
  297. this.add(name);
  298. }
  299. }
  300. return this;
  301. };
  302. /**
  303. * Return an array of classes.
  304. *
  305. * @return {Array}
  306. * @api public
  307. */
  308. ClassList.prototype.array = function() {
  309. var className = this.el.getAttribute('class') || '';
  310. var str = className.replace(/^\s+|\s+$/g, '');
  311. var arr = str.split(re);
  312. if ('' === arr[0]) {
  313. arr.shift();
  314. }
  315. return arr;
  316. };
  317. /**
  318. * Check if class `name` is present.
  319. *
  320. * @param {String} name
  321. * @return {ClassList}
  322. * @api public
  323. */
  324. ClassList.prototype.has =
  325. ClassList.prototype.contains = function(name) {
  326. return (
  327. this.list ?
  328. this.list.contains(name) :
  329. !! ~index(this.array(), name)
  330. );
  331. };
  332. function remove(element) {
  333. var parent = element.parentNode;
  334. if (parent) {
  335. parent.removeChild(element);
  336. }
  337. return element;
  338. }
  339. /**
  340. * Clear utility
  341. */
  342. /**
  343. * Removes all children from the given element
  344. *
  345. * @param {DOMElement} element
  346. * @return {DOMElement} the element (for chaining)
  347. */
  348. function clear(element) {
  349. var child;
  350. while ((child = element.firstChild)) {
  351. remove(child);
  352. }
  353. return element;
  354. }
  355. function clone(element) {
  356. return element.cloneNode(true);
  357. }
  358. var ns = {
  359. svg: 'http://www.w3.org/2000/svg'
  360. };
  361. /**
  362. * DOM parsing utility
  363. */
  364. var SVG_START = '<svg xmlns="' + ns.svg + '"';
  365. function parse(svg) {
  366. var unwrap = false;
  367. // ensure we import a valid svg document
  368. if (svg.substring(0, 4) === '<svg') {
  369. if (svg.indexOf(ns.svg) === -1) {
  370. svg = SVG_START + svg.substring(4);
  371. }
  372. } else {
  373. // namespace svg
  374. svg = SVG_START + '>' + svg + '</svg>';
  375. unwrap = true;
  376. }
  377. var parsed = parseDocument(svg);
  378. if (!unwrap) {
  379. return parsed;
  380. }
  381. var fragment = document.createDocumentFragment();
  382. var parent = parsed.firstChild;
  383. while (parent.firstChild) {
  384. fragment.appendChild(parent.firstChild);
  385. }
  386. return fragment;
  387. }
  388. function parseDocument(svg) {
  389. var parser;
  390. // parse
  391. parser = new DOMParser();
  392. parser.async = false;
  393. return parser.parseFromString(svg, 'text/xml');
  394. }
  395. /**
  396. * Create utility for SVG elements
  397. */
  398. /**
  399. * Create a specific type from name or SVG markup.
  400. *
  401. * @param {String} name the name or markup of the element
  402. * @param {Object} [attrs] attributes to set on the element
  403. *
  404. * @returns {SVGElement}
  405. */
  406. function create(name, attrs) {
  407. var element;
  408. if (name.charAt(0) === '<') {
  409. element = parse(name).firstChild;
  410. element = document.importNode(element, true);
  411. } else {
  412. element = document.createElementNS(ns.svg, name);
  413. }
  414. if (attrs) {
  415. attr(element, attrs);
  416. }
  417. return element;
  418. }
  419. /**
  420. * Events handling utility
  421. */
  422. function on(node, event, listener, useCapture) {
  423. node.addEventListener(event, listener, useCapture);
  424. }
  425. function off(node, event, listener, useCapture) {
  426. node.removeEventListener(event, listener, useCapture);
  427. }
  428. /**
  429. * Geometry helpers
  430. */
  431. // fake node used to instantiate svg geometry elements
  432. var node = create('svg');
  433. function extend(object, props) {
  434. var i, k, keys = Object.keys(props);
  435. for (i = 0; (k = keys[i]); i++) {
  436. object[k] = props[k];
  437. }
  438. return object;
  439. }
  440. function createPoint(x, y) {
  441. var point = node.createSVGPoint();
  442. switch (arguments.length) {
  443. case 0:
  444. return point;
  445. case 2:
  446. x = {
  447. x: x,
  448. y: y
  449. };
  450. break;
  451. }
  452. return extend(point, x);
  453. }
  454. /**
  455. * Create matrix via args.
  456. *
  457. * @example
  458. *
  459. * createMatrix({ a: 1, b: 1 });
  460. * createMatrix();
  461. * createMatrix(1, 2, 0, 0, 30, 20);
  462. *
  463. * @return {SVGMatrix}
  464. */
  465. function createMatrix(a, b, c, d, e, f) {
  466. var matrix = node.createSVGMatrix();
  467. switch (arguments.length) {
  468. case 0:
  469. return matrix;
  470. case 1:
  471. return extend(matrix, a);
  472. case 6:
  473. return extend(matrix, {
  474. a: a,
  475. b: b,
  476. c: c,
  477. d: d,
  478. e: e,
  479. f: f
  480. });
  481. }
  482. }
  483. function createTransform(matrix) {
  484. if (matrix) {
  485. return node.createSVGTransformFromMatrix(matrix);
  486. } else {
  487. return node.createSVGTransform();
  488. }
  489. }
  490. /**
  491. * Serialization util
  492. */
  493. var TEXT_ENTITIES = /([&<>]{1})/g;
  494. var ATTR_ENTITIES = /([\n\r"]{1})/g;
  495. var ENTITY_REPLACEMENT = {
  496. '&': '&amp;',
  497. '<': '&lt;',
  498. '>': '&gt;',
  499. '"': '\''
  500. };
  501. function escape(str, pattern) {
  502. function replaceFn(match, entity) {
  503. return ENTITY_REPLACEMENT[entity] || entity;
  504. }
  505. return str.replace(pattern, replaceFn);
  506. }
  507. function serialize(node, output) {
  508. var i, len, attrMap, attrNode, childNodes;
  509. switch (node.nodeType) {
  510. // TEXT
  511. case 3:
  512. // replace special XML characters
  513. output.push(escape(node.textContent, TEXT_ENTITIES));
  514. break;
  515. // ELEMENT
  516. case 1:
  517. output.push('<', node.tagName);
  518. if (node.hasAttributes()) {
  519. attrMap = node.attributes;
  520. for (i = 0, len = attrMap.length; i < len; ++i) {
  521. attrNode = attrMap.item(i);
  522. output.push(' ', attrNode.name, '="', escape(attrNode.value, ATTR_ENTITIES), '"');
  523. }
  524. }
  525. if (node.hasChildNodes()) {
  526. output.push('>');
  527. childNodes = node.childNodes;
  528. for (i = 0, len = childNodes.length; i < len; ++i) {
  529. serialize(childNodes.item(i), output);
  530. }
  531. output.push('</', node.tagName, '>');
  532. } else {
  533. output.push('/>');
  534. }
  535. break;
  536. // COMMENT
  537. case 8:
  538. output.push('<!--', escape(node.nodeValue, TEXT_ENTITIES), '-->');
  539. break;
  540. // CDATA
  541. case 4:
  542. output.push('<![CDATA[', node.nodeValue, ']]>');
  543. break;
  544. default:
  545. throw new Error('unable to handle node ' + node.nodeType);
  546. }
  547. return output;
  548. }
  549. /**
  550. * innerHTML like functionality for SVG elements.
  551. * based on innerSVG (https://code.google.com/p/innersvg)
  552. */
  553. function set(element, svg) {
  554. var parsed = parse(svg);
  555. // clear element contents
  556. clear(element);
  557. if (!svg) {
  558. return;
  559. }
  560. if (!isFragment(parsed)) {
  561. // extract <svg> from parsed document
  562. parsed = parsed.documentElement;
  563. }
  564. var nodes = slice(parsed.childNodes);
  565. // import + append each node
  566. for (var i = 0; i < nodes.length; i++) {
  567. appendTo(nodes[i], element);
  568. }
  569. }
  570. function get(element) {
  571. var child = element.firstChild,
  572. output = [];
  573. while (child) {
  574. serialize(child, output);
  575. child = child.nextSibling;
  576. }
  577. return output.join('');
  578. }
  579. function isFragment(node) {
  580. return node.nodeName === '#document-fragment';
  581. }
  582. function innerSVG(element, svg) {
  583. if (svg !== undefined) {
  584. try {
  585. set(element, svg);
  586. } catch (e) {
  587. throw new Error('error parsing SVG: ' + e.message);
  588. }
  589. return element;
  590. } else {
  591. return get(element);
  592. }
  593. }
  594. function slice(arr) {
  595. return Array.prototype.slice.call(arr);
  596. }
  597. /**
  598. * Selection utilities
  599. */
  600. function select(node, selector) {
  601. return node.querySelector(selector);
  602. }
  603. function selectAll(node, selector) {
  604. var nodes = node.querySelectorAll(selector);
  605. return [].map.call(nodes, function(element) {
  606. return element;
  607. });
  608. }
  609. /**
  610. * prependTo utility
  611. */
  612. /**
  613. * Prepend a node to a target element and return the prepended node.
  614. *
  615. * @param {SVGElement} node
  616. * @param {SVGElement} target
  617. *
  618. * @return {SVGElement} the prepended node
  619. */
  620. function prependTo(node, target) {
  621. return target.insertBefore(ensureImported(node, target), target.firstChild || null);
  622. }
  623. /**
  624. * prepend utility
  625. */
  626. /**
  627. * Prepend a node to a target element
  628. *
  629. * @param {SVGElement} target
  630. * @param {SVGElement} node
  631. *
  632. * @return {SVGElement} the target element
  633. */
  634. function prepend(target, node) {
  635. prependTo(node, target);
  636. return target;
  637. }
  638. /**
  639. * Replace utility
  640. */
  641. function replace(element, replacement) {
  642. element.parentNode.replaceChild(ensureImported(replacement, element), element);
  643. return replacement;
  644. }
  645. /**
  646. * transform accessor utility
  647. */
  648. function wrapMatrix(transformList, transform) {
  649. if (transform instanceof SVGMatrix) {
  650. return transformList.createSVGTransformFromMatrix(transform);
  651. }
  652. return transform;
  653. }
  654. function setTransforms(transformList, transforms) {
  655. var i, t;
  656. transformList.clear();
  657. for (i = 0; (t = transforms[i]); i++) {
  658. transformList.appendItem(wrapMatrix(transformList, t));
  659. }
  660. }
  661. /**
  662. * Get or set the transforms on the given node.
  663. *
  664. * @param {SVGElement} node
  665. * @param {SVGTransform|SVGMatrix|Array<SVGTransform|SVGMatrix>} [transforms]
  666. *
  667. * @return {SVGTransform} the consolidated transform
  668. */
  669. function transform(node, transforms) {
  670. var transformList = node.transform.baseVal;
  671. if (transforms) {
  672. if (!Array.isArray(transforms)) {
  673. transforms = [ transforms ];
  674. }
  675. setTransforms(transformList, transforms);
  676. }
  677. return transformList.consolidate();
  678. }
  679. exports.append = append;
  680. exports.appendTo = appendTo;
  681. exports.attr = attr;
  682. exports.classes = classes;
  683. exports.clear = clear;
  684. exports.clone = clone;
  685. exports.create = create;
  686. exports.innerSVG = innerSVG;
  687. exports.prepend = prepend;
  688. exports.prependTo = prependTo;
  689. exports.remove = remove;
  690. exports.replace = replace;
  691. exports.transform = transform;
  692. exports.on = on;
  693. exports.off = off;
  694. exports.createPoint = createPoint;
  695. exports.createMatrix = createMatrix;
  696. exports.createTransform = createTransform;
  697. exports.select = select;
  698. exports.selectAll = selectAll;