min-dom.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (factory((global.MinDom = {})));
  5. }(this, (function (exports) { 'use strict';
  6. /**
  7. * Set attribute `name` to `val`, or get attr `name`.
  8. *
  9. * @param {Element} el
  10. * @param {String} name
  11. * @param {String} [val]
  12. * @api public
  13. */
  14. function attr(el, name, val) {
  15. // get
  16. if (arguments.length == 2) {
  17. return el.getAttribute(name);
  18. }
  19. // remove
  20. if (val === null) {
  21. return el.removeAttribute(name);
  22. }
  23. // set
  24. el.setAttribute(name, val);
  25. return el;
  26. }
  27. var indexOf = [].indexOf;
  28. var indexof = function(arr, obj){
  29. if (indexOf) return arr.indexOf(obj);
  30. for (var i = 0; i < arr.length; ++i) {
  31. if (arr[i] === obj) return i;
  32. }
  33. return -1;
  34. };
  35. /**
  36. * Taken from https://github.com/component/classes
  37. *
  38. * Without the component bits.
  39. */
  40. /**
  41. * Whitespace regexp.
  42. */
  43. var re = /\s+/;
  44. /**
  45. * toString reference.
  46. */
  47. var toString = Object.prototype.toString;
  48. /**
  49. * Wrap `el` in a `ClassList`.
  50. *
  51. * @param {Element} el
  52. * @return {ClassList}
  53. * @api public
  54. */
  55. function classes(el) {
  56. return new ClassList(el);
  57. }
  58. /**
  59. * Initialize a new ClassList for `el`.
  60. *
  61. * @param {Element} el
  62. * @api private
  63. */
  64. function ClassList(el) {
  65. if (!el || !el.nodeType) {
  66. throw new Error('A DOM element reference is required');
  67. }
  68. this.el = el;
  69. this.list = el.classList;
  70. }
  71. /**
  72. * Add class `name` if not already present.
  73. *
  74. * @param {String} name
  75. * @return {ClassList}
  76. * @api public
  77. */
  78. ClassList.prototype.add = function (name) {
  79. // classList
  80. if (this.list) {
  81. this.list.add(name);
  82. return this;
  83. }
  84. // fallback
  85. var arr = this.array();
  86. var i = indexof(arr, name);
  87. if (!~i) arr.push(name);
  88. this.el.className = arr.join(' ');
  89. return this;
  90. };
  91. /**
  92. * Remove class `name` when present, or
  93. * pass a regular expression to remove
  94. * any which match.
  95. *
  96. * @param {String|RegExp} name
  97. * @return {ClassList}
  98. * @api public
  99. */
  100. ClassList.prototype.remove = function (name) {
  101. if ('[object RegExp]' == toString.call(name)) {
  102. return this.removeMatching(name);
  103. }
  104. // classList
  105. if (this.list) {
  106. this.list.remove(name);
  107. return this;
  108. }
  109. // fallback
  110. var arr = this.array();
  111. var i = indexof(arr, name);
  112. if (~i) arr.splice(i, 1);
  113. this.el.className = arr.join(' ');
  114. return this;
  115. };
  116. /**
  117. * Remove all classes matching `re`.
  118. *
  119. * @param {RegExp} re
  120. * @return {ClassList}
  121. * @api private
  122. */
  123. ClassList.prototype.removeMatching = function (re) {
  124. var arr = this.array();
  125. for (var i = 0; i < arr.length; i++) {
  126. if (re.test(arr[i])) {
  127. this.remove(arr[i]);
  128. }
  129. }
  130. return this;
  131. };
  132. /**
  133. * Toggle class `name`, can force state via `force`.
  134. *
  135. * For browsers that support classList, but do not support `force` yet,
  136. * the mistake will be detected and corrected.
  137. *
  138. * @param {String} name
  139. * @param {Boolean} force
  140. * @return {ClassList}
  141. * @api public
  142. */
  143. ClassList.prototype.toggle = function (name, force) {
  144. // classList
  145. if (this.list) {
  146. if ('undefined' !== typeof force) {
  147. if (force !== this.list.toggle(name, force)) {
  148. this.list.toggle(name); // toggle again to correct
  149. }
  150. } else {
  151. this.list.toggle(name);
  152. }
  153. return this;
  154. }
  155. // fallback
  156. if ('undefined' !== typeof force) {
  157. if (!force) {
  158. this.remove(name);
  159. } else {
  160. this.add(name);
  161. }
  162. } else {
  163. if (this.has(name)) {
  164. this.remove(name);
  165. } else {
  166. this.add(name);
  167. }
  168. }
  169. return this;
  170. };
  171. /**
  172. * Return an array of classes.
  173. *
  174. * @return {Array}
  175. * @api public
  176. */
  177. ClassList.prototype.array = function () {
  178. var className = this.el.getAttribute('class') || '';
  179. var str = className.replace(/^\s+|\s+$/g, '');
  180. var arr = str.split(re);
  181. if ('' === arr[0]) arr.shift();
  182. return arr;
  183. };
  184. /**
  185. * Check if class `name` is present.
  186. *
  187. * @param {String} name
  188. * @return {ClassList}
  189. * @api public
  190. */
  191. ClassList.prototype.has = ClassList.prototype.contains = function (name) {
  192. return this.list ? this.list.contains(name) : !!~indexof(this.array(), name);
  193. };
  194. /**
  195. * Remove all children from the given element.
  196. */
  197. function clear(el) {
  198. var c;
  199. while (el.childNodes.length) {
  200. c = el.childNodes[0];
  201. el.removeChild(c);
  202. }
  203. return el;
  204. }
  205. var proto = typeof Element !== 'undefined' ? Element.prototype : {};
  206. var vendor = proto.matches
  207. || proto.matchesSelector
  208. || proto.webkitMatchesSelector
  209. || proto.mozMatchesSelector
  210. || proto.msMatchesSelector
  211. || proto.oMatchesSelector;
  212. var matchesSelector = match;
  213. /**
  214. * Match `el` to `selector`.
  215. *
  216. * @param {Element} el
  217. * @param {String} selector
  218. * @return {Boolean}
  219. * @api public
  220. */
  221. function match(el, selector) {
  222. if (!el || el.nodeType !== 1) return false;
  223. if (vendor) return vendor.call(el, selector);
  224. var nodes = el.parentNode.querySelectorAll(selector);
  225. for (var i = 0; i < nodes.length; i++) {
  226. if (nodes[i] == el) return true;
  227. }
  228. return false;
  229. }
  230. /**
  231. * Closest
  232. *
  233. * @param {Element} el
  234. * @param {String} selector
  235. * @param {Boolean} checkYourSelf (optional)
  236. */
  237. function closest (element, selector, checkYourSelf) {
  238. var currentElem = checkYourSelf ? element : element.parentNode;
  239. while (currentElem && currentElem.nodeType !== document.DOCUMENT_NODE && currentElem.nodeType !== document.DOCUMENT_FRAGMENT_NODE) {
  240. if (matchesSelector(currentElem, selector)) {
  241. return currentElem;
  242. }
  243. currentElem = currentElem.parentNode;
  244. }
  245. return matchesSelector(currentElem, selector) ? currentElem : null;
  246. }
  247. var bind = window.addEventListener ? 'addEventListener' : 'attachEvent',
  248. unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent',
  249. prefix = bind !== 'addEventListener' ? 'on' : '';
  250. /**
  251. * Bind `el` event `type` to `fn`.
  252. *
  253. * @param {Element} el
  254. * @param {String} type
  255. * @param {Function} fn
  256. * @param {Boolean} capture
  257. * @return {Function}
  258. * @api public
  259. */
  260. var bind_1 = function(el, type, fn, capture){
  261. el[bind](prefix + type, fn, capture || false);
  262. return fn;
  263. };
  264. /**
  265. * Unbind `el` event `type`'s callback `fn`.
  266. *
  267. * @param {Element} el
  268. * @param {String} type
  269. * @param {Function} fn
  270. * @param {Boolean} capture
  271. * @return {Function}
  272. * @api public
  273. */
  274. var unbind_1 = function(el, type, fn, capture){
  275. el[unbind](prefix + type, fn, capture || false);
  276. return fn;
  277. };
  278. var componentEvent = {
  279. bind: bind_1,
  280. unbind: unbind_1
  281. };
  282. /**
  283. * Module dependencies.
  284. */
  285. /**
  286. * Delegate event `type` to `selector`
  287. * and invoke `fn(e)`. A callback function
  288. * is returned which may be passed to `.unbind()`.
  289. *
  290. * @param {Element} el
  291. * @param {String} selector
  292. * @param {String} type
  293. * @param {Function} fn
  294. * @param {Boolean} capture
  295. * @return {Function}
  296. * @api public
  297. */
  298. // Some events don't bubble, so we want to bind to the capture phase instead
  299. // when delegating.
  300. var forceCaptureEvents = ['focus', 'blur'];
  301. function bind$1(el, selector, type, fn, capture) {
  302. if (forceCaptureEvents.indexOf(type) !== -1) {
  303. capture = true;
  304. }
  305. return componentEvent.bind(el, type, function (e) {
  306. var target = e.target || e.srcElement;
  307. e.delegateTarget = closest(target, selector, true, el);
  308. if (e.delegateTarget) {
  309. fn.call(el, e);
  310. }
  311. }, capture);
  312. }
  313. /**
  314. * Unbind event `type`'s callback `fn`.
  315. *
  316. * @param {Element} el
  317. * @param {String} type
  318. * @param {Function} fn
  319. * @param {Boolean} capture
  320. * @api public
  321. */
  322. function unbind$1(el, type, fn, capture) {
  323. if (forceCaptureEvents.indexOf(type) !== -1) {
  324. capture = true;
  325. }
  326. return componentEvent.unbind(el, type, fn, capture);
  327. }
  328. var delegate = {
  329. bind: bind$1,
  330. unbind: unbind$1
  331. };
  332. /**
  333. * Expose `parse`.
  334. */
  335. var domify = parse;
  336. /**
  337. * Tests for browser support.
  338. */
  339. var innerHTMLBug = false;
  340. var bugTestDiv;
  341. if (typeof document !== 'undefined') {
  342. bugTestDiv = document.createElement('div');
  343. // Setup
  344. bugTestDiv.innerHTML = ' <link/><table></table><a href="/a">a</a><input type="checkbox"/>';
  345. // Make sure that link elements get serialized correctly by innerHTML
  346. // This requires a wrapper element in IE
  347. innerHTMLBug = !bugTestDiv.getElementsByTagName('link').length;
  348. bugTestDiv = undefined;
  349. }
  350. /**
  351. * Wrap map from jquery.
  352. */
  353. var map = {
  354. legend: [1, '<fieldset>', '</fieldset>'],
  355. tr: [2, '<table><tbody>', '</tbody></table>'],
  356. col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
  357. // for script/link/style tags to work in IE6-8, you have to wrap
  358. // in a div with a non-whitespace character in front, ha!
  359. _default: innerHTMLBug ? [1, 'X<div>', '</div>'] : [0, '', '']
  360. };
  361. map.td =
  362. map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'];
  363. map.option =
  364. map.optgroup = [1, '<select multiple="multiple">', '</select>'];
  365. map.thead =
  366. map.tbody =
  367. map.colgroup =
  368. map.caption =
  369. map.tfoot = [1, '<table>', '</table>'];
  370. map.polyline =
  371. map.ellipse =
  372. map.polygon =
  373. map.circle =
  374. map.text =
  375. map.line =
  376. map.path =
  377. map.rect =
  378. map.g = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'];
  379. /**
  380. * Parse `html` and return a DOM Node instance, which could be a TextNode,
  381. * HTML DOM Node of some kind (<div> for example), or a DocumentFragment
  382. * instance, depending on the contents of the `html` string.
  383. *
  384. * @param {String} html - HTML string to "domify"
  385. * @param {Document} doc - The `document` instance to create the Node for
  386. * @return {DOMNode} the TextNode, DOM Node, or DocumentFragment instance
  387. * @api private
  388. */
  389. function parse(html, doc) {
  390. if ('string' != typeof html) throw new TypeError('String expected');
  391. // default to the global `document` object
  392. if (!doc) doc = document;
  393. // tag name
  394. var m = /<([\w:]+)/.exec(html);
  395. if (!m) return doc.createTextNode(html);
  396. html = html.replace(/^\s+|\s+$/g, ''); // Remove leading/trailing whitespace
  397. var tag = m[1];
  398. // body support
  399. if (tag == 'body') {
  400. var el = doc.createElement('html');
  401. el.innerHTML = html;
  402. return el.removeChild(el.lastChild);
  403. }
  404. // wrap map
  405. var wrap = map[tag] || map._default;
  406. var depth = wrap[0];
  407. var prefix = wrap[1];
  408. var suffix = wrap[2];
  409. var el = doc.createElement('div');
  410. el.innerHTML = prefix + html + suffix;
  411. while (depth--) el = el.lastChild;
  412. // one element
  413. if (el.firstChild == el.lastChild) {
  414. return el.removeChild(el.firstChild);
  415. }
  416. // several elements
  417. var fragment = doc.createDocumentFragment();
  418. while (el.firstChild) {
  419. fragment.appendChild(el.removeChild(el.firstChild));
  420. }
  421. return fragment;
  422. }
  423. function query(selector, el) {
  424. el = el || document;
  425. return el.querySelector(selector);
  426. }
  427. function all(selector, el) {
  428. el = el || document;
  429. return el.querySelectorAll(selector);
  430. }
  431. function remove(el) {
  432. el.parentNode && el.parentNode.removeChild(el);
  433. }
  434. exports.attr = attr;
  435. exports.classes = classes;
  436. exports.clear = clear;
  437. exports.closest = closest;
  438. exports.delegate = delegate;
  439. exports.domify = domify;
  440. exports.event = componentEvent;
  441. exports.matches = matchesSelector;
  442. exports.query = query;
  443. exports.queryAll = all;
  444. exports.remove = remove;
  445. Object.defineProperty(exports, '__esModule', { value: true });
  446. })));