UNPKG

skylark-utils

Version:

An Elegant HTML5 JavaScript Library.

860 lines (724 loc) 25.3 kB
define([ "./skylark", "./langx", "./browser", "./noder" ], function(skylark, langx, browser, noder, velm) { var local = {}, filter = Array.prototype.filter, slice = Array.prototype.slice, nativeMatchesSelector = browser.matchesSelector; /* --- name: Slick.Parser description: Standalone CSS3 Selector parser provides: Slick.Parser ... */ ; (function() { var parsed, separatorIndex, combinatorIndex, reversed, cache = {}, reverseCache = {}, reUnescape = /\\/g; var parse = function(expression, isReversed) { if (expression == null) return null; if (expression.Slick === true) return expression; expression = ('' + expression).replace(/^\s+|\s+$/g, ''); reversed = !!isReversed; var currentCache = (reversed) ? reverseCache : cache; if (currentCache[expression]) return currentCache[expression]; parsed = { Slick: true, expressions: [], raw: expression, reverse: function() { return parse(this.raw, true); } }; separatorIndex = -1; while (expression != (expression = expression.replace(regexp, parser))); parsed.length = parsed.expressions.length; return currentCache[parsed.raw] = (reversed) ? reverse(parsed) : parsed; }; var reverseCombinator = function(combinator) { if (combinator === '!') return ' '; else if (combinator === ' ') return '!'; else if ((/^!/).test(combinator)) return combinator.replace(/^!/, ''); else return '!' + combinator; }; var reverse = function(expression) { var expressions = expression.expressions; for (var i = 0; i < expressions.length; i++) { var exp = expressions[i]; var last = { parts: [], tag: '*', combinator: reverseCombinator(exp[0].combinator) }; for (var j = 0; j < exp.length; j++) { var cexp = exp[j]; if (!cexp.reverseCombinator) cexp.reverseCombinator = ' '; cexp.combinator = cexp.reverseCombinator; delete cexp.reverseCombinator; } exp.reverse().push(last); } return expression; }; var escapeRegExp = (function() { // Credit: XRegExp 0.6.1 (c) 2007-2008 Steven Levithan <http://stevenlevithan.com/regex/xregexp/> MIT License var from = /(?=[\-\[\]{}()*+?.\\\^$|,#\s])/g, to = '\\'; return function(string) { return string.replace(from, to) } }()) var regexp = new RegExp( "^(?:\\s*(,)\\s*|\\s*(<combinator>+)\\s*|(\\s+)|(<unicode>+|\\*)|\\#(<unicode>+)|\\.(<unicode>+)|\\[\\s*(<unicode1>+)(?:\\s*([*^$!~|]?=)(?:\\s*(?:([\"']?)(.*?)\\9)))?\\s*\\](?!\\])|(:+)(<unicode>+)(?:\\((?:(?:([\"'])([^\\13]*)\\13)|((?:\\([^)]+\\)|[^()]*)+))\\))?)" .replace(/<combinator>/, '[' + escapeRegExp(">+~`!@$%^&={}\\;</") + ']') .replace(/<unicode>/g, '(?:[\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') .replace(/<unicode1>/g, '(?:[:\\w\\u00a1-\\uFFFF-]|\\\\[^\\s0-9a-f])') ); function parser( rawMatch, separator, combinator, combinatorChildren, tagName, id, className, attributeKey, attributeOperator, attributeQuote, attributeValue, pseudoMarker, pseudoClass, pseudoQuote, pseudoClassQuotedValue, pseudoClassValue ) { if (separator || separatorIndex === -1) { parsed.expressions[++separatorIndex] = []; combinatorIndex = -1; if (separator) return ''; } if (combinator || combinatorChildren || combinatorIndex === -1) { combinator = combinator || ' '; var currentSeparator = parsed.expressions[separatorIndex]; if (reversed && currentSeparator[combinatorIndex]) currentSeparator[combinatorIndex].reverseCombinator = reverseCombinator(combinator); currentSeparator[++combinatorIndex] = { combinator: combinator, tag: '*' }; } var currentParsed = parsed.expressions[separatorIndex][combinatorIndex]; if (tagName) { currentParsed.tag = tagName.replace(reUnescape, ''); } else if (id) { currentParsed.id = id.replace(reUnescape, ''); } else if (className) { className = className.replace(reUnescape, ''); if (!currentParsed.classList) currentParsed.classList = []; if (!currentParsed.classes) currentParsed.classes = []; currentParsed.classList.push(className); currentParsed.classes.push({ value: className, regexp: new RegExp('(^|\\s)' + escapeRegExp(className) + '(\\s|$)') }); } else if (pseudoClass) { pseudoClassValue = pseudoClassValue || pseudoClassQuotedValue; pseudoClassValue = pseudoClassValue ? pseudoClassValue.replace(reUnescape, '') : null; if (!currentParsed.pseudos) currentParsed.pseudos = []; currentParsed.pseudos.push({ key: pseudoClass.replace(reUnescape, ''), value: pseudoClassValue, type: pseudoMarker.length == 1 ? 'class' : 'element' }); } else if (attributeKey) { attributeKey = attributeKey.replace(reUnescape, ''); attributeValue = (attributeValue || '').replace(reUnescape, ''); var test, regexp; switch (attributeOperator) { case '^=': regexp = new RegExp('^' + escapeRegExp(attributeValue)); break; case '$=': regexp = new RegExp(escapeRegExp(attributeValue) + '$'); break; case '~=': regexp = new RegExp('(^|\\s)' + escapeRegExp(attributeValue) + '(\\s|$)'); break; case '|=': regexp = new RegExp('^' + escapeRegExp(attributeValue) + '(-|$)'); break; case '=': test = function(value) { return attributeValue == value; }; break; case '*=': test = function(value) { return value && value.indexOf(attributeValue) > -1; }; break; case '!=': test = function(value) { return attributeValue != value; }; break; default: test = function(value) { return !!value; }; } if (attributeValue == '' && (/^[*$^]=$/).test(attributeOperator)) test = function() { return false; }; if (!test) test = function(value) { return value && regexp.test(value); }; if (!currentParsed.attributes) currentParsed.attributes = []; currentParsed.attributes.push({ key: attributeKey, operator: attributeOperator, value: attributeValue, test: test }); } return ''; }; // Slick NS var Slick = (this.Slick || {}); Slick.parse = function(expression) { return parse(expression); }; Slick.escapeRegExp = escapeRegExp; if (!this.Slick) this.Slick = Slick; }).apply(local); var simpleClassSelectorRE = /^\.([\w-]*)$/, simpleIdSelectorRE = /^#([\w-]*)$/, slice = Array.prototype.slice; local.parseSelector = local.Slick.parse; local.pseudos = { // custom pseudos checked: function(elm) { return !!elm.checked; }, contains: function(elm, idx, nodes, text) { if ($(this).text().indexOf(text) > -1) return this }, 'disabled': function(elm) { return !!elm.disabled; }, 'enabled': function(elm) { return !elm.disabled; }, eq: function(elm, idx, nodes, value) { return (idx == value); }, 'focus': function(elm) { return document.activeElement === elm && (elm.href || elm.type || elm.tabindex); }, first: function(elm, idx) { return (idx === 0); }, gt: function(elm, idx, nodes, value) { return (idx > value); }, has: function(elm, idx, nodes, sel) { return local.querySelector(elm, sel).length > 0; }, hidden: function(elm) { return !local.pseudos["visible"](elm); }, last: function(elm, idx, nodes) { return (idx === nodes.length - 1); }, lt: function(elm, idx, nodes, value) { return (idx < value); }, not: function(elm, idx, nodes, sel) { return local.match(elm, sel); }, parent: function(elm) { return !!elm.parentNode; }, selected: function(elm) { return !!elm.selected; }, visible: function(elm) { return elm.offsetWidth && elm.offsetWidth } }; local.divide = function(cond) { var nativeSelector = "", customPseudos = [], tag, id, classes, attributes, pseudos; if (id = cond.id) { nativeSelector += ("#" + id); } if (classes = cond.classes) { for (var i = classes.length; i--;) { nativeSelector += ("." + classes[i].value); } } if (attributes = cond.attributes) { for (var i = 0; i < attributes.length; i++) { if (attributes[i].operator) { nativeSelector += ("[" + attributes[i].key + attributes[i].operator + JSON.stringify(attributes[i].value) +"]"); } else { nativeSelector += ("[" + attributes[i].key + "]"); } } } if (pseudos = cond.pseudos) { for (i = pseudos.length; i--;) { part = pseudos[i]; if (this.pseudos[part.key]) { customPseudos.push(part); } else { if (part.value !== undefined) { nativeSelector += (":" + part.key + "(" + JSON.stringify(part)) } } } } if (tag = cond.tag) { nativeSelector = tag.toUpperCase() + nativeSelector; } if (!nativeSelector) { nativeSelector = "*"; } return { nativeSelector: nativeSelector, customPseudos: customPseudos } }; local.check = function(node, cond, idx, nodes) { var tag, id, classes, attributes, pseudos; if (tag = cond.tag) { var nodeName = node.nodeName.toUpperCase(); if (tag == '*') { if (nodeName < '@') return false; // Fix for comment nodes and closed nodes } else { if (nodeName != (tag || "").toUpperCase()) return false; } } if (id = cond.id) { if (node.getAttribute('id') != id) { return false; } } var i, part, cls, pseudo; if (classes = cond.classes) { for (i = classes.length; i--;) { cls = node.getAttribute('class'); if (!(cls && classes[i].regexp.test(cls))) return false; } } if (attributes) for (i = attributes.length; i--;) { part = attributes[i]; if (part.operator ? !part.test(node.getAttribute(part.key)) : !node.hasAttribute(part.key)) return false; } if (pseudos = cond.pseudos) { for (i = pseudos.length; i--;) { part = pseudos[i]; if (pseudo = this.pseudos[part.key]) { if (!pseudo(node, idx, nodes, part.value)) { return false; } } else { if (!nativeMatchesSelector.call(node, part.key)) { return false; } } } } return true; } local.match = function(node, selector) { var parsed = local.Slick.parse(selector); if (!parsed) { return true; } // simple (single) selectors var expressions = parsed.expressions, simpleExpCounter = 0, i; for (i = 0; (currentExpression = expressions[i]); i++) { if (currentExpression.length == 1) { var exp = currentExpression[0]; if (this.check(node, exp)) { return true; } simpleExpCounter++; } } if (simpleExpCounter == parsed.length) { return false; } var nodes = this.query(document, parsed), item; for (i = 0; item = nodes[i++];) { if (item === node) { return true; } } return false; }; local.combine = function(elm, bit) { var op = bit.combinator, cond = bit, node1, nodes = []; switch (op) { case '>': // direct children nodes = children(elm, cond); break; case '+': // next sibling node1 = nextSibling(elm, cond, true); if (node1) { nodes.push(node1); } break; case '^': // first child node1 = firstChild(elm, cond, true); if (node1) { nodes.push(node1); } break; case '~': // next siblings nodes = nextSiblings(elm, cond); break; case '++': // next sibling and previous sibling var prev = previousSibling(elm, cond, true), next = nextSibling(elm, cond, true); if (prev) { nodes.push(prev); } if (next) { nodes.push(next); } break; case '~~': // next siblings and previous siblings nodes = siblings(elm, cond); break; case '!': // all parent nodes up to document nodes = ancestors(elm, cond); break; case '!>': // direct parent (one level) node1 = parent(elm, cond); if (node1) { nodes.push(node1); } break; case '!+': // previous sibling nodes = previousSibling(elm, cond, true); break; case '!^': // last child node1 = lastChild(elm, cond, true); if (node1) { nodes.push(node1); } break; case '!~': // previous siblings nodes = previousSiblings(elm, cond); break; default: var divided = this.divide(bit); nodes = slice.call(elm.querySelectorAll(divided.nativeSelector)); if (divided.customPseudos) { for (var i = divided.customPseudos.length - 1; i >= 0; i--) { nodes = filter.call(nodes, function(item, idx) { return local.check(item, { pseudos: [divided.customPseudos[i]] }, idx, nodes) }); } } break; } return nodes; } local.query = function(node, selector, single) { var parsed = this.Slick.parse(selector); var founds = [], currentExpression, currentBit, expressions = parsed.expressions; for (var i = 0; (currentExpression = expressions[i]); i++) { var currentItems = [node], found; for (var j = 0; (currentBit = currentExpression[j]); j++) { found = langx.map(currentItems, function(item, i) { return local.combine(item, currentBit) }); if (found) { currentItems = found; } } if (found) { founds = founds.concat(found); } } return founds; } function ancestor(node, selector, root) { var rootIsSelector = root && langx.isString(root); while (node = node.parentNode) { if (matches(node, selector)) { return node; } if (root) { if (rootIsSelector) { if (matches(node,root)) { break; } } else if (node == root) { break; } } } return null; } function ancestors(node, selector,root) { var ret = [], rootIsSelector = root && langx.isString(root); while (node = node.parentNode) { if (matches(node, selector)) { ret.push(node); } if (root) { if (rootIsSelector) { if (matches(node,root)) { break; } } else if (node == root) { break; } } } return ret; } function byId(id, doc) { doc = doc || noder.doc(); return doc.getElementById(id); } function children(node, selector) { var childNodes = node.childNodes, ret = []; for (var i = 0; i < childNodes.length; i++) { var node = childNodes[i]; if (node.nodeType == 1) { if (!selector || matches(node, selector)) { ret.push(node); } } } return ret; } function closest(node, selector) { while (node && !(matches(node, selector))) { node = node.parentNode; } return node; } function descendants(elm, selector) { // Selector try { return slice.call(elm.querySelectorAll(selector)); } catch (matchError) { //console.log(matchError); } return local.query(elm, selector); } function descendant(elm, selector) { // Selector try { return elm.querySelector(selector); } catch (matchError) { //console.log(matchError); } var nodes = local.query(elm, selector); if (nodes.length > 0) { return nodes[0]; } else { return null; } } function find(selector) { return descendant(document.body, selector); } function findAll(selector) { return descendants(document.body, selector); } function firstChild(elm, selector, first) { var childNodes = elm.childNodes, node = childNodes[0]; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { return node; } if (first) { break; } } node = node.nextSibling; } return null; } function lastChild(elm, selector, last) { var childNodes = elm.childNodes, node = childNodes[childNodes.length - 1]; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { return node; } if (last) { break; } } node = node.previousSibling; } return null; } function matches(elm, selector) { if (!selector || !elm || elm.nodeType !== 1) { return false } if (langx.isString(selector)) { try { return nativeMatchesSelector.call(elm, selector.replace(/\[([^=]+)=\s*([^'"\]]+?)\s*\]/g, '[$1="$2"]')); } catch (matchError) { //console.log(matchError); } return local.match(elm, selector); } else if (langx.isArrayLike(selector)) { return langx.inArray(elm,selector); } else if (langx.isPlainObject(selector)){ return local.check(elm, selector); } else { return elm === selector; } } function nextSibling(elm, selector, adjacent) { var node = elm.nextSibling; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { return node; } if (adjacent) { break; } } node = node.nextSibling; } return null; } function nextSiblings(elm, selector) { var node = elm.nextSibling, ret = []; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { ret.push(node); } } node = node.nextSibling; } return ret; } function parent(elm, selector) { var node = elm.parentNode; if (node && (!selector || matches(node, selector))) { return node; } return null; } function previousSibling(elm, selector, adjacent) { var node = elm.previousSibling; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { return node; } if (adjacent) { break; } } node = node.previousSibling; } return null; } function previousSiblings(elm, selector) { var node = elm.previousSibling, ret = []; while (node) { if (node.nodeType == 1) { if (!selector || matches(node, selector)) { ret.push(node); } } node = node.previousSibling; } return ret; } function siblings(elm, selector) { var node = elm.parentNode.firstChild, ret = []; while (node) { if (node.nodeType == 1 && node !== elm) { if (!selector || matches(node, selector)) { ret.push(node); } } node = node.nextSibling; } return ret; } var finder = function() { return finder; }; langx.mixin(finder, { ancestor: ancestor, ancestors: ancestors, byId: byId, children: children, closest: closest, descendant: descendant, descendants: descendants, find: find, findAll: findAll, firstChild: firstChild, lastChild: lastChild, matches: matches, nextSibling: nextSibling, nextSiblings: nextSiblings, parent: parent, previousSibling: previousSibling, previousSiblings: previousSiblings, pseudos: local.pseudos, siblings: siblings }); return skylark.finder = finder; });