less 2.3.3 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Changelog.md +5 -0
- data/less.gemspec +1 -1
- data/lib/less/js/.gitattributes +9 -0
- data/lib/less/js/.gitignore +1 -0
- data/lib/less/js/.npmignore +1 -1
- data/lib/less/js/CHANGELOG.md +68 -0
- data/lib/less/js/CONTRIBUTING.md +33 -34
- data/lib/less/js/Makefile +24 -9
- data/lib/less/js/README.md +2 -2
- data/lib/less/js/bin/lessc +102 -25
- data/lib/less/js/build/amd.js +1 -1
- data/lib/less/js/build/header.js +9 -7
- data/lib/less/js/dist/less-1.3.3.js +2 -2
- data/lib/less/js/dist/less-1.3.3.min.js +2 -2
- data/lib/less/js/dist/less-1.4.0-beta.js +5830 -0
- data/lib/less/js/dist/less-1.4.0-beta.min.js +11 -0
- data/lib/less/js/dist/less-1.4.0.js +5830 -0
- data/lib/less/js/dist/less-1.4.0.min.js +11 -0
- data/lib/less/js/dist/less-1.4.1.js +5837 -0
- data/lib/less/js/dist/less-1.4.1.min.js +11 -0
- data/lib/less/js/dist/less-1.4.2.js +5837 -0
- data/lib/less/js/dist/less-1.4.2.min.js +11 -0
- data/lib/less/js/dist/less-rhino-1.4.0.js +4273 -0
- data/lib/less/js/lib/less/browser.js +131 -101
- data/lib/less/js/lib/less/env.js +105 -0
- data/lib/less/js/lib/less/extend-visitor.js +391 -0
- data/lib/less/js/lib/less/functions.js +174 -19
- data/lib/less/js/lib/less/import-visitor.js +107 -0
- data/lib/less/js/lib/less/index.js +70 -63
- data/lib/less/js/lib/less/join-selector-visitor.js +37 -0
- data/lib/less/js/lib/less/lessc_helper.js +13 -4
- data/lib/less/js/lib/less/parser.js +353 -264
- data/lib/less/js/lib/less/rhino.js +5 -2
- data/lib/less/js/lib/less/tree.js +1 -1
- data/lib/less/js/lib/less/tree/alpha.js +7 -3
- data/lib/less/js/lib/less/tree/anonymous.js +1 -0
- data/lib/less/js/lib/less/tree/assignment.js +4 -0
- data/lib/less/js/lib/less/tree/call.js +14 -8
- data/lib/less/js/lib/less/tree/color.js +50 -5
- data/lib/less/js/lib/less/tree/comment.js +1 -0
- data/lib/less/js/lib/less/tree/condition.js +35 -28
- data/lib/less/js/lib/less/tree/dimension.js +270 -16
- data/lib/less/js/lib/less/tree/directive.js +7 -2
- data/lib/less/js/lib/less/tree/element.js +57 -21
- data/lib/less/js/lib/less/tree/expression.js +29 -4
- data/lib/less/js/lib/less/tree/extend.js +43 -0
- data/lib/less/js/lib/less/tree/import.js +49 -28
- data/lib/less/js/lib/less/tree/javascript.js +1 -0
- data/lib/less/js/lib/less/tree/keyword.js +3 -2
- data/lib/less/js/lib/less/tree/media.js +20 -4
- data/lib/less/js/lib/less/tree/mixin.js +38 -18
- data/lib/less/js/lib/less/tree/negative.js +22 -0
- data/lib/less/js/lib/less/tree/operation.js +32 -17
- data/lib/less/js/lib/less/tree/paren.js +5 -1
- data/lib/less/js/lib/less/tree/quoted.js +5 -3
- data/lib/less/js/lib/less/tree/rule.js +44 -31
- data/lib/less/js/lib/less/tree/ruleset.js +50 -23
- data/lib/less/js/lib/less/tree/selector.js +49 -39
- data/lib/less/js/lib/less/tree/unicode-descriptor.js +1 -0
- data/lib/less/js/lib/less/tree/url.js +9 -5
- data/lib/less/js/lib/less/tree/value.js +4 -1
- data/lib/less/js/lib/less/tree/variable.js +4 -3
- data/lib/less/js/lib/less/visitor.js +54 -0
- data/lib/less/js/package.json +69 -19
- data/lib/less/js/test/browser-test-prepare.js +23 -6
- data/lib/less/js/test/browser/common.js +55 -3
- data/lib/less/js/test/browser/css/urls.css +13 -0
- data/lib/less/js/test/browser/less/relative-urls/urls.less +1 -1
- data/lib/less/js/test/browser/less/urls.less +16 -0
- data/lib/less/js/test/browser/phantom-runner.js +7 -5
- data/lib/less/js/test/browser/runner-browser.js +5 -1
- data/lib/less/js/test/browser/runner-errors.js +5 -0
- data/lib/less/js/test/browser/runner-legacy.js +6 -0
- data/lib/less/js/test/browser/runner-production.js +7 -0
- data/lib/less/js/test/browser/template.htm +6 -6
- data/lib/less/js/test/css/comments.css +1 -0
- data/lib/less/js/test/css/compression/compression.css +2 -0
- data/lib/less/js/test/css/css-3.css +4 -0
- data/lib/less/js/test/css/css.css +9 -3
- data/lib/less/js/test/css/extend-chaining.css +72 -0
- data/lib/less/js/test/css/extend-clearfix.css +19 -0
- data/lib/less/js/test/css/extend-exact.css +37 -0
- data/lib/less/js/test/css/extend-media.css +24 -0
- data/lib/less/js/test/css/extend-nest.css +57 -0
- data/lib/less/js/test/css/extend-selector.css +72 -0
- data/lib/less/js/test/css/extend.css +76 -0
- data/lib/less/js/test/css/functions.css +28 -0
- data/lib/less/js/test/css/import-interpolation.css +6 -0
- data/lib/less/js/test/css/import.css +18 -1
- data/lib/less/js/test/css/legacy/legacy.css +7 -0
- data/lib/less/js/test/css/media.css +9 -1
- data/lib/less/js/test/css/mixins-args.css +18 -0
- data/lib/less/js/test/css/mixins-guards.css +5 -0
- data/lib/less/js/test/css/parens.css +18 -5
- data/lib/less/js/test/css/selectors.css +14 -6
- data/lib/less/js/test/css/urls.css +17 -0
- data/lib/less/js/test/css/variables.css +22 -3
- data/lib/less/js/test/data/data-uri-fail.png +0 -0
- data/lib/less/js/test/data/image.jpg +0 -0
- data/lib/less/js/test/data/page.html +1 -0
- data/lib/less/js/test/less-test.js +41 -9
- data/lib/less/js/test/less/colors.less +4 -4
- data/lib/less/js/test/less/comments.less +2 -2
- data/lib/less/js/test/less/compression/compression.less +16 -0
- data/lib/less/js/test/less/css-3.less +5 -1
- data/lib/less/js/test/less/css.less +9 -3
- data/lib/less/js/test/less/errors/add-mixed-units.less +3 -0
- data/lib/less/js/test/less/errors/add-mixed-units.txt +2 -0
- data/lib/less/js/test/less/errors/add-mixed-units2.less +3 -0
- data/lib/less/js/test/less/errors/add-mixed-units2.txt +2 -0
- data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +1 -1
- data/lib/less/js/test/less/errors/color-operation-error.less +3 -0
- data/lib/less/js/test/less/errors/color-operation-error.txt +2 -0
- data/lib/less/js/test/less/errors/comment-in-selector.txt +1 -1
- data/lib/less/js/test/less/errors/divide-mixed-units.less +3 -0
- data/lib/less/js/test/less/errors/divide-mixed-units.txt +4 -0
- data/lib/less/js/test/less/errors/extend-no-selector.less +3 -0
- data/lib/less/js/test/less/errors/extend-no-selector.txt +3 -0
- data/lib/less/js/test/less/errors/extend-not-at-end.less +3 -0
- data/lib/less/js/test/less/errors/extend-not-at-end.txt +3 -0
- data/lib/less/js/test/less/errors/import-missing.less +5 -0
- data/lib/less/js/test/less/errors/import-missing.txt +3 -3
- data/lib/less/js/test/less/errors/import-no-semi.txt +1 -1
- data/lib/less/js/test/less/errors/import-subfolder1.txt +1 -1
- data/lib/less/js/test/less/errors/import-subfolder2.txt +1 -1
- data/lib/less/js/test/less/errors/javascript-error.txt +1 -1
- data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +1 -1
- data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +1 -1
- data/lib/less/js/test/less/errors/mixin-not-defined.txt +1 -1
- data/lib/less/js/test/less/errors/mixin-not-matched.txt +1 -1
- data/lib/less/js/test/less/errors/mixin-not-matched2.txt +1 -1
- data/lib/less/js/test/less/errors/multiply-mixed-units.less +7 -0
- data/lib/less/js/test/less/errors/multiply-mixed-units.txt +4 -0
- data/lib/less/js/test/less/errors/parens-error-1.less +3 -0
- data/lib/less/js/test/less/errors/parens-error-1.txt +4 -0
- data/lib/less/js/test/less/errors/parens-error-2.less +3 -0
- data/lib/less/js/test/less/errors/parens-error-2.txt +4 -0
- data/lib/less/js/test/less/errors/parens-error-3.less +3 -0
- data/lib/less/js/test/less/errors/parens-error-3.txt +4 -0
- data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +1 -1
- data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -1
- data/lib/less/js/test/less/errors/parse-error-with-import.txt +1 -1
- data/lib/less/js/test/less/errors/property-ie5-hack.txt +1 -1
- data/lib/less/js/test/less/errors/property-in-root.less +4 -0
- data/lib/less/js/test/less/errors/property-in-root.txt +4 -0
- data/lib/less/js/test/less/errors/property-in-root2.less +1 -0
- data/lib/less/js/test/less/errors/property-in-root2.txt +4 -0
- data/lib/less/js/test/less/errors/property-in-root3.less +4 -0
- data/lib/less/js/test/less/errors/property-in-root3.txt +3 -0
- data/lib/less/js/test/less/errors/recursive-variable.txt +1 -1
- data/lib/less/js/test/less/extend-chaining.less +79 -0
- data/lib/less/js/test/less/extend-clearfix.less +19 -0
- data/lib/less/js/test/less/extend-exact.less +46 -0
- data/lib/less/js/test/less/extend-media.less +24 -0
- data/lib/less/js/test/less/extend-nest.less +65 -0
- data/lib/less/js/test/less/extend-selector.less +84 -0
- data/lib/less/js/test/less/extend.less +81 -0
- data/lib/less/js/test/less/functions.less +37 -6
- data/lib/less/js/test/less/import-interpolation.less +8 -0
- data/lib/less/js/test/less/import-once.less +4 -4
- data/lib/less/js/test/less/import.less +11 -2
- data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -1
- data/lib/less/js/test/less/import/import-interpolation.less +1 -0
- data/lib/less/js/test/less/import/import-interpolation2.less +5 -0
- data/lib/less/js/test/less/javascript.less +1 -1
- data/lib/less/js/test/less/legacy/legacy.less +7 -0
- data/lib/less/js/test/less/media.less +14 -3
- data/lib/less/js/test/less/mixins-args.less +43 -5
- data/lib/less/js/test/less/mixins-guards.less +13 -0
- data/lib/less/js/test/less/mixins-named-args.less +5 -5
- data/lib/less/js/test/less/mixins-nested.less +2 -2
- data/lib/less/js/test/less/mixins-pattern.less +1 -1
- data/lib/less/js/test/less/mixins.less +1 -1
- data/lib/less/js/test/less/operations.less +27 -27
- data/lib/less/js/test/less/parens.less +20 -5
- data/lib/less/js/test/less/selectors.less +14 -7
- data/lib/less/js/test/less/urls.less +24 -0
- data/lib/less/js/test/less/variables.less +42 -12
- data/lib/less/loader.rb +33 -0
- data/lib/less/version.rb +1 -1
- data/spec/less/parser_spec.rb +5 -5
- metadata +76 -6
- data/lib/less/js/build/ecma-5.js +0 -120
- data/lib/less/js/lib/less/tree/ratio.js +0 -13
@@ -0,0 +1,391 @@
|
|
1
|
+
(function (tree) {
|
2
|
+
tree.extendFinderVisitor = function() {
|
3
|
+
this._visitor = new tree.visitor(this);
|
4
|
+
this.contexts = [];
|
5
|
+
this.allExtendsStack = [[]];
|
6
|
+
};
|
7
|
+
|
8
|
+
tree.extendFinderVisitor.prototype = {
|
9
|
+
run: function (root) {
|
10
|
+
root = this._visitor.visit(root);
|
11
|
+
root.allExtends = this.allExtendsStack[0];
|
12
|
+
return root;
|
13
|
+
},
|
14
|
+
visitRule: function (ruleNode, visitArgs) {
|
15
|
+
visitArgs.visitDeeper = false;
|
16
|
+
},
|
17
|
+
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
18
|
+
visitArgs.visitDeeper = false;
|
19
|
+
},
|
20
|
+
visitRuleset: function (rulesetNode, visitArgs) {
|
21
|
+
|
22
|
+
if (rulesetNode.root) {
|
23
|
+
return;
|
24
|
+
}
|
25
|
+
|
26
|
+
var i, j, extend, allSelectorsExtendList = [], extendList;
|
27
|
+
|
28
|
+
// get &:extend(.a); rules which apply to all selectors in this ruleset
|
29
|
+
for(i = 0; i < rulesetNode.rules.length; i++) {
|
30
|
+
if (rulesetNode.rules[i] instanceof tree.Extend) {
|
31
|
+
allSelectorsExtendList.push(rulesetNode.rules[i]);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
// now find every selector and apply the extends that apply to all extends
|
36
|
+
// and the ones which apply to an individual extend
|
37
|
+
for(i = 0; i < rulesetNode.paths.length; i++) {
|
38
|
+
var selectorPath = rulesetNode.paths[i],
|
39
|
+
selector = selectorPath[selectorPath.length-1];
|
40
|
+
extendList = selector.extendList.slice(0).concat(allSelectorsExtendList).map(function(allSelectorsExtend) {
|
41
|
+
return allSelectorsExtend.clone();
|
42
|
+
});
|
43
|
+
for(j = 0; j < extendList.length; j++) {
|
44
|
+
this.foundExtends = true;
|
45
|
+
extend = extendList[j];
|
46
|
+
extend.findSelfSelectors(selectorPath);
|
47
|
+
extend.ruleset = rulesetNode;
|
48
|
+
if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
|
49
|
+
this.allExtendsStack[this.allExtendsStack.length-1].push(extend);
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
this.contexts.push(rulesetNode.selectors);
|
54
|
+
},
|
55
|
+
visitRulesetOut: function (rulesetNode) {
|
56
|
+
if (!rulesetNode.root) {
|
57
|
+
this.contexts.length = this.contexts.length - 1;
|
58
|
+
}
|
59
|
+
},
|
60
|
+
visitMedia: function (mediaNode, visitArgs) {
|
61
|
+
mediaNode.allExtends = [];
|
62
|
+
this.allExtendsStack.push(mediaNode.allExtends);
|
63
|
+
},
|
64
|
+
visitMediaOut: function (mediaNode) {
|
65
|
+
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
66
|
+
},
|
67
|
+
visitDirective: function (directiveNode, visitArgs) {
|
68
|
+
directiveNode.allExtends = [];
|
69
|
+
this.allExtendsStack.push(directiveNode.allExtends);
|
70
|
+
},
|
71
|
+
visitDirectiveOut: function (directiveNode) {
|
72
|
+
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
73
|
+
}
|
74
|
+
};
|
75
|
+
|
76
|
+
tree.processExtendsVisitor = function() {
|
77
|
+
this._visitor = new tree.visitor(this);
|
78
|
+
};
|
79
|
+
|
80
|
+
tree.processExtendsVisitor.prototype = {
|
81
|
+
run: function(root) {
|
82
|
+
var extendFinder = new tree.extendFinderVisitor();
|
83
|
+
extendFinder.run(root);
|
84
|
+
if (!extendFinder.foundExtends) { return root; }
|
85
|
+
root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
|
86
|
+
this.allExtendsStack = [root.allExtends];
|
87
|
+
return this._visitor.visit(root);
|
88
|
+
},
|
89
|
+
doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
|
90
|
+
//
|
91
|
+
// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
|
92
|
+
// the selector we would do normally, but we are also adding an extend with the same target selector
|
93
|
+
// this means this new extend can then go and alter other extends
|
94
|
+
//
|
95
|
+
// this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
|
96
|
+
// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
|
97
|
+
// we look at each selector at a time, as is done in visitRuleset
|
98
|
+
|
99
|
+
var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend;
|
100
|
+
|
101
|
+
iterationCount = iterationCount || 0;
|
102
|
+
|
103
|
+
//loop through comparing every extend with every target extend.
|
104
|
+
// a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
|
105
|
+
// e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
|
106
|
+
// and the second is the target.
|
107
|
+
// the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
|
108
|
+
// case when processing media queries
|
109
|
+
for(extendIndex = 0; extendIndex < extendsList.length; extendIndex++){
|
110
|
+
for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){
|
111
|
+
|
112
|
+
extend = extendsList[extendIndex];
|
113
|
+
targetExtend = extendsListTarget[targetExtendIndex];
|
114
|
+
|
115
|
+
// look for circular references
|
116
|
+
if (this.inInheritanceChain(targetExtend, extend)) { continue; }
|
117
|
+
|
118
|
+
// find a match in the target extends self selector (the bit before :extend)
|
119
|
+
selectorPath = [targetExtend.selfSelectors[0]];
|
120
|
+
matches = extendVisitor.findMatch(extend, selectorPath);
|
121
|
+
|
122
|
+
if (matches.length) {
|
123
|
+
|
124
|
+
// we found a match, so for each self selector..
|
125
|
+
extend.selfSelectors.forEach(function(selfSelector) {
|
126
|
+
|
127
|
+
// process the extend as usual
|
128
|
+
newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector);
|
129
|
+
|
130
|
+
// but now we create a new extend from it
|
131
|
+
newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0);
|
132
|
+
newExtend.selfSelectors = newSelector;
|
133
|
+
|
134
|
+
// add the extend onto the list of extends for that selector
|
135
|
+
newSelector[newSelector.length-1].extendList = [newExtend];
|
136
|
+
|
137
|
+
// record that we need to add it.
|
138
|
+
extendsToAdd.push(newExtend);
|
139
|
+
newExtend.ruleset = targetExtend.ruleset;
|
140
|
+
|
141
|
+
//remember its parents for circular references
|
142
|
+
newExtend.parents = [targetExtend, extend];
|
143
|
+
|
144
|
+
// only process the selector once.. if we have :extend(.a,.b) then multiple
|
145
|
+
// extends will look at the same selector path, so when extending
|
146
|
+
// we know that any others will be duplicates in terms of what is added to the css
|
147
|
+
if (targetExtend.firstExtendOnThisSelectorPath) {
|
148
|
+
newExtend.firstExtendOnThisSelectorPath = true;
|
149
|
+
targetExtend.ruleset.paths.push(newSelector);
|
150
|
+
}
|
151
|
+
});
|
152
|
+
}
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
if (extendsToAdd.length) {
|
157
|
+
// try to detect circular references to stop a stack overflow.
|
158
|
+
// may no longer be needed.
|
159
|
+
this.extendChainCount++;
|
160
|
+
if (iterationCount > 100) {
|
161
|
+
var selectorOne = "{unable to calculate}";
|
162
|
+
var selectorTwo = "{unable to calculate}";
|
163
|
+
try
|
164
|
+
{
|
165
|
+
selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
|
166
|
+
selectorTwo = extendsToAdd[0].selector.toCSS();
|
167
|
+
}
|
168
|
+
catch(e) {}
|
169
|
+
throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"};
|
170
|
+
}
|
171
|
+
|
172
|
+
// now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
|
173
|
+
return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1));
|
174
|
+
} else {
|
175
|
+
return extendsToAdd;
|
176
|
+
}
|
177
|
+
},
|
178
|
+
inInheritanceChain: function (possibleParent, possibleChild) {
|
179
|
+
if (possibleParent === possibleChild) {
|
180
|
+
return true;
|
181
|
+
}
|
182
|
+
if (possibleChild.parents) {
|
183
|
+
if (this.inInheritanceChain(possibleParent, possibleChild.parents[0])) {
|
184
|
+
return true;
|
185
|
+
}
|
186
|
+
if (this.inInheritanceChain(possibleParent, possibleChild.parents[1])) {
|
187
|
+
return true;
|
188
|
+
}
|
189
|
+
}
|
190
|
+
return false;
|
191
|
+
},
|
192
|
+
visitRule: function (ruleNode, visitArgs) {
|
193
|
+
visitArgs.visitDeeper = false;
|
194
|
+
},
|
195
|
+
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
196
|
+
visitArgs.visitDeeper = false;
|
197
|
+
},
|
198
|
+
visitSelector: function (selectorNode, visitArgs) {
|
199
|
+
visitArgs.visitDeeper = false;
|
200
|
+
},
|
201
|
+
visitRuleset: function (rulesetNode, visitArgs) {
|
202
|
+
if (rulesetNode.root) {
|
203
|
+
return;
|
204
|
+
}
|
205
|
+
var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath;
|
206
|
+
|
207
|
+
// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
|
208
|
+
|
209
|
+
for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
|
210
|
+
for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
|
211
|
+
|
212
|
+
selectorPath = rulesetNode.paths[pathIndex];
|
213
|
+
|
214
|
+
// extending extends happens initially, before the main pass
|
215
|
+
if (selectorPath[selectorPath.length-1].extendList.length) { continue; }
|
216
|
+
|
217
|
+
matches = this.findMatch(allExtends[extendIndex], selectorPath);
|
218
|
+
|
219
|
+
if (matches.length) {
|
220
|
+
|
221
|
+
allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
|
222
|
+
selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
|
223
|
+
});
|
224
|
+
}
|
225
|
+
}
|
226
|
+
}
|
227
|
+
rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
|
228
|
+
},
|
229
|
+
findMatch: function (extend, haystackSelectorPath) {
|
230
|
+
//
|
231
|
+
// look through the haystack selector path to try and find the needle - extend.selector
|
232
|
+
// returns an array of selector matches that can then be replaced
|
233
|
+
//
|
234
|
+
var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
|
235
|
+
targetCombinator, i,
|
236
|
+
extendVisitor = this,
|
237
|
+
needleElements = extend.selector.elements,
|
238
|
+
potentialMatches = [], potentialMatch, matches = [];
|
239
|
+
|
240
|
+
// loop through the haystack elements
|
241
|
+
for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
|
242
|
+
hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
|
243
|
+
|
244
|
+
for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
|
245
|
+
|
246
|
+
haystackElement = hackstackSelector.elements[hackstackElementIndex];
|
247
|
+
|
248
|
+
// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
|
249
|
+
if (extend.allowBefore || (haystackSelectorIndex == 0 && hackstackElementIndex == 0)) {
|
250
|
+
potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
|
251
|
+
}
|
252
|
+
|
253
|
+
for(i = 0; i < potentialMatches.length; i++) {
|
254
|
+
potentialMatch = potentialMatches[i];
|
255
|
+
|
256
|
+
// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
|
257
|
+
// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
|
258
|
+
// what the resulting combinator will be
|
259
|
+
targetCombinator = haystackElement.combinator.value;
|
260
|
+
if (targetCombinator == '' && hackstackElementIndex === 0) {
|
261
|
+
targetCombinator = ' ';
|
262
|
+
}
|
263
|
+
|
264
|
+
// if we don't match, null our match to indicate failure
|
265
|
+
if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
|
266
|
+
(potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
|
267
|
+
potentialMatch = null;
|
268
|
+
} else {
|
269
|
+
potentialMatch.matched++;
|
270
|
+
}
|
271
|
+
|
272
|
+
// if we are still valid and have finished, test whether we have elements after and whether these are allowed
|
273
|
+
if (potentialMatch) {
|
274
|
+
potentialMatch.finished = potentialMatch.matched === needleElements.length;
|
275
|
+
if (potentialMatch.finished &&
|
276
|
+
(!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
|
277
|
+
potentialMatch = null;
|
278
|
+
}
|
279
|
+
}
|
280
|
+
// if null we remove, if not, we are still valid, so either push as a valid match or continue
|
281
|
+
if (potentialMatch) {
|
282
|
+
if (potentialMatch.finished) {
|
283
|
+
potentialMatch.length = needleElements.length;
|
284
|
+
potentialMatch.endPathIndex = haystackSelectorIndex;
|
285
|
+
potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
|
286
|
+
potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
|
287
|
+
matches.push(potentialMatch);
|
288
|
+
}
|
289
|
+
} else {
|
290
|
+
potentialMatches.splice(i, 1);
|
291
|
+
i--;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
}
|
296
|
+
return matches;
|
297
|
+
},
|
298
|
+
isElementValuesEqual: function(elementValue1, elementValue2) {
|
299
|
+
if (typeof elementValue1 === "string" || typeof elementValue2 === "string") {
|
300
|
+
return elementValue1 === elementValue2;
|
301
|
+
}
|
302
|
+
if (elementValue1 instanceof tree.Attribute) {
|
303
|
+
if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
|
304
|
+
return false;
|
305
|
+
}
|
306
|
+
if (!elementValue1.value || !elementValue2.value) {
|
307
|
+
if (elementValue1.value || elementValue2.value) {
|
308
|
+
return false;
|
309
|
+
}
|
310
|
+
return true;
|
311
|
+
}
|
312
|
+
elementValue1 = elementValue1.value.value || elementValue1.value;
|
313
|
+
elementValue2 = elementValue2.value.value || elementValue2.value;
|
314
|
+
return elementValue1 === elementValue2;
|
315
|
+
}
|
316
|
+
return false;
|
317
|
+
},
|
318
|
+
extendSelector:function (matches, selectorPath, replacementSelector) {
|
319
|
+
|
320
|
+
//for a set of matches, replace each match with the replacement selector
|
321
|
+
|
322
|
+
var currentSelectorPathIndex = 0,
|
323
|
+
currentSelectorPathElementIndex = 0,
|
324
|
+
path = [],
|
325
|
+
matchIndex,
|
326
|
+
selector,
|
327
|
+
firstElement,
|
328
|
+
match;
|
329
|
+
|
330
|
+
for (matchIndex = 0; matchIndex < matches.length; matchIndex++) {
|
331
|
+
match = matches[matchIndex];
|
332
|
+
selector = selectorPath[match.pathIndex];
|
333
|
+
firstElement = new tree.Element(
|
334
|
+
match.initialCombinator,
|
335
|
+
replacementSelector.elements[0].value,
|
336
|
+
replacementSelector.elements[0].index
|
337
|
+
);
|
338
|
+
|
339
|
+
if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
|
340
|
+
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
341
|
+
currentSelectorPathElementIndex = 0;
|
342
|
+
currentSelectorPathIndex++;
|
343
|
+
}
|
344
|
+
|
345
|
+
path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
|
346
|
+
|
347
|
+
path.push(new tree.Selector(
|
348
|
+
selector.elements
|
349
|
+
.slice(currentSelectorPathElementIndex, match.index)
|
350
|
+
.concat([firstElement])
|
351
|
+
.concat(replacementSelector.elements.slice(1))
|
352
|
+
));
|
353
|
+
currentSelectorPathIndex = match.endPathIndex;
|
354
|
+
currentSelectorPathElementIndex = match.endPathElementIndex;
|
355
|
+
if (currentSelectorPathElementIndex >= selector.elements.length) {
|
356
|
+
currentSelectorPathElementIndex = 0;
|
357
|
+
currentSelectorPathIndex++;
|
358
|
+
}
|
359
|
+
}
|
360
|
+
|
361
|
+
if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
|
362
|
+
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
|
363
|
+
currentSelectorPathElementIndex = 0;
|
364
|
+
currentSelectorPathIndex++;
|
365
|
+
}
|
366
|
+
|
367
|
+
path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
|
368
|
+
|
369
|
+
return path;
|
370
|
+
},
|
371
|
+
visitRulesetOut: function (rulesetNode) {
|
372
|
+
},
|
373
|
+
visitMedia: function (mediaNode, visitArgs) {
|
374
|
+
var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
|
375
|
+
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
|
376
|
+
this.allExtendsStack.push(newAllExtends);
|
377
|
+
},
|
378
|
+
visitMediaOut: function (mediaNode) {
|
379
|
+
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
380
|
+
},
|
381
|
+
visitDirective: function (directiveNode, visitArgs) {
|
382
|
+
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
|
383
|
+
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
|
384
|
+
this.allExtendsStack.push(newAllExtends);
|
385
|
+
},
|
386
|
+
visitDirectiveOut: function (directiveNode) {
|
387
|
+
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
388
|
+
}
|
389
|
+
};
|
390
|
+
|
391
|
+
})(require('./tree'));
|
@@ -14,7 +14,7 @@ tree.functions = {
|
|
14
14
|
},
|
15
15
|
hsla: function (h, s, l, a) {
|
16
16
|
h = (number(h) % 360) / 360;
|
17
|
-
s = number(s); l = number(l); a = number(a);
|
17
|
+
s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
|
18
18
|
|
19
19
|
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
|
20
20
|
var m1 = l * 2 - m2;
|
@@ -71,6 +71,15 @@ tree.functions = {
|
|
71
71
|
lightness: function (color) {
|
72
72
|
return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
|
73
73
|
},
|
74
|
+
hsvhue: function(color) {
|
75
|
+
return new(tree.Dimension)(Math.round(color.toHSV().h));
|
76
|
+
},
|
77
|
+
hsvsaturation: function (color) {
|
78
|
+
return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%');
|
79
|
+
},
|
80
|
+
hsvvalue: function (color) {
|
81
|
+
return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%');
|
82
|
+
},
|
74
83
|
red: function (color) {
|
75
84
|
return new(tree.Dimension)(color.rgb[0]);
|
76
85
|
},
|
@@ -84,10 +93,7 @@ tree.functions = {
|
|
84
93
|
return new(tree.Dimension)(color.toHSL().a);
|
85
94
|
},
|
86
95
|
luma: function (color) {
|
87
|
-
return new(tree.Dimension)(Math.round((
|
88
|
-
0.7152 * (color.rgb[1]/255) +
|
89
|
-
0.0722 * (color.rgb[2]/255)) *
|
90
|
-
color.alpha * 100), '%');
|
96
|
+
return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');
|
91
97
|
},
|
92
98
|
saturate: function (color, amount) {
|
93
99
|
var hsl = color.toHSL();
|
@@ -184,12 +190,18 @@ tree.functions = {
|
|
184
190
|
if (typeof dark === 'undefined') {
|
185
191
|
dark = this.rgba(0, 0, 0, 1.0);
|
186
192
|
}
|
193
|
+
//Figure out which is actually light and dark!
|
194
|
+
if (dark.luma() > light.luma()) {
|
195
|
+
var t = light;
|
196
|
+
light = dark;
|
197
|
+
dark = t;
|
198
|
+
}
|
187
199
|
if (typeof threshold === 'undefined') {
|
188
200
|
threshold = 0.43;
|
189
201
|
} else {
|
190
|
-
threshold = threshold
|
202
|
+
threshold = number(threshold);
|
191
203
|
}
|
192
|
-
if ((
|
204
|
+
if ((color.luma() * color.alpha) < threshold) {
|
193
205
|
return light;
|
194
206
|
} else {
|
195
207
|
return dark;
|
@@ -217,19 +229,32 @@ tree.functions = {
|
|
217
229
|
unit: function (val, unit) {
|
218
230
|
return new(tree.Dimension)(val.value, unit ? unit.toCSS() : "");
|
219
231
|
},
|
232
|
+
convert: function (val, unit) {
|
233
|
+
return val.convertTo(unit.value);
|
234
|
+
},
|
220
235
|
round: function (n, f) {
|
221
236
|
var fraction = typeof(f) === "undefined" ? 0 : f.value;
|
222
|
-
return this._math(function(num) { return num.toFixed(fraction); }, n);
|
237
|
+
return this._math(function(num) { return num.toFixed(fraction); }, null, n);
|
238
|
+
},
|
239
|
+
pi: function () {
|
240
|
+
return new(tree.Dimension)(Math.PI);
|
223
241
|
},
|
224
|
-
|
225
|
-
return
|
242
|
+
mod: function(a, b) {
|
243
|
+
return new(tree.Dimension)(a.value % b.value, a.unit);
|
226
244
|
},
|
227
|
-
|
228
|
-
|
245
|
+
pow: function(x, y) {
|
246
|
+
if (typeof x === "number" && typeof y === "number") {
|
247
|
+
x = new(tree.Dimension)(x);
|
248
|
+
y = new(tree.Dimension)(y);
|
249
|
+
} else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) {
|
250
|
+
throw { type: "Argument", message: "arguments must be numbers" };
|
251
|
+
}
|
252
|
+
|
253
|
+
return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit);
|
229
254
|
},
|
230
|
-
_math: function (fn, n) {
|
255
|
+
_math: function (fn, unit, n) {
|
231
256
|
if (n instanceof tree.Dimension) {
|
232
|
-
return new(tree.Dimension)(fn(parseFloat(n.value)), n.unit);
|
257
|
+
return new(tree.Dimension)(fn(parseFloat(n.value)), unit == null ? n.unit : unit);
|
233
258
|
} else if (typeof(n) === 'number') {
|
234
259
|
return fn(n);
|
235
260
|
} else {
|
@@ -266,13 +291,16 @@ tree.functions = {
|
|
266
291
|
return this._isa(n, tree.URL);
|
267
292
|
},
|
268
293
|
ispixel: function (n) {
|
269
|
-
return (n
|
294
|
+
return this.isunit(n, 'px');
|
270
295
|
},
|
271
296
|
ispercentage: function (n) {
|
272
|
-
return (n
|
297
|
+
return this.isunit(n, '%');
|
273
298
|
},
|
274
299
|
isem: function (n) {
|
275
|
-
return (n
|
300
|
+
return this.isunit(n, 'em');
|
301
|
+
},
|
302
|
+
isunit: function (n, unit) {
|
303
|
+
return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
|
276
304
|
},
|
277
305
|
_isa: function (n, Type) {
|
278
306
|
return (n instanceof Type) ? tree.True : tree.False;
|
@@ -342,15 +370,135 @@ tree.functions = {
|
|
342
370
|
},
|
343
371
|
shade: function(color, amount) {
|
344
372
|
return this.mix(this.rgb(0, 0, 0), color, amount);
|
373
|
+
},
|
374
|
+
extract: function(values, index) {
|
375
|
+
index = index.value - 1; // (1-based index)
|
376
|
+
return values.value[index];
|
377
|
+
},
|
378
|
+
|
379
|
+
"data-uri": function(mimetypeNode, filePathNode) {
|
380
|
+
|
381
|
+
if (typeof window !== 'undefined') {
|
382
|
+
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
|
383
|
+
}
|
384
|
+
|
385
|
+
var mimetype = mimetypeNode.value;
|
386
|
+
var filePath = (filePathNode && filePathNode.value);
|
387
|
+
|
388
|
+
var fs = require("fs"),
|
389
|
+
path = require("path"),
|
390
|
+
useBase64 = false;
|
391
|
+
|
392
|
+
if (arguments.length < 2) {
|
393
|
+
filePath = mimetype;
|
394
|
+
}
|
395
|
+
|
396
|
+
if (this.env.isPathRelative(filePath)) {
|
397
|
+
if (this.currentFileInfo.relativeUrls) {
|
398
|
+
filePath = path.join(this.currentFileInfo.currentDirectory, filePath);
|
399
|
+
} else {
|
400
|
+
filePath = path.join(this.currentFileInfo.entryPath, filePath);
|
401
|
+
}
|
402
|
+
}
|
403
|
+
|
404
|
+
// detect the mimetype if not given
|
405
|
+
if (arguments.length < 2) {
|
406
|
+
var mime;
|
407
|
+
try {
|
408
|
+
mime = require('mime');
|
409
|
+
} catch (ex) {
|
410
|
+
mime = tree._mime;
|
411
|
+
}
|
412
|
+
|
413
|
+
mimetype = mime.lookup(filePath);
|
414
|
+
|
415
|
+
// use base 64 unless it's an ASCII or UTF-8 format
|
416
|
+
var charset = mime.charsets.lookup(mimetype);
|
417
|
+
useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
|
418
|
+
if (useBase64) mimetype += ';base64';
|
419
|
+
}
|
420
|
+
else {
|
421
|
+
useBase64 = /;base64$/.test(mimetype)
|
422
|
+
}
|
423
|
+
|
424
|
+
var buf = fs.readFileSync(filePath);
|
425
|
+
|
426
|
+
// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
|
427
|
+
// and the --ieCompat flag is enabled, return a normal url() instead.
|
428
|
+
var DATA_URI_MAX_KB = 32,
|
429
|
+
fileSizeInKB = parseInt((buf.length / 1024), 10);
|
430
|
+
if (fileSizeInKB >= DATA_URI_MAX_KB) {
|
431
|
+
|
432
|
+
if (this.env.ieCompat !== false) {
|
433
|
+
if (!this.env.silent) {
|
434
|
+
console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
|
435
|
+
}
|
436
|
+
|
437
|
+
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
|
438
|
+
} else if (!this.env.silent) {
|
439
|
+
// if explicitly disabled (via --no-ie-compat on CLI, or env.ieCompat === false), merely warn
|
440
|
+
console.warn("WARNING: Embedding %s (%dKB) exceeds IE8's data-uri size limit of %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
|
441
|
+
}
|
442
|
+
}
|
443
|
+
|
444
|
+
buf = useBase64 ? buf.toString('base64')
|
445
|
+
: encodeURIComponent(buf);
|
446
|
+
|
447
|
+
var uri = "'data:" + mimetype + ',' + buf + "'";
|
448
|
+
return new(tree.URL)(new(tree.Anonymous)(uri));
|
449
|
+
}
|
450
|
+
};
|
451
|
+
|
452
|
+
// these static methods are used as a fallback when the optional 'mime' dependency is missing
|
453
|
+
tree._mime = {
|
454
|
+
// this map is intentionally incomplete
|
455
|
+
// if you want more, install 'mime' dep
|
456
|
+
_types: {
|
457
|
+
'.htm' : 'text/html',
|
458
|
+
'.html': 'text/html',
|
459
|
+
'.gif' : 'image/gif',
|
460
|
+
'.jpg' : 'image/jpeg',
|
461
|
+
'.jpeg': 'image/jpeg',
|
462
|
+
'.png' : 'image/png'
|
463
|
+
},
|
464
|
+
lookup: function (filepath) {
|
465
|
+
var ext = require('path').extname(filepath),
|
466
|
+
type = tree._mime._types[ext];
|
467
|
+
if (type === undefined) {
|
468
|
+
throw new Error('Optional dependency "mime" is required for ' + ext);
|
469
|
+
}
|
470
|
+
return type;
|
471
|
+
},
|
472
|
+
charsets: {
|
473
|
+
lookup: function (type) {
|
474
|
+
// assumes all text types are UTF-8
|
475
|
+
return type && (/^text\//).test(type) ? 'UTF-8' : '';
|
476
|
+
}
|
345
477
|
}
|
346
478
|
};
|
347
479
|
|
480
|
+
var mathFunctions = [{name:"ceil"}, {name:"floor"}, {name: "sqrt"}, {name:"abs"},
|
481
|
+
{name:"tan", unit: ""}, {name:"sin", unit: ""}, {name:"cos", unit: ""},
|
482
|
+
{name:"atan", unit: "rad"}, {name:"asin", unit: "rad"}, {name:"acos", unit: "rad"}],
|
483
|
+
createMathFunction = function(name, unit) {
|
484
|
+
return function(n) {
|
485
|
+
if (unit != null) {
|
486
|
+
n = n.unify();
|
487
|
+
}
|
488
|
+
return this._math(Math[name], unit, n);
|
489
|
+
};
|
490
|
+
};
|
491
|
+
|
492
|
+
for(var i = 0; i < mathFunctions.length; i++) {
|
493
|
+
tree.functions[mathFunctions[i].name] = createMathFunction(mathFunctions[i].name, mathFunctions[i].unit);
|
494
|
+
}
|
495
|
+
|
348
496
|
function hsla(color) {
|
349
497
|
return tree.functions.hsla(color.h, color.s, color.l, color.a);
|
350
498
|
}
|
351
499
|
|
352
500
|
function scaled(n, size) {
|
353
|
-
if (n instanceof tree.Dimension && n.unit
|
501
|
+
if (n instanceof tree.Dimension && n.unit.is('%')) {
|
354
502
|
return parseFloat(n.value * size / 100);
|
355
503
|
} else {
|
356
504
|
return number(n);
|
@@ -359,7 +507,7 @@ function scaled(n, size) {
|
|
359
507
|
|
360
508
|
function number(n) {
|
361
509
|
if (n instanceof tree.Dimension) {
|
362
|
-
return parseFloat(n.unit
|
510
|
+
return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
|
363
511
|
} else if (typeof(n) === 'number') {
|
364
512
|
return n;
|
365
513
|
} else {
|
@@ -374,4 +522,11 @@ function clamp(val) {
|
|
374
522
|
return Math.min(1, Math.max(0, val));
|
375
523
|
}
|
376
524
|
|
525
|
+
tree.functionCall = function(env, currentFileInfo) {
|
526
|
+
this.env = env;
|
527
|
+
this.currentFileInfo = currentFileInfo;
|
528
|
+
};
|
529
|
+
|
530
|
+
tree.functionCall.prototype = tree.functions;
|
531
|
+
|
377
532
|
})(require('./tree'));
|