Version 1
[yaffs-website] / node_modules / video.js / es5 / utils / dom.js
1 'use strict';
2
3 exports.__esModule = true;
4 exports.$$ = exports.$ = undefined;
5
6 var _templateObject = _taggedTemplateLiteralLoose(['Setting attributes in the second argument of createEl()\n                has been deprecated. Use the third argument instead.\n                createEl(type, properties, attributes). Attempting to set ', ' to ', '.'], ['Setting attributes in the second argument of createEl()\n                has been deprecated. Use the third argument instead.\n                createEl(type, properties, attributes). Attempting to set ', ' to ', '.']);
7
8 exports.isReal = isReal;
9 exports.isEl = isEl;
10 exports.getEl = getEl;
11 exports.createEl = createEl;
12 exports.textContent = textContent;
13 exports.insertElFirst = insertElFirst;
14 exports.getElData = getElData;
15 exports.hasElData = hasElData;
16 exports.removeElData = removeElData;
17 exports.hasElClass = hasElClass;
18 exports.addElClass = addElClass;
19 exports.removeElClass = removeElClass;
20 exports.toggleElClass = toggleElClass;
21 exports.setElAttributes = setElAttributes;
22 exports.getElAttributes = getElAttributes;
23 exports.getAttribute = getAttribute;
24 exports.setAttribute = setAttribute;
25 exports.removeAttribute = removeAttribute;
26 exports.blockTextSelection = blockTextSelection;
27 exports.unblockTextSelection = unblockTextSelection;
28 exports.findElPosition = findElPosition;
29 exports.getPointerPosition = getPointerPosition;
30 exports.isTextNode = isTextNode;
31 exports.emptyEl = emptyEl;
32 exports.normalizeContent = normalizeContent;
33 exports.appendContent = appendContent;
34 exports.insertContent = insertContent;
35
36 var _document = require('global/document');
37
38 var _document2 = _interopRequireDefault(_document);
39
40 var _window = require('global/window');
41
42 var _window2 = _interopRequireDefault(_window);
43
44 var _guid = require('./guid.js');
45
46 var Guid = _interopRequireWildcard(_guid);
47
48 var _log = require('./log.js');
49
50 var _log2 = _interopRequireDefault(_log);
51
52 var _tsml = require('tsml');
53
54 var _tsml2 = _interopRequireDefault(_tsml);
55
56 var _obj = require('./obj');
57
58 function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj['default'] = obj; return newObj; } }
59
60 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
61
62 function _taggedTemplateLiteralLoose(strings, raw) { strings.raw = raw; return strings; } /**
63                                                                                            * @file dom.js
64                                                                                            * @module dom
65                                                                                            */
66
67
68 /**
69  * Detect if a value is a string with any non-whitespace characters.
70  *
71  * @param {string} str
72  *        The string to check
73  *
74  * @return {boolean}
75  *         - True if the string is non-blank
76  *         - False otherwise
77  *
78  */
79 function isNonBlankString(str) {
80   return typeof str === 'string' && /\S/.test(str);
81 }
82
83 /**
84  * Throws an error if the passed string has whitespace. This is used by
85  * class methods to be relatively consistent with the classList API.
86  *
87  * @param {string} str
88  *         The string to check for whitespace.
89  *
90  * @throws {Error}
91  *         Throws an error if there is whitespace in the string.
92  *
93  */
94 function throwIfWhitespace(str) {
95   if (/\s/.test(str)) {
96     throw new Error('class has illegal whitespace characters');
97   }
98 }
99
100 /**
101  * Produce a regular expression for matching a className within an elements className.
102  *
103  * @param {string} className
104  *         The className to generate the RegExp for.
105  *
106  * @return {RegExp}
107  *         The RegExp that will check for a specific `className` in an elements
108  *         className.
109  */
110 function classRegExp(className) {
111   return new RegExp('(^|\\s)' + className + '($|\\s)');
112 }
113
114 /**
115  * Whether the current DOM interface appears to be real.
116  *
117  * @return {Boolean}
118  */
119 function isReal() {
120   return (
121
122     // Both document and window will never be undefined thanks to `global`.
123     _document2['default'] === _window2['default'].document &&
124
125     // In IE < 9, DOM methods return "object" as their type, so all we can
126     // confidently check is that it exists.
127     typeof _document2['default'].createElement !== 'undefined'
128   );
129 }
130
131 /**
132  * Determines, via duck typing, whether or not a value is a DOM element.
133  *
134  * @param {Mixed} value
135  *        The thing to check
136  *
137  * @return {boolean}
138  *         - True if it is a DOM element
139  *         - False otherwise
140  */
141 function isEl(value) {
142   return (0, _obj.isObject)(value) && value.nodeType === 1;
143 }
144
145 /**
146  * Creates functions to query the DOM using a given method.
147  *
148  * @param {string} method
149  *         The method to create the query with.
150  *
151  * @return {Function}
152  *         The query method
153  */
154 function createQuerier(method) {
155   return function (selector, context) {
156     if (!isNonBlankString(selector)) {
157       return _document2['default'][method](null);
158     }
159     if (isNonBlankString(context)) {
160       context = _document2['default'].querySelector(context);
161     }
162
163     var ctx = isEl(context) ? context : _document2['default'];
164
165     return ctx[method] && ctx[method](selector);
166   };
167 }
168
169 /**
170  * Shorthand for document.getElementById()
171  * Also allows for CSS (jQuery) ID syntax. But nothing other than IDs.
172  *
173  * @param {string} id
174  *         The id of the element to get
175  *
176  * @return {Element|null}
177  *         Element with supplied ID or null if there wasn't one.
178  */
179 function getEl(id) {
180   if (id.indexOf('#') === 0) {
181     id = id.slice(1);
182   }
183
184   return _document2['default'].getElementById(id);
185 }
186
187 /**
188  * Creates an element and applies properties.
189  *
190  * @param {string} [tagName='div']
191  *         Name of tag to be created.
192  *
193  * @param {Object} [properties={}]
194  *         Element properties to be applied.
195  *
196  * @param {Object} [attributes={}]
197  *         Element attributes to be applied.
198  *
199  * @param {String|Element|TextNode|Array|Function} [content]
200  *         Contents for the element (see: {@link dom:normalizeContent})
201  *
202  * @return {Element}
203  *         The element that was created.
204  */
205 function createEl() {
206   var tagName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'div';
207   var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
208   var attributes = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
209   var content = arguments[3];
210
211   var el = _document2['default'].createElement(tagName);
212
213   Object.getOwnPropertyNames(properties).forEach(function (propName) {
214     var val = properties[propName];
215
216     // See #2176
217     // We originally were accepting both properties and attributes in the
218     // same object, but that doesn't work so well.
219     if (propName.indexOf('aria-') !== -1 || propName === 'role' || propName === 'type') {
220       _log2['default'].warn((0, _tsml2['default'])(_templateObject, propName, val));
221       el.setAttribute(propName, val);
222
223       // Handle textContent since it's not supported everywhere and we have a
224       // method for it.
225     } else if (propName === 'textContent') {
226       textContent(el, val);
227     } else {
228       el[propName] = val;
229     }
230   });
231
232   Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
233     el.setAttribute(attrName, attributes[attrName]);
234   });
235
236   if (content) {
237     appendContent(el, content);
238   }
239
240   return el;
241 }
242
243 /**
244  * Injects text into an element, replacing any existing contents entirely.
245  *
246  * @param {Element} el
247  *        The element to add text content into
248  *
249  * @param {string} text
250  *        The text content to add.
251  *
252  * @return {Element}
253  *         The element with added text content.
254  */
255 function textContent(el, text) {
256   if (typeof el.textContent === 'undefined') {
257     el.innerText = text;
258   } else {
259     el.textContent = text;
260   }
261   return el;
262 }
263
264 /**
265  * Insert an element as the first child node of another
266  *
267  * @param {Element} child
268  *        Element to insert
269  *
270  * @param {Element} parent
271  *        Element to insert child into
272  *
273  */
274 function insertElFirst(child, parent) {
275   if (parent.firstChild) {
276     parent.insertBefore(child, parent.firstChild);
277   } else {
278     parent.appendChild(child);
279   }
280 }
281
282 /**
283  * Element Data Store. Allows for binding data to an element without putting it directly on the element.
284  * Ex. Event listeners are stored here.
285  * (also from jsninja.com, slightly modified and updated for closure compiler)
286  *
287  * @type {Object}
288  * @private
289  */
290 var elData = {};
291
292 /*
293  * Unique attribute name to store an element's guid in
294  *
295  * @type {string}
296  * @constant
297  * @private
298  */
299 var elIdAttr = 'vdata' + new Date().getTime();
300
301 /**
302  * Returns the cache object where data for an element is stored
303  *
304  * @param {Element} el
305  *        Element to store data for.
306  *
307  * @return {Object}
308  *         The cache object for that el that was passed in.
309  */
310 function getElData(el) {
311   var id = el[elIdAttr];
312
313   if (!id) {
314     id = el[elIdAttr] = Guid.newGUID();
315   }
316
317   if (!elData[id]) {
318     elData[id] = {};
319   }
320
321   return elData[id];
322 }
323
324 /**
325  * Returns whether or not an element has cached data
326  *
327  * @param {Element} el
328  *        Check if this element has cached data.
329  *
330  * @return {boolean}
331  *         - True if the DOM element has cached data.
332  *         - False otherwise.
333  */
334 function hasElData(el) {
335   var id = el[elIdAttr];
336
337   if (!id) {
338     return false;
339   }
340
341   return !!Object.getOwnPropertyNames(elData[id]).length;
342 }
343
344 /**
345  * Delete data for the element from the cache and the guid attr from getElementById
346  *
347  * @param {Element} el
348  *        Remove cached data for this element.
349  */
350 function removeElData(el) {
351   var id = el[elIdAttr];
352
353   if (!id) {
354     return;
355   }
356
357   // Remove all stored data
358   delete elData[id];
359
360   // Remove the elIdAttr property from the DOM node
361   try {
362     delete el[elIdAttr];
363   } catch (e) {
364     if (el.removeAttribute) {
365       el.removeAttribute(elIdAttr);
366     } else {
367       // IE doesn't appear to support removeAttribute on the document element
368       el[elIdAttr] = null;
369     }
370   }
371 }
372
373 /**
374  * Check if an element has a CSS class
375  *
376  * @param {Element} element
377  *        Element to check
378  *
379  * @param {string} classToCheck
380  *        Class name to check for
381  *
382  * @return {boolean}
383  *         - True if the element had the class
384  *         - False otherwise.
385  *
386  * @throws {Error}
387  *         Throws an error if `classToCheck` has white space.
388  */
389 function hasElClass(element, classToCheck) {
390   throwIfWhitespace(classToCheck);
391   if (element.classList) {
392     return element.classList.contains(classToCheck);
393   }
394   return classRegExp(classToCheck).test(element.className);
395 }
396
397 /**
398  * Add a CSS class name to an element
399  *
400  * @param {Element} element
401  *        Element to add class name to.
402  *
403  * @param {string} classToAdd
404  *        Class name to add.
405  *
406  * @return {Element}
407  *         The dom element with the added class name.
408  */
409 function addElClass(element, classToAdd) {
410   if (element.classList) {
411     element.classList.add(classToAdd);
412
413     // Don't need to `throwIfWhitespace` here because `hasElClass` will do it
414     // in the case of classList not being supported.
415   } else if (!hasElClass(element, classToAdd)) {
416     element.className = (element.className + ' ' + classToAdd).trim();
417   }
418
419   return element;
420 }
421
422 /**
423  * Remove a CSS class name from an element
424  *
425  * @param {Element} element
426  *        Element to remove a class name from.
427  *
428  * @param {string} classToRemove
429  *        Class name to remove
430  *
431  * @return {Element}
432  *         The dom element with class name removed.
433  */
434 function removeElClass(element, classToRemove) {
435   if (element.classList) {
436     element.classList.remove(classToRemove);
437   } else {
438     throwIfWhitespace(classToRemove);
439     element.className = element.className.split(/\s+/).filter(function (c) {
440       return c !== classToRemove;
441     }).join(' ');
442   }
443
444   return element;
445 }
446
447 /**
448  * The callback definition for toggleElClass.
449  *
450  * @callback Dom~PredicateCallback
451  * @param {Element} element
452  *        The DOM element of the Component.
453  *
454  * @param {string} classToToggle
455  *        The `className` that wants to be toggled
456  *
457  * @return {boolean|undefined}
458  *         - If true the `classToToggle` will get added to `element`.
459  *         - If false the `classToToggle` will get removed from `element`.
460  *         - If undefined this callback will be ignored
461  */
462
463 /**
464  * Adds or removes a CSS class name on an element depending on an optional
465  * condition or the presence/absence of the class name.
466  *
467  * @param {Element} element
468  *        The element to toggle a class name on.
469  *
470  * @param {string} classToToggle
471  *        The class that should be toggled
472  *
473  * @param {boolean|PredicateCallback} [predicate]
474  *        See the return value for {@link Dom~PredicateCallback}
475  *
476  * @return {Element}
477  *         The element with a class that has been toggled.
478  */
479 function toggleElClass(element, classToToggle, predicate) {
480
481   // This CANNOT use `classList` internally because IE does not support the
482   // second parameter to the `classList.toggle()` method! Which is fine because
483   // `classList` will be used by the add/remove functions.
484   var has = hasElClass(element, classToToggle);
485
486   if (typeof predicate === 'function') {
487     predicate = predicate(element, classToToggle);
488   }
489
490   if (typeof predicate !== 'boolean') {
491     predicate = !has;
492   }
493
494   // If the necessary class operation matches the current state of the
495   // element, no action is required.
496   if (predicate === has) {
497     return;
498   }
499
500   if (predicate) {
501     addElClass(element, classToToggle);
502   } else {
503     removeElClass(element, classToToggle);
504   }
505
506   return element;
507 }
508
509 /**
510  * Apply attributes to an HTML element.
511  *
512  * @param {Element} el
513  *        Element to add attributes to.
514  *
515  * @param {Object} [attributes]
516  *        Attributes to be applied.
517  */
518 function setElAttributes(el, attributes) {
519   Object.getOwnPropertyNames(attributes).forEach(function (attrName) {
520     var attrValue = attributes[attrName];
521
522     if (attrValue === null || typeof attrValue === 'undefined' || attrValue === false) {
523       el.removeAttribute(attrName);
524     } else {
525       el.setAttribute(attrName, attrValue === true ? '' : attrValue);
526     }
527   });
528 }
529
530 /**
531  * Get an element's attribute values, as defined on the HTML tag
532  * Attributes are not the same as properties. They're defined on the tag
533  * or with setAttribute (which shouldn't be used with HTML)
534  * This will return true or false for boolean attributes.
535  *
536  * @param {Element} tag
537  *        Element from which to get tag attributes.
538  *
539  * @return {Object}
540  *         All attributes of the element.
541  */
542 function getElAttributes(tag) {
543   var obj = {};
544
545   // known boolean attributes
546   // we can check for matching boolean properties, but older browsers
547   // won't know about HTML5 boolean attributes that we still read from
548   var knownBooleans = ',' + 'autoplay,controls,loop,muted,default' + ',';
549
550   if (tag && tag.attributes && tag.attributes.length > 0) {
551     var attrs = tag.attributes;
552
553     for (var i = attrs.length - 1; i >= 0; i--) {
554       var attrName = attrs[i].name;
555       var attrVal = attrs[i].value;
556
557       // check for known booleans
558       // the matching element property will return a value for typeof
559       if (typeof tag[attrName] === 'boolean' || knownBooleans.indexOf(',' + attrName + ',') !== -1) {
560         // the value of an included boolean attribute is typically an empty
561         // string ('') which would equal false if we just check for a false value.
562         // we also don't want support bad code like autoplay='false'
563         attrVal = attrVal !== null ? true : false;
564       }
565
566       obj[attrName] = attrVal;
567     }
568   }
569
570   return obj;
571 }
572
573 /**
574  * Get the value of an element's attribute
575  *
576  * @param {Element} el
577  *        A DOM element
578  *
579  * @param {string} attribute
580  *        Attribute to get the value of
581  *
582  * @return {string}
583  *         value of the attribute
584  */
585 function getAttribute(el, attribute) {
586   return el.getAttribute(attribute);
587 }
588
589 /**
590  * Set the value of an element's attribute
591  *
592  * @param {Element} el
593  *        A DOM element
594  *
595  * @param {string} attribute
596  *        Attribute to set
597  *
598  * @param {string} value
599  *        Value to set the attribute to
600  */
601 function setAttribute(el, attribute, value) {
602   el.setAttribute(attribute, value);
603 }
604
605 /**
606  * Remove an element's attribute
607  *
608  * @param {Element} el
609  *        A DOM element
610  *
611  * @param {string} attribute
612  *        Attribute to remove
613  */
614 function removeAttribute(el, attribute) {
615   el.removeAttribute(attribute);
616 }
617
618 /**
619  * Attempt to block the ability to select text while dragging controls
620  */
621 function blockTextSelection() {
622   _document2['default'].body.focus();
623   _document2['default'].onselectstart = function () {
624     return false;
625   };
626 }
627
628 /**
629  * Turn off text selection blocking
630  */
631 function unblockTextSelection() {
632   _document2['default'].onselectstart = function () {
633     return true;
634   };
635 }
636
637 /**
638  * The postion of a DOM element on the page.
639  *
640  * @typedef {Object} Dom~Position
641  *
642  * @property {number} left
643  *           Pixels to the left
644  *
645  * @property {number} top
646  *           Pixels on top
647  */
648
649 /**
650  * Offset Left.
651  * getBoundingClientRect technique from
652  * John Resig
653  *
654  * @see http://ejohn.org/blog/getboundingclientrect-is-awesome/
655  *
656  * @param {Element} el
657  *        Element from which to get offset
658  *
659  * @return {Dom~Position}
660  *         The position of the element that was passed in.
661  */
662 function findElPosition(el) {
663   var box = void 0;
664
665   if (el.getBoundingClientRect && el.parentNode) {
666     box = el.getBoundingClientRect();
667   }
668
669   if (!box) {
670     return {
671       left: 0,
672       top: 0
673     };
674   }
675
676   var docEl = _document2['default'].documentElement;
677   var body = _document2['default'].body;
678
679   var clientLeft = docEl.clientLeft || body.clientLeft || 0;
680   var scrollLeft = _window2['default'].pageXOffset || body.scrollLeft;
681   var left = box.left + scrollLeft - clientLeft;
682
683   var clientTop = docEl.clientTop || body.clientTop || 0;
684   var scrollTop = _window2['default'].pageYOffset || body.scrollTop;
685   var top = box.top + scrollTop - clientTop;
686
687   // Android sometimes returns slightly off decimal values, so need to round
688   return {
689     left: Math.round(left),
690     top: Math.round(top)
691   };
692 }
693
694 /**
695  * x and y coordinates for a dom element or mouse pointer
696  *
697  * @typedef {Object} Dom~Coordinates
698  *
699  * @property {number} x
700  *           x coordinate in pixels
701  *
702  * @property {number} y
703  *           y coordinate in pixels
704  */
705
706 /**
707  * Get pointer position in element
708  * Returns an object with x and y coordinates.
709  * The base on the coordinates are the bottom left of the element.
710  *
711  * @param {Element} el
712  *        Element on which to get the pointer position on
713  *
714  * @param {EventTarget~Event} event
715  *        Event object
716  *
717  * @return {Dom~Coordinates}
718  *         A Coordinates object corresponding to the mouse position.
719  *
720  */
721 function getPointerPosition(el, event) {
722   var position = {};
723   var box = findElPosition(el);
724   var boxW = el.offsetWidth;
725   var boxH = el.offsetHeight;
726
727   var boxY = box.top;
728   var boxX = box.left;
729   var pageY = event.pageY;
730   var pageX = event.pageX;
731
732   if (event.changedTouches) {
733     pageX = event.changedTouches[0].pageX;
734     pageY = event.changedTouches[0].pageY;
735   }
736
737   position.y = Math.max(0, Math.min(1, (boxY - pageY + boxH) / boxH));
738   position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
739
740   return position;
741 }
742
743 /**
744  * Determines, via duck typing, whether or not a value is a text node.
745  *
746  * @param {Mixed} value
747  *        Check if this value is a text node.
748  *
749  * @return {boolean}
750  *         - True if it is a text node
751  *         - False otherwise
752  */
753 function isTextNode(value) {
754   return (0, _obj.isObject)(value) && value.nodeType === 3;
755 }
756
757 /**
758  * Empties the contents of an element.
759  *
760  * @param {Element} el
761  *        The element to empty children from
762  *
763  * @return {Element}
764  *         The element with no children
765  */
766 function emptyEl(el) {
767   while (el.firstChild) {
768     el.removeChild(el.firstChild);
769   }
770   return el;
771 }
772
773 /**
774  * Normalizes content for eventual insertion into the DOM.
775  *
776  * This allows a wide range of content definition methods, but protects
777  * from falling into the trap of simply writing to `innerHTML`, which is
778  * an XSS concern.
779  *
780  * The content for an element can be passed in multiple types and
781  * combinations, whose behavior is as follows:
782  *
783  * @param {String|Element|TextNode|Array|Function} content
784  *        - String: Normalized into a text node.
785  *        - Element/TextNode: Passed through.
786  *        - Array: A one-dimensional array of strings, elements, nodes, or functions
787  *          (which return single strings, elements, or nodes).
788  *        - Function: If the sole argument, is expected to produce a string, element,
789  *          node, or array as defined above.
790  *
791  * @return {Array}
792  *         All of the content that was passed in normalized.
793  */
794 function normalizeContent(content) {
795
796   // First, invoke content if it is a function. If it produces an array,
797   // that needs to happen before normalization.
798   if (typeof content === 'function') {
799     content = content();
800   }
801
802   // Next up, normalize to an array, so one or many items can be normalized,
803   // filtered, and returned.
804   return (Array.isArray(content) ? content : [content]).map(function (value) {
805
806     // First, invoke value if it is a function to produce a new value,
807     // which will be subsequently normalized to a Node of some kind.
808     if (typeof value === 'function') {
809       value = value();
810     }
811
812     if (isEl(value) || isTextNode(value)) {
813       return value;
814     }
815
816     if (typeof value === 'string' && /\S/.test(value)) {
817       return _document2['default'].createTextNode(value);
818     }
819   }).filter(function (value) {
820     return value;
821   });
822 }
823
824 /**
825  * Normalizes and appends content to an element.
826  *
827  * @param {Element} el
828  *        Element to append normalized content to.
829  *
830  *
831  * @param {String|Element|TextNode|Array|Function} content
832  *        See the `content` argument of {@link dom:normalizeContent}
833  *
834  * @return {Element}
835  *         The element with appended normalized content.
836  */
837 function appendContent(el, content) {
838   normalizeContent(content).forEach(function (node) {
839     return el.appendChild(node);
840   });
841   return el;
842 }
843
844 /**
845  * Normalizes and inserts content into an element; this is identical to
846  * `appendContent()`, except it empties the element first.
847  *
848  * @param {Element} el
849  *        Element to insert normalized content into.
850  *
851  * @param {String|Element|TextNode|Array|Function} content
852  *        See the `content` argument of {@link dom:normalizeContent}
853  *
854  * @return {Element}
855  *         The element with inserted normalized content.
856  *
857  */
858 function insertContent(el, content) {
859   return appendContent(emptyEl(el), content);
860 }
861
862 /**
863  * Finds a single DOM element matching `selector` within the optional
864  * `context` of another DOM element (defaulting to `document`).
865  *
866  * @param {string} selector
867  *        A valid CSS selector, which will be passed to `querySelector`.
868  *
869  * @param {Element|String} [context=document]
870  *        A DOM element within which to query. Can also be a selector
871  *        string in which case the first matching element will be used
872  *        as context. If missing (or no element matches selector), falls
873  *        back to `document`.
874  *
875  * @return {Element|null}
876  *         The element that was found or null.
877  */
878 var $ = exports.$ = createQuerier('querySelector');
879
880 /**
881  * Finds a all DOM elements matching `selector` within the optional
882  * `context` of another DOM element (defaulting to `document`).
883  *
884  * @param {string} selector
885  *           A valid CSS selector, which will be passed to `querySelectorAll`.
886  *
887  * @param {Element|String} [context=document]
888  *           A DOM element within which to query. Can also be a selector
889  *           string in which case the first matching element will be used
890  *           as context. If missing (or no element matches selector), falls
891  *           back to `document`.
892  *
893  * @return {NodeList}
894  *         A element list of elements that were found. Will be empty if none were found.
895  *
896  */
897 var $$ = exports.$$ = createQuerier('querySelectorAll');