aboutsummaryrefslogtreecommitdiff
path: root/node_modules/csso/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/csso/lib/utils')
-rw-r--r--node_modules/csso/lib/utils/clone.js23
-rw-r--r--node_modules/csso/lib/utils/list.js389
-rw-r--r--node_modules/csso/lib/utils/names.js73
-rw-r--r--node_modules/csso/lib/utils/translate.js178
-rw-r--r--node_modules/csso/lib/utils/translateWithSourceMap.js291
-rw-r--r--node_modules/csso/lib/utils/walk.js189
6 files changed, 1143 insertions, 0 deletions
diff --git a/node_modules/csso/lib/utils/clone.js b/node_modules/csso/lib/utils/clone.js
new file mode 100644
index 00000000..7592e9e4
--- /dev/null
+++ b/node_modules/csso/lib/utils/clone.js
@@ -0,0 +1,23 @@
+var List = require('./list');
+
+module.exports = function clone(node) {
+ var result = {};
+
+ for (var key in node) {
+ var value = node[key];
+
+ if (value) {
+ if (Array.isArray(value)) {
+ value = value.slice(0);
+ } else if (value instanceof List) {
+ value = new List(value.map(clone));
+ } else if (value.constructor === Object) {
+ value = clone(value);
+ }
+ }
+
+ result[key] = value;
+ }
+
+ return result;
+};
diff --git a/node_modules/csso/lib/utils/list.js b/node_modules/csso/lib/utils/list.js
new file mode 100644
index 00000000..30d14871
--- /dev/null
+++ b/node_modules/csso/lib/utils/list.js
@@ -0,0 +1,389 @@
+//
+// item item item item
+// /------\ /------\ /------\ /------\
+// | data | | data | | data | | data |
+// null <--+-prev |<---+-prev |<---+-prev |<---+-prev |
+// | next-+--->| next-+--->| next-+--->| next-+--> null
+// \------/ \------/ \------/ \------/
+// ^ ^
+// | list |
+// | /------\ |
+// \--------------+-head | |
+// | tail-+--------------/
+// \------/
+//
+
+function createItem(data) {
+ return {
+ data: data,
+ next: null,
+ prev: null
+ };
+}
+
+var List = function(values) {
+ this.cursor = null;
+ this.head = null;
+ this.tail = null;
+
+ if (Array.isArray(values)) {
+ var cursor = null;
+
+ for (var i = 0; i < values.length; i++) {
+ var item = createItem(values[i]);
+
+ if (cursor !== null) {
+ cursor.next = item;
+ } else {
+ this.head = item;
+ }
+
+ item.prev = cursor;
+ cursor = item;
+ }
+
+ this.tail = cursor;
+ }
+};
+
+Object.defineProperty(List.prototype, 'size', {
+ get: function() {
+ var size = 0;
+ var cursor = this.head;
+
+ while (cursor) {
+ size++;
+ cursor = cursor.next;
+ }
+
+ return size;
+ }
+});
+
+List.createItem = createItem;
+List.prototype.createItem = createItem;
+
+List.prototype.toArray = function() {
+ var cursor = this.head;
+ var result = [];
+
+ while (cursor) {
+ result.push(cursor.data);
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+List.prototype.toJSON = function() {
+ return this.toArray();
+};
+
+List.prototype.isEmpty = function() {
+ return this.head === null;
+};
+
+List.prototype.first = function() {
+ return this.head && this.head.data;
+};
+
+List.prototype.last = function() {
+ return this.tail && this.tail.data;
+};
+
+List.prototype.each = function(fn, context) {
+ var item;
+ var cursor = {
+ prev: null,
+ next: this.head,
+ cursor: this.cursor
+ };
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ this.cursor = cursor;
+
+ while (cursor.next !== null) {
+ item = cursor.next;
+ cursor.next = item.next;
+
+ fn.call(context, item.data, item, this);
+ }
+
+ // pop cursor
+ this.cursor = this.cursor.cursor;
+};
+
+List.prototype.eachRight = function(fn, context) {
+ var item;
+ var cursor = {
+ prev: this.tail,
+ next: null,
+ cursor: this.cursor
+ };
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ this.cursor = cursor;
+
+ while (cursor.prev !== null) {
+ item = cursor.prev;
+ cursor.prev = item.prev;
+
+ fn.call(context, item.data, item, this);
+ }
+
+ // pop cursor
+ this.cursor = this.cursor.cursor;
+};
+
+List.prototype.nextUntil = function(start, fn, context) {
+ if (start === null) {
+ return;
+ }
+
+ var item;
+ var cursor = {
+ prev: null,
+ next: start,
+ cursor: this.cursor
+ };
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ this.cursor = cursor;
+
+ while (cursor.next !== null) {
+ item = cursor.next;
+ cursor.next = item.next;
+
+ if (fn.call(context, item.data, item, this)) {
+ break;
+ }
+ }
+
+ // pop cursor
+ this.cursor = this.cursor.cursor;
+};
+
+List.prototype.prevUntil = function(start, fn, context) {
+ if (start === null) {
+ return;
+ }
+
+ var item;
+ var cursor = {
+ prev: start,
+ next: null,
+ cursor: this.cursor
+ };
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ // push cursor
+ this.cursor = cursor;
+
+ while (cursor.prev !== null) {
+ item = cursor.prev;
+ cursor.prev = item.prev;
+
+ if (fn.call(context, item.data, item, this)) {
+ break;
+ }
+ }
+
+ // pop cursor
+ this.cursor = this.cursor.cursor;
+};
+
+List.prototype.some = function(fn, context) {
+ var cursor = this.head;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ while (cursor !== null) {
+ if (fn.call(context, cursor.data, cursor, this)) {
+ return true;
+ }
+
+ cursor = cursor.next;
+ }
+
+ return false;
+};
+
+List.prototype.map = function(fn, context) {
+ var result = [];
+ var cursor = this.head;
+
+ if (context === undefined) {
+ context = this;
+ }
+
+ while (cursor !== null) {
+ result.push(fn.call(context, cursor.data, cursor, this));
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.copy = function() {
+ var result = new List();
+ var cursor = this.head;
+
+ while (cursor !== null) {
+ result.insert(createItem(cursor.data));
+ cursor = cursor.next;
+ }
+
+ return result;
+};
+
+List.prototype.updateCursors = function(prevOld, prevNew, nextOld, nextNew) {
+ var cursor = this.cursor;
+
+ while (cursor !== null) {
+ if (prevNew === true || cursor.prev === prevOld) {
+ cursor.prev = prevNew;
+ }
+
+ if (nextNew === true || cursor.next === nextOld) {
+ cursor.next = nextNew;
+ }
+
+ cursor = cursor.cursor;
+ }
+};
+
+List.prototype.insert = function(item, before) {
+ if (before !== undefined && before !== null) {
+ // prev before
+ // ^
+ // item
+ this.updateCursors(before.prev, item, before, item);
+
+ if (before.prev === null) {
+ // insert to the beginning of list
+ if (this.head !== before) {
+ throw new Error('before doesn\'t below to list');
+ }
+
+ // since head points to before therefore list doesn't empty
+ // no need to check tail
+ this.head = item;
+ before.prev = item;
+ item.next = before;
+
+ this.updateCursors(null, item);
+ } else {
+
+ // insert between two items
+ before.prev.next = item;
+ item.prev = before.prev;
+
+ before.prev = item;
+ item.next = before;
+ }
+ } else {
+ // tail
+ // ^
+ // item
+ this.updateCursors(this.tail, item, null, item);
+
+ // insert to end of the list
+ if (this.tail !== null) {
+ // if list has a tail, then it also has a head, but head doesn't change
+
+ // last item -> new item
+ this.tail.next = item;
+
+ // last item <- new item
+ item.prev = this.tail;
+ } else {
+ // if list has no a tail, then it also has no a head
+ // in this case points head to new item
+ this.head = item;
+ }
+
+ // tail always start point to new item
+ this.tail = item;
+ }
+};
+
+List.prototype.remove = function(item) {
+ // item
+ // ^
+ // prev next
+ this.updateCursors(item, item.prev, item, item.next);
+
+ if (item.prev !== null) {
+ item.prev.next = item.next;
+ } else {
+ if (this.head !== item) {
+ throw new Error('item doesn\'t below to list');
+ }
+
+ this.head = item.next;
+ }
+
+ if (item.next !== null) {
+ item.next.prev = item.prev;
+ } else {
+ if (this.tail !== item) {
+ throw new Error('item doesn\'t below to list');
+ }
+
+ this.tail = item.prev;
+ }
+
+ item.prev = null;
+ item.next = null;
+
+ return item;
+};
+
+List.prototype.appendList = function(list) {
+ // ignore empty lists
+ if (list.head === null) {
+ return;
+ }
+
+ this.updateCursors(this.tail, list.tail, null, list.head);
+
+ // insert to end of the list
+ if (this.tail !== null) {
+ // if destination list has a tail, then it also has a head,
+ // but head doesn't change
+
+ // dest tail -> source head
+ this.tail.next = list.head;
+
+ // dest tail <- source head
+ list.head.prev = this.tail;
+ } else {
+ // if list has no a tail, then it also has no a head
+ // in this case points head to new item
+ this.head = list.head;
+ }
+
+ // tail always start point to new item
+ this.tail = list.tail;
+
+ list.head = null;
+ list.tail = null;
+};
+
+module.exports = List;
diff --git a/node_modules/csso/lib/utils/names.js b/node_modules/csso/lib/utils/names.js
new file mode 100644
index 00000000..27277711
--- /dev/null
+++ b/node_modules/csso/lib/utils/names.js
@@ -0,0 +1,73 @@
+var hasOwnProperty = Object.prototype.hasOwnProperty;
+var knownKeywords = Object.create(null);
+var knownProperties = Object.create(null);
+
+function getVendorPrefix(string) {
+ if (string[0] === '-') {
+ // skip 2 chars to avoid wrong match with variables names
+ var secondDashIndex = string.indexOf('-', 2);
+
+ if (secondDashIndex !== -1) {
+ return string.substr(0, secondDashIndex + 1);
+ }
+ }
+
+ return '';
+}
+
+function getKeywordInfo(keyword) {
+ if (hasOwnProperty.call(knownKeywords, keyword)) {
+ return knownKeywords[keyword];
+ }
+
+ var lowerCaseKeyword = keyword.toLowerCase();
+ var vendor = getVendorPrefix(lowerCaseKeyword);
+ var name = lowerCaseKeyword;
+
+ if (vendor) {
+ name = name.substr(vendor.length);
+ }
+
+ return knownKeywords[keyword] = Object.freeze({
+ vendor: vendor,
+ prefix: vendor,
+ name: name
+ });
+}
+
+function getPropertyInfo(property) {
+ if (hasOwnProperty.call(knownProperties, property)) {
+ return knownProperties[property];
+ }
+
+ var lowerCaseProperty = property.toLowerCase();
+ var hack = lowerCaseProperty[0];
+
+ if (hack === '*' || hack === '_' || hack === '$') {
+ lowerCaseProperty = lowerCaseProperty.substr(1);
+ } else if (hack === '/' && property[1] === '/') {
+ hack = '//';
+ lowerCaseProperty = lowerCaseProperty.substr(2);
+ } else {
+ hack = '';
+ }
+
+ var vendor = getVendorPrefix(lowerCaseProperty);
+ var name = lowerCaseProperty;
+
+ if (vendor) {
+ name = name.substr(vendor.length);
+ }
+
+ return knownProperties[property] = Object.freeze({
+ hack: hack,
+ vendor: vendor,
+ prefix: hack + vendor,
+ name: name
+ });
+}
+
+module.exports = {
+ keyword: getKeywordInfo,
+ property: getPropertyInfo
+};
diff --git a/node_modules/csso/lib/utils/translate.js b/node_modules/csso/lib/utils/translate.js
new file mode 100644
index 00000000..ae7beebe
--- /dev/null
+++ b/node_modules/csso/lib/utils/translate.js
@@ -0,0 +1,178 @@
+function each(list) {
+ if (list.head === null) {
+ return '';
+ }
+
+ if (list.head === list.tail) {
+ return translate(list.head.data);
+ }
+
+ return list.map(translate).join('');
+}
+
+function eachDelim(list, delimeter) {
+ if (list.head === null) {
+ return '';
+ }
+
+ if (list.head === list.tail) {
+ return translate(list.head.data);
+ }
+
+ return list.map(translate).join(delimeter);
+}
+
+function translate(node) {
+ switch (node.type) {
+ case 'StyleSheet':
+ return each(node.rules);
+
+ case 'Atrule':
+ var nodes = ['@', node.name];
+
+ if (node.expression && !node.expression.sequence.isEmpty()) {
+ nodes.push(' ', translate(node.expression));
+ }
+
+ if (node.block) {
+ nodes.push('{', translate(node.block), '}');
+ } else {
+ nodes.push(';');
+ }
+
+ return nodes.join('');
+
+ case 'Ruleset':
+ return translate(node.selector) + '{' + translate(node.block) + '}';
+
+ case 'Selector':
+ return eachDelim(node.selectors, ',');
+
+ case 'SimpleSelector':
+ var nodes = node.sequence.map(function(node) {
+ // add extra spaces around /deep/ combinator since comment beginning/ending may to be produced
+ if (node.type === 'Combinator' && node.name === '/deep/') {
+ return ' ' + translate(node) + ' ';
+ }
+
+ return translate(node);
+ });
+
+ return nodes.join('');
+
+ case 'Block':
+ return eachDelim(node.declarations, ';');
+
+ case 'Declaration':
+ return translate(node.property) + ':' + translate(node.value);
+
+ case 'Property':
+ return node.name;
+
+ case 'Value':
+ return node.important
+ ? each(node.sequence) + '!important'
+ : each(node.sequence);
+
+ case 'Attribute':
+ var result = translate(node.name);
+ var flagsPrefix = ' ';
+
+ if (node.operator !== null) {
+ result += node.operator;
+
+ if (node.value !== null) {
+ result += translate(node.value);
+
+ // space between string and flags is not required
+ if (node.value.type === 'String') {
+ flagsPrefix = '';
+ }
+ }
+ }
+
+ if (node.flags !== null) {
+ result += flagsPrefix + node.flags;
+ }
+
+ return '[' + result + ']';
+
+ case 'FunctionalPseudo':
+ return ':' + node.name + '(' + eachDelim(node.arguments, ',') + ')';
+
+ case 'Function':
+ return node.name + '(' + eachDelim(node.arguments, ',') + ')';
+
+ case 'Negation':
+ return ':not(' + eachDelim(node.sequence, ',') + ')';
+
+ case 'Braces':
+ return node.open + each(node.sequence) + node.close;
+
+ case 'Argument':
+ case 'AtruleExpression':
+ return each(node.sequence);
+
+ case 'Url':
+ return 'url(' + translate(node.value) + ')';
+
+ case 'Progid':
+ return translate(node.value);
+
+ case 'Combinator':
+ return node.name;
+
+ case 'Identifier':
+ return node.name;
+
+ case 'PseudoClass':
+ return ':' + node.name;
+
+ case 'PseudoElement':
+ return '::' + node.name;
+
+ case 'Class':
+ return '.' + node.name;
+
+ case 'Id':
+ return '#' + node.name;
+
+ case 'Hash':
+ return '#' + node.value;
+
+ case 'Dimension':
+ return node.value + node.unit;
+
+ case 'Nth':
+ return node.value;
+
+ case 'Number':
+ return node.value;
+
+ case 'String':
+ return node.value;
+
+ case 'Operator':
+ return node.value;
+
+ case 'Raw':
+ return node.value;
+
+ case 'Unknown':
+ return node.value;
+
+ case 'Percentage':
+ return node.value + '%';
+
+ case 'Space':
+ return ' ';
+
+ case 'Comment':
+ return '/*' + node.value + '*/';
+
+ default:
+ throw new Error('Unknown node type: ' + node.type);
+ }
+}
+
+module.exports = translate;
diff --git a/node_modules/csso/lib/utils/translateWithSourceMap.js b/node_modules/csso/lib/utils/translateWithSourceMap.js
new file mode 100644
index 00000000..819b7af0
--- /dev/null
+++ b/node_modules/csso/lib/utils/translateWithSourceMap.js
@@ -0,0 +1,291 @@
+var SourceMapGenerator = require('source-map').SourceMapGenerator;
+var SourceNode = require('source-map').SourceNode;
+
+// Our own implementation of SourceNode#toStringWithSourceMap,
+// since SourceNode doesn't allow multiple references to original source.
+// Also, as we know structure of result we could be optimize generation
+// (currently it's ~40% faster).
+function walk(node, fn) {
+ for (var chunk, i = 0; i < node.children.length; i++) {
+ chunk = node.children[i];
+
+ if (chunk instanceof SourceNode) {
+ // this is a hack, because source maps doesn't support for 1(generated):N(original)
+ // if (chunk.merged) {
+ // fn('', chunk);
+ // }
+
+ walk(chunk, fn);
+ } else {
+ fn(chunk, node);
+ }
+ }
+}
+
+function generateSourceMap(root) {
+ var map = new SourceMapGenerator();
+ var css = '';
+ var sourceMappingActive = false;
+ var lastOriginalLine = null;
+ var lastOriginalColumn = null;
+ var lastIndexOfNewline;
+ var generated = {
+ line: 1,
+ column: 0
+ };
+ var activatedMapping = {
+ generated: generated
+ };
+
+ walk(root, function(chunk, original) {
+ if (original.line !== null &&
+ original.column !== null) {
+ if (lastOriginalLine !== original.line ||
+ lastOriginalColumn !== original.column) {
+ map.addMapping({
+ source: original.source,
+ original: original,
+ generated: generated
+ });
+ }
+
+ lastOriginalLine = original.line;
+ lastOriginalColumn = original.column;
+ sourceMappingActive = true;
+ } else if (sourceMappingActive) {
+ map.addMapping(activatedMapping);
+ sourceMappingActive = false;
+ }
+
+ css += chunk;
+
+ lastIndexOfNewline = chunk.lastIndexOf('\n');
+ if (lastIndexOfNewline !== -1) {
+ generated.line += chunk.match(/\n/g).length;
+ generated.column = chunk.length - lastIndexOfNewline - 1;
+ } else {
+ generated.column += chunk.length;
+ }
+ });
+
+ return {
+ css: css,
+ map: map
+ };
+}
+
+function createAnonymousSourceNode(children) {
+ return new SourceNode(
+ null,
+ null,
+ null,
+ children
+ );
+}
+
+function createSourceNode(info, children) {
+ if (info.primary) {
+ // special marker node to add several references to original
+ // var merged = createSourceNode(info.merged, []);
+ // merged.merged = true;
+ // children.unshift(merged);
+
+ // use recursion, because primary can also has a primary/merged info
+ return createSourceNode(info.primary, children);
+ }
+
+ return new SourceNode(
+ info.line,
+ info.column - 1,
+ info.source,
+ children
+ );
+}
+
+function each(list) {
+ if (list.head === null) {
+ return '';
+ }
+
+ if (list.head === list.tail) {
+ return translate(list.head.data);
+ }
+
+ return list.map(translate).join('');
+}
+
+function eachDelim(list, delimeter) {
+ if (list.head === null) {
+ return '';
+ }
+
+ if (list.head === list.tail) {
+ return translate(list.head.data);
+ }
+
+ return list.map(translate).join(delimeter);
+}
+
+function translate(node) {
+ switch (node.type) {
+ case 'StyleSheet':
+ return createAnonymousSourceNode(node.rules.map(translate));
+
+ case 'Atrule':
+ var nodes = ['@', node.name];
+
+ if (node.expression && !node.expression.sequence.isEmpty()) {
+ nodes.push(' ', translate(node.expression));
+ }
+
+ if (node.block) {
+ nodes.push('{', translate(node.block), '}');
+ } else {
+ nodes.push(';');
+ }
+
+ return createSourceNode(node.info, nodes);
+
+ case 'Ruleset':
+ return createAnonymousSourceNode([
+ translate(node.selector), '{', translate(node.block), '}'
+ ]);
+
+ case 'Selector':
+ return createAnonymousSourceNode(node.selectors.map(translate)).join(',');
+
+ case 'SimpleSelector':
+ var nodes = node.sequence.map(function(node) {
+ // add extra spaces around /deep/ combinator since comment beginning/ending may to be produced
+ if (node.type === 'Combinator' && node.name === '/deep/') {
+ return ' ' + translate(node) + ' ';
+ }
+
+ return translate(node);
+ });
+
+ return createSourceNode(node.info, nodes);
+
+ case 'Block':
+ return createAnonymousSourceNode(node.declarations.map(translate)).join(';');
+
+ case 'Declaration':
+ return createSourceNode(
+ node.info,
+ [translate(node.property), ':', translate(node.value)]
+ );
+
+ case 'Property':
+ return node.name;
+
+ case 'Value':
+ return node.important
+ ? each(node.sequence) + '!important'
+ : each(node.sequence);
+
+ case 'Attribute':
+ var result = translate(node.name);
+ var flagsPrefix = ' ';
+
+ if (node.operator !== null) {
+ result += node.operator;
+
+ if (node.value !== null) {
+ result += translate(node.value);
+
+ // space between string and flags is not required
+ if (node.value.type === 'String') {
+ flagsPrefix = '';
+ }
+ }
+ }
+
+ if (node.flags !== null) {
+ result += flagsPrefix + node.flags;
+ }
+
+ return '[' + result + ']';
+
+ case 'FunctionalPseudo':
+ return ':' + node.name + '(' + eachDelim(node.arguments, ',') + ')';
+
+ case 'Function':
+ return node.name + '(' + eachDelim(node.arguments, ',') + ')';
+
+ case 'Negation':
+ return ':not(' + eachDelim(node.sequence, ',') + ')';
+
+ case 'Braces':
+ return node.open + each(node.sequence) + node.close;
+
+ case 'Argument':
+ case 'AtruleExpression':
+ return each(node.sequence);
+
+ case 'Url':
+ return 'url(' + translate(node.value) + ')';
+
+ case 'Progid':
+ return translate(node.value);
+
+ case 'Combinator':
+ return node.name;
+
+ case 'Identifier':
+ return node.name;
+
+ case 'PseudoClass':
+ return ':' + node.name;
+
+ case 'PseudoElement':
+ return '::' + node.name;
+
+ case 'Class':
+ return '.' + node.name;
+
+ case 'Id':
+ return '#' + node.name;
+
+ case 'Hash':
+ return '#' + node.value;
+
+ case 'Dimension':
+ return node.value + node.unit;
+
+ case 'Nth':
+ return node.value;
+
+ case 'Number':
+ return node.value;
+
+ case 'String':
+ return node.value;
+
+ case 'Operator':
+ return node.value;
+
+ case 'Raw':
+ return node.value;
+
+ case 'Unknown':
+ return node.value;
+
+ case 'Percentage':
+ return node.value + '%';
+
+ case 'Space':
+ return ' ';
+
+ case 'Comment':
+ return '/*' + node.value + '*/';
+
+ default:
+ throw new Error('Unknown node type: ' + node.type);
+ }
+}
+
+module.exports = function(node) {
+ return generateSourceMap(
+ createAnonymousSourceNode(translate(node))
+ );
+};
diff --git a/node_modules/csso/lib/utils/walk.js b/node_modules/csso/lib/utils/walk.js
new file mode 100644
index 00000000..a7948eb8
--- /dev/null
+++ b/node_modules/csso/lib/utils/walk.js
@@ -0,0 +1,189 @@
+function walkRules(node, item, list) {
+ switch (node.type) {
+ case 'StyleSheet':
+ var oldStylesheet = this.stylesheet;
+ this.stylesheet = node;
+
+ node.rules.each(walkRules, this);
+
+ this.stylesheet = oldStylesheet;
+ break;
+
+ case 'Atrule':
+ if (node.block !== null) {
+ walkRules.call(this, node.block);
+ }
+
+ this.fn(node, item, list);
+ break;
+
+ case 'Ruleset':
+ this.fn(node, item, list);
+ break;
+ }
+
+}
+
+function walkRulesRight(node, item, list) {
+ switch (node.type) {
+ case 'StyleSheet':
+ var oldStylesheet = this.stylesheet;
+ this.stylesheet = node;
+
+ node.rules.eachRight(walkRulesRight, this);
+
+ this.stylesheet = oldStylesheet;
+ break;
+
+ case 'Atrule':
+ if (node.block !== null) {
+ walkRulesRight.call(this, node.block);
+ }
+
+ this.fn(node, item, list);
+ break;
+
+ case 'Ruleset':
+ this.fn(node, item, list);
+ break;
+ }
+}
+
+function walkAll(node, item, list) {
+ switch (node.type) {
+ case 'StyleSheet':
+ var oldStylesheet = this.stylesheet;
+ this.stylesheet = node;
+
+ node.rules.each(walkAll, this);
+
+ this.stylesheet = oldStylesheet;
+ break;
+
+ case 'Atrule':
+ if (node.expression !== null) {
+ walkAll.call(this, node.expression);
+ }
+ if (node.block !== null) {
+ walkAll.call(this, node.block);
+ }
+ break;
+
+ case 'Ruleset':
+ this.ruleset = node;
+
+ if (node.selector !== null) {
+ walkAll.call(this, node.selector);
+ }
+ walkAll.call(this, node.block);
+
+ this.ruleset = null;
+ break;
+
+ case 'Selector':
+ var oldSelector = this.selector;
+ this.selector = node;
+
+ node.selectors.each(walkAll, this);
+
+ this.selector = oldSelector;
+ break;
+
+ case 'Block':
+ node.declarations.each(walkAll, this);
+ break;
+
+ case 'Declaration':
+ this.declaration = node;
+
+ walkAll.call(this, node.property);
+ walkAll.call(this, node.value);
+
+ this.declaration = null;
+ break;
+
+ case 'Attribute':
+ walkAll.call(this, node.name);
+ if (node.value !== null) {
+ walkAll.call(this, node.value);
+ }
+ break;
+
+ case 'FunctionalPseudo':
+ case 'Function':
+ this['function'] = node;
+
+ node.arguments.each(walkAll, this);
+
+ this['function'] = null;
+ break;
+
+ case 'AtruleExpression':
+ this.atruleExpression = node;
+
+ node.sequence.each(walkAll, this);
+
+ this.atruleExpression = null;
+ break;
+
+ case 'Value':
+ case 'Argument':
+ case 'SimpleSelector':
+ case 'Braces':
+ case 'Negation':
+ node.sequence.each(walkAll, this);
+ break;
+
+ case 'Url':
+ case 'Progid':
+ walkAll.call(this, node.value);
+ break;
+
+ // nothig to do with
+ // case 'Property':
+ // case 'Combinator':
+ // case 'Dimension':
+ // case 'Hash':
+ // case 'Identifier':
+ // case 'Nth':
+ // case 'Class':
+ // case 'Id':
+ // case 'Percentage':
+ // case 'PseudoClass':
+ // case 'PseudoElement':
+ // case 'Space':
+ // case 'Number':
+ // case 'String':
+ // case 'Operator':
+ // case 'Raw':
+ }
+
+ this.fn(node, item, list);
+}
+
+function createContext(root, fn) {
+ var context = {
+ fn: fn,
+ root: root,
+ stylesheet: null,
+ atruleExpression: null,
+ ruleset: null,
+ selector: null,
+ declaration: null,
+ function: null
+ };
+
+ return context;
+}
+
+module.exports = {
+ all: function(root, fn) {
+ walkAll.call(createContext(root, fn), root);
+ },
+ rules: function(root, fn) {
+ walkRules.call(createContext(root, fn), root);
+ },
+ rulesRight: function(root, fn) {
+ walkRulesRight.call(createContext(root, fn), root);
+ }
+};