Comparing version 1.0.0-alpha6 to 1.0.0-alpha7
@@ -450,3 +450,3 @@ { | ||
"syntax": "<svg-length>" | ||
}, | ||
}, | ||
"stroke-linecap": { | ||
@@ -618,5 +618,6 @@ "comment": "added SVG property", | ||
"references": [ | ||
"http://cssdot.ru/%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D1%87%D0%BD%D0%B8%D0%BA_CSS/color-i305.html" | ||
"http://cssdot.ru/%D0%A1%D0%BF%D1%80%D0%B0%D0%B2%D0%BE%D1%87%D0%BD%D0%B8%D0%BA_CSS/color-i305.html", | ||
"https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Mozilla_Color_Preference_Extensions" | ||
], | ||
"syntax": "-moz-ButtonDefault | -moz-ButtonHoverFace | -moz-ButtonHoverText | -moz-CellHighlight | -moz-CellHighlightText | -moz-Combobox | -moz-ComboboxText | -moz-Dialog | -moz-DialogText | -moz-dragtargetzone | -moz-EvenTreeRow | -moz-Field | -moz-FieldText | -moz-html-CellHighlight | -moz-html-CellHighlightText | -moz-mac-accentdarkestshadow | -moz-mac-accentdarkshadow | -moz-mac-accentface | -moz-mac-accentlightesthighlight | -moz-mac-accentlightshadow | -moz-mac-accentregularhighlight | -moz-mac-accentregularshadow | -moz-mac-chrome-active | -moz-mac-chrome-inactive | -moz-mac-focusring | -moz-mac-menuselect | -moz-mac-menushadow | -moz-mac-menutextselect | -moz-MenuHover | -moz-MenuHoverText | -moz-MenuBarText | -moz-MenuBarHoverText | -moz-nativehyperlinktext | -moz-OddTreeRow | -moz-win-communicationstext | -moz-win-mediatext | -webkit-activelink | -webkit-focus-ring-color | -webkit-link | -webkit-text" | ||
"syntax": "-moz-ButtonDefault | -moz-ButtonHoverFace | -moz-ButtonHoverText | -moz-CellHighlight | -moz-CellHighlightText | -moz-Combobox | -moz-ComboboxText | -moz-Dialog | -moz-DialogText | -moz-dragtargetzone | -moz-EvenTreeRow | -moz-Field | -moz-FieldText | -moz-html-CellHighlight | -moz-html-CellHighlightText | -moz-mac-accentdarkestshadow | -moz-mac-accentdarkshadow | -moz-mac-accentface | -moz-mac-accentlightesthighlight | -moz-mac-accentlightshadow | -moz-mac-accentregularhighlight | -moz-mac-accentregularshadow | -moz-mac-chrome-active | -moz-mac-chrome-inactive | -moz-mac-focusring | -moz-mac-menuselect | -moz-mac-menushadow | -moz-mac-menutextselect | -moz-MenuHover | -moz-MenuHoverText | -moz-MenuBarText | -moz-MenuBarHoverText | -moz-nativehyperlinktext | -moz-OddTreeRow | -moz-win-communicationstext | -moz-win-mediatext | -moz-activehyperlinktext | -moz-default-background-color | -moz-default-color | -moz-hyperlinktext | -moz-visitedhyperlinktext | -webkit-activelink | -webkit-focus-ring-color | -webkit-link | -webkit-text" | ||
}, | ||
@@ -623,0 +624,0 @@ "-non-standart-image-rendering": { |
@@ -0,1 +1,14 @@ | ||
## 1.0.0-alpha7 (October 7, 2016) | ||
- Added support for explicit descendant combinator (`>>`) | ||
- Implemented `Type` and `Universal` type nodes | ||
- Improved `Number` parsing by including sign and exponent (#26) | ||
- Parse `before`, `after`, `first-letter` and `first-line` pseudos with single colon as `PseudoElement` | ||
- Changed `FunctionalPseudo` node type to `PseudoClass` | ||
- Fixed attribute selector name parsing (namespace edge cases) | ||
- Fixed location calculation for specified offset when `eof` is reached | ||
- Added more non-standard colors (#25) | ||
- Removed obsolete `Syntax#getAll()` method | ||
- Fixed various edge cases, code clean up and performance improvements | ||
## 1.0.0-alpha6 (September 23, 2016) | ||
@@ -2,0 +15,0 @@ |
@@ -7,3 +7,3 @@ // token types (note: value shouldn't intersect with using char codes) | ||
var COMMENT = 5; | ||
var PUNCTUATOR = 6; | ||
var PUNCTUATION = 6; | ||
@@ -17,8 +17,8 @@ var TAB = 9; | ||
var TokenType = { | ||
Whitespace: WHITESPACE, | ||
Identifier: IDENTIFIER, | ||
DecimalNumber: NUMBER, | ||
String: STRING, | ||
Comment: COMMENT, | ||
Punctuator: PUNCTUATOR, | ||
Whitespace: WHITESPACE, | ||
Identifier: IDENTIFIER, | ||
Number: NUMBER, | ||
String: STRING, | ||
Comment: COMMENT, | ||
Punctuation: PUNCTUATION, | ||
@@ -95,3 +95,3 @@ ExclamationMark: 33, // ! | ||
var SYMBOL_CATEGORY = new Uint32Array(SYMBOL_CATEGORY_LENGTH); | ||
var IS_PUNCTUATOR = new Uint32Array(SYMBOL_CATEGORY_LENGTH); | ||
var IS_PUNCTUATION = new Uint32Array(SYMBOL_CATEGORY_LENGTH); | ||
@@ -104,13 +104,13 @@ for (var i = 0; i < SYMBOL_CATEGORY.length; i++) { | ||
punctuation.forEach(function(key) { | ||
SYMBOL_CATEGORY[Number(key)] = PUNCTUATOR; | ||
IS_PUNCTUATOR[Number(key)] = PUNCTUATOR; | ||
SYMBOL_CATEGORY[Number(key)] = PUNCTUATION; | ||
IS_PUNCTUATION[Number(key)] = PUNCTUATION; | ||
}, SYMBOL_CATEGORY); | ||
IS_PUNCTUATOR[TokenType.HyphenMinus] = 0; | ||
IS_PUNCTUATION[TokenType.HyphenMinus] = 0; | ||
// whitespace is punctuator | ||
IS_PUNCTUATOR[SPACE] = PUNCTUATOR; | ||
IS_PUNCTUATOR[TAB] = PUNCTUATOR; | ||
IS_PUNCTUATOR[N] = PUNCTUATOR; | ||
IS_PUNCTUATOR[R] = PUNCTUATOR; | ||
IS_PUNCTUATOR[F] = PUNCTUATOR; | ||
IS_PUNCTUATION[SPACE] = PUNCTUATION; | ||
IS_PUNCTUATION[TAB] = PUNCTUATION; | ||
IS_PUNCTUATION[N] = PUNCTUATION; | ||
IS_PUNCTUATION[R] = PUNCTUATION; | ||
IS_PUNCTUATION[F] = PUNCTUATION; | ||
@@ -135,3 +135,3 @@ for (var i = 48; i <= 57; i++) { | ||
SYMBOL_CATEGORY: SYMBOL_CATEGORY, | ||
IS_PUNCTUATOR: IS_PUNCTUATOR | ||
IS_PUNCTUATION: IS_PUNCTUATION | ||
}; |
@@ -27,3 +27,3 @@ var MAX_LINE_LENGTH = 100; | ||
cutLeft = column - OFFSET_CORRECTION + 3; | ||
column = OFFSET_CORRECTION; | ||
column = OFFSET_CORRECTION - 2; | ||
} | ||
@@ -30,0 +30,0 @@ |
@@ -6,2 +6,3 @@ 'use strict'; | ||
var List = require('../utils/list'); | ||
var cmpChar = require('./utils').cmpChar; | ||
var cmpStr = require('./utils').cmpStr; | ||
@@ -14,3 +15,3 @@ var endsWith = require('./utils').endsWith; | ||
var DESCENDANT_COMBINATOR = ' '; | ||
var DESCENDANT_COMBINATOR = {}; | ||
var SPACE_NODE = { type: 'Space' }; | ||
@@ -20,3 +21,3 @@ | ||
var IDENTIFIER = TokenType.Identifier; | ||
var DECIMALNUMBER = TokenType.DecimalNumber; | ||
var NUMBER = TokenType.Number; | ||
var STRING = TokenType.String; | ||
@@ -49,2 +50,3 @@ var COMMENT = TokenType.Comment; | ||
var TILDE = TokenType.Tilde; | ||
var N = 110; // 'n'.charCodeAt(0) | ||
@@ -160,2 +162,4 @@ var SCOPE_ATRULE_EXPRESSION = { | ||
readSC(); | ||
scan: | ||
@@ -199,5 +203,3 @@ while (!scanner.eof) { | ||
wasSpace = false; | ||
if (sequence.head !== null) { // ignore spaces in the beginning | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
@@ -222,21 +224,17 @@ | ||
if (!scanner.eof) { | ||
switch (scanner.tokenType) { | ||
case SEMICOLON: | ||
scanner.next(); // { | ||
break; | ||
// at-rule expression can ends with semicolon, left curly bracket or eof | ||
switch (scanner.tokenType) { | ||
case SEMICOLON: | ||
scanner.next(); // ; | ||
break; | ||
case LEFTCURLYBRACKET: | ||
scanner.next(); // { | ||
case LEFTCURLYBRACKET: | ||
scanner.next(); // { | ||
node.block = isBlockAtrule() | ||
? getBlock() | ||
: getStylesheet(true); | ||
node.block = isBlockAtrule() | ||
? getBlock() | ||
: getStylesheet(true); | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
break; | ||
default: | ||
scanner.error('Unexpected input'); | ||
} | ||
scanner.eat(RIGHTCURLYBRACKET); | ||
break; | ||
} | ||
@@ -312,2 +310,3 @@ | ||
var combinator = null; | ||
var combinatorOffset = -1; | ||
var child; | ||
@@ -328,3 +327,3 @@ var node = { | ||
if (nested) { | ||
scanner.error('Unexpected input'); | ||
scanner.error(); | ||
} | ||
@@ -336,3 +335,3 @@ | ||
if (!nested) { | ||
scanner.error('Unexpected input'); | ||
scanner.error(); | ||
} | ||
@@ -348,3 +347,4 @@ | ||
if (combinator === null && sequence.head !== null) { | ||
combinator = getCombinator(); | ||
combinatorOffset = scanner.tokenStart; | ||
combinator = DESCENDANT_COMBINATOR; | ||
} else { | ||
@@ -359,6 +359,8 @@ scanner.next(); | ||
case SOLIDUS: | ||
if (combinator !== null && combinator.name !== DESCENDANT_COMBINATOR) { | ||
if ((sequence.head === null) || // combinator in the beginning | ||
(combinator !== null && combinator !== DESCENDANT_COMBINATOR)) { | ||
scanner.error('Unexpected combinator'); | ||
} | ||
combinatorOffset = scanner.tokenStart; | ||
combinator = getCombinator(); | ||
@@ -386,6 +388,7 @@ continue; | ||
case ASTERISK: | ||
child = getNamespacedIdentifier(false); | ||
case VERTICALLINE: | ||
child = getTypeOrUniversal(); | ||
break; | ||
case DECIMALNUMBER: | ||
case NUMBER: | ||
child = getPercentage(getInfo(), readNumber()); | ||
@@ -395,6 +398,15 @@ break; | ||
default: | ||
scanner.error('Unexpected input'); | ||
scanner.error(); | ||
} | ||
if (combinator !== null) { | ||
// create descendant combinator on demand to avoid garbage | ||
if (combinator === DESCENDANT_COMBINATOR) { | ||
combinator = { | ||
type: 'Combinator', | ||
info: needPositions ? scanner.getLocation(combinatorOffset, filename) : null, | ||
name: ' ' | ||
}; | ||
} | ||
sequence.appendData(combinator); | ||
@@ -407,4 +419,4 @@ combinator = null; | ||
if (combinator && combinator.name !== DESCENDANT_COMBINATOR) { | ||
scanner.error('Unexpected combinator'); | ||
if (combinator !== null && combinator !== DESCENDANT_COMBINATOR) { | ||
scanner.error('Unexpected combinator', combinatorOffset); | ||
} | ||
@@ -527,2 +539,3 @@ | ||
case SEMICOLON: | ||
case EXCLAMATIONMARK: | ||
break scan; | ||
@@ -532,3 +545,3 @@ | ||
if (!nested) { | ||
scanner.error('Unexpected input'); | ||
scanner.error(); | ||
} | ||
@@ -560,8 +573,2 @@ | ||
case EXCLAMATIONMARK: | ||
if (nested) { | ||
scanner.error('Unexpected exclamation mark'); | ||
} | ||
break scan; | ||
case STRING: | ||
@@ -574,9 +581,14 @@ child = getString(); | ||
if (scanner.tokenType === IDENTIFIER && | ||
scanner.lookupValue(0, 'u') && | ||
scanner.lookupType(1) === PLUSSIGN) { | ||
child = getUnicodeRange(); | ||
} else { | ||
child = getAny(SCOPE_VALUE); | ||
scanner.lookupValue(0, 'u')) { | ||
if ( | ||
scanner.lookupType(1) === PLUSSIGN || ( | ||
scanner.lookupType(1) === NUMBER && | ||
cmpChar(scanner.source, scanner.tokenEnd, PLUSSIGN) | ||
)) { | ||
child = getUnicodeRange(); | ||
break; | ||
} | ||
} | ||
child = getAny(SCOPE_VALUE); | ||
} | ||
@@ -586,5 +598,3 @@ | ||
wasSpace = false; | ||
if (sequence.tail !== null) { // ignore spaces in the beginning | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
@@ -604,6 +614,13 @@ | ||
case FULLSTOP: | ||
case DECIMALNUMBER: | ||
case HYPHENMINUS: | ||
var nextType = scanner.lookupType(1); | ||
if (nextType === IDENTIFIER || nextType === HYPHENMINUS) { | ||
break; | ||
} | ||
return getOperator(); | ||
case PLUSSIGN: | ||
return getOperator(); | ||
case NUMBER: | ||
var info = getInfo(); | ||
@@ -613,34 +630,18 @@ var number = readNumber(); | ||
if (number !== null) { | ||
if (type === PERCENTSIGN) { | ||
return getPercentage(info, number); | ||
} | ||
if (type === IDENTIFIER) { | ||
return getDimension(info, number); | ||
} | ||
return { | ||
type: 'Number', | ||
info: info, | ||
value: number | ||
}; | ||
if (type === PERCENTSIGN) { | ||
return getPercentage(info, number); | ||
} | ||
if (type === HYPHENMINUS) { | ||
var nextType = scanner.lookupType(1); | ||
if (nextType === IDENTIFIER || nextType === HYPHENMINUS) { | ||
break; | ||
} | ||
if (type === IDENTIFIER) { | ||
return getDimension(info, number); | ||
} | ||
if (type === HYPHENMINUS || | ||
type === PLUSSIGN) { | ||
return getOperator(); | ||
} | ||
return { | ||
type: 'Number', | ||
info: info, | ||
value: number | ||
}; | ||
scanner.error('Unexpected input'); | ||
default: | ||
scanner.error('Unexpected input'); | ||
scanner.error(); | ||
} | ||
@@ -704,3 +705,3 @@ | ||
node.name = getNamespacedIdentifier(true); | ||
node.name = getAttributeName(); | ||
@@ -749,3 +750,2 @@ readSC(); | ||
scanner.next(); | ||
readSC(); | ||
@@ -768,6 +768,2 @@ | ||
case NUMBERSIGN: // ?? | ||
child = getHash(); | ||
break; | ||
case LEFTPARENTHESIS: | ||
@@ -784,2 +780,6 @@ child = getParentheses(scope); | ||
case NUMBERSIGN: | ||
child = getHash(); | ||
break; | ||
case STRING: | ||
@@ -795,5 +795,3 @@ child = getString(); | ||
wasSpace = false; | ||
if (sequence.head !== null) { // ignore spaces in the beginning | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
sequence.appendData(SPACE_NODE); | ||
} | ||
@@ -836,3 +834,3 @@ | ||
// + | > | ~ | /deep/ | ||
// + | > | ~ | /deep/ | >> | ||
function getCombinator() { | ||
@@ -843,5 +841,11 @@ var info = getInfo(); | ||
switch (scanner.tokenType) { | ||
case WHITESPACE: | ||
combinator = DESCENDANT_COMBINATOR; | ||
case GREATERTHANSIGN: | ||
scanner.next(); | ||
if (scanner.tokenType === GREATERTHANSIGN) { | ||
combinator = '>>'; | ||
scanner.next(); | ||
} else { | ||
combinator = '>'; | ||
} | ||
break; | ||
@@ -851,3 +855,2 @@ | ||
case TILDE: | ||
case GREATERTHANSIGN: | ||
combinator = scanner.getTokenValue(); | ||
@@ -864,5 +867,2 @@ scanner.next(); | ||
break; | ||
default: | ||
scanner.error('Combinator (+, >, ~, /deep/) is expected'); | ||
} | ||
@@ -900,6 +900,2 @@ | ||
function readUnit() { | ||
if (scanner.tokenType !== IDENTIFIER) { | ||
scanner.error('Identifier is expected'); | ||
} | ||
var unit = scanner.getTokenValue(); | ||
@@ -978,3 +974,3 @@ var backSlashPos = unit.indexOf('\\'); | ||
return { | ||
type: scope === SCOPE_SELECTOR ? 'FunctionalPseudo' : 'Function', | ||
type: scope === SCOPE_SELECTOR ? 'PseudoClass' : 'Function', | ||
info: info, | ||
@@ -1037,4 +1033,4 @@ name: name, | ||
// ignore spaces in the beginning and around operator | ||
if (sequence.tail !== null && !nonSpaceOperator && !prevNonSpaceOperator) { | ||
// ignore spaces around operator | ||
if (!nonSpaceOperator && !prevNonSpaceOperator) { | ||
sequence.appendData(SPACE_NODE); | ||
@@ -1197,9 +1193,8 @@ } | ||
// https://drafts.csswg.org/css-syntax-3/#urange | ||
function scanUnicodeRange() { | ||
var hexStart = scanner.tokenStart; | ||
var hexStart = scanner.tokenStart + 1; // skip + | ||
var hexLength = 0; | ||
if (scanner.tokenType === DECIMALNUMBER) { | ||
scanner.next(); | ||
} | ||
scanner.next(); // always PLUSSIGN or NUMBER | ||
@@ -1210,3 +1205,3 @@ if (scanner.tokenType === HYPHENMINUS) { | ||
if (scanner.tokenType === DECIMALNUMBER) { | ||
if (scanner.tokenType === NUMBER) { | ||
scanner.next(); | ||
@@ -1219,5 +1214,3 @@ } | ||
hexLength = scanner.tokenStart - hexStart; | ||
if (hexLength === 0) { | ||
if (scanner.tokenStart === hexStart) { | ||
scanner.error('Unexpected input', hexStart); | ||
@@ -1232,8 +1225,22 @@ } | ||
if (isHex(code) === false && (code !== HYPHENMINUS || wasHyphenMinus)) { | ||
scanner.error('Unexpected input', hexStart + i); | ||
scanner.error('Unexpected input', i); | ||
} | ||
if (code === HYPHENMINUS) { | ||
// hex sequence shouldn't be an empty | ||
if (hexLength === 0) { | ||
scanner.error('Unexpected input', i); | ||
} | ||
wasHyphenMinus = true; | ||
hexLength = 0; | ||
} else { | ||
hexLength++; | ||
// to long hex sequence | ||
if (hexLength > 6) { | ||
scanner.error('Unexpected input', i); | ||
} | ||
} | ||
} | ||
@@ -1243,2 +1250,3 @@ | ||
if (!wasHyphenMinus) { | ||
// consume as many U+003F QUESTION MARK (?) code points as possible | ||
for (; hexLength < 6 && !scanner.eof; scanner.next()) { | ||
@@ -1260,3 +1268,3 @@ if (scanner.tokenType !== QUESTIONMARK) { | ||
scanner.skip(2); // U+ or u+ | ||
scanner.next(); // U or u | ||
scanUnicodeRange(); | ||
@@ -1293,3 +1301,3 @@ | ||
function getNamespacedIdentifier(checkColon) { | ||
function getAttributeName() { | ||
if (scanner.eof) { | ||
@@ -1301,19 +1309,27 @@ scanner.error('Unexpected end of input'); | ||
var start = scanner.tokenStart; | ||
var expectIdentifier = false; | ||
var checkColon = true; | ||
if (scanner.tokenType === ASTERISK) { | ||
expectIdentifier = true; | ||
checkColon = false; | ||
scanner.next(); | ||
} else { | ||
} else if (scanner.tokenType !== VERTICALLINE) { | ||
scanIdent(false); | ||
} | ||
if (scanner.tokenType === VERTICALLINE && scanner.lookupType(1) !== EQUALSSIGN) { | ||
scanner.next(); | ||
if (scanner.tokenType === VERTICALLINE) { | ||
if (scanner.lookupType(1) !== EQUALSSIGN) { | ||
scanner.next(); | ||
if (scanner.tokenType === HYPHENMINUS || scanner.tokenType === IDENTIFIER) { | ||
scanIdent(false); | ||
} else if (scanner.tokenType === ASTERISK) { | ||
checkColon = false; | ||
scanner.next(); | ||
if (scanner.tokenType === HYPHENMINUS || scanner.tokenType === IDENTIFIER) { | ||
scanIdent(false); | ||
} else { | ||
scanner.error('Identifier is expected'); | ||
} | ||
} else if (expectIdentifier) { | ||
scanner.error('Identifier is expected', scanner.tokenEnd); | ||
} | ||
} else if (expectIdentifier) { | ||
scanner.error('Vertical line is expected'); | ||
} | ||
@@ -1333,2 +1349,35 @@ | ||
function getTypeOrUniversal() { | ||
var info = getInfo(); | ||
var start = scanner.tokenStart; | ||
var universal = false; | ||
if (scanner.tokenType === ASTERISK) { | ||
universal = true; | ||
scanner.next(); | ||
} else if (scanner.tokenType !== VERTICALLINE) { | ||
scanIdent(false); | ||
} | ||
if (scanner.tokenType === VERTICALLINE) { | ||
universal = false; | ||
scanner.next(); | ||
if (scanner.tokenType === HYPHENMINUS || scanner.tokenType === IDENTIFIER) { | ||
scanIdent(false); | ||
} else if (scanner.tokenType === ASTERISK) { | ||
universal = true; | ||
scanner.next(); | ||
} else { | ||
scanner.error('Identifier or asterisk is expected'); | ||
} | ||
} | ||
return { | ||
type: universal ? 'Universal' : 'Type', | ||
info: info, | ||
name: scanner.substrToCursor(start) | ||
}; | ||
} | ||
function getIdentifier(varAllowed) { | ||
@@ -1354,2 +1403,3 @@ return { | ||
// https://drafts.csswg.org/css-syntax-3/#the-anb-type | ||
function getNthSelector() { | ||
@@ -1363,3 +1413,3 @@ var info = getInfo(); | ||
node = { | ||
type: 'FunctionalPseudo', | ||
type: 'PseudoClass', | ||
info: info, | ||
@@ -1384,12 +1434,10 @@ name: readIdent(false), | ||
} else { | ||
if (scanner.tokenType === HYPHENMINUS || | ||
scanner.tokenType === PLUSSIGN) { | ||
sequence.appendData(getOperator()); | ||
readSC(); | ||
} | ||
var prefix = ''; | ||
var start = scanner.tokenStart; | ||
var info = getInfo(); | ||
if (scanner.tokenType === DECIMALNUMBER) { | ||
if (scanner.tokenType === HYPHENMINUS || | ||
scanner.tokenType === PLUSSIGN || | ||
scanner.tokenType === NUMBER) { | ||
prefix = scanner.getTokenValue(); | ||
scanner.next(); | ||
@@ -1399,4 +1447,4 @@ } | ||
if (scanner.tokenType === IDENTIFIER) { | ||
if (!cmpStr(scanner.source, scanner.tokenStart, scanner.tokenStart + 1, 'n')) { | ||
scanner.error('Unexpected input'); | ||
if (!cmpChar(scanner.source, scanner.tokenStart, N)) { | ||
scanner.error(); | ||
} | ||
@@ -1407,3 +1455,3 @@ | ||
info: info, | ||
value: scanner.source.substring(start, scanner.tokenStart + 1) | ||
value: prefix + scanner.source.charAt(scanner.tokenStart) | ||
}); | ||
@@ -1445,4 +1493,11 @@ | ||
} else { | ||
scanner.next(); | ||
readSC(); | ||
if (scanner.tokenType !== NUMBER || | ||
cmpChar(scanner.source, scanner.tokenStart, PLUSSIGN) || | ||
cmpChar(scanner.source, scanner.tokenStart, HYPHENMINUS)) { | ||
scanner.error(); | ||
} | ||
sequence.appendData({ | ||
@@ -1453,6 +1508,7 @@ type: 'Nth', | ||
}); | ||
scanner.eat(DECIMALNUMBER); | ||
scanner.next(); | ||
} | ||
} else { | ||
prefix = ''; | ||
scanner.next(); | ||
@@ -1463,7 +1519,33 @@ readSC(); | ||
scanner.tokenType === PLUSSIGN) { | ||
sequence.appendData(getOperator()); | ||
info = getInfo(); | ||
prefix = scanner.getTokenValue(); | ||
scanner.next(); | ||
readSC(); | ||
} | ||
if (scanner.tokenType === NUMBER) { | ||
var sign = ''; | ||
if (cmpChar(scanner.source, scanner.tokenStart, PLUSSIGN) || | ||
cmpChar(scanner.source, scanner.tokenStart, HYPHENMINUS)) { | ||
info = getInfo(); | ||
sign = scanner.source.charAt(scanner.tokenStart); | ||
} | ||
// prefix or sign should be specified but not both | ||
if (!(prefix === '' ^ sign === '')) { | ||
scanner.error(); | ||
} | ||
if (sign) { | ||
scanner.tokenStart++; | ||
} | ||
sequence.appendData({ | ||
type: 'Operator', | ||
info: info, | ||
value: prefix || sign | ||
}); | ||
sequence.appendData({ | ||
type: 'Nth', | ||
@@ -1473,7 +1555,8 @@ info: getInfo(), | ||
}); | ||
scanner.eat(DECIMALNUMBER); | ||
scanner.next(); | ||
} | ||
} | ||
} else { | ||
if (scanner.tokenStart === start) { // no number | ||
if (prefix === '' || prefix === '-' || prefix === '+') { // no number | ||
scanner.error('Number or identifier is expected'); | ||
@@ -1485,3 +1568,3 @@ } | ||
info: info, | ||
value: scanner.substrToCursor(start) | ||
value: prefix | ||
}); | ||
@@ -1498,32 +1581,7 @@ } | ||
function readNumber() { | ||
var start = scanner.tokenStart; | ||
var wasDigits = false; | ||
var offset = 0; | ||
var tokenType = scanner.tokenType; | ||
var number = scanner.getTokenValue(); | ||
if (tokenType === HYPHENMINUS) { | ||
tokenType = scanner.lookupType(++offset); | ||
} | ||
scanner.eat(NUMBER); | ||
if (tokenType === DECIMALNUMBER) { | ||
wasDigits = true; | ||
tokenType = scanner.lookupType(++offset); | ||
} | ||
if (tokenType === FULLSTOP) { | ||
tokenType = scanner.lookupType(++offset); | ||
} | ||
if (tokenType === DECIMALNUMBER) { | ||
wasDigits = true; | ||
offset++; | ||
} | ||
if (wasDigits) { | ||
scanner.skip(offset); | ||
return scanner.substrToCursor(start); | ||
} | ||
return null; | ||
return number; | ||
} | ||
@@ -1648,4 +1706,15 @@ | ||
if (nextType === IDENTIFIER && cmpStr(scanner.source, scanner.tokenEnd, scanner.tokenEnd + 4, 'nth-')) { | ||
return getNthSelector(); | ||
if (nextType === IDENTIFIER) { | ||
// '::' starts a pseudo-element, ':' a pseudo-class | ||
// Exceptions: :first-line, :first-letter, :before and :after | ||
if (scanner.lookupValue(1, 'before') || | ||
scanner.lookupValue(1, 'after') || | ||
scanner.lookupValue(1, 'first-letter') || | ||
scanner.lookupValue(1, 'first-line')) { | ||
return getLegacyPseudoElement(); | ||
} | ||
if (cmpStr(scanner.source, scanner.tokenEnd, scanner.tokenEnd + 4, 'nth-')) { | ||
return getNthSelector(); | ||
} | ||
} | ||
@@ -1666,6 +1735,25 @@ | ||
info: info, | ||
name: readIdent(false) | ||
name: readIdent(false), | ||
legacy: false | ||
}; | ||
} | ||
// : ident | ||
// https://drafts.csswg.org/selectors-4/#grammar | ||
// Some older pseudo-elements (::before, ::after, ::first-line, and ::first-letter) | ||
// can, for legacy reasons, be written using the <pseudo-class-selector> grammar, | ||
// with only a single ":" character at their start. | ||
function getLegacyPseudoElement() { | ||
var info = getInfo(); | ||
scanner.eat(COLON); | ||
return { | ||
type: 'PseudoElement', | ||
info: info, | ||
name: readIdent(false), | ||
legacy: true | ||
}; | ||
} | ||
// : ( ident | function ) | ||
@@ -1686,3 +1774,4 @@ function getPseudoClass() { | ||
info: info, | ||
name: scanner.substrToCursor(start) | ||
name: scanner.substrToCursor(start), | ||
sequence: null | ||
}; | ||
@@ -1719,3 +1808,3 @@ } | ||
if (scanner.tokenType !== DECIMALNUMBER && | ||
if (scanner.tokenType !== NUMBER && | ||
scanner.tokenType !== IDENTIFIER) { | ||
@@ -1722,0 +1811,0 @@ scanner.error('Number or identifier is expected'); |
@@ -6,3 +6,3 @@ 'use strict'; | ||
var TokenName = require('./const').TokenName; | ||
var IS_PUNCTUATOR = require('./const').IS_PUNCTUATOR; | ||
var IS_PUNCTUATION = require('./const').IS_PUNCTUATION; | ||
var SYMBOL_CATEGORY = require('./const').SYMBOL_CATEGORY; | ||
@@ -16,6 +16,6 @@ var SYMBOL_CATEGORY_LENGTH = SYMBOL_CATEGORY.length; | ||
var IDENTIFIER = TokenType.Identifier; | ||
var NUMBER = TokenType.DecimalNumber; | ||
var NUMBER = TokenType.Number; | ||
var STRING = TokenType.String; | ||
var COMMENT = TokenType.Comment; | ||
var PUNCTUATOR = TokenType.Punctuator; | ||
var PUNCTUATION = TokenType.Punctuation; | ||
@@ -30,2 +30,6 @@ var TAB = 9; | ||
var BACK_SLASH = 92; | ||
var FULLSTOP = TokenType.FullStop; | ||
var PLUSSIGN = TokenType.PlusSign; | ||
var HYPHENMINUS = TokenType.HyphenMinus; | ||
var E = 101; // 'e'.charCodeAt(0) | ||
@@ -37,3 +41,3 @@ var MIN_ARRAY_SIZE = 16 * 1024; | ||
var offsetAndType = null; | ||
var offsetAndType = new LongArray(MIN_ARRAY_SIZE); | ||
var lines = null; | ||
@@ -45,2 +49,6 @@ | ||
function isNumber(code) { | ||
return code >= 48 && code <= 57; | ||
} | ||
function computeLines(scanner, source) { | ||
@@ -74,2 +82,14 @@ var sourceLength = source.length; | ||
function findLastNonSpaceLocation(scanner) { | ||
for (var i = scanner.source.length - 1; i >= 0; i--) { | ||
var code = scanner.source.charCodeAt(i); | ||
if (code !== SPACE && code !== TAB && code !== R && code !== N && code !== F) { | ||
break; | ||
} | ||
} | ||
return scanner.getLocation(i + 1); | ||
}; | ||
function isNewline(source, offset, code) { | ||
@@ -137,2 +157,36 @@ if (code === N || code === F || code === R) { | ||
function findNumberEnd(source, offset, allowFraction) { | ||
var code; | ||
offset = findDecimalNumberEnd(source, offset); | ||
// fraction: .\d+ | ||
if (allowFraction && offset + 1 < source.length && source.charCodeAt(offset) === FULLSTOP) { | ||
code = source.charCodeAt(offset + 1); | ||
if (isNumber(code)) { | ||
offset = findDecimalNumberEnd(source, offset + 1); | ||
} | ||
} | ||
// exponent: e[+-]\d+ | ||
if (offset + 1 < source.length) { | ||
if ((source.charCodeAt(offset) | 32) === E) { // case insensitive check for `e` | ||
code = source.charCodeAt(offset + 1); | ||
if (code === PLUSSIGN || code === HYPHENMINUS) { | ||
if (offset + 2 < source.length) { | ||
code = source.charCodeAt(offset + 2); | ||
} | ||
} | ||
if (isNumber(code)) { | ||
offset = findDecimalNumberEnd(source, offset + 2); | ||
} | ||
} | ||
} | ||
return offset; | ||
} | ||
// skip escaped unicode sequence that can ends with space | ||
@@ -167,3 +221,3 @@ // [0-9a-f]{1,6}(\r\n|[ \n\r\t\f])? | ||
offset = findEscaseEnd(source, offset + 1); | ||
} else if (code < SYMBOL_CATEGORY_LENGTH && IS_PUNCTUATOR[code] === PUNCTUATOR) { | ||
} else if (code < SYMBOL_CATEGORY_LENGTH && IS_PUNCTUATION[code] === PUNCTUATION) { | ||
break; | ||
@@ -180,11 +234,10 @@ } | ||
var prevType = 0; | ||
var start = startPos; | ||
var end; | ||
var offset = startPos; | ||
if (offsetAndType === null || offsetAndType.length < sourceLength + 1) { | ||
offsetAndType = new LongArray(Math.max(sourceLength + 1024, MIN_ARRAY_SIZE)); | ||
if (offsetAndType.length < sourceLength + 1) { | ||
offsetAndType = new LongArray(sourceLength + 1024); | ||
} | ||
while (start < sourceLength) { | ||
var code = source.charCodeAt(start); | ||
while (offset < sourceLength) { | ||
var code = source.charCodeAt(offset); | ||
var type = code < SYMBOL_CATEGORY_LENGTH ? SYMBOL_CATEGORY[code] : IDENTIFIER; | ||
@@ -194,16 +247,23 @@ | ||
case WHITESPACE: | ||
end = findWhitespaceEnd(source, start + 1); | ||
offset = findWhitespaceEnd(source, offset + 1); | ||
break; | ||
case PUNCTUATOR: | ||
case PUNCTUATION: | ||
if (code === STAR && prevType === SLASH) { // /* | ||
type = COMMENT; | ||
end = findCommentEnd(source, start + 1); | ||
offset = findCommentEnd(source, offset + 1); | ||
tokenCount--; // rewrite prev token | ||
} else { | ||
// edge case for -.123 and +.123 | ||
if (code === FULLSTOP && (prevType === PLUSSIGN || prevType === HYPHENMINUS)) { | ||
if (offset + 1 < sourceLength && isNumber(source.charCodeAt(offset + 1))) { | ||
type = NUMBER; | ||
offset = findNumberEnd(source, offset + 2, false); | ||
tokenCount--; | ||
break; | ||
} | ||
} | ||
// rewrite prevType token | ||
tokenCount--; | ||
start--; | ||
} else { | ||
type = code; | ||
end = start + 1; | ||
offset = offset + 1; | ||
} | ||
@@ -214,22 +274,23 @@ | ||
case NUMBER: | ||
end = findDecimalNumberEnd(source, start + 1); | ||
offset = findNumberEnd(source, offset + 1, prevType !== FULLSTOP); | ||
if (prevType === FULLSTOP || | ||
prevType === HYPHENMINUS || | ||
prevType === PLUSSIGN) { | ||
tokenCount--; // rewrite prev token | ||
} | ||
break; | ||
case STRING: | ||
end = findStringEnd(source, start + 1, code); | ||
offset = findStringEnd(source, offset + 1, code); | ||
break; | ||
default: | ||
end = findIdentifierEnd(source, start); | ||
offset = findIdentifierEnd(source, offset); | ||
} | ||
// console.log(type, scanner.source.substring(start, end)); | ||
offsetAndType[tokenCount] = (type << 24) | start; | ||
tokenCount++; | ||
start = end; | ||
offsetAndType[tokenCount++] = (type << 24) | offset; | ||
prevType = type; | ||
} | ||
offsetAndType[tokenCount] = end; | ||
offsetAndType[tokenCount] = offset; | ||
@@ -280,4 +341,4 @@ scanner.offsetAndType = offsetAndType; | ||
this.source, | ||
this.offsetAndType[offset - 1] & OFFSET_MASK, | ||
this.offsetAndType[offset] & OFFSET_MASK, | ||
this.offsetAndType[offset + 1] & OFFSET_MASK, | ||
referenceStr | ||
@@ -298,7 +359,2 @@ ); | ||
skip: function(tokenCount) { | ||
if (tokenCount === 1) { | ||
this.next(); | ||
return; | ||
} | ||
var next = this.currentToken + tokenCount; | ||
@@ -308,10 +364,8 @@ | ||
this.currentToken = next; | ||
this.tokenType = this.offsetAndType[next] >> 24; | ||
this.tokenStart = this.offsetAndType[next] & OFFSET_MASK; | ||
this.tokenEnd = this.offsetAndType[next + 1] & OFFSET_MASK; | ||
this.tokenStart = this.offsetAndType[next - 1] & OFFSET_MASK; | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
} else { | ||
this.currentToken = this.tokenCount; | ||
this.eof = true; | ||
this.tokenType = NULL; | ||
this.tokenStart = this.tokenEnd = this.source.length; | ||
this.next(); | ||
} | ||
@@ -324,5 +378,6 @@ }, | ||
this.currentToken = next; | ||
this.tokenType = this.offsetAndType[next] >> 24; | ||
this.tokenStart = this.tokenEnd; | ||
this.tokenEnd = this.offsetAndType[next + 1] & OFFSET_MASK; | ||
next = this.offsetAndType[next]; | ||
this.tokenType = next >> 24; | ||
this.tokenEnd = next & OFFSET_MASK; | ||
} else { | ||
@@ -385,21 +440,12 @@ this.currentToken = this.tokenCount; | ||
}, | ||
findLastNonSpaceLocation: function() { | ||
for (var i = (this.offsetAndType[this.currentToken] & OFFSET_MASK) - 1; i >= 0; i--) { | ||
var code = this.source.charCodeAt(i); | ||
if (code !== SPACE && code !== TAB && code !== R && code !== N && code !== F) { | ||
break; | ||
} | ||
} | ||
return this.getLocation(i + 1); | ||
}, | ||
error: function(message, offset) { | ||
var location = !this.eof | ||
? this.getLocation(typeof offset !== 'undefined' ? offset : this.tokenStart) | ||
: this.findLastNonSpaceLocation(); | ||
var location = typeof offset !== 'undefined' && offset < this.source.length | ||
? this.getLocation(offset) | ||
: this.eof | ||
? findLastNonSpaceLocation(this) | ||
: this.getLocation(this.tokenStart); | ||
throw new CssSyntaxError( | ||
message, | ||
message || 'Unexpected input', | ||
this.source, | ||
@@ -410,2 +456,8 @@ location.offset, | ||
); | ||
}, | ||
getTypes: function() { | ||
return Array.prototype.slice.call(this.offsetAndType, 0, this.tokenCount).map(function(item) { | ||
return TokenName[item >> 24]; | ||
}); | ||
} | ||
@@ -416,4 +468,4 @@ }; | ||
// fix soft deoptimizations (insufficient type feedback) | ||
new Scanner('\n\r\r\n\f//""\'\'/*\r\n\f*/1a;.\\31\t\+2{url(a)}'); | ||
new Scanner('\n\r\r\n\f//""\'\'/*\r\n\f*/1a;.\\31\t\+2{url(a);+1.2e3 -.4e-5 .6e+7}'); | ||
module.exports = Scanner; |
@@ -7,2 +7,13 @@ function isHex(code) { | ||
function cmpChar(testStr, offset, referenceCode) { | ||
var code = testStr.charCodeAt(offset); | ||
// code.toLowerCase() | ||
if (code >= 65 && code <= 90) { | ||
code = code | 32; | ||
} | ||
return code === referenceCode; | ||
} | ||
function cmpStr(testStr, start, end, referenceStr) { | ||
@@ -18,11 +29,11 @@ if (end - start !== referenceStr.length) { | ||
for (var i = start; i < end; i++) { | ||
var sourceCode = testStr.charCodeAt(i); | ||
var strCode = referenceStr.charCodeAt(i - start); | ||
var testCode = testStr.charCodeAt(i); | ||
var refCode = referenceStr.charCodeAt(i - start); | ||
// referenceStr[i].toLowerCase() | ||
if (sourceCode >= 65 && sourceCode <= 90) { | ||
sourceCode = sourceCode | 32; | ||
// testStr[i].toLowerCase() | ||
if (testCode >= 65 && testCode <= 90) { | ||
testCode = testCode | 32; | ||
} | ||
if (sourceCode !== strCode) { | ||
if (testCode !== refCode) { | ||
return false; | ||
@@ -41,4 +52,5 @@ } | ||
isHex: isHex, | ||
cmpChar: cmpChar, | ||
cmpStr: cmpStr, | ||
endsWith: endsWith | ||
}; |
var data = require('../../data'); | ||
var syntax = require('./syntax').create({ | ||
generic: true | ||
module.exports = require('./syntax').create({ | ||
generic: true, | ||
types: data.types, | ||
properties: data.properties | ||
}); | ||
for (var key in data.properties) { | ||
syntax.addProperty(key, data.properties[key]); | ||
} | ||
for (var key in data.types) { | ||
syntax.addType(key, data.types[key]); | ||
} | ||
module.exports = syntax; |
@@ -1,2 +0,1 @@ | ||
var List = require('../utils/list'); | ||
var names = require('../utils/names'); | ||
@@ -3,0 +2,0 @@ |
@@ -141,18 +141,3 @@ var MatchError = require('./error').MatchError; | ||
}, | ||
getAll: function() { | ||
var all = []; | ||
for (var name in this.types) { | ||
if (this.types[name].type !== 'generic') { | ||
all.push(this.types[name]); | ||
} | ||
} | ||
for (var name in this.properties) { | ||
all.push(this.properties[name]); | ||
} | ||
return all; | ||
}, | ||
validate: function() { | ||
@@ -159,0 +144,0 @@ function validate(syntax, name, broken, descriptor) { |
@@ -111,5 +111,2 @@ function each(list) { | ||
case 'Property': | ||
return node.name; | ||
case 'Value': | ||
@@ -141,5 +138,2 @@ return each(node.sequence); | ||
case 'FunctionalPseudo': | ||
return ':' + node.name + '(' + each(node.sequence) + ')'; | ||
case 'Function': | ||
@@ -166,2 +160,8 @@ return node.name + '(' + each(node.sequence) + ')'; | ||
case 'Type': | ||
return node.name; | ||
case 'Universal': | ||
return node.name; | ||
case 'Identifier': | ||
@@ -174,6 +174,10 @@ return node.name; | ||
case 'PseudoClass': | ||
return ':' + node.name; | ||
return node.sequence !== null | ||
? ':' + node.name + '(' + each(node.sequence) + ')' | ||
: ':' + node.name; | ||
case 'PseudoElement': | ||
return '::' + node.name; | ||
return node.legacy | ||
? ':' + node.name // :before, :after, :first-letter and :first-line | ||
: '::' + node.name; | ||
@@ -207,5 +211,2 @@ case 'Class': | ||
case 'Unknown': | ||
return node.value; | ||
case 'Percentage': | ||
@@ -212,0 +213,0 @@ return node.value + '%'; |
@@ -185,5 +185,2 @@ var SourceMapGenerator = require('source-map').SourceMapGenerator; | ||
case 'Property': | ||
return node.name; | ||
case 'Value': | ||
@@ -215,5 +212,2 @@ return each(node.sequence); | ||
case 'FunctionalPseudo': | ||
return ':' + node.name + '(' + each(node.sequence) + ')'; | ||
case 'Function': | ||
@@ -240,2 +234,8 @@ return node.name + '(' + each(node.sequence) + ')'; | ||
case 'Type': | ||
return node.name; | ||
case 'Universal': | ||
return node.name; | ||
case 'Identifier': | ||
@@ -248,6 +248,10 @@ return node.name; | ||
case 'PseudoClass': | ||
return ':' + node.name; | ||
return node.sequence !== null | ||
? ':' + node.name + '(' + each(node.sequence) + ')' | ||
: ':' + node.name; | ||
case 'PseudoElement': | ||
return '::' + node.name; | ||
return node.legacy | ||
? ':' + node.name // :before, :after, :first-letter and :first-line | ||
: '::' + node.name; | ||
@@ -281,5 +285,2 @@ case 'Class': | ||
case 'Unknown': | ||
return node.value; | ||
case 'Percentage': | ||
@@ -286,0 +287,0 @@ return node.value + '%'; |
@@ -140,3 +140,12 @@ function walkRules(node, item, list) { | ||
case 'FunctionalPseudo': | ||
case 'PseudoClass': | ||
if (node.sequence !== null) { | ||
this['function'] = node; | ||
node.sequence.each(walkAll, this); | ||
this['function'] = null; | ||
} | ||
break; | ||
case 'Function': | ||
@@ -176,2 +185,4 @@ this['function'] = node; | ||
// case 'Hash': | ||
// case 'Type': | ||
// case 'Universal': | ||
// case 'Identifier': | ||
@@ -178,0 +189,0 @@ // case 'UnicodeRange': |
{ | ||
"name": "css-tree", | ||
"version": "1.0.0-alpha6", | ||
"version": "1.0.0-alpha7", | ||
"description": "Detailed CSS parser", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
@@ -5,2 +5,3 @@ [![NPM version](https://img.shields.io/npm/v/css-tree.svg)](https://www.npmjs.com/package/css-tree) | ||
[![Join the CSSTree chat at https://gitter.im/csstree/csstree](https://badges.gitter.im/csstree/csstree.svg)](https://gitter.im/csstree/csstree) | ||
[![Twitter](https://img.shields.io/badge/Twitter-@csstree-blue.svg)](https://twitter.com/csstree) | ||
@@ -14,2 +15,3 @@ * [CSS syntax reference](https://csstree.github.io/docs/syntax.html) | ||
* [stylelint-csstree-validator](https://github.com/csstree/stylelint-validator) – plugin for stylelint to validate CSS | ||
* [Grunt plugin](https://github.com/sergejmueller/grunt-csstree-validator) | ||
* [Gulp plugin](https://github.com/csstree/gulp-csstree) | ||
@@ -189,2 +191,2 @@ * [Sublime plugin](https://github.com/csstree/SublimeLinter-contrib-csstree) | ||
[Template:CSSData](https://developer.mozilla.org/en-US/docs/Template:CSSData) by Mozilla Contributors is licensed under [CC-BY-SA 2.5] | ||
[Template:CSSData](https://developer.mozilla.org/en-US/docs/Template:CSSData) by Mozilla Contributors is licensed under [CC-BY-SA 2.5](http://creativecommons.org/licenses/by-sa/2.5/) |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
569647
13276
190
6