| /* |
| * Copyright (C) 2007-2018 Diego Perini |
| * All rights reserved. |
| * |
| * CSS3 pseudo-classes extension for NWMatcher |
| * |
| * Added capabilities: |
| * |
| * - structural pseudo-classes |
| * |
| * :root, :empty, |
| * :nth-child(), nth-of-type(), |
| * :nth-last-child(), nth-last-of-type(), |
| * :first-child, :last-child, :only-child |
| * :first-of-type, :last-of-type, :only-of-type |
| * |
| * - negation, language, target and UI element pseudo-classes |
| * |
| * :not(), :target, :lang(), :target |
| * :link, :visited, :active, :focus, :hover, |
| * :checked, :disabled, :enabled, :selected |
| */ |
| |
| (function(global) { |
| |
| var LINK_NODES = { |
| 'a': 1, 'A': 1, |
| 'area': 1, 'AREA': 1, |
| 'link': 1, 'LINK': 1 |
| }, |
| |
| root = document.documentElement, |
| |
| contains = 'compareDocumentPosition' in root ? |
| function(container, element) { |
| return (container.compareDocumentPosition(element) & 16) == 16; |
| } : 'contains' in root ? |
| function(container, element) { |
| return element.nodeType == 1 && container.contains(element); |
| } : |
| function(container, element) { |
| while ((element = element.parentNode) && element.nodeType == 1) { |
| if (element === container) return true; |
| } |
| return false; |
| }, |
| |
| isLink = |
| function(element) { |
| return element.getAttribute('href') && LINK_NODES[element.nodeName]; |
| }, |
| |
| isEmpty = |
| function(node) { |
| node = node.firstChild; |
| while (node) { |
| if (node.nodeType == 3 || node.nodeName > '@') return false; |
| node = node.nextSibling; |
| } |
| return true; |
| }, |
| |
| nthElement = |
| function(element, last) { |
| var count = 1, succ = last ? 'nextSibling' : 'previousSibling'; |
| while ((element = element[succ])) { |
| if (element.nodeName > '@') ++count; |
| } |
| return count; |
| }, |
| |
| nthOfType = |
| function(element, last) { |
| var count = 1, succ = last ? 'nextSibling' : 'previousSibling', type = element.nodeName; |
| while ((element = element[succ])) { |
| if (element.nodeName == type) ++count; |
| } |
| return count; |
| }; |
| |
| NW.Dom.Snapshot['contains'] = contains; |
| |
| NW.Dom.Snapshot['isLink'] = isLink; |
| NW.Dom.Snapshot['isEmpty'] = isEmpty; |
| NW.Dom.Snapshot['nthOfType'] = nthOfType; |
| NW.Dom.Snapshot['nthElement'] = nthElement; |
| |
| })(this); |
| |
| NW.Dom.registerSelector( |
| 'nwmatcher:spseudos', |
| /^\:(root|empty|(?:first|last|only)(?:-child|-of-type)|nth(?:-last)?(?:-child|-of-type)\(\s*(even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\))?(.*)/i, |
| (function(global) { |
| |
| return function(match, source) { |
| |
| var a, n, b, status = true, test, type; |
| |
| switch (match[1]) { |
| |
| case 'root': |
| if (match[3]) |
| source = 'if(e===h||s.contains(h,e)){' + source + '}'; |
| else |
| source = 'if(e===h){' + source + '}'; |
| break; |
| |
| case 'empty': |
| source = 'if(s.isEmpty(e)){' + source + '}'; |
| break; |
| |
| default: |
| if (match[1] && match[2]) { |
| |
| if (match[2] == 'n') { |
| source = 'if(e!==h){' + source + '}'; |
| break; |
| } else if (match[2] == 'even') { |
| a = 2; |
| b = 0; |
| } else if (match[2] == 'odd') { |
| a = 2; |
| b = 1; |
| } else { |
| b = ((n = match[2].match(/(-?\d+)$/)) ? parseInt(n[1], 10) : 0); |
| a = ((n = match[2].match(/(-?\d*)n/i)) ? parseInt(n[1], 10) : 0); |
| if (n && n[1] == '-') a = -1; |
| } |
| test = a > 1 ? |
| (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' : |
| 'n>=' + b + '&&(n-(' + b + '))%' + a + '==0' : a < -1 ? |
| (/last/i.test(match[1])) ? '(n-(' + b + '))%' + a + '==0' : |
| 'n<=' + b + '&&(n-(' + b + '))%' + a + '==0' : a === 0 ? |
| 'n==' + b : a == -1 ? 'n<=' + b : 'n>=' + b; |
| source = |
| 'if(e!==h){' + |
| 'n=s[' + (/-of-type/i.test(match[1]) ? '"nthOfType"' : '"nthElement"') + ']' + |
| '(e,' + (/last/i.test(match[1]) ? 'true' : 'false') + ');' + |
| 'if(' + test + '){' + source + '}' + |
| '}'; |
| |
| } else if (match[1]) { |
| |
| a = /first/i.test(match[1]) ? 'previous' : 'next'; |
| n = /only/i.test(match[1]) ? 'previous' : 'next'; |
| b = /first|last/i.test(match[1]); |
| type = /-of-type/i.test(match[1]) ? '&&n.nodeName!==e.nodeName' : '&&n.nodeName<"@"'; |
| source = 'if(e!==h){' + |
| ( 'n=e;while((n=n.' + a + 'Sibling)' + type + ');if(!n){' + (b ? source : |
| 'n=e;while((n=n.' + n + 'Sibling)' + type + ');if(!n){' + source + '}') + '}' ) + '}'; |
| |
| } else { |
| |
| status = false; |
| |
| } |
| break; |
| } |
| |
| return { |
| 'source': source, |
| 'status': status |
| }; |
| |
| }; |
| |
| })(this)); |
| |
| NW.Dom.registerSelector( |
| 'nwmatcher:dpseudos', |
| /^\:(link|visited|target|active|focus|hover|checked|disabled|enabled|selected|lang\(([-\w]{2,})\)|not\(\s*(:nth(?:-last)?(?:-child|-of-type)\(\s*(?:even|odd|(?:[-+]{0,1}\d*n\s*)?[-+]{0,1}\s*\d*)\s*\)|[^()]*)\s*\))?(.*)/i, |
| (function(global) { |
| |
| var doc = global.document, |
| Config = NW.Dom.Config, |
| Tokens = NW.Dom.Tokens, |
| |
| reTrimSpace = RegExp('^\\s+|\\s+$', 'g'), |
| |
| reSimpleNot = RegExp('^((?!:not)' + |
| '(' + Tokens.prefixes + '|' + Tokens.identifier + |
| '|\\([^()]*\\))+|\\[' + Tokens.attributes + '\\])$'); |
| |
| return function(match, source) { |
| |
| var expr, status = true, test; |
| |
| switch (match[1].match(/^\w+/)[0]) { |
| |
| case 'not': |
| expr = match[3].replace(reTrimSpace, ''); |
| if (Config.SIMPLENOT && !reSimpleNot.test(expr)) { |
| NW.Dom.emit('Negation pseudo-class only accepts simple selectors "' + selector + '"'); |
| } else { |
| if ('compatMode' in doc) { |
| source = 'if(!' + NW.Dom.compile(expr, '', false) + '(e,s,d,h,g)){' + source + '}'; |
| } else { |
| source = 'if(!s.match(e, "' + expr.replace(/\x22/g, '\\"') + '",g)){' + source +'}'; |
| } |
| } |
| break; |
| |
| case 'checked': |
| source = 'if((typeof e.form!=="undefined"&&(/^(?:radio|checkbox)$/i).test(e.type)&&e.checked)' + |
| (Config.USE_HTML5 ? '||(/^option$/i.test(e.nodeName)&&(e.selected||e.checked))' : '') + |
| '){' + source + '}'; |
| break; |
| |
| case 'disabled': |
| source = 'if(((typeof e.form!=="undefined"' + |
| (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') + |
| ')||s.isLink(e))&&e.disabled===true){' + source + '}'; |
| break; |
| |
| case 'enabled': |
| source = 'if(((typeof e.form!=="undefined"' + |
| (Config.USE_HTML5 ? '' : '&&!(/^hidden$/i).test(e.type)') + |
| ')||s.isLink(e))&&e.disabled===false){' + source + '}'; |
| break; |
| |
| case 'lang': |
| test = ''; |
| if (match[2]) test = match[2].substr(0, 2) + '-'; |
| source = 'do{(n=e.lang||"").toLowerCase();' + |
| 'if((n==""&&h.lang=="' + match[2].toLowerCase() + '")||' + |
| '(n&&(n=="' + match[2].toLowerCase() + |
| '"||n.substr(0,3)=="' + test.toLowerCase() + '")))' + |
| '{' + source + 'break;}}while((e=e.parentNode)&&e!==g);'; |
| break; |
| |
| case 'target': |
| source = 'if(e.id==d.location.hash.slice(1)){' + source + '}'; |
| break; |
| |
| case 'link': |
| source = 'if(s.isLink(e)&&!e.visited){' + source + '}'; |
| break; |
| |
| case 'visited': |
| source = 'if(s.isLink(e)&&e.visited){' + source + '}'; |
| break; |
| |
| case 'active': |
| source = 'if(e===d.activeElement){' + source + '}'; |
| break; |
| |
| case 'hover': |
| source = 'if(e===d.hoverElement){' + source + '}'; |
| break; |
| |
| case 'focus': |
| source = 'hasFocus' in doc ? |
| 'if(e===d.activeElement&&d.hasFocus()&&(e.type||e.href||typeof e.tabIndex=="number")){' + source + '}' : |
| 'if(e===d.activeElement&&(e.type||e.href)){' + source + '}'; |
| break; |
| |
| case 'selected': |
| source = 'if(/^option$/i.test(e.nodeName)&&(e.selected||e.checked)){' + source + '}'; |
| break; |
| |
| default: |
| status = false; |
| break; |
| } |
| |
| return { |
| 'source': source, |
| 'status': status |
| }; |
| |
| }; |
| |
| })(this)); |
| |
| NW.Dom.registerSelector( |
| 'nwmatcher:epseudos', |
| /^((?:[:]{1,2}(?:after|before|first-letter|first-line))|(?:[:]{2,2}(?:selection|backdrop|placeholder)))?(.*)/i, |
| (function(global) { |
| |
| return function(match, source) { |
| |
| var status = true; |
| |
| switch (match[1].match(/(\w+)$/)[0]) { |
| |
| case 'after': |
| case 'before': |
| case 'first-letter': |
| case 'first-line': |
| case 'selection': |
| case 'backdrop': |
| case 'placeholder': |
| source = 'if(!(/1|11/).test(e.nodeType)){' + source + '}'; |
| break; |
| |
| default: |
| status = false; |
| break; |
| } |
| |
| return { |
| 'source': source, |
| 'status': status |
| }; |
| |
| }; |
| |
| })(this)); |