Spaces:
Sleeping
Sleeping
| var Marker = require('../../tokenizer/marker'); | |
| var split = require('../../utils/split'); | |
| var DEEP_SELECTOR_PATTERN = /\/deep\//; | |
| var DOUBLE_COLON_PATTERN = /^::/; | |
| var VENDOR_PREFIXED_PATTERN = /:(-moz-|-ms-|-o-|-webkit-)/; | |
| var NOT_PSEUDO = ':not'; | |
| var PSEUDO_CLASSES_WITH_ARGUMENTS = [ | |
| ':dir', | |
| ':lang', | |
| ':not', | |
| ':nth-child', | |
| ':nth-last-child', | |
| ':nth-last-of-type', | |
| ':nth-of-type' | |
| ]; | |
| var RELATION_PATTERN = /[>+~]/; | |
| var UNMIXABLE_PSEUDO_CLASSES = [ | |
| ':after', | |
| ':before', | |
| ':first-letter', | |
| ':first-line', | |
| ':lang' | |
| ]; | |
| var UNMIXABLE_PSEUDO_ELEMENTS = [ | |
| '::after', | |
| '::before', | |
| '::first-letter', | |
| '::first-line' | |
| ]; | |
| var Level = { | |
| DOUBLE_QUOTE: 'double-quote', | |
| SINGLE_QUOTE: 'single-quote', | |
| ROOT: 'root' | |
| }; | |
| function isMergeable(selector, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { | |
| var singleSelectors = split(selector, Marker.COMMA); | |
| var singleSelector; | |
| var i, l; | |
| for (i = 0, l = singleSelectors.length; i < l; i++) { | |
| singleSelector = singleSelectors[i]; | |
| if (singleSelector.length === 0 | |
| || isDeepSelector(singleSelector) | |
| || isVendorPrefixed(singleSelector) | |
| || (singleSelector.indexOf(Marker.COLON) > -1 | |
| && !areMergeable( | |
| singleSelector, | |
| extractPseudoFrom(singleSelector), | |
| mergeablePseudoClasses, | |
| mergeablePseudoElements, | |
| multiplePseudoMerging | |
| ))) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function isDeepSelector(selector) { | |
| return DEEP_SELECTOR_PATTERN.test(selector); | |
| } | |
| function isVendorPrefixed(selector) { | |
| return VENDOR_PREFIXED_PATTERN.test(selector); | |
| } | |
| function extractPseudoFrom(selector) { | |
| var list = []; | |
| var character; | |
| var buffer = []; | |
| var level = Level.ROOT; | |
| var roundBracketLevel = 0; | |
| var isQuoted; | |
| var isEscaped; | |
| var isPseudo = false; | |
| var isRelation; | |
| var wasColon = false; | |
| var index; | |
| var len; | |
| for (index = 0, len = selector.length; index < len; index++) { | |
| character = selector[index]; | |
| isRelation = !isEscaped && RELATION_PATTERN.test(character); | |
| isQuoted = level == Level.DOUBLE_QUOTE || level == Level.SINGLE_QUOTE; | |
| if (isEscaped) { | |
| buffer.push(character); | |
| } else if (character == Marker.DOUBLE_QUOTE && level == Level.ROOT) { | |
| buffer.push(character); | |
| level = Level.DOUBLE_QUOTE; | |
| } else if (character == Marker.DOUBLE_QUOTE && level == Level.DOUBLE_QUOTE) { | |
| buffer.push(character); | |
| level = Level.ROOT; | |
| } else if (character == Marker.SINGLE_QUOTE && level == Level.ROOT) { | |
| buffer.push(character); | |
| level = Level.SINGLE_QUOTE; | |
| } else if (character == Marker.SINGLE_QUOTE && level == Level.SINGLE_QUOTE) { | |
| buffer.push(character); | |
| level = Level.ROOT; | |
| } else if (isQuoted) { | |
| buffer.push(character); | |
| } else if (character == Marker.OPEN_ROUND_BRACKET) { | |
| buffer.push(character); | |
| roundBracketLevel++; | |
| } else if (character == Marker.CLOSE_ROUND_BRACKET && roundBracketLevel == 1 && isPseudo) { | |
| buffer.push(character); | |
| list.push(buffer.join('')); | |
| roundBracketLevel--; | |
| buffer = []; | |
| isPseudo = false; | |
| } else if (character == Marker.CLOSE_ROUND_BRACKET) { | |
| buffer.push(character); | |
| roundBracketLevel--; | |
| } else if (character == Marker.COLON && roundBracketLevel === 0 && isPseudo && !wasColon) { | |
| list.push(buffer.join('')); | |
| buffer = []; | |
| buffer.push(character); | |
| } else if (character == Marker.COLON && roundBracketLevel === 0 && !wasColon) { | |
| buffer = []; | |
| buffer.push(character); | |
| isPseudo = true; | |
| } else if (character == Marker.SPACE && roundBracketLevel === 0 && isPseudo) { | |
| list.push(buffer.join('')); | |
| buffer = []; | |
| isPseudo = false; | |
| } else if (isRelation && roundBracketLevel === 0 && isPseudo) { | |
| list.push(buffer.join('')); | |
| buffer = []; | |
| isPseudo = false; | |
| } else { | |
| buffer.push(character); | |
| } | |
| isEscaped = character == Marker.BACK_SLASH; | |
| wasColon = character == Marker.COLON; | |
| } | |
| if (buffer.length > 0 && isPseudo) { | |
| list.push(buffer.join('')); | |
| } | |
| return list; | |
| } | |
| function areMergeable(selector, matches, mergeablePseudoClasses, mergeablePseudoElements, multiplePseudoMerging) { | |
| return areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) | |
| && needArguments(matches) | |
| && (matches.length < 2 || !someIncorrectlyChained(selector, matches)) | |
| && (matches.length < 2 || multiplePseudoMerging && allMixable(matches)); | |
| } | |
| function areAllowed(matches, mergeablePseudoClasses, mergeablePseudoElements) { | |
| var match; | |
| var name; | |
| var i, l; | |
| for (i = 0, l = matches.length; i < l; i++) { | |
| match = matches[i]; | |
| name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 | |
| ? match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) | |
| : match; | |
| if (mergeablePseudoClasses.indexOf(name) === -1 && mergeablePseudoElements.indexOf(name) === -1) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function needArguments(matches) { | |
| var match; | |
| var name; | |
| var bracketOpensAt; | |
| var hasArguments; | |
| var i, l; | |
| for (i = 0, l = matches.length; i < l; i++) { | |
| match = matches[i]; | |
| bracketOpensAt = match.indexOf(Marker.OPEN_ROUND_BRACKET); | |
| hasArguments = bracketOpensAt > -1; | |
| name = hasArguments | |
| ? match.substring(0, bracketOpensAt) | |
| : match; | |
| if (hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) == -1) { | |
| return false; | |
| } | |
| if (!hasArguments && PSEUDO_CLASSES_WITH_ARGUMENTS.indexOf(name) > -1) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function someIncorrectlyChained(selector, matches) { | |
| var positionInSelector = 0; | |
| var match; | |
| var matchAt; | |
| var nextMatch; | |
| var nextMatchAt; | |
| var name; | |
| var nextName; | |
| var areChained; | |
| var i, l; | |
| for (i = 0, l = matches.length; i < l; i++) { | |
| match = matches[i]; | |
| nextMatch = matches[i + 1]; | |
| if (!nextMatch) { | |
| break; | |
| } | |
| matchAt = selector.indexOf(match, positionInSelector); | |
| nextMatchAt = selector.indexOf(match, matchAt + 1); | |
| positionInSelector = nextMatchAt; | |
| areChained = matchAt + match.length == nextMatchAt; | |
| if (areChained) { | |
| name = match.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 | |
| ? match.substring(0, match.indexOf(Marker.OPEN_ROUND_BRACKET)) | |
| : match; | |
| nextName = nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET) > -1 | |
| ? nextMatch.substring(0, nextMatch.indexOf(Marker.OPEN_ROUND_BRACKET)) | |
| : nextMatch; | |
| if (name != NOT_PSEUDO || nextName != NOT_PSEUDO) { | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| function allMixable(matches) { | |
| var unmixableMatches = 0; | |
| var match; | |
| var i, l; | |
| for (i = 0, l = matches.length; i < l; i++) { | |
| match = matches[i]; | |
| if (isPseudoElement(match)) { | |
| unmixableMatches += UNMIXABLE_PSEUDO_ELEMENTS.indexOf(match) > -1 ? 1 : 0; | |
| } else { | |
| unmixableMatches += UNMIXABLE_PSEUDO_CLASSES.indexOf(match) > -1 ? 1 : 0; | |
| } | |
| if (unmixableMatches > 1) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function isPseudoElement(pseudo) { | |
| return DOUBLE_COLON_PATTERN.test(pseudo); | |
| } | |
| module.exports = isMergeable; | |