UNPKG

@extjs/sencha-cmd-linux-32

Version:

Productivity and performance optimization tool for building applications with Sencha Ext JS and Sencha Touch.

671 lines (600 loc) 18.3 kB
"use strict"; var Fashion = require('../export/Base.js'), Base = Fashion.Base; var Tokens = require('./Tokens.js'), Operator = Tokens.Operator, Ident = Tokens.Ident, StringLiteral = Tokens.StringLiteral, Percentage = Tokens.Percentage, Length = Tokens.Length, Time = Tokens.Time, Angle = Tokens.Angle, NumberLiteral = Tokens.NumberLiteral, Class = Tokens.Class, Hash = Tokens.Hash, Variable = Tokens.Variable, Directive = Tokens.Directive, EOF = Tokens.EOF; var _limit = 0xFF, _chars = new Array(_limit), // _EOF = new EOF(null); _EOF = null; /* 1 = alpha 2 = digit 4 = name 8 = hex */ for (var c = 0; c < _limit; c++) { var ch = String.fromCharCode(c), mask = 0; if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')) { mask |= 1; } if ((ch >= '0') && (ch <= '9')) { mask |= 2; } if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || (ch === '-') || (ch === '_') || (c >= 128 && c <= _limit && c !== 215 && c !== 247) || ch === '\\') { mask |= 4; } if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { mask |= 8; } _chars[c] = mask; } function isAlpha(ch) { return _chars[ch.charCodeAt(0)] & 1; } function isDigit(ch) { return _chars[ch.charCodeAt(0)] & 2; } // http://en.wikipedia.org/wiki/Latin-1 function isNameChar(ch) { return _chars[ch.charCodeAt(0)] & 4; } function isHexDigit(ch) { return _chars[ch.charCodeAt(0)] & 8; } // px, pt, pc, cm, mm, in, em, rem, ex, vw, vh, vmin, vmax function isLength(unit) { var ch1 = unit.charAt(0).toLowerCase(), ch2 = unit.charAt(1).toLowerCase(), ch3 = unit.charAt(2).toLowerCase(), ch4 = unit.charAt(3).toLowerCase(); if (ch1 === 'p') { return (ch2 === 'x' || ch2 === 't' || ch2 === 'c'); } if (ch2 === 'm') { if (ch1 === 'c' || ch1 === 'm' || ch1 === 'e'){ return 2; } } if (ch2 === 'x') { return ch1 === 'e'; } if (ch3 === 'm') { if (ch1 === 'r' && ch2 === 'e') { // return the length of the unit return 3; } } if (ch1 === 'x' && isHexDigit(ch2)) { var len = 1; while (isHexDigit(unit[len])) { len++; } return len; } if (ch1 === 'v') { if (ch2 === 'w' || ch2 === 'h') { return 2; } if (ch2 === 'm') { if (ch3 === 'i' && ch4 === 'n') { return 4; } if (ch3 === 'a' && ch4 === 'x') { return 4; } } } return false; } // s, ms function isTime(unit) { if (unit.length === 1) { return unit === 's'; } else if (unit.length === 2) { return unit === 'ms'; } return false; } // deg, rad function isAngle(unit) { var ch = unit[0]; if (ch === 'd' || ch === 'D') { return unit.toLowerCase() === 'deg'; } if (ch === 'r' || ch === 'R') { return unit.toLowerCase() === 'rad'; } return false; } function debug(message) { //console.log(message); } function info(message) { //console.log(message); } class Scanner extends Base { constructor(style, file) { super(); this.style = style; this.lineNumber = this.style.length ? 1 : 0; this.currentFile = file || Fashion.currentFile; this.docs = []; this.tokenBuff = []; } next(advance) { var me = this, start = me.index, startLine = me.lineNumber, token = !advance && me.tokenBuff.shift(); if (!token) { token = me._advance(); if (token) { token.startIdx = start; token.startLine = startLine; } } return token; } // Get the next token and return it. // Loosely based on http://www.w3.org/TR/CSS2/grammar.html#scanner // TODO: nonascii, badcomments, escape _advance() { var me = this, style = me.style, length = style.length, ch, ch2, ch3, start, str, level, negate, charOffset, value; // Go past white space, block comment, and single-line comment while (true) { ch = style[me.index]; // Skip white space or any other control characters while (me.index < length && (ch <= ' ' || ch >= 128)) { if (ch === '\n') { me.lineNumber += 1; me.start = me.index; } me.index += 1; ch = style[me.index]; } ch2 = style[me.index + 1]; // Block comment if (ch === '/' && ch2 === '*') { me.index += 1; start = me.index + 1; while (me.index < length) { ch = style[me.index]; ch2 = style[me.index + 1]; if (ch === '\n') { me.lineNumber += 1; me.start = me.index; } if (ch === '*' && ch2 === '/') { me.index += 2; break; } me.index += 1; } me.docs.push(style.substring(start - 2, me.index)); continue; } // Single-line comment if (ch === '/' && ch2 === '/') { me.index += 1; start = me.index; while (me.index < length) { ch = style[me.index]; if (ch === '\r' || ch === '\n') { break; } me.index += 1; } me.docs.push(style.substring(start - 1, me.index)); continue; } break; } start = me.index; if (start >= length) { return _EOF; } ch = style[me.index]; ch2 = style[me.index + 1]; ch3 = style[me.index + 2]; // Identifier if ( (isNameChar(ch) && !isDigit(ch) && ch !== '-') || (ch === '-' && isNameChar(ch2) && !isDigit(ch2)) || (ch === '#' && ch2 === '{') ) { level = 0; me.index += 1; if (ch === '#' && ch2 === '{') { level += 1; me.index += 1; } if (ch === '\\') { // automatically consume the escaped character me.index += 1; } while (me.index < length) { ch = style[me.index]; ch2 = style[me.index + 1]; if (isNameChar(ch)) { me.index += 1; continue; } if (ch === '\\') { me.index += 2; continue; } if (ch == ">") { me.index += 1; //level += 1; continue; } if (ch === '#' && ch2 === '{') { level += 1; me.index += 2; continue; } if (level > 0) { me.index += 1; if (ch === '}') { level -= 1; } continue; } break; } str = style.substring(start, me.index).toLowerCase(); if (str === 'or' || str === 'and' || str === 'not') { return new Operator(me, str); } return new Ident(me, style.substring(start, me.index)); } // String if ((ch === '\'' || ch === '"') || (ch === '\\' && (ch2 === "'" || ch2 === '"'))) { // quotes may be escaped charOffset = (ch === '\\') ? 2 : 1; // quotes may be escaped. me.index += charOffset; start = me.index; var openCh = (ch === '\\') ? ch2 : ch; level = 0; var buff = ''; while (me.index < length) { ch = style[me.index]; me.index++; if (ch === '\\') { ch2 = style[me.index]; if (ch2 === '\n' || ch2 === "\r") { me.index++; continue; } buff += ch; ch = style[me.index]; me.index++; if (!level && charOffset === 2 && openCh === style[me.index]) { break; } } else if (ch === '#') { if (style[me.index] === '{') { level++; } } else if (ch === '}') { if (level) { level--; } } else if (!level && ch === openCh) { break; } buff += ch; } return new StringLiteral(me, buff, style[start - 1]); } // Number if (isDigit(ch) || (ch === '.' && isDigit(ch2)) || (ch === '-' && isDigit(ch2)) || (ch === '-' && ch2 === '.' && isDigit(ch3))) { if (ch === '-') { me.index += 1; } me.index += 1; while (me.index < length) { ch = style[me.index]; if (ch < '0' || ch > '9') { break; } me.index += 1; } if (ch === '\\') { me.index += 1; ch = style[me.index]; } if (ch === '.') { me.index += 1; while (me.index < length) { ch = style[me.index]; if (ch < '0' || ch > '9') { break; } me.index += 1; } } // Percentage if (ch === '%') { me.index += 1; var pcnt = new Percentage(me, style.substring(start, me.index)); pcnt.start = start; pcnt.end = me.index; return pcnt; } // Length if (ch !== ' ') { var unitLen = isLength(style.substr(me.index, 10)); if (unitLen) { me.index += (unitLen === true) ? 2 : unitLen; return new Length(me, style.substring(start, me.index)); } if (isTime(style.substr(me.index, 1))) { me.index += 1; return new Time(me, style.substring(start, me.index)); } if (isTime(style.substr(me.index, 2))) { me.index += 2; return new Time(me, style.substring(start, me.index)); } if (isAngle(style.substr(me.index, 3))) { me.index += 3; return new Angle(me, style.substring(start, me.index)); } } return new NumberLiteral(me, style.substring(start, me.index)); } // Class if (ch === '.') { level = 0; me.index += 1; ch = style[me.index]; if (ch === '{') { level += 1; me.index += 1; } while (me.index < length) { ch = style[me.index]; ch2 = style[me.index + 1]; if (isNameChar(ch)) { me.index += 1; continue; } if (ch === '#' && ch2 === '{') { level += 1; me.index += 2; continue; } if (level > 0) { me.index += 1; if (ch === '}') { level -= 1; } continue; } break; } return new Class(me, style.substring(start, me.index)); } // Hash if (ch === '#') { level = 0; me.index += 1; ch = style[me.index]; if (ch === '{') { level += 1; me.index += 1; } while (me.index < length) { ch = style[me.index]; ch2 = style[me.index + 1]; if (isNameChar(ch)) { me.index += 1; continue; } if (ch === '#' && ch2 === '{') { level += 1; me.index += 2; continue; } if (level > 0) { me.index += 1; if (ch === '}') { level -= 1; } continue; } break; } return new Hash(me, style.substring(start, me.index)); } // Variable if (ch === '$' || (ch === '-' && ch2 === '$')) { if (ch === '-') { negate = true; start += 1; me.index += 1; } me.index += 1; while (me.index < length) { ch = style[me.index]; if (isNameChar(ch)) { me.index += 1; } else { break; } } return new Variable(me, style.substring(start, me.index), negate); } // Directive, e.g. @import if (ch === '@') { me.index += 1; while (me.index < length) { ch = style[me.index]; if (!isAlpha(ch) && ch !== '-') { break; } me.index += 1; } value = style.substring(start, me.index); // If the value is not a SASS directive, then treat it as an identifier // This prevents a parsing error on CSS @-rules like @font-face // id: "@", return me.directives[value] ? new Directive(me, value) : new Ident(me, value); } // Fallback to single-character or two-character operator me.index += 1; str = ch; if (ch === '=' && ch2 === '=') { str = '=='; me.index += 1; } if (ch === '~' && ch2 === '=') { str = '~='; me.index += 1; } if (ch === '|' && ch2 === '=') { str = '|='; me.index += 1; } if (ch === '^' && ch2 === '=') { str = '^='; me.index += 1; } if (ch === '$' && ch2 === '=') { str = '$='; me.index += 1; } if (ch === '*' && ch2 === '=') { str = '*='; me.index += 1; } if (ch === '!' && ch2 === '=') { str = '!='; me.index += 1; } if (ch === '<' && ch2 === '=') { str = '<='; me.index += 1; } if (ch === '>' && ch2 === '=') { str = '>='; me.index += 1; } return new Operator(me, str); } // next() flushDocs() { if (this.docs.length > 0) { var docs = this.docs; this.docs = []; return docs; } return null; } // Lookahead the next token (without consuming it). peek(i) { var buff = this.tokenBuff, len = buff.length; i = i || 1; for (var x = len; x < i; x++) { buff.push(this.next(true)); } return buff[i - 1] || _EOF; } setIndex(index) { this.index = index; this.tokenBuff = []; } // Check if the next token matches the expected operator. // If not, throw an exception. expect(op) { var token = this.next(), lineNo = this.lineNumber - 1, fileName = this.currentFile || "sass-content", message = [ 'Expected \'', op, '\' but saw \'', token ? token.value : '(null token)', '\'', ' => ', fileName, ':', lineNo + 1, ':', this.index - this.start + 1 ].join(''); if (!token) { Fashion.raise(message); } if (!token.isOperator || token.value !== op) { Fashion.raise(message); } } } Fashion.apply(Scanner.prototype, { isFashionScanner: true, // The list of SASS directives. Everything else beginning with "@" will be // assumed to be a css @-rule, an treated as an identifier. e.g. @font-face // treated as a normal identifier with no special processing for now. directives: { "@charset": true, "@import": true, "@extend": true, "@debug": true, "@warn": true, "@error": true, "@if": true, "@else": true, "@for": true, "@each": true, "@while": true, "@mixin": true, "@include": true, "@function": true, "@return": true, "@debugger": true, "@elseif": true, "@content": true, "@require": true }, index: 0, docs: undefined, start: undefined, style: undefined, currentFile: undefined, lineNumber: undefined }); Scanner.EOF = _EOF; module.exports = Scanner;