[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/selector-native/ -> selector-native-debug.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          Y.log('query: ' + selector + ' returning: ' + ret.length, 'info', 'Selector');
 172          return (firstOnly) ? (ret[0] || null) : ret;
 173  
 174      },
 175  
 176      _replaceSelector: function(selector) {
 177          var esc = Y.Selector._parse('esc', selector), // pull escaped colon, brackets, etc.
 178              attrs,
 179              pseudos;
 180  
 181          // first replace escaped chars, which could be present in attrs or pseudos
 182          selector = Y.Selector._replace('esc', selector);
 183  
 184          // then replace pseudos before attrs to avoid replacing :not([foo])
 185          pseudos = Y.Selector._parse('pseudo', selector);
 186          selector = Selector._replace('pseudo', selector);
 187  
 188          attrs = Y.Selector._parse('attr', selector);
 189          selector = Y.Selector._replace('attr', selector);
 190  
 191          return {
 192              esc: esc,
 193              attrs: attrs,
 194              pseudos: pseudos,
 195              selector: selector
 196          };
 197      },
 198  
 199      _restoreSelector: function(replaced) {
 200          var selector = replaced.selector;
 201          selector = Y.Selector._restore('attr', selector, replaced.attrs);
 202          selector = Y.Selector._restore('pseudo', selector, replaced.pseudos);
 203          selector = Y.Selector._restore('esc', selector, replaced.esc);
 204          return selector;
 205      },
 206  
 207      _replaceCommas: function(selector) {
 208          var replaced = Y.Selector._replaceSelector(selector),
 209              selector = replaced.selector;
 210  
 211          if (selector) {
 212              selector = selector.replace(/,/g, '\uE007');
 213              replaced.selector = selector;
 214              selector = Y.Selector._restoreSelector(replaced);
 215          }
 216          return selector;
 217      },
 218  
 219      // allows element scoped queries to begin with combinator
 220      // e.g. query('> p', document.body) === query('body > p')
 221      _splitQueries: function(selector, node) {
 222          if (selector.indexOf(',') > -1) {
 223              selector = Y.Selector._replaceCommas(selector);
 224          }
 225  
 226          var groups = selector.split('\uE007'), // split on replaced comma token
 227              queries = [],
 228              prefix = '',
 229              id,
 230              i,
 231              len;
 232  
 233          if (node) {
 234              // enforce for element scoping
 235              if (node.nodeType === 1) { // Elements only
 236                  id = Y.Selector._escapeId(Y.DOM.getId(node));
 237  
 238                  if (!id) {
 239                      id = Y.guid();
 240                      Y.DOM.setId(node, id);
 241                  }
 242  
 243                  prefix = '[id="' + id + '"] ';
 244              }
 245  
 246              for (i = 0, len = groups.length; i < len; ++i) {
 247                  selector =  prefix + groups[i];
 248                  queries.push([selector, node]);
 249              }
 250          }
 251  
 252          return queries;
 253      },
 254  
 255      _nativeQuery: function(selector, root, one) {
 256          if (
 257              (Y.UA.webkit || Y.UA.opera) &&          // webkit (chrome, safari) and Opera
 258              selector.indexOf(':checked') > -1 &&    // fail to pick up "selected"  with ":checked"
 259              (Y.Selector.pseudos && Y.Selector.pseudos.checked)
 260          ) {
 261              return Y.Selector.query(selector, root, one, true); // redo with skipNative true to try brute query
 262          }
 263          try {
 264              //Y.log('trying native query with: ' + selector, 'info', 'selector-native');
 265              return root['querySelector' + (one ? '' : 'All')](selector);
 266          } catch(e) { // fallback to brute if available
 267              //Y.log('native query error; reverting to brute query with: ' + selector, 'info', 'selector-native');
 268              return Y.Selector.query(selector, root, one, true); // redo with skipNative true
 269          }
 270      },
 271  
 272      /**
 273       * Filters out nodes that do not match the given CSS selector.
 274       * @method filter
 275       *
 276       * @param {HTMLElement[]} nodes An array of nodes.
 277       * @param {String} selector A CSS selector to test each node against.
 278       * @return {HTMLElement[]} The nodes that matched the given CSS selector.
 279       * @static
 280       */
 281      filter: function(nodes, selector) {
 282          var ret = [],
 283              i, node;
 284  
 285          if (nodes && selector) {
 286              for (i = 0; (node = nodes[i++]);) {
 287                  if (Y.Selector.test(node, selector)) {
 288                      ret[ret.length] = node;
 289                  }
 290              }
 291          } else {
 292              Y.log('invalid filter input (nodes: ' + nodes +
 293                      ', selector: ' + selector + ')', 'warn', 'Selector');
 294          }
 295  
 296          return ret;
 297      },
 298  
 299      /**
 300       * Determines whether or not the given node matches the given CSS selector.
 301       * @method test
 302       * 
 303       * @param {HTMLElement} node A node to test.
 304       * @param {String} selector A CSS selector to test the node against.
 305       * @param {HTMLElement} root optional A node to start the query from. Defaults to the parent document of the node.
 306       * @return {Boolean} Whether or not the given node matched the given CSS selector.
 307       * @static
 308       */
 309      test: function(node, selector, root) {
 310          var ret = false,
 311              useFrag = false,
 312              groups,
 313              parent,
 314              item,
 315              items,
 316              frag,
 317              id,
 318              i, j, group;
 319  
 320          if (node && node.tagName) { // only test HTMLElements
 321  
 322              if (typeof selector == 'function') { // test with function
 323                  ret = selector.call(node, node);
 324              } else { // test with query
 325                  // we need a root if off-doc
 326                  groups = selector.split(',');
 327                  if (!root && !Y.DOM.inDoc(node)) {
 328                      parent = node.parentNode;
 329                      if (parent) {
 330                          root = parent;
 331                      } else { // only use frag when no parent to query
 332                          frag = node[OWNER_DOCUMENT].createDocumentFragment();
 333                          frag.appendChild(node);
 334                          root = frag;
 335                          useFrag = true;
 336                      }
 337                  }
 338                  root = root || node[OWNER_DOCUMENT];
 339  
 340                  id = Y.Selector._escapeId(Y.DOM.getId(node));
 341                  if (!id) {
 342                      id = Y.guid();
 343                      Y.DOM.setId(node, id);
 344                  }
 345  
 346                  for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
 347                      group += '[id="' + id + '"]';
 348                      items = Y.Selector.query(group, root);
 349  
 350                      for (j = 0; item = items[j++];) {
 351                          if (item === node) {
 352                              ret = true;
 353                              break;
 354                          }
 355                      }
 356                      if (ret) {
 357                          break;
 358                      }
 359                  }
 360  
 361                  if (useFrag) { // cleanup
 362                      frag.removeChild(node);
 363                  }
 364              };
 365          }
 366  
 367          return ret;
 368      },
 369  
 370      /**
 371       * A convenience method to emulate Y.Node's aNode.ancestor(selector).
 372       * @method ancestor
 373       *
 374       * @param {HTMLElement} node A node to start the query from.
 375       * @param {String} selector A CSS selector to test the node against.
 376       * @param {Boolean} testSelf optional Whether or not to include the node in the scan.
 377       * @return {HTMLElement} The ancestor node matching the selector, or null.
 378       * @static
 379       */
 380      ancestor: function (node, selector, testSelf) {
 381          return Y.DOM.ancestor(node, function(n) {
 382              return Y.Selector.test(n, selector);
 383          }, testSelf);
 384      },
 385  
 386      _parse: function(name, selector) {
 387          return selector.match(Y.Selector._types[name].re);
 388      },
 389  
 390      _replace: function(name, selector) {
 391          var o = Y.Selector._types[name];
 392          return selector.replace(o.re, o.token);
 393      },
 394  
 395      _restore: function(name, selector, items) {
 396          if (items) {
 397              var token = Y.Selector._types[name].token,
 398                  i, len;
 399              for (i = 0, len = items.length; i < len; ++i) {
 400                  selector = selector.replace(token, items[i]);
 401              }
 402          }
 403          return selector;
 404      }
 405  };
 406  
 407  Y.mix(Y.Selector, Selector, true);
 408  
 409  })(Y);
 410  
 411  
 412  }, '3.17.2', {"requires": ["dom-base"]});


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