[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/selector-native/ -> selector-native.js (source)

   1  /*
   2  YUI 3.17.2 (build 9c3c78e)
   3  Copyright 2014 Yahoo! Inc. All rights reserved.
   4  Licensed under the BSD License.
   5  http://yuilibrary.com/license/
   6  */
   7  
   8  YUI.add('selector-native', function (Y, NAME) {
   9  
  10  (function(Y) {
  11  /**
  12   * The selector-native module provides support for native querySelector
  13   * @module dom
  14   * @submodule selector-native
  15   * @for Selector
  16   */
  17  
  18  /**
  19   * Provides support for using CSS selectors to query the DOM
  20   * @class Selector
  21   * @static
  22   * @for Selector
  23   */
  24  
  25  Y.namespace('Selector'); // allow native module to standalone
  26  
  27  var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
  28      OWNER_DOCUMENT = 'ownerDocument';
  29  
  30  var Selector = {
  31      _types: {
  32          esc: {
  33              token: '\uE000',
  34              re: /\\[:\[\]\(\)#\.\'\>+~"]/gi
  35          },
  36  
  37          attr: {
  38              token: '\uE001',
  39              re: /(\[[^\]]*\])/g
  40          },
  41  
  42          pseudo: {
  43              token: '\uE002',
  44              re: /(\([^\)]*\))/g
  45          }
  46      },
  47  
  48      /**
  49       *  Use the native version of `querySelectorAll`, if it exists.
  50       *
  51       * @property useNative
  52       * @default true
  53       * @static
  54       */
  55      useNative: true,
  56  
  57      _escapeId: function(id) {
  58          if (id) {
  59              id = id.replace(/([:\[\]\(\)#\.'<>+~"])/g,'\\$1');
  60          }
  61          return id;
  62      },
  63  
  64      _compare: ('sourceIndex' in Y.config.doc.documentElement) ?
  65          function(nodeA, nodeB) {
  66              var a = nodeA.sourceIndex,
  67                  b = nodeB.sourceIndex;
  68  
  69              if (a === b) {
  70                  return 0;
  71              } else if (a > b) {
  72                  return 1;
  73              }
  74  
  75              return -1;
  76  
  77          } : (Y.config.doc.documentElement[COMPARE_DOCUMENT_POSITION] ?
  78          function(nodeA, nodeB) {
  79              if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
  80                  return -1;
  81              } else {
  82                  return 1;
  83              }
  84          } :
  85          function(nodeA, nodeB) {
  86              var rangeA, rangeB, compare;
  87              if (nodeA && nodeB) {
  88                  rangeA = nodeA[OWNER_DOCUMENT].createRange();
  89                  rangeA.setStart(nodeA, 0);
  90                  rangeB = nodeB[OWNER_DOCUMENT].createRange();
  91                  rangeB.setStart(nodeB, 0);
  92                  compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
  93              }
  94  
  95              return compare;
  96  
  97      }),
  98  
  99      _sort: function(nodes) {
 100          if (nodes) {
 101              nodes = Y.Array(nodes, 0, true);
 102              if (nodes.sort) {
 103                  nodes.sort(Selector._compare);
 104              }
 105          }
 106  
 107          return nodes;
 108      },
 109  
 110      _deDupe: function(nodes) {
 111          var ret = [],
 112              i, node;
 113  
 114          for (i = 0; (node = nodes[i++]);) {
 115              if (!node._found) {
 116                  ret[ret.length] = node;
 117                  node._found = true;
 118              }
 119          }
 120  
 121          for (i = 0; (node = ret[i++]);) {
 122              node._found = null;
 123              node.removeAttribute('_found');
 124          }
 125  
 126          return ret;
 127      },
 128  
 129      /**
 130       * Retrieves a set of nodes based on a given CSS selector.
 131       * @method query
 132       *
 133       * @param {String} selector A CSS selector.
 134       * @param {HTMLElement} root optional A node to start the query from. Defaults to `Y.config.doc`.
 135       * @param {Boolean} firstOnly optional Whether or not to return only the first match.
 136       * @return {HTMLElement[]} The array of nodes that matched the given selector.
 137       * @static
 138       */
 139      query: function(selector, root, firstOnly, skipNative) {
 140          root = root || Y.config.doc;
 141          var ret = [],
 142              useNative = (Y.Selector.useNative && Y.config.doc.querySelector && !skipNative),
 143              queries = [[selector, root]],
 144              query,
 145              result,
 146              i,
 147              fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
 148  
 149          if (selector && fn) {
 150              // split group into seperate queries
 151              if (!skipNative && // already done if skipping
 152                      (!useNative || root.tagName)) { // split native when element scoping is needed
 153                  queries = Selector._splitQueries(selector, root);
 154              }
 155  
 156              for (i = 0; (query = queries[i++]);) {
 157                  result = fn(query[0], query[1], firstOnly);
 158                  if (!firstOnly) { // coerce DOM Collection to Array
 159                      result = Y.Array(result, 0, true);
 160                  }
 161                  if (result) {
 162                      ret = ret.concat(result);
 163                  }
 164              }
 165  
 166              if (queries.length > 1) { // remove dupes and sort by doc order
 167                  ret = Selector._sort(Selector._deDupe(ret));
 168              }
 169          }
 170  
 171          return (firstOnly) ? (ret[0] || null) : ret;
 172  
 173      },
 174  
 175      _replaceSelector: function(selector) {
 176          var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.
 177              attrs,
 178              pseudos;
 179  
 180          // first replace escaped chars, which could be present in attrs or pseudos
 181          selector = Y.Selector._replace('esc', selector);
 182  
 183          // then replace pseudos before attrs to avoid replacing :not([foo])
 184          pseudos = Y.Selector._parse('pseudo', selector);
 185          selector = Selector._replace('pseudo', selector);
 186  
 187          attrs = Y.Selector._parse('attr', selector);
 188          selector = Y.Selector._replace('attr', selector);
 189  
 190          return {
 191              esc: esc,
 192              attrs: attrs,
 193              pseudos: pseudos,
 194              selector: selector
 195          };
 196      },
 197  
 198      _restoreSelector: function(replaced) {
 199          var selector = replaced.selector;
 200          selector = Y.Selector._restore('attr', selector, replaced.attrs);
 201          selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
 202          selector = Y.Selector._restore('esc', selector, replaced.esc);
 203          return selector;
 204      },
 205  
 206      _replaceCommas: function(selector) {
 207          var replaced = Y.Selector._replaceSelector(selector),
 208              selector = replaced.selector;
 209  
 210          if (selector) {
 211              selector = selector.replace(/,/g, '\uE007');
 212              replaced.selector = selector;
 213              selector = Y.Selector._restoreSelector(replaced);
 214          }
 215          return selector;
 216      },
 217  
 218      // allows element scoped queries to begin with combinator
 219      // e.g. query('> p', document.body) === query('body > p')
 220      _splitQueries: function(selector, node) {
 221          if (selector.indexOf(',') > -1) {
 222              selector = Y.Selector._replaceCommas(selector);
 223          }
 224  
 225          var groups = selector.split('\uE007'), // split on replaced comma token
 226              queries = [],
 227              prefix = '',
 228              id,
 229              i,
 230              len;
 231  
 232          if (node) {
 233              // enforce for element scoping
 234              if (node.nodeType === 1) { // Elements only
 235                  id = Y.Selector._escapeId(Y.DOM.getId(node));
 236  
 237                  if (!id) {
 238                      id = Y.guid();
 239                      Y.DOM.setId(node, id);
 240                  }
 241  
 242                  prefix = '[id="' + id + '"] ';
 243              }
 244  
 245              for (i = 0, len = groups.length; i < len; ++i) {
 246                  selector =  prefix + groups[i];
 247                  queries.push([selector, node]);
 248              }
 249          }
 250  
 251          return queries;
 252      },
 253  
 254      _nativeQuery: function(selector, root, one) {
 255          if (
 256              (Y.UA.webkit || Y.UA.opera) &&          // webkit (chrome, safari) and Opera
 257              selector.indexOf(':checked') > -1 &&    // fail to pick up "selected"  with ":checked"
 258              (Y.Selector.pseudos && Y.Selector.pseudos.checked)
 259          ) {
 260              return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
 261          }
 262          try {
 263              return root['querySelector' + (one ? '' : 'All')](selector);
 264          } catch(e) { // fallback to brute if available
 265              return Y.Selector.query(selector, root, one, true); // redo with skipNative true
 266          }
 267      },
 268  
 269      /**
 270       * Filters out nodes that do not match the given CSS selector.
 271       * @method filter
 272       *
 273       * @param {HTMLElement[]} nodes An array of nodes.
 274       * @param {String} selector A CSS selector to test each node against.
 275       * @return {HTMLElement[]} The nodes that matched the given CSS selector.
 276       * @static
 277       */
 278      filter: function(nodes, selector) {
 279          var ret = [],
 280              i, node;
 281  
 282          if (nodes && selector) {
 283              for (i = 0; (node = nodes[i++]);) {
 284                  if (Y.Selector.test(node, selector)) {
 285                      ret[ret.length] = node;
 286                  }
 287              }
 288          } else {
 289          }
 290  
 291          return ret;
 292      },
 293  
 294      /**
 295       * Determines whether or not the given node matches the given CSS selector.
 296       * @method test
 297       * 
 298       * @param {HTMLElement} node A node to test.
 299       * @param {String} selector A CSS selector to test the node against.
 300       * @param {HTMLElement} root optional A node to start the query from. Defaults to the parent document of the node.
 301       * @return {Boolean} Whether or not the given node matched the given CSS selector.
 302       * @static
 303       */
 304      test: function(node, selector, root) {
 305          var ret = false,
 306              useFrag = false,
 307              groups,
 308              parent,
 309              item,
 310              items,
 311              frag,
 312              id,
 313              i, j, group;
 314  
 315          if (node && node.tagName) { // only test HTMLElements
 316  
 317              if (typeof selector == 'function') { // test with function
 318                  ret = selector.call(node, node);
 319              } else { // test with query
 320                  // we need a root if off-doc
 321                  groups = selector.split(',');
 322                  if (!root && !Y.DOM.inDoc(node)) {
 323                      parent = node.parentNode;
 324                      if (parent) {
 325                          root = parent;
 326                      } else { // only use frag when no parent to query
 327                          frag = node[OWNER_DOCUMENT].createDocumentFragment();
 328                          frag.appendChild(node);
 329                          root = frag;
 330                          useFrag = true;
 331                      }
 332                  }
 333                  root = root || node[OWNER_DOCUMENT];
 334  
 335                  id = Y.Selector._escapeId(Y.DOM.getId(node));
 336                  if (!id) {
 337                      id = Y.guid();
 338                      Y.DOM.setId(node, id);
 339                  }
 340  
 341                  for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
 342                      group += '[id="' + id + '"]';
 343                      items = Y.Selector.query(group, root);
 344  
 345                      for (j = 0; item = items[j++];) {
 346                          if (item === node) {
 347                              ret = true;
 348                              break;
 349                          }
 350                      }
 351                      if (ret) {
 352                          break;
 353                      }
 354                  }
 355  
 356                  if (useFrag) { // cleanup
 357                      frag.removeChild(node);
 358                  }
 359              };
 360          }
 361  
 362          return ret;
 363      },
 364  
 365      /**
 366       * A convenience method to emulate Y.Node's aNode.ancestor(selector).
 367       * @method ancestor
 368       *
 369       * @param {HTMLElement} node A node to start the query from.
 370       * @param {String} selector A CSS selector to test the node against.
 371       * @param {Boolean} testSelf optional Whether or not to include the node in the scan.
 372       * @return {HTMLElement} The ancestor node matching the selector, or null.
 373       * @static
 374       */
 375      ancestor: function (node, selector, testSelf) {
 376          return Y.DOM.ancestor(node, function(n) {
 377              return Y.Selector.test(n, selector);
 378          }, testSelf);
 379      },
 380  
 381      _parse: function(name, selector) {
 382          return selector.match(Y.Selector._types[name].re);
 383      },
 384  
 385      _replace: function(name, selector) {
 386          var o = Y.Selector._types[name];
 387          return selector.replace(o.re, o.token);
 388      },
 389  
 390      _restore: function(name, selector, items) {
 391          if (items) {
 392              var token = Y.Selector._types[name].token,
 393                  i, len;
 394              for (i = 0, len = items.length; i < len; ++i) {
 395                  selector = selector.replace(token, items[i]);
 396              }
 397          }
 398          return selector;
 399      }
 400  };
 401  
 402  Y.mix(Y.Selector, Selector, true);
 403  
 404  })(Y);
 405  
 406  
 407  }, '3.17.2', {"requires": ["dom-base"]});


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1