motion
Version:
motion - moving development forward
558 lines (509 loc) • 20.3 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
var _selectorsRoot = require('./selectors/root');
var _selectorsRoot2 = _interopRequireDefault(_selectorsRoot);
var _selectorsSelector = require('./selectors/selector');
var _selectorsSelector2 = _interopRequireDefault(_selectorsSelector);
var _selectorsClassName = require('./selectors/className');
var _selectorsClassName2 = _interopRequireDefault(_selectorsClassName);
var _selectorsComment = require('./selectors/comment');
var _selectorsComment2 = _interopRequireDefault(_selectorsComment);
var _selectorsId = require('./selectors/id');
var _selectorsId2 = _interopRequireDefault(_selectorsId);
var _selectorsTag = require('./selectors/tag');
var _selectorsTag2 = _interopRequireDefault(_selectorsTag);
var _selectorsString = require('./selectors/string');
var _selectorsString2 = _interopRequireDefault(_selectorsString);
var _selectorsPseudo = require('./selectors/pseudo');
var _selectorsPseudo2 = _interopRequireDefault(_selectorsPseudo);
var _selectorsAttribute = require('./selectors/attribute');
var _selectorsAttribute2 = _interopRequireDefault(_selectorsAttribute);
var _selectorsUniversal = require('./selectors/universal');
var _selectorsUniversal2 = _interopRequireDefault(_selectorsUniversal);
var _selectorsCombinator = require('./selectors/combinator');
var _selectorsCombinator2 = _interopRequireDefault(_selectorsCombinator);
var _sortAscending = require('./sortAscending');
var _sortAscending2 = _interopRequireDefault(_sortAscending);
var _tokenize = require('./tokenize');
var _tokenize2 = _interopRequireDefault(_tokenize);
var _flatten = require('flatten');
var _flatten2 = _interopRequireDefault(_flatten);
var _indexesOf = require('indexes-of');
var _indexesOf2 = _interopRequireDefault(_indexesOf);
var _uniq = require('uniq');
var _uniq2 = _interopRequireDefault(_uniq);
var Parser = (function () {
function Parser(input) {
_classCallCheck(this, Parser);
this.input = input;
this.position = 0;
this.root = new _selectorsRoot2['default']();
var selectors = new _selectorsSelector2['default']();
this.root.append(selectors);
this.current = selectors;
this.tokens = (0, _tokenize2['default'])(input);
return this.loop();
}
_createClass(Parser, [{
key: 'attribute',
value: function attribute() {
var attribute = '';
var attr = undefined;
var startingToken = this.currToken;
this.position++;
while (this.position < this.tokens.length && this.currToken[0] !== ']') {
attribute += this.tokens[this.position][1];
this.position++;
}
if (this.position === this.tokens.length && ! ~attribute.indexOf(']')) {
this.error('Expected a closing square bracket.');
}
var parts = attribute.split(/((?:[*~^$|]?)=)/);
var namespace = parts[0].split(/(\|)/g);
var attributeProps = {
operator: parts[1],
value: parts[2],
source: {
start: {
line: startingToken[2],
column: startingToken[3]
},
end: {
line: this.currToken[2],
column: this.currToken[3]
}
},
sourceIndex: startingToken[4]
};
if (namespace.length > 1) {
if (namespace[0] === '') {
namespace[0] = true;
}
attributeProps.attribute = namespace[2];
attributeProps.namespace = namespace[0];
} else {
attributeProps.attribute = parts[0];
}
attr = new _selectorsAttribute2['default'](attributeProps);
if (parts[2]) {
var insensitive = parts[2].split(/(\s+i\s*?)$/);
attr.value = insensitive[0];
if (insensitive[1]) {
attr.insensitive = true;
attr.raw.insensitive = insensitive[1];
}
}
this.newNode(attr);
this.position++;
}
}, {
key: 'combinator',
value: function combinator() {
if (this.currToken[1] === '|') {
return this.namespace();
}
var combinator = new _selectorsCombinator2['default']({
value: '',
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[2],
column: this.currToken[3]
}
},
sourceIndex: this.currToken[4]
});
while (this.position < this.tokens.length && this.currToken && (this.currToken[0] === 'space' || this.currToken[0] === 'combinator')) {
if (this.nextToken && this.nextToken[0] === 'combinator') {
combinator.spaces.before = this.currToken[1];
combinator.source.start.line = this.nextToken[2];
combinator.source.start.column = this.nextToken[3];
combinator.source.end.column = this.nextToken[3];
combinator.source.end.line = this.nextToken[2];
combinator.sourceIndex = this.nextToken[4];
} else if (this.prevToken && this.prevToken[0] === 'combinator') {
combinator.spaces.after = this.currToken[1];
} else if (this.currToken[0] === 'space' || this.currToken[0] === 'combinator') {
combinator.value = this.currToken[1];
}
this.position++;
}
return this.newNode(combinator);
}
}, {
key: 'comma',
value: function comma() {
if (this.position === this.tokens.length - 1) {
this.root.trailingComma = true;
this.position++;
return;
}
var selectors = new _selectorsSelector2['default']();
this.current.parent.append(selectors);
this.current = selectors;
this.position++;
}
}, {
key: 'comment',
value: function comment() {
var comment = new _selectorsComment2['default']({
value: this.currToken[1],
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[4],
column: this.currToken[5]
}
},
sourceIndex: this.currToken[6]
});
this.newNode(comment);
this.position++;
}
}, {
key: 'error',
value: function error(message) {
throw new this.input.error(message);
}
}, {
key: 'namespace',
value: function namespace() {
var before = this.prevToken && this.prevToken[1] || true;
if (this.nextToken[0] === 'word') {
this.position++;
return this.word(before);
} else if (this.nextToken[0] === '*') {
this.position++;
return this.universal(before);
}
}
}, {
key: 'parentheses',
value: function parentheses() {
var last = this.current.last;
if (last && last.type === 'pseudo') {
var selector = new _selectorsSelector2['default']();
var cache = this.current;
last.append(selector);
this.current = selector;
var balanced = 1;
this.position++;
while (this.position < this.tokens.length && balanced) {
if (this.currToken[0] === '(') {
balanced++;
}
if (this.currToken[0] === ')') {
balanced--;
}
if (balanced) {
this.parse();
} else {
selector.parent.source.end.line = this.currToken[2];
selector.parent.source.end.column = this.currToken[3];
this.position++;
}
}
if (balanced) {
this.error('Expected closing parenthesis.');
}
this.current = cache;
} else {
var balanced = 1;
this.position++;
last.value += '(';
while (this.position < this.tokens.length && balanced) {
if (this.currToken[0] === '(') {
balanced++;
}
if (this.currToken[0] === ')') {
balanced--;
}
last.value += this.currToken[1];
this.position++;
}
if (balanced) {
this.error('Expected closing parenthesis.');
}
}
}
}, {
key: 'pseudo',
value: function pseudo() {
var _this = this;
var pseudoStr = '';
var startingToken = this.currToken;
while (this.currToken && this.currToken[0] === ':') {
pseudoStr += this.currToken[1];
this.position++;
}
if (!this.currToken) {
return this.error('Expected pseudo-class or pseudo-element');
}
if (this.currToken[0] === 'word') {
(function () {
var pseudo = undefined;
_this.splitWord(false, function (first, length) {
pseudoStr += first;
pseudo = new _selectorsPseudo2['default']({
value: pseudoStr,
source: {
start: {
line: startingToken[2],
column: startingToken[3]
},
end: {
line: _this.currToken[4],
column: _this.currToken[5]
}
},
sourceIndex: startingToken[4]
});
_this.newNode(pseudo);
if (length > 1 && _this.nextToken && _this.nextToken[0] === '(') {
_this.error('Misplaced parenthesis.');
}
});
})();
} else {
this.error('Unexpected "' + this.currToken[0] + '" found.');
}
}
}, {
key: 'space',
value: function space() {
var token = this.currToken;
// Handle space before and after the selector
if (this.position === 0 || this.prevToken[0] === ',' || this.prevToken[0] === '(') {
this.spaces = token[1];
this.position++;
} else if (this.position === this.tokens.length - 1 || this.nextToken[0] === ',' || this.nextToken[0] === ')') {
this.current.last.spaces.after = token[1];
this.position++;
} else {
this.combinator();
}
}
}, {
key: 'string',
value: function string() {
var token = this.currToken;
this.newNode(new _selectorsString2['default']({
value: this.currToken[1],
source: {
start: {
line: token[2],
column: token[3]
},
end: {
line: token[4],
column: token[5]
}
},
sourceIndex: token[6]
}));
this.position++;
}
}, {
key: 'universal',
value: function universal(namespace) {
var nextToken = this.nextToken;
if (nextToken && nextToken[1] === '|') {
this.position++;
return this.namespace();
}
this.newNode(new _selectorsUniversal2['default']({
value: this.currToken[1],
source: {
start: {
line: this.currToken[2],
column: this.currToken[3]
},
end: {
line: this.currToken[2],
column: this.currToken[3]
}
},
sourceIndex: this.currToken[4]
}), namespace);
this.position++;
}
}, {
key: 'splitWord',
value: function splitWord(namespace, firstCallback) {
var _this2 = this;
var nextToken = this.nextToken;
var word = this.currToken[1];
while (nextToken && nextToken[0] === 'word') {
this.position++;
var current = this.currToken[1];
word += current;
if (current.lastIndexOf('\\') === current.length - 1) {
var next = this.nextToken;
if (next && next[0] === 'space') {
word += next[1];
this.position++;
}
}
nextToken = this.nextToken;
}
var hasClass = (0, _indexesOf2['default'])(word, '.');
var hasId = (0, _indexesOf2['default'])(word, '#');
var indices = (0, _sortAscending2['default'])((0, _uniq2['default'])((0, _flatten2['default'])([[0], hasClass, hasId])));
indices.forEach(function (ind, i) {
var index = indices[i + 1] || word.length;
var value = word.slice(ind, index);
if (i === 0 && firstCallback) {
return firstCallback.call(_this2, value, indices.length);
}
var node = undefined;
if (~hasClass.indexOf(ind)) {
node = new _selectorsClassName2['default']({
value: value.slice(1),
source: {
start: {
line: _this2.currToken[2],
column: _this2.currToken[3] + ind
},
end: {
line: _this2.currToken[4],
column: _this2.currToken[3] + (index - 1)
}
},
sourceIndex: _this2.currToken[6] + indices[i]
});
} else if (~hasId.indexOf(ind)) {
node = new _selectorsId2['default']({
value: value.slice(1),
source: {
start: {
line: _this2.currToken[2],
column: _this2.currToken[3] + ind
},
end: {
line: _this2.currToken[4],
column: _this2.currToken[3] + (index - 1)
}
},
sourceIndex: _this2.currToken[6] + indices[i]
});
} else {
node = new _selectorsTag2['default']({
value: value,
source: {
start: {
line: _this2.currToken[2],
column: _this2.currToken[3] + ind
},
end: {
line: _this2.currToken[4],
column: _this2.currToken[3] + (index - 1)
}
},
sourceIndex: _this2.currToken[6] + indices[i]
});
}
_this2.newNode(node, namespace);
});
this.position++;
}
}, {
key: 'word',
value: function word(namespace) {
var nextToken = this.nextToken;
if (nextToken && nextToken[1] === '|') {
this.position++;
return this.namespace();
}
return this.splitWord(namespace);
}
}, {
key: 'loop',
value: function loop() {
while (this.position < this.tokens.length) {
this.parse();
}
return this.root;
}
}, {
key: 'parse',
value: function parse() {
switch (this.currToken[0]) {
case 'space':
this.space();
break;
case 'comment':
this.comment();
break;
case '(':
this.parentheses();
break;
case '[':
this.attribute();
break;
case 'at-word':
case 'word':
this.word();
break;
case ':':
this.pseudo();
break;
case ',':
this.comma();
break;
case '*':
this.universal();
break;
case 'combinator':
this.combinator();
break;
case 'string':
this.string();
break;
}
}
/**
* Helpers
*/
}, {
key: 'newNode',
value: function newNode(node, namespace) {
if (namespace) {
node.namespace = namespace;
}
if (this.spaces) {
node.spaces.before = this.spaces;
this.spaces = '';
}
return this.current.append(node);
}
}, {
key: 'currToken',
get: function get() {
return this.tokens[this.position];
}
}, {
key: 'nextToken',
get: function get() {
return this.tokens[this.position + 1];
}
}, {
key: 'prevToken',
get: function get() {
return this.tokens[this.position - 1];
}
}]);
return Parser;
})();
exports['default'] = Parser;
module.exports = exports['default'];