mirror of
https://github.com/less/less.js.git
synced 2026-01-10 08:08:19 -05:00
* Remove class extends pattern for nodes * Fix node benchmark * v3.13.0 * Restore tree caching
This commit is contained in:
6058
dist/less.js
vendored
6058
dist/less.js
vendored
File diff suppressed because it is too large
Load Diff
4
dist/less.min.js
vendored
4
dist/less.min.js
vendored
File diff suppressed because one or more lines are too long
2
dist/less.min.js.map
vendored
2
dist/less.min.js.map
vendored
File diff suppressed because one or more lines are too long
@@ -3,5 +3,5 @@
|
||||
"packages/*"
|
||||
],
|
||||
"npmClient": "npm",
|
||||
"version": "3.12.2"
|
||||
"version": "3.13.0"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@less/root",
|
||||
"private": true,
|
||||
"version": "3.12.2",
|
||||
"version": "3.13.0",
|
||||
"description": "Less monorepo",
|
||||
"homepage": "http://lesscss.org",
|
||||
"scripts": {
|
||||
|
||||
@@ -243,9 +243,6 @@ module.exports = function(grunt) {
|
||||
benchmark: {
|
||||
command: "node benchmark/index.js"
|
||||
},
|
||||
benchmarkbrowser: {
|
||||
command: "node test/browser/generator/runner.js benchmark"
|
||||
},
|
||||
opts: {
|
||||
// test running with all current options (using `opts` since `options` means something already)
|
||||
command: [
|
||||
@@ -416,15 +413,8 @@ module.exports = function(grunt) {
|
||||
]);
|
||||
|
||||
// Run benchmark
|
||||
grunt.registerTask("benchmark-node", [
|
||||
grunt.registerTask("benchmark", [
|
||||
"shell:testcjs",
|
||||
"shell:benchmark"
|
||||
]);
|
||||
|
||||
// Run all browser tests
|
||||
grunt.registerTask("benchmark", [
|
||||
"browsertest-lessjs",
|
||||
"connect",
|
||||
"shell:benchmarkbrowser"
|
||||
]);
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2327,7 +2327,7 @@ p + h1 {
|
||||
}
|
||||
|
||||
.no-parens {
|
||||
.mixin;
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.no-args {
|
||||
@@ -2388,7 +2388,7 @@ body {
|
||||
@var: 10px;
|
||||
width: @var;
|
||||
}
|
||||
#var-inside { .var-inside; }
|
||||
#var-inside { .var-inside(); }
|
||||
.mix-inner (@var) {
|
||||
border-width: @var;
|
||||
}
|
||||
@@ -2525,41 +2525,41 @@ body {
|
||||
}
|
||||
#container {
|
||||
color: black;
|
||||
.mixin;
|
||||
.mixout;
|
||||
#theme > .mixin;
|
||||
.mixin();
|
||||
.mixout();
|
||||
#theme > .mixin();
|
||||
}
|
||||
|
||||
#header {
|
||||
.milk {
|
||||
color: white;
|
||||
.mixin;
|
||||
#theme > .mixin;
|
||||
.mixin();
|
||||
#theme > .mixin();
|
||||
}
|
||||
#cookie {
|
||||
.chips {
|
||||
#namespace .borders;
|
||||
#namespace .borders();
|
||||
.calories {
|
||||
#container;
|
||||
#container();
|
||||
}
|
||||
}
|
||||
.borders;
|
||||
.borders();
|
||||
}
|
||||
}
|
||||
.secure-zone { #namespace .biohazard .man; }
|
||||
.secure-zone { #namespace .biohazard .man(); }
|
||||
.direct {
|
||||
#namespace > .borders;
|
||||
#namespace > .borders();
|
||||
}
|
||||
#operations {
|
||||
color: #110000 + #000011 + #001100; // #111111
|
||||
height: 10px / 2px + 6px - 1px * 2; // 9px
|
||||
height: (10px / 2px) + 6px - 1px * 2; // 9px
|
||||
width: 2 * 4 - 5em; // 3em
|
||||
.spacing {
|
||||
height: 10px / 2px+6px-1px*2;
|
||||
height: (10px / 2px)+6px-1px*2;
|
||||
width: 2 * 4-5em;
|
||||
}
|
||||
subtraction: 20 - 10 - 5 - 5; // 0
|
||||
division: 20 / 5 / 4; // 1
|
||||
division: (20 / 5 / 4); // 1
|
||||
}
|
||||
|
||||
@x: 4;
|
||||
@@ -2588,7 +2588,7 @@ body {
|
||||
background-color: #222222 - #fff; // #000000
|
||||
.other {
|
||||
color: 2 * #111; // #222222
|
||||
border-color: #333333 / 3 + #111; // #222222
|
||||
border-color: (#333333 / 3) + #111; // #222222
|
||||
}
|
||||
}
|
||||
.parens {
|
||||
@@ -2604,7 +2604,7 @@ body {
|
||||
padding: (2 * @var) 4 4 (@var * 1px);
|
||||
width: (@var * @var) * 6;
|
||||
height: (7 * 7) + (8 * 8);
|
||||
margin: 4 * (5 + 5) / 2 - (@var * 2);
|
||||
margin: 4 * ((5 + 5) / 2) - (@var * 2);
|
||||
//margin: (6 * 6)px;
|
||||
}
|
||||
|
||||
@@ -2657,7 +2657,7 @@ body {
|
||||
|
||||
.tiny-scope {
|
||||
color: @mix; // #989
|
||||
.mixin;
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.scope1 {
|
||||
@@ -3091,7 +3091,7 @@ p + h1 {
|
||||
}
|
||||
|
||||
.no-parens {
|
||||
.mixin;
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.no-args {
|
||||
@@ -3152,7 +3152,7 @@ body {
|
||||
@var: 10px;
|
||||
width: @var;
|
||||
}
|
||||
#var-inside { .var-inside; }
|
||||
#var-inside { .var-inside(); }
|
||||
.mix-inner (@var) {
|
||||
border-width: @var;
|
||||
}
|
||||
@@ -3289,41 +3289,41 @@ body {
|
||||
}
|
||||
#container {
|
||||
color: black;
|
||||
.mixin;
|
||||
.mixout;
|
||||
#theme > .mixin;
|
||||
.mixin();
|
||||
.mixout();
|
||||
#theme > .mixin();
|
||||
}
|
||||
|
||||
#header {
|
||||
.milk {
|
||||
color: white;
|
||||
.mixin;
|
||||
#theme > .mixin;
|
||||
.mixin();
|
||||
#theme > .mixin();
|
||||
}
|
||||
#cookie {
|
||||
.chips {
|
||||
#namespace .borders;
|
||||
#namespace .borders();
|
||||
.calories {
|
||||
#container;
|
||||
#container();
|
||||
}
|
||||
}
|
||||
.borders;
|
||||
.borders();
|
||||
}
|
||||
}
|
||||
.secure-zone { #namespace .biohazard .man; }
|
||||
.secure-zone { #namespace .biohazard .man(); }
|
||||
.direct {
|
||||
#namespace > .borders;
|
||||
#namespace > .borders();
|
||||
}
|
||||
#operations {
|
||||
color: #110000 + #000011 + #001100; // #111111
|
||||
height: 10px / 2px + 6px - 1px * 2; // 9px
|
||||
height: (10px / 2px) + 6px - 1px * 2; // 9px
|
||||
width: 2 * 4 - 5em; // 3em
|
||||
.spacing {
|
||||
height: 10px / 2px+6px-1px*2;
|
||||
height: (10px / 2px)+6px-1px*2;
|
||||
width: 2 * 4-5em;
|
||||
}
|
||||
subtraction: 20 - 10 - 5 - 5; // 0
|
||||
division: 20 / 5 / 4; // 1
|
||||
division: (20 / 5 / 4); // 1
|
||||
}
|
||||
|
||||
@x: 4;
|
||||
@@ -3352,7 +3352,7 @@ body {
|
||||
background-color: #222222 - #fff; // #000000
|
||||
.other {
|
||||
color: 2 * #111; // #222222
|
||||
border-color: #333333 / 3 + #111; // #222222
|
||||
border-color: (#333333 / 3) + #111; // #222222
|
||||
}
|
||||
}
|
||||
.parens {
|
||||
@@ -3368,7 +3368,7 @@ body {
|
||||
padding: (2 * @var) 4 4 (@var * 1px);
|
||||
width: (@var * @var) * 6;
|
||||
height: (7 * 7) + (8 * 8);
|
||||
margin: 4 * (5 + 5) / 2 - (@var * 2);
|
||||
margin: 4 * ((5 + 5) / 2) - (@var * 2);
|
||||
//margin: (6 * 6)px;
|
||||
}
|
||||
|
||||
@@ -3421,7 +3421,7 @@ body {
|
||||
|
||||
.tiny-scope {
|
||||
color: @mix; // #989
|
||||
.mixin;
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.scope1 {
|
||||
@@ -3855,7 +3855,7 @@ p + h1 {
|
||||
}
|
||||
|
||||
.no-parens {
|
||||
.mixin;
|
||||
.mixin();
|
||||
}
|
||||
|
||||
.no-args {
|
||||
@@ -3916,7 +3916,7 @@ body {
|
||||
@var: 10px;
|
||||
width: @var;
|
||||
}
|
||||
#var-inside { .var-inside; }
|
||||
#var-inside { .var-inside(); }
|
||||
.mix-inner (@var) {
|
||||
border-width: @var;
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
var less = {
|
||||
logLevel: 4,
|
||||
rewriteUrls: 0
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
describe('Benchmark', function() {
|
||||
testLessEqualsInDocument();
|
||||
});
|
||||
@@ -2,7 +2,7 @@ var path = require('path'),
|
||||
fs = require('fs'),
|
||||
now = require("performance-now");
|
||||
|
||||
var less = require('../test/less');
|
||||
var less = require('../.');
|
||||
var file = path.join(__dirname, 'benchmark.less');
|
||||
|
||||
if (process.argv[2]) { file = path.join(process.cwd(), process.argv[2]) }
|
||||
@@ -31,7 +31,7 @@ fs.readFile(file, 'utf8', function (e, data) {
|
||||
|
||||
less.parse(data, {}, function(err, root, imports, options) {
|
||||
if (err) {
|
||||
less.writeError(err);
|
||||
console.log(err);
|
||||
process.exit(3);
|
||||
}
|
||||
parserEnd = now();
|
||||
|
||||
6058
packages/less/dist/less.js
vendored
6058
packages/less/dist/less.js
vendored
File diff suppressed because it is too large
Load Diff
4
packages/less/dist/less.min.js
vendored
4
packages/less/dist/less.min.js
vendored
File diff suppressed because one or more lines are too long
2
packages/less/dist/less.min.js.map
vendored
2
packages/less/dist/less.min.js.map
vendored
File diff suppressed because one or more lines are too long
2
packages/less/package-lock.json
generated
2
packages/less/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "less",
|
||||
"version": "3.12.2",
|
||||
"version": "3.13.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "less",
|
||||
"version": "3.12.2",
|
||||
"version": "3.13.0",
|
||||
"description": "Leaner CSS",
|
||||
"homepage": "http://lesscss.org",
|
||||
"author": {
|
||||
@@ -54,8 +54,8 @@
|
||||
"source-map": "~0.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@less/test-data": "^3.12.2",
|
||||
"@less/test-import-module": "^3.12.0",
|
||||
"@less/test-data": "^3.13.0",
|
||||
"@less/test-import-module": "^3.13.0",
|
||||
"@typescript-eslint/eslint-plugin": "^3.3.0",
|
||||
"@typescript-eslint/parser": "^3.3.0",
|
||||
"benny": "^3.6.12",
|
||||
|
||||
@@ -4,7 +4,7 @@ import LessError from './less-error';
|
||||
import * as utils from './utils';
|
||||
import logger from './logger';
|
||||
|
||||
export default environment => {
|
||||
export default function(environment) {
|
||||
// FileInfo = {
|
||||
// 'rewriteUrls' - option - whether to adjust URL's to be relative
|
||||
// 'filename' - full resolved filename of current file
|
||||
@@ -26,7 +26,7 @@ export default environment => {
|
||||
this.context = context;
|
||||
// Deprecated? Unused outside of here, could be useful.
|
||||
this.queue = []; // Files which haven't been imported yet
|
||||
this.files = []; // List of files imported
|
||||
this.files = {}; // Holds the imported parse trees.
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -38,12 +38,11 @@ export default environment => {
|
||||
* @param callback - callback for when it is imported
|
||||
*/
|
||||
push(path, tryAppendExtension, currentFileInfo, importOptions, callback) {
|
||||
const importManager = this;
|
||||
const pluginLoader = this.context.pluginManager.Loader;
|
||||
const importManager = this, pluginLoader = this.context.pluginManager.Loader;
|
||||
|
||||
this.queue.push(path);
|
||||
|
||||
const fileParsedFunc = (e, root, fullPath) => {
|
||||
const fileParsedFunc = function (e, root, fullPath) {
|
||||
importManager.queue.splice(importManager.queue.indexOf(path), 1); // Remove the path from the queue
|
||||
|
||||
const importedEqualsRoot = fullPath === importManager.rootFilename;
|
||||
@@ -52,9 +51,11 @@ export default environment => {
|
||||
logger.info(`The file ${fullPath} was skipped because it was not found and the import was marked optional.`);
|
||||
}
|
||||
else {
|
||||
const files = importManager.files
|
||||
if (files.indexOf(fullPath) === -1) {
|
||||
files.push(fullPath)
|
||||
// Inline imports aren't cached here.
|
||||
// If we start to cache them, please make sure they won't conflict with non-inline imports of the
|
||||
// same name as they used to do before this comment and the condition below have been added.
|
||||
if (!importManager.files[fullPath] && !importOptions.inline) {
|
||||
importManager.files[fullPath] = { root, options: importOptions };
|
||||
}
|
||||
if (e && !importManager.error) { importManager.error = e; }
|
||||
callback(e, root, importedEqualsRoot, fullPath);
|
||||
@@ -75,7 +76,7 @@ export default environment => {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadFileCallback = loadedFile => {
|
||||
const loadFileCallback = function(loadedFile) {
|
||||
let plugin;
|
||||
const resolvedFilename = loadedFile.filename;
|
||||
const contents = loadedFile.contents.replace(/^\uFEFF/, '');
|
||||
@@ -120,9 +121,20 @@ export default environment => {
|
||||
} else if (importOptions.inline) {
|
||||
fileParsedFunc(null, contents, resolvedFilename);
|
||||
} else {
|
||||
new Parser(newEnv, importManager, newFileInfo).parse(contents, (e, root) => {
|
||||
fileParsedFunc(e, root, resolvedFilename);
|
||||
});
|
||||
|
||||
// import (multiple) parse trees apparently get altered and can't be cached.
|
||||
// TODO: investigate why this is
|
||||
if (importManager.files[resolvedFilename]
|
||||
&& !importManager.files[resolvedFilename].options.multiple
|
||||
&& !importOptions.multiple) {
|
||||
|
||||
fileParsedFunc(null, importManager.files[resolvedFilename].root, resolvedFilename);
|
||||
}
|
||||
else {
|
||||
new Parser(newEnv, importManager, newFileInfo).parse(contents, function (e, root) {
|
||||
fileParsedFunc(e, root, resolvedFilename);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
let loadedFile;
|
||||
|
||||
@@ -42,7 +42,7 @@ export default (environment, fileManagers) => {
|
||||
* It's not clear what should / must be public and why.
|
||||
*/
|
||||
const initial = {
|
||||
version: [3, 12, 2],
|
||||
version: [3, 13, 0],
|
||||
data,
|
||||
tree,
|
||||
Environment,
|
||||
|
||||
@@ -52,8 +52,12 @@ export default SourceMapBuilder => {
|
||||
result.map = sourceMapBuilder.getExternalSourceMap();
|
||||
}
|
||||
|
||||
const rootFilename = this.imports.rootFilename
|
||||
result.imports = this.imports.files.filter(file => file !== rootFilename);
|
||||
result.imports = [];
|
||||
for (const file in this.imports.files) {
|
||||
if (this.imports.files.hasOwnProperty(file) && file !== this.imports.rootFilename) {
|
||||
result.imports.push(file);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
import Node from './node';
|
||||
import { extend } from './util';
|
||||
|
||||
class Anonymous extends Node {
|
||||
constructor(value, index, currentFileInfo, mapLines, rulesetLike, visibilityInfo) {
|
||||
super();
|
||||
|
||||
this.value = value;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.mapLines = mapLines;
|
||||
this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
|
||||
this.allowRoot = true;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
}
|
||||
|
||||
eval() {
|
||||
return new Anonymous(this.value, this._index, this._fileInfo, this.mapLines, this.rulesetLike, this.visibilityInfo());
|
||||
}
|
||||
|
||||
compare(other) {
|
||||
return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
|
||||
}
|
||||
|
||||
isRulesetLike() {
|
||||
return this.rulesetLike;
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
this.nodeVisible = Boolean(this.value);
|
||||
if (this.nodeVisible) {
|
||||
output.add(this.value, this._fileInfo, this._index, this.mapLines);
|
||||
}
|
||||
}
|
||||
const Anonymous = function(value, index, currentFileInfo, mapLines, rulesetLike, visibilityInfo) {
|
||||
this.value = value;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.mapLines = mapLines;
|
||||
this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
|
||||
this.allowRoot = true;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
}
|
||||
|
||||
Anonymous.prototype = new Node();
|
||||
|
||||
Anonymous.prototype.eval = function() {
|
||||
return new Anonymous(this.value, this._index, this._fileInfo, this.mapLines, this.rulesetLike, this.visibilityInfo());
|
||||
};
|
||||
|
||||
Anonymous.prototype.compare = function(other) {
|
||||
return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
|
||||
};
|
||||
|
||||
Anonymous.prototype.isRulesetLike = function() {
|
||||
return this.rulesetLike;
|
||||
};
|
||||
|
||||
Anonymous.prototype.genCSS = function(context, output) {
|
||||
this.nodeVisible = Boolean(this.value);
|
||||
if (this.nodeVisible) {
|
||||
output.add(this.value, this._fileInfo, this._index, this.mapLines);
|
||||
}
|
||||
};
|
||||
|
||||
Anonymous.prototype.type = 'Anonymous';
|
||||
export default Anonymous;
|
||||
|
||||
@@ -1,33 +1,31 @@
|
||||
import Node from './node';
|
||||
|
||||
class Assignment extends Node {
|
||||
constructor(key, val) {
|
||||
super();
|
||||
const Assignment = function(key, val) {
|
||||
this.key = key;
|
||||
this.value = val;
|
||||
};
|
||||
|
||||
this.key = key;
|
||||
this.value = val;
|
||||
}
|
||||
Assignment.prototype = new Node();
|
||||
|
||||
accept(visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
}
|
||||
Assignment.prototype.accept = function(visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
|
||||
eval(context) {
|
||||
if (this.value.eval) {
|
||||
return new Assignment(this.key, this.value.eval(context));
|
||||
}
|
||||
return this;
|
||||
Assignment.prototype.eval = function(context) {
|
||||
if (this.value.eval) {
|
||||
return new Assignment(this.key, this.value.eval(context));
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(`${this.key}=`);
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(context, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
Assignment.prototype.genCSS = function(context, output) {
|
||||
output.add(`${this.key}=`);
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(context, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Assignment.prototype.type = 'Assignment';
|
||||
export default Assignment;
|
||||
|
||||
@@ -3,163 +3,161 @@ import Selector from './selector';
|
||||
import Ruleset from './ruleset';
|
||||
import Anonymous from './anonymous';
|
||||
|
||||
class AtRule extends Node {
|
||||
constructor(
|
||||
name,
|
||||
value,
|
||||
rules,
|
||||
index,
|
||||
currentFileInfo,
|
||||
debugInfo,
|
||||
isRooted,
|
||||
visibilityInfo
|
||||
) {
|
||||
super();
|
||||
const AtRule = function(
|
||||
name,
|
||||
value,
|
||||
rules,
|
||||
index,
|
||||
currentFileInfo,
|
||||
debugInfo,
|
||||
isRooted,
|
||||
visibilityInfo
|
||||
) {
|
||||
let i;
|
||||
|
||||
let i;
|
||||
|
||||
this.name = name;
|
||||
this.value = (value instanceof Node) ? value : (value ? new Anonymous(value) : value);
|
||||
if (rules) {
|
||||
if (Array.isArray(rules)) {
|
||||
this.rules = rules;
|
||||
} else {
|
||||
this.rules = [rules];
|
||||
this.rules[0].selectors = (new Selector([], null, null, index, currentFileInfo)).createEmptySelectors();
|
||||
}
|
||||
for (i = 0; i < this.rules.length; i++) {
|
||||
this.rules[i].allowImports = true;
|
||||
}
|
||||
this.setParent(this.rules, this);
|
||||
}
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.debugInfo = debugInfo;
|
||||
this.isRooted = isRooted || false;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
const value = this.value;
|
||||
const rules = this.rules;
|
||||
if (rules) {
|
||||
this.rules = visitor.visitArray(rules);
|
||||
}
|
||||
if (value) {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
}
|
||||
|
||||
isRulesetLike() {
|
||||
return this.rules || !this.isCharset();
|
||||
}
|
||||
|
||||
isCharset() {
|
||||
return '@charset' === this.name;
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
const value = this.value;
|
||||
const rules = this.rules;
|
||||
output.add(this.name, this.fileInfo(), this.getIndex());
|
||||
if (value) {
|
||||
output.add(' ');
|
||||
value.genCSS(context, output);
|
||||
}
|
||||
if (rules) {
|
||||
this.outputRuleset(context, output, rules);
|
||||
this.name = name;
|
||||
this.value = (value instanceof Node) ? value : (value ? new Anonymous(value) : value);
|
||||
if (rules) {
|
||||
if (Array.isArray(rules)) {
|
||||
this.rules = rules;
|
||||
} else {
|
||||
output.add(';');
|
||||
this.rules = [rules];
|
||||
this.rules[0].selectors = (new Selector([], null, null, index, currentFileInfo)).createEmptySelectors();
|
||||
}
|
||||
for (i = 0; i < this.rules.length; i++) {
|
||||
this.rules[i].allowImports = true;
|
||||
}
|
||||
this.setParent(this.rules, this);
|
||||
}
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.debugInfo = debugInfo;
|
||||
this.isRooted = isRooted || false;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
};
|
||||
|
||||
eval(context) {
|
||||
let mediaPathBackup;
|
||||
let mediaBlocksBackup;
|
||||
let value = this.value;
|
||||
let rules = this.rules;
|
||||
AtRule.prototype = new Node();
|
||||
|
||||
// media stored inside other atrule should not bubble over it
|
||||
// backpup media bubbling information
|
||||
mediaPathBackup = context.mediaPath;
|
||||
mediaBlocksBackup = context.mediaBlocks;
|
||||
// deleted media bubbling information
|
||||
context.mediaPath = [];
|
||||
context.mediaBlocks = [];
|
||||
|
||||
if (value) {
|
||||
value = value.eval(context);
|
||||
}
|
||||
if (rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
rules = [rules[0].eval(context)];
|
||||
rules[0].root = true;
|
||||
}
|
||||
// restore media bubbling information
|
||||
context.mediaPath = mediaPathBackup;
|
||||
context.mediaBlocks = mediaBlocksBackup;
|
||||
|
||||
return new AtRule(this.name, value, rules,
|
||||
this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
|
||||
AtRule.prototype.accept = function(visitor) {
|
||||
const value = this.value;
|
||||
const rules = this.rules;
|
||||
if (rules) {
|
||||
this.rules = visitor.visitArray(rules);
|
||||
}
|
||||
|
||||
variable(name) {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.variable.call(this.rules[0], name);
|
||||
}
|
||||
if (value) {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
};
|
||||
|
||||
find(...args) {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.find.apply(this.rules[0], args);
|
||||
}
|
||||
AtRule.prototype.isRulesetLike = function() {
|
||||
return this.rules || !this.isCharset();
|
||||
};
|
||||
|
||||
AtRule.prototype.isCharset = function() {
|
||||
return '@charset' === this.name;
|
||||
};
|
||||
|
||||
AtRule.prototype.genCSS = function(context, output) {
|
||||
const value = this.value;
|
||||
const rules = this.rules;
|
||||
output.add(this.name, this.fileInfo(), this.getIndex());
|
||||
if (value) {
|
||||
output.add(' ');
|
||||
value.genCSS(context, output);
|
||||
}
|
||||
|
||||
rulesets() {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.rulesets.apply(this.rules[0]);
|
||||
}
|
||||
if (rules) {
|
||||
this.outputRuleset(context, output, rules);
|
||||
} else {
|
||||
output.add(';');
|
||||
}
|
||||
};
|
||||
|
||||
outputRuleset(context, output, rules) {
|
||||
const ruleCnt = rules.length;
|
||||
let i;
|
||||
context.tabLevel = (context.tabLevel | 0) + 1;
|
||||
AtRule.prototype.eval = function(context) {
|
||||
let mediaPathBackup;
|
||||
let mediaBlocksBackup;
|
||||
let value = this.value;
|
||||
let rules = this.rules;
|
||||
|
||||
// Compressed
|
||||
if (context.compress) {
|
||||
output.add('{');
|
||||
for (i = 0; i < ruleCnt; i++) {
|
||||
rules[i].genCSS(context, output);
|
||||
}
|
||||
output.add('}');
|
||||
context.tabLevel--;
|
||||
return;
|
||||
// media stored inside other atrule should not bubble over it
|
||||
// backpup media bubbling information
|
||||
mediaPathBackup = context.mediaPath;
|
||||
mediaBlocksBackup = context.mediaBlocks;
|
||||
// deleted media bubbling information
|
||||
context.mediaPath = [];
|
||||
context.mediaBlocks = [];
|
||||
|
||||
if (value) {
|
||||
value = value.eval(context);
|
||||
}
|
||||
if (rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
rules = [rules[0].eval(context)];
|
||||
rules[0].root = true;
|
||||
}
|
||||
// restore media bubbling information
|
||||
context.mediaPath = mediaPathBackup;
|
||||
context.mediaBlocks = mediaBlocksBackup;
|
||||
|
||||
return new AtRule(this.name, value, rules,
|
||||
this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
|
||||
};
|
||||
|
||||
AtRule.prototype.variable = function(name) {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.variable.call(this.rules[0], name);
|
||||
}
|
||||
};
|
||||
|
||||
AtRule.prototype.find = function(...args) {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.find.apply(this.rules[0], args);
|
||||
}
|
||||
};
|
||||
|
||||
AtRule.prototype.rulesets = function() {
|
||||
if (this.rules) {
|
||||
// assuming that there is only one rule at this point - that is how parser constructs the rule
|
||||
return Ruleset.prototype.rulesets.apply(this.rules[0]);
|
||||
}
|
||||
};
|
||||
|
||||
AtRule.prototype.outputRuleset = function(context, output, rules) {
|
||||
const ruleCnt = rules.length;
|
||||
let i;
|
||||
context.tabLevel = (context.tabLevel | 0) + 1;
|
||||
|
||||
// Compressed
|
||||
if (context.compress) {
|
||||
output.add('{');
|
||||
for (i = 0; i < ruleCnt; i++) {
|
||||
rules[i].genCSS(context, output);
|
||||
}
|
||||
|
||||
// Non-compressed
|
||||
const tabSetStr = `\n${Array(context.tabLevel).join(' ')}`;
|
||||
|
||||
const tabRuleStr = `${tabSetStr} `;
|
||||
if (!ruleCnt) {
|
||||
output.add(` {${tabSetStr}}`);
|
||||
} else {
|
||||
output.add(` {${tabRuleStr}`);
|
||||
rules[0].genCSS(context, output);
|
||||
for (i = 1; i < ruleCnt; i++) {
|
||||
output.add(tabRuleStr);
|
||||
rules[i].genCSS(context, output);
|
||||
}
|
||||
output.add(`${tabSetStr}}`);
|
||||
}
|
||||
|
||||
output.add('}');
|
||||
context.tabLevel--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Non-compressed
|
||||
const tabSetStr = `\n${Array(context.tabLevel).join(' ')}`;
|
||||
|
||||
const tabRuleStr = `${tabSetStr} `;
|
||||
if (!ruleCnt) {
|
||||
output.add(` {${tabSetStr}}`);
|
||||
} else {
|
||||
output.add(` {${tabRuleStr}`);
|
||||
rules[0].genCSS(context, output);
|
||||
for (i = 1; i < ruleCnt; i++) {
|
||||
output.add(tabRuleStr);
|
||||
rules[i].genCSS(context, output);
|
||||
}
|
||||
output.add(`${tabSetStr}}`);
|
||||
}
|
||||
|
||||
context.tabLevel--;
|
||||
};
|
||||
|
||||
AtRule.prototype.type = 'AtRule';
|
||||
export default AtRule;
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
import Node from './node';
|
||||
|
||||
class Attribute extends Node {
|
||||
constructor(key, op, value) {
|
||||
super();
|
||||
const Attribute = function(key, op, value) {
|
||||
this.key = key;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
this.key = key;
|
||||
this.op = op;
|
||||
this.value = value;
|
||||
Attribute.prototype = new Node();
|
||||
|
||||
Attribute.prototype.eval = function(context) {
|
||||
return new Attribute(this.key.eval ? this.key.eval(context) : this.key,
|
||||
this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value);
|
||||
};
|
||||
|
||||
Attribute.prototype.genCSS = function(context, output) {
|
||||
output.add(this.toCSS(context));
|
||||
};
|
||||
|
||||
Attribute.prototype.toCSS = function(context) {
|
||||
let value = this.key.toCSS ? this.key.toCSS(context) : this.key;
|
||||
|
||||
if (this.op) {
|
||||
value += this.op;
|
||||
value += (this.value.toCSS ? this.value.toCSS(context) : this.value);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
return new Attribute(this.key.eval ? this.key.eval(context) : this.key,
|
||||
this.op, (this.value && this.value.eval) ? this.value.eval(context) : this.value);
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(this.toCSS(context));
|
||||
}
|
||||
|
||||
toCSS(context) {
|
||||
let value = this.key.toCSS ? this.key.toCSS(context) : this.key;
|
||||
|
||||
if (this.op) {
|
||||
value += this.op;
|
||||
value += (this.value.toCSS ? this.value.toCSS(context) : this.value);
|
||||
}
|
||||
|
||||
return `[${value}]`;
|
||||
}
|
||||
}
|
||||
return `[${value}]`;
|
||||
};
|
||||
|
||||
Attribute.prototype.type = 'Attribute';
|
||||
export default Attribute;
|
||||
|
||||
@@ -5,109 +5,107 @@ import FunctionCaller from '../functions/function-caller';
|
||||
//
|
||||
// A function call node.
|
||||
//
|
||||
class Call extends Node {
|
||||
constructor(name, args, index, currentFileInfo) {
|
||||
super();
|
||||
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.calc = name === 'calc';
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.args) {
|
||||
this.args = visitor.visitArray(this.args);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// When evaluating a function call,
|
||||
// we either find the function in the functionRegistry,
|
||||
// in which case we call it, passing the evaluated arguments,
|
||||
// if this returns null or we cannot find the function, we
|
||||
// simply print it out as it appeared originally [2].
|
||||
//
|
||||
// The reason why we evaluate the arguments, is in the case where
|
||||
// we try to pass a variable to a function, like: `saturate(@color)`.
|
||||
// The function should receive the value, not the variable.
|
||||
//
|
||||
eval(context) {
|
||||
/**
|
||||
* Turn off math for calc(), and switch back on for evaluating nested functions
|
||||
*/
|
||||
const currentMathContext = context.mathOn;
|
||||
context.mathOn = !this.calc;
|
||||
if (this.calc || context.inCalc) {
|
||||
context.enterCalc();
|
||||
}
|
||||
|
||||
const exitCalc = () => {
|
||||
if (this.calc || context.inCalc) {
|
||||
context.exitCalc();
|
||||
}
|
||||
context.mathOn = currentMathContext;
|
||||
};
|
||||
|
||||
let result;
|
||||
const funcCaller = new FunctionCaller(this.name, context, this.getIndex(), this.fileInfo());
|
||||
|
||||
if (funcCaller.isValid()) {
|
||||
try {
|
||||
result = funcCaller.call(this.args);
|
||||
exitCalc();
|
||||
} catch (e) {
|
||||
if (e.hasOwnProperty('line') && e.hasOwnProperty('column')) {
|
||||
throw e
|
||||
}
|
||||
throw {
|
||||
type: e.type || 'Runtime',
|
||||
message: `error evaluating function \`${this.name}\`${e.message ? `: ${e.message}` : ''}`,
|
||||
index: this.getIndex(),
|
||||
filename: this.fileInfo().filename,
|
||||
line: e.lineNumber,
|
||||
column: e.columnNumber
|
||||
};
|
||||
}
|
||||
|
||||
if (result !== null && result !== undefined) {
|
||||
// Results that that are not nodes are cast as Anonymous nodes
|
||||
// Falsy values or booleans are returned as empty nodes
|
||||
if (!(result instanceof Node)) {
|
||||
if (!result || result === true) {
|
||||
result = new Anonymous(null);
|
||||
}
|
||||
else {
|
||||
result = new Anonymous(result.toString());
|
||||
}
|
||||
|
||||
}
|
||||
result._index = this._index;
|
||||
result._fileInfo = this._fileInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const args = this.args.map(a => a.eval(context));
|
||||
exitCalc();
|
||||
|
||||
return new Call(this.name, args, this.getIndex(), this.fileInfo());
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(`${this.name}(`, this.fileInfo(), this.getIndex());
|
||||
|
||||
for (let i = 0; i < this.args.length; i++) {
|
||||
this.args[i].genCSS(context, output);
|
||||
if (i + 1 < this.args.length) {
|
||||
output.add(', ');
|
||||
}
|
||||
}
|
||||
|
||||
output.add(')');
|
||||
}
|
||||
const Call = function(name, args, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.calc = name === 'calc';
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
}
|
||||
|
||||
Call.prototype = new Node();
|
||||
|
||||
Call.prototype.accept = function(visitor) {
|
||||
if (this.args) {
|
||||
this.args = visitor.visitArray(this.args);
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// When evaluating a function call,
|
||||
// we either find the function in the functionRegistry,
|
||||
// in which case we call it, passing the evaluated arguments,
|
||||
// if this returns null or we cannot find the function, we
|
||||
// simply print it out as it appeared originally [2].
|
||||
//
|
||||
// The reason why we evaluate the arguments, is in the case where
|
||||
// we try to pass a variable to a function, like: `saturate(@color)`.
|
||||
// The function should receive the value, not the variable.
|
||||
//
|
||||
Call.prototype.eval = function(context) {
|
||||
/**
|
||||
* Turn off math for calc(), and switch back on for evaluating nested functions
|
||||
*/
|
||||
const currentMathContext = context.mathOn;
|
||||
context.mathOn = !this.calc;
|
||||
if (this.calc || context.inCalc) {
|
||||
context.enterCalc();
|
||||
}
|
||||
|
||||
const exitCalc = () => {
|
||||
if (this.calc || context.inCalc) {
|
||||
context.exitCalc();
|
||||
}
|
||||
context.mathOn = currentMathContext;
|
||||
};
|
||||
|
||||
let result;
|
||||
const funcCaller = new FunctionCaller(this.name, context, this.getIndex(), this.fileInfo());
|
||||
|
||||
if (funcCaller.isValid()) {
|
||||
try {
|
||||
result = funcCaller.call(this.args);
|
||||
exitCalc();
|
||||
} catch (e) {
|
||||
if (e.hasOwnProperty('line') && e.hasOwnProperty('column')) {
|
||||
throw e
|
||||
}
|
||||
throw {
|
||||
type: e.type || 'Runtime',
|
||||
message: `error evaluating function \`${this.name}\`${e.message ? `: ${e.message}` : ''}`,
|
||||
index: this.getIndex(),
|
||||
filename: this.fileInfo().filename,
|
||||
line: e.lineNumber,
|
||||
column: e.columnNumber
|
||||
};
|
||||
}
|
||||
|
||||
if (result !== null && result !== undefined) {
|
||||
// Results that that are not nodes are cast as Anonymous nodes
|
||||
// Falsy values or booleans are returned as empty nodes
|
||||
if (!(result instanceof Node)) {
|
||||
if (!result || result === true) {
|
||||
result = new Anonymous(null);
|
||||
}
|
||||
else {
|
||||
result = new Anonymous(result.toString());
|
||||
}
|
||||
|
||||
}
|
||||
result._index = this._index;
|
||||
result._fileInfo = this._fileInfo;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
const args = this.args.map(a => a.eval(context));
|
||||
exitCalc();
|
||||
|
||||
return new Call(this.name, args, this.getIndex(), this.fileInfo());
|
||||
};
|
||||
|
||||
Call.prototype.genCSS = function(context, output) {
|
||||
output.add(`${this.name}(`, this.fileInfo(), this.getIndex());
|
||||
|
||||
for (let i = 0; i < this.args.length; i++) {
|
||||
this.args[i].genCSS(context, output);
|
||||
if (i + 1 < this.args.length) {
|
||||
output.add(', ');
|
||||
}
|
||||
}
|
||||
|
||||
output.add(')');
|
||||
};
|
||||
|
||||
Call.prototype.type = 'Call';
|
||||
export default Call;
|
||||
|
||||
@@ -4,216 +4,214 @@ import colors from '../data/colors';
|
||||
//
|
||||
// RGB Colors - #ff0014, #eee
|
||||
//
|
||||
class Color extends Node {
|
||||
constructor(rgb, a, originalForm) {
|
||||
super();
|
||||
|
||||
const self = this;
|
||||
//
|
||||
// The end goal here, is to parse the arguments
|
||||
// into an integer triplet, such as `128, 255, 0`
|
||||
//
|
||||
// This facilitates operations and conversions.
|
||||
//
|
||||
if (Array.isArray(rgb)) {
|
||||
this.rgb = rgb;
|
||||
} else if (rgb.length >= 6) {
|
||||
this.rgb = [];
|
||||
rgb.match(/.{2}/g).map((c, i) => {
|
||||
if (i < 3) {
|
||||
self.rgb.push(parseInt(c, 16));
|
||||
} else {
|
||||
self.alpha = (parseInt(c, 16)) / 255;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.rgb = [];
|
||||
rgb.split('').map((c, i) => {
|
||||
if (i < 3) {
|
||||
self.rgb.push(parseInt(c + c, 16));
|
||||
} else {
|
||||
self.alpha = (parseInt(c + c, 16)) / 255;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.alpha = this.alpha || (typeof a === 'number' ? a : 1);
|
||||
if (typeof originalForm !== 'undefined') {
|
||||
this.value = originalForm;
|
||||
}
|
||||
}
|
||||
|
||||
luma() {
|
||||
let r = this.rgb[0] / 255;
|
||||
let g = this.rgb[1] / 255;
|
||||
let b = this.rgb[2] / 255;
|
||||
|
||||
r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
|
||||
g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
|
||||
b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
|
||||
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(this.toCSS(context));
|
||||
}
|
||||
|
||||
toCSS(context, doNotCompress) {
|
||||
const compress = context && context.compress && !doNotCompress;
|
||||
let color;
|
||||
let alpha;
|
||||
let colorFunction;
|
||||
let args = [];
|
||||
|
||||
// `value` is set if this color was originally
|
||||
// converted from a named color string so we need
|
||||
// to respect this and try to output named color too.
|
||||
alpha = this.fround(context, this.alpha);
|
||||
|
||||
if (this.value) {
|
||||
if (this.value.indexOf('rgb') === 0) {
|
||||
if (alpha < 1) {
|
||||
colorFunction = 'rgba';
|
||||
}
|
||||
} else if (this.value.indexOf('hsl') === 0) {
|
||||
if (alpha < 1) {
|
||||
colorFunction = 'hsla';
|
||||
} else {
|
||||
colorFunction = 'hsl';
|
||||
}
|
||||
const Color = function(rgb, a, originalForm) {
|
||||
const self = this;
|
||||
//
|
||||
// The end goal here, is to parse the arguments
|
||||
// into an integer triplet, such as `128, 255, 0`
|
||||
//
|
||||
// This facilitates operations and conversions.
|
||||
//
|
||||
if (Array.isArray(rgb)) {
|
||||
this.rgb = rgb;
|
||||
} else if (rgb.length >= 6) {
|
||||
this.rgb = [];
|
||||
rgb.match(/.{2}/g).map((c, i) => {
|
||||
if (i < 3) {
|
||||
self.rgb.push(parseInt(c, 16));
|
||||
} else {
|
||||
return this.value;
|
||||
self.alpha = (parseInt(c, 16)) / 255;
|
||||
}
|
||||
} else {
|
||||
});
|
||||
} else {
|
||||
this.rgb = [];
|
||||
rgb.split('').map((c, i) => {
|
||||
if (i < 3) {
|
||||
self.rgb.push(parseInt(c + c, 16));
|
||||
} else {
|
||||
self.alpha = (parseInt(c + c, 16)) / 255;
|
||||
}
|
||||
});
|
||||
}
|
||||
this.alpha = this.alpha || (typeof a === 'number' ? a : 1);
|
||||
if (typeof originalForm !== 'undefined') {
|
||||
this.value = originalForm;
|
||||
}
|
||||
};
|
||||
|
||||
Color.prototype = new Node();
|
||||
|
||||
Color.prototype.luma = function() {
|
||||
let r = this.rgb[0] / 255;
|
||||
let g = this.rgb[1] / 255;
|
||||
let b = this.rgb[2] / 255;
|
||||
|
||||
r = (r <= 0.03928) ? r / 12.92 : Math.pow(((r + 0.055) / 1.055), 2.4);
|
||||
g = (g <= 0.03928) ? g / 12.92 : Math.pow(((g + 0.055) / 1.055), 2.4);
|
||||
b = (b <= 0.03928) ? b / 12.92 : Math.pow(((b + 0.055) / 1.055), 2.4);
|
||||
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
};
|
||||
|
||||
Color.prototype.genCSS = function(context, output) {
|
||||
output.add(this.toCSS(context));
|
||||
};
|
||||
|
||||
Color.prototype.toCSS = function(context, doNotCompress) {
|
||||
const compress = context && context.compress && !doNotCompress;
|
||||
let color;
|
||||
let alpha;
|
||||
let colorFunction;
|
||||
let args = [];
|
||||
|
||||
// `value` is set if this color was originally
|
||||
// converted from a named color string so we need
|
||||
// to respect this and try to output named color too.
|
||||
alpha = this.fround(context, this.alpha);
|
||||
|
||||
if (this.value) {
|
||||
if (this.value.indexOf('rgb') === 0) {
|
||||
if (alpha < 1) {
|
||||
colorFunction = 'rgba';
|
||||
}
|
||||
}
|
||||
|
||||
switch (colorFunction) {
|
||||
case 'rgba':
|
||||
args = this.rgb.map(c => clamp(Math.round(c), 255)).concat(clamp(alpha, 1));
|
||||
break;
|
||||
case 'hsla':
|
||||
args.push(clamp(alpha, 1));
|
||||
case 'hsl':
|
||||
color = this.toHSL();
|
||||
args = [
|
||||
this.fround(context, color.h),
|
||||
`${this.fround(context, color.s * 100)}%`,
|
||||
`${this.fround(context, color.l * 100)}%`
|
||||
].concat(args);
|
||||
}
|
||||
|
||||
if (colorFunction) {
|
||||
// Values are capped between `0` and `255`, rounded and zero-padded.
|
||||
return `${colorFunction}(${args.join(`,${compress ? '' : ' '}`)})`;
|
||||
}
|
||||
|
||||
color = this.toRGB();
|
||||
|
||||
if (compress) {
|
||||
const splitcolor = color.split('');
|
||||
|
||||
// Convert color to short format
|
||||
if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
|
||||
color = `#${splitcolor[1]}${splitcolor[3]}${splitcolor[5]}`;
|
||||
} else if (this.value.indexOf('hsl') === 0) {
|
||||
if (alpha < 1) {
|
||||
colorFunction = 'hsla';
|
||||
} else {
|
||||
colorFunction = 'hsl';
|
||||
}
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
//
|
||||
// Operations have to be done per-channel, if not,
|
||||
// channels will spill onto each other. Once we have
|
||||
// our result, in the form of an integer triplet,
|
||||
// we create a new Color node to hold the result.
|
||||
//
|
||||
operate(context, op, other) {
|
||||
const rgb = new Array(3);
|
||||
const alpha = this.alpha * (1 - other.alpha) + other.alpha;
|
||||
for (let c = 0; c < 3; c++) {
|
||||
rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
|
||||
}
|
||||
return new Color(rgb, alpha);
|
||||
}
|
||||
|
||||
toRGB() {
|
||||
return toHex(this.rgb);
|
||||
}
|
||||
|
||||
toHSL() {
|
||||
const r = this.rgb[0] / 255;
|
||||
const g = this.rgb[1] / 255;
|
||||
const b = this.rgb[2] / 255;
|
||||
const a = this.alpha;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h;
|
||||
let s;
|
||||
const l = (max + min) / 2;
|
||||
const d = max - min;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
return this.value;
|
||||
}
|
||||
return { h: h * 360, s, l, a };
|
||||
}
|
||||
|
||||
// Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
toHSV() {
|
||||
const r = this.rgb[0] / 255;
|
||||
const g = this.rgb[1] / 255;
|
||||
const b = this.rgb[2] / 255;
|
||||
const a = this.alpha;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h;
|
||||
let s;
|
||||
const v = max;
|
||||
|
||||
const d = max - min;
|
||||
if (max === 0) {
|
||||
s = 0;
|
||||
} else {
|
||||
s = d / max;
|
||||
} else {
|
||||
if (alpha < 1) {
|
||||
colorFunction = 'rgba';
|
||||
}
|
||||
}
|
||||
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
switch (colorFunction) {
|
||||
case 'rgba':
|
||||
args = this.rgb.map(c => clamp(Math.round(c), 255)).concat(clamp(alpha, 1));
|
||||
break;
|
||||
case 'hsla':
|
||||
args.push(clamp(alpha, 1));
|
||||
case 'hsl':
|
||||
color = this.toHSL();
|
||||
args = [
|
||||
this.fround(context, color.h),
|
||||
`${this.fround(context, color.s * 100)}%`,
|
||||
`${this.fround(context, color.l * 100)}%`
|
||||
].concat(args);
|
||||
}
|
||||
|
||||
if (colorFunction) {
|
||||
// Values are capped between `0` and `255`, rounded and zero-padded.
|
||||
return `${colorFunction}(${args.join(`,${compress ? '' : ' '}`)})`;
|
||||
}
|
||||
|
||||
color = this.toRGB();
|
||||
|
||||
if (compress) {
|
||||
const splitcolor = color.split('');
|
||||
|
||||
// Convert color to short format
|
||||
if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
|
||||
color = `#${splitcolor[1]}${splitcolor[3]}${splitcolor[5]}`;
|
||||
}
|
||||
return { h: h * 360, s, v, a };
|
||||
}
|
||||
|
||||
toARGB() {
|
||||
return toHex([this.alpha * 255].concat(this.rgb));
|
||||
return color;
|
||||
};
|
||||
|
||||
//
|
||||
// Operations have to be done per-channel, if not,
|
||||
// channels will spill onto each other. Once we have
|
||||
// our result, in the form of an integer triplet,
|
||||
// we create a new Color node to hold the result.
|
||||
//
|
||||
Color.prototype.operate = function(context, op, other) {
|
||||
const rgb = new Array(3);
|
||||
const alpha = this.alpha * (1 - other.alpha) + other.alpha;
|
||||
for (let c = 0; c < 3; c++) {
|
||||
rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
|
||||
}
|
||||
return new Color(rgb, alpha);
|
||||
};
|
||||
|
||||
Color.prototype.toRGB = function() {
|
||||
return toHex(this.rgb);
|
||||
};
|
||||
|
||||
Color.prototype.toHSL = function() {
|
||||
const r = this.rgb[0] / 255;
|
||||
const g = this.rgb[1] / 255;
|
||||
const b = this.rgb[2] / 255;
|
||||
const a = this.alpha;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h;
|
||||
let s;
|
||||
const l = (max + min) / 2;
|
||||
const d = max - min;
|
||||
|
||||
if (max === min) {
|
||||
h = s = 0;
|
||||
} else {
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h * 360, s, l, a };
|
||||
};
|
||||
|
||||
// Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
Color.prototype.toHSV = function() {
|
||||
const r = this.rgb[0] / 255;
|
||||
const g = this.rgb[1] / 255;
|
||||
const b = this.rgb[2] / 255;
|
||||
const a = this.alpha;
|
||||
const max = Math.max(r, g, b);
|
||||
const min = Math.min(r, g, b);
|
||||
let h;
|
||||
let s;
|
||||
const v = max;
|
||||
|
||||
const d = max - min;
|
||||
if (max === 0) {
|
||||
s = 0;
|
||||
} else {
|
||||
s = d / max;
|
||||
}
|
||||
|
||||
compare(x) {
|
||||
return (x.rgb &&
|
||||
x.rgb[0] === this.rgb[0] &&
|
||||
x.rgb[1] === this.rgb[1] &&
|
||||
x.rgb[2] === this.rgb[2] &&
|
||||
x.alpha === this.alpha) ? 0 : undefined;
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch (max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
}
|
||||
return { h: h * 360, s, v, a };
|
||||
};
|
||||
|
||||
Color.prototype.toARGB = function() {
|
||||
return toHex([this.alpha * 255].concat(this.rgb));
|
||||
};
|
||||
|
||||
Color.prototype.compare = function(x) {
|
||||
return (x.rgb &&
|
||||
x.rgb[0] === this.rgb[0] &&
|
||||
x.rgb[1] === this.rgb[1] &&
|
||||
x.rgb[2] === this.rgb[2] &&
|
||||
x.alpha === this.alpha) ? 0 : undefined;
|
||||
};
|
||||
|
||||
Color.prototype.type = 'Color';
|
||||
|
||||
@@ -243,4 +241,5 @@ Color.fromKeyword = keyword => {
|
||||
return c;
|
||||
}
|
||||
};
|
||||
|
||||
export default Color;
|
||||
|
||||
@@ -5,24 +5,22 @@ const _noSpaceCombinators = {
|
||||
'|': true
|
||||
};
|
||||
|
||||
class Combinator extends Node {
|
||||
constructor(value) {
|
||||
super();
|
||||
|
||||
if (value === ' ') {
|
||||
this.value = ' ';
|
||||
this.emptyOrWhitespace = true;
|
||||
} else {
|
||||
this.value = value ? value.trim() : '';
|
||||
this.emptyOrWhitespace = this.value === '';
|
||||
}
|
||||
const Combinator = function(value) {
|
||||
if (value === ' ') {
|
||||
this.value = ' ';
|
||||
this.emptyOrWhitespace = true;
|
||||
} else {
|
||||
this.value = value ? value.trim() : '';
|
||||
this.emptyOrWhitespace = this.value === '';
|
||||
}
|
||||
};
|
||||
|
||||
genCSS(context, output) {
|
||||
const spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
|
||||
output.add(spaceOrEmpty + this.value + spaceOrEmpty);
|
||||
}
|
||||
}
|
||||
Combinator.prototype = new Node();
|
||||
|
||||
Combinator.prototype.genCSS = function(context, output) {
|
||||
const spaceOrEmpty = (context.compress || _noSpaceCombinators[this.value]) ? '' : ' ';
|
||||
output.add(spaceOrEmpty + this.value + spaceOrEmpty);
|
||||
};
|
||||
|
||||
Combinator.prototype.type = 'Combinator';
|
||||
|
||||
|
||||
@@ -1,29 +1,27 @@
|
||||
import Node from './node';
|
||||
import getDebugInfo from './debug-info';
|
||||
|
||||
class Comment extends Node {
|
||||
constructor(value, isLineComment, index, currentFileInfo) {
|
||||
super();
|
||||
const Comment = function(value, isLineComment, index, currentFileInfo) {
|
||||
this.value = value;
|
||||
this.isLineComment = isLineComment;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
};
|
||||
|
||||
this.value = value;
|
||||
this.isLineComment = isLineComment;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
}
|
||||
Comment.prototype = new Node();
|
||||
|
||||
genCSS(context, output) {
|
||||
if (this.debugInfo) {
|
||||
output.add(getDebugInfo(context, this), this.fileInfo(), this.getIndex());
|
||||
}
|
||||
output.add(this.value);
|
||||
Comment.prototype.genCSS = function(context, output) {
|
||||
if (this.debugInfo) {
|
||||
output.add(getDebugInfo(context, this), this.fileInfo(), this.getIndex());
|
||||
}
|
||||
output.add(this.value);
|
||||
};
|
||||
|
||||
isSilent(context) {
|
||||
const isCompressed = context.compress && this.value[2] !== '!';
|
||||
return this.isLineComment || isCompressed;
|
||||
}
|
||||
}
|
||||
Comment.prototype.isSilent = function(context) {
|
||||
const isCompressed = context.compress && this.value[2] !== '!';
|
||||
return this.isLineComment || isCompressed;
|
||||
};
|
||||
|
||||
Comment.prototype.type = 'Comment';
|
||||
export default Comment;
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
import Node from './node';
|
||||
|
||||
class Condition extends Node {
|
||||
constructor(op, l, r, i, negate) {
|
||||
super();
|
||||
const Condition = function(op, l, r, i, negate) {
|
||||
this.op = op.trim();
|
||||
this.lvalue = l;
|
||||
this.rvalue = r;
|
||||
this._index = i;
|
||||
this.negate = negate;
|
||||
};
|
||||
|
||||
this.op = op.trim();
|
||||
this.lvalue = l;
|
||||
this.rvalue = r;
|
||||
this._index = i;
|
||||
this.negate = negate;
|
||||
}
|
||||
Condition.prototype = new Node();
|
||||
|
||||
accept(visitor) {
|
||||
this.lvalue = visitor.visit(this.lvalue);
|
||||
this.rvalue = visitor.visit(this.rvalue);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
const result = ((op, a, b) => {
|
||||
switch (op) {
|
||||
case 'and': return a && b;
|
||||
case 'or': return a || b;
|
||||
default:
|
||||
switch (Node.compare(a, b)) {
|
||||
case -1:
|
||||
return op === '<' || op === '=<' || op === '<=';
|
||||
case 0:
|
||||
return op === '=' || op === '>=' || op === '=<' || op === '<=';
|
||||
case 1:
|
||||
return op === '>' || op === '>=';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
|
||||
|
||||
return this.negate ? !result : result;
|
||||
}
|
||||
Condition.prototype.accept = function(visitor) {
|
||||
this.lvalue = visitor.visit(this.lvalue);
|
||||
this.rvalue = visitor.visit(this.rvalue);
|
||||
}
|
||||
|
||||
Condition.prototype.eval = function(context) {
|
||||
const result = ((op, a, b) => {
|
||||
switch (op) {
|
||||
case 'and': return a && b;
|
||||
case 'or': return a || b;
|
||||
default:
|
||||
switch (Node.compare(a, b)) {
|
||||
case -1:
|
||||
return op === '<' || op === '=<' || op === '<=';
|
||||
case 0:
|
||||
return op === '=' || op === '>=' || op === '=<' || op === '<=';
|
||||
case 1:
|
||||
return op === '>' || op === '>=';
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
})(this.op, this.lvalue.eval(context), this.rvalue.eval(context));
|
||||
|
||||
return this.negate ? !result : result;
|
||||
};
|
||||
|
||||
Condition.prototype.type = 'Condition';
|
||||
export default Condition;
|
||||
|
||||
@@ -6,100 +6,98 @@ import * as Constants from '../constants';
|
||||
const MATH = Constants.Math;
|
||||
|
||||
|
||||
class Declaration extends Node {
|
||||
constructor(name, value, important, merge, index, currentFileInfo, inline, variable) {
|
||||
super();
|
||||
const Declaration = function(name, value, important, merge, index, currentFileInfo, inline, variable) {
|
||||
this.name = name;
|
||||
this.value = (value instanceof Node) ? value : new Value([value ? new Anonymous(value) : null]);
|
||||
this.important = important ? ` ${important.trim()}` : '';
|
||||
this.merge = merge;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.inline = inline || false;
|
||||
this.variable = (variable !== undefined) ? variable
|
||||
: (name.charAt && (name.charAt(0) === '@'));
|
||||
this.allowRoot = true;
|
||||
this.setParent(this.value, this);
|
||||
};
|
||||
|
||||
this.name = name;
|
||||
this.value = (value instanceof Node) ? value : new Value([value ? new Anonymous(value) : null]);
|
||||
this.important = important ? ` ${important.trim()}` : '';
|
||||
this.merge = merge;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.inline = inline || false;
|
||||
this.variable = (variable !== undefined) ? variable
|
||||
: (name.charAt && (name.charAt(0) === '@'));
|
||||
this.allowRoot = true;
|
||||
this.setParent(this.value, this);
|
||||
Declaration.prototype = new Node();
|
||||
|
||||
Declaration.prototype.genCSS = function(context, output) {
|
||||
output.add(this.name + (context.compress ? ':' : ': '), this.fileInfo(), this.getIndex());
|
||||
try {
|
||||
this.value.genCSS(context, output);
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(this.name + (context.compress ? ':' : ': '), this.fileInfo(), this.getIndex());
|
||||
try {
|
||||
this.value.genCSS(context, output);
|
||||
}
|
||||
catch (e) {
|
||||
e.index = this._index;
|
||||
e.filename = this._fileInfo.filename;
|
||||
throw e;
|
||||
}
|
||||
output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? '' : ';'), this._fileInfo, this._index);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let mathBypass = false;
|
||||
let prevMath;
|
||||
let name = this.name;
|
||||
let evaldValue;
|
||||
let variable = this.variable;
|
||||
if (typeof name !== 'string') {
|
||||
// expand 'primitive' name directly to get
|
||||
// things faster (~10% for benchmark.less):
|
||||
name = (name.length === 1) && (name[0] instanceof Keyword) ?
|
||||
name[0].value : evalName(context, name);
|
||||
variable = false; // never treat expanded interpolation as new variable name
|
||||
}
|
||||
|
||||
// @todo remove when parens-division is default
|
||||
if (name === 'font' && context.math === MATH.ALWAYS) {
|
||||
mathBypass = true;
|
||||
prevMath = context.math;
|
||||
context.math = MATH.PARENS_DIVISION;
|
||||
}
|
||||
try {
|
||||
context.importantScope.push({});
|
||||
evaldValue = this.value.eval(context);
|
||||
|
||||
if (!this.variable && evaldValue.type === 'DetachedRuleset') {
|
||||
throw { message: 'Rulesets cannot be evaluated on a property.',
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
let important = this.important;
|
||||
const importantResult = context.importantScope.pop();
|
||||
if (!important && importantResult.important) {
|
||||
important = importantResult.important;
|
||||
}
|
||||
|
||||
return new Declaration(name,
|
||||
evaldValue,
|
||||
important,
|
||||
this.merge,
|
||||
this.getIndex(), this.fileInfo(), this.inline,
|
||||
variable);
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e.index !== 'number') {
|
||||
e.index = this.getIndex();
|
||||
e.filename = this.fileInfo().filename;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (mathBypass) {
|
||||
context.math = prevMath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
makeImportant() {
|
||||
return new Declaration(this.name,
|
||||
this.value,
|
||||
'!important',
|
||||
this.merge,
|
||||
this.getIndex(), this.fileInfo(), this.inline);
|
||||
catch (e) {
|
||||
e.index = this._index;
|
||||
e.filename = this._fileInfo.filename;
|
||||
throw e;
|
||||
}
|
||||
output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? '' : ';'), this._fileInfo, this._index);
|
||||
}
|
||||
|
||||
Declaration.prototype.eval = function(context) {
|
||||
let mathBypass = false;
|
||||
let prevMath;
|
||||
let name = this.name;
|
||||
let evaldValue;
|
||||
let variable = this.variable;
|
||||
if (typeof name !== 'string') {
|
||||
// expand 'primitive' name directly to get
|
||||
// things faster (~10% for benchmark.less):
|
||||
name = (name.length === 1) && (name[0] instanceof Keyword) ?
|
||||
name[0].value : evalName(context, name);
|
||||
variable = false; // never treat expanded interpolation as new variable name
|
||||
}
|
||||
|
||||
// @todo remove when parens-division is default
|
||||
if (name === 'font' && context.math === MATH.ALWAYS) {
|
||||
mathBypass = true;
|
||||
prevMath = context.math;
|
||||
context.math = MATH.PARENS_DIVISION;
|
||||
}
|
||||
try {
|
||||
context.importantScope.push({});
|
||||
evaldValue = this.value.eval(context);
|
||||
|
||||
if (!this.variable && evaldValue.type === 'DetachedRuleset') {
|
||||
throw { message: 'Rulesets cannot be evaluated on a property.',
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
let important = this.important;
|
||||
const importantResult = context.importantScope.pop();
|
||||
if (!important && importantResult.important) {
|
||||
important = importantResult.important;
|
||||
}
|
||||
|
||||
return new Declaration(name,
|
||||
evaldValue,
|
||||
important,
|
||||
this.merge,
|
||||
this.getIndex(), this.fileInfo(), this.inline,
|
||||
variable);
|
||||
}
|
||||
catch (e) {
|
||||
if (typeof e.index !== 'number') {
|
||||
e.index = this.getIndex();
|
||||
e.filename = this.fileInfo().filename;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
if (mathBypass) {
|
||||
context.math = prevMath;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Declaration.prototype.makeImportant = function() {
|
||||
return new Declaration(this.name,
|
||||
this.value,
|
||||
'!important',
|
||||
this.merge,
|
||||
this.getIndex(), this.fileInfo(), this.inline);
|
||||
};
|
||||
|
||||
function evalName(context, name) {
|
||||
let value = '';
|
||||
let i;
|
||||
|
||||
@@ -2,28 +2,26 @@ import Node from './node';
|
||||
import contexts from '../contexts';
|
||||
import * as utils from '../utils';
|
||||
|
||||
class DetachedRuleset extends Node {
|
||||
constructor(ruleset, frames) {
|
||||
super();
|
||||
const DetachedRuleset = function(ruleset, frames) {
|
||||
this.ruleset = ruleset;
|
||||
this.frames = frames;
|
||||
this.setParent(this.ruleset, this);
|
||||
};
|
||||
|
||||
this.ruleset = ruleset;
|
||||
this.frames = frames;
|
||||
this.setParent(this.ruleset, this);
|
||||
}
|
||||
DetachedRuleset.prototype = new Node();
|
||||
|
||||
accept(visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
}
|
||||
DetachedRuleset.prototype.accept = function(visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
};
|
||||
|
||||
eval(context) {
|
||||
const frames = this.frames || utils.copyArray(context.frames);
|
||||
return new DetachedRuleset(this.ruleset, frames);
|
||||
}
|
||||
DetachedRuleset.prototype.eval = function(context) {
|
||||
const frames = this.frames || utils.copyArray(context.frames);
|
||||
return new DetachedRuleset(this.ruleset, frames);
|
||||
};
|
||||
|
||||
callEval(context) {
|
||||
return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context);
|
||||
}
|
||||
}
|
||||
DetachedRuleset.prototype.callEval = function(context) {
|
||||
return this.ruleset.eval(this.frames ? new contexts.Eval(context, this.frames.concat(context.frames)) : context);
|
||||
};
|
||||
|
||||
DetachedRuleset.prototype.type = 'DetachedRuleset';
|
||||
DetachedRuleset.prototype.evalFirst = true;
|
||||
|
||||
@@ -6,174 +6,172 @@ import Color from './color';
|
||||
//
|
||||
// A number with a unit
|
||||
//
|
||||
class Dimension extends Node {
|
||||
constructor(value, unit) {
|
||||
super();
|
||||
const Dimension = function(value, unit) {
|
||||
this.value = parseFloat(value);
|
||||
if (isNaN(this.value)) {
|
||||
throw new Error('Dimension is not a number.');
|
||||
}
|
||||
this.unit = (unit && unit instanceof Unit) ? unit :
|
||||
new Unit(unit ? [unit] : undefined);
|
||||
this.setParent(this.unit, this);
|
||||
};
|
||||
|
||||
this.value = parseFloat(value);
|
||||
if (isNaN(this.value)) {
|
||||
throw new Error('Dimension is not a number.');
|
||||
}
|
||||
this.unit = (unit && unit instanceof Unit) ? unit :
|
||||
new Unit(unit ? [unit] : undefined);
|
||||
this.setParent(this.unit, this);
|
||||
Dimension.prototype = new Node();
|
||||
|
||||
Dimension.prototype.accept = function(visitor) {
|
||||
this.unit = visitor.visit(this.unit);
|
||||
};
|
||||
|
||||
Dimension.prototype.eval = function(context) {
|
||||
return this;
|
||||
};
|
||||
|
||||
Dimension.prototype.toColor = function() {
|
||||
return new Color([this.value, this.value, this.value]);
|
||||
};
|
||||
|
||||
Dimension.prototype.genCSS = function(context, output) {
|
||||
if ((context && context.strictUnits) && !this.unit.isSingular()) {
|
||||
throw new Error(`Multiple units in dimension. Correct the units or use the unit function. Bad unit: ${this.unit.toString()}`);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
this.unit = visitor.visit(this.unit);
|
||||
const value = this.fround(context, this.value);
|
||||
let strValue = String(value);
|
||||
|
||||
if (value !== 0 && value < 0.000001 && value > -0.000001) {
|
||||
// would be output 1e-6 etc.
|
||||
strValue = value.toFixed(20).replace(/0+$/, '');
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
return this;
|
||||
}
|
||||
|
||||
toColor() {
|
||||
return new Color([this.value, this.value, this.value]);
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
if ((context && context.strictUnits) && !this.unit.isSingular()) {
|
||||
throw new Error(`Multiple units in dimension. Correct the units or use the unit function. Bad unit: ${this.unit.toString()}`);
|
||||
if (context && context.compress) {
|
||||
// Zero values doesn't need a unit
|
||||
if (value === 0 && this.unit.isLength()) {
|
||||
output.add(strValue);
|
||||
return;
|
||||
}
|
||||
|
||||
const value = this.fround(context, this.value);
|
||||
let strValue = String(value);
|
||||
|
||||
if (value !== 0 && value < 0.000001 && value > -0.000001) {
|
||||
// would be output 1e-6 etc.
|
||||
strValue = value.toFixed(20).replace(/0+$/, '');
|
||||
// Float values doesn't need a leading zero
|
||||
if (value > 0 && value < 1) {
|
||||
strValue = (strValue).substr(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (context && context.compress) {
|
||||
// Zero values doesn't need a unit
|
||||
if (value === 0 && this.unit.isLength()) {
|
||||
output.add(strValue);
|
||||
return;
|
||||
output.add(strValue);
|
||||
this.unit.genCSS(context, output);
|
||||
};
|
||||
|
||||
// In an operation between two Dimensions,
|
||||
// we default to the first Dimension's unit,
|
||||
// so `1px + 2` will yield `3px`.
|
||||
Dimension.prototype.operate = function(context, op, other) {
|
||||
/* jshint noempty:false */
|
||||
let value = this._operate(context, op, this.value, other.value);
|
||||
|
||||
let unit = this.unit.clone();
|
||||
|
||||
if (op === '+' || op === '-') {
|
||||
if (unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
unit = other.unit.clone();
|
||||
if (this.unit.backupUnit) {
|
||||
unit.backupUnit = this.unit.backupUnit;
|
||||
}
|
||||
} else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
other = other.convertTo(this.unit.usedUnits());
|
||||
|
||||
if (context.strictUnits && other.unit.toString() !== unit.toString()) {
|
||||
throw new Error(`Incompatible units. Change the units or use the unit function. ` +
|
||||
`Bad units: '${unit.toString()}' and '${other.unit.toString()}'.`);
|
||||
}
|
||||
|
||||
// Float values doesn't need a leading zero
|
||||
if (value > 0 && value < 1) {
|
||||
strValue = (strValue).substr(1);
|
||||
}
|
||||
value = this._operate(context, op, this.value, other.value);
|
||||
}
|
||||
} else if (op === '*') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
|
||||
unit.cancel();
|
||||
} else if (op === '/') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
|
||||
unit.cancel();
|
||||
}
|
||||
return new Dimension(value, unit);
|
||||
};
|
||||
|
||||
output.add(strValue);
|
||||
this.unit.genCSS(context, output);
|
||||
Dimension.prototype.compare = function(other) {
|
||||
let a;
|
||||
let b;
|
||||
|
||||
if (!(other instanceof Dimension)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// In an operation between two Dimensions,
|
||||
// we default to the first Dimension's unit,
|
||||
// so `1px + 2` will yield `3px`.
|
||||
operate(context, op, other) {
|
||||
/* jshint noempty:false */
|
||||
let value = this._operate(context, op, this.value, other.value);
|
||||
|
||||
let unit = this.unit.clone();
|
||||
|
||||
if (op === '+' || op === '-') {
|
||||
if (unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
unit = other.unit.clone();
|
||||
if (this.unit.backupUnit) {
|
||||
unit.backupUnit = this.unit.backupUnit;
|
||||
}
|
||||
} else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
other = other.convertTo(this.unit.usedUnits());
|
||||
|
||||
if (context.strictUnits && other.unit.toString() !== unit.toString()) {
|
||||
throw new Error(`Incompatible units. Change the units or use the unit function. ` +
|
||||
`Bad units: '${unit.toString()}' and '${other.unit.toString()}'.`);
|
||||
}
|
||||
|
||||
value = this._operate(context, op, this.value, other.value);
|
||||
}
|
||||
} else if (op === '*') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
|
||||
unit.cancel();
|
||||
} else if (op === '/') {
|
||||
unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
|
||||
unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
|
||||
unit.cancel();
|
||||
}
|
||||
return new Dimension(value, unit);
|
||||
}
|
||||
|
||||
compare(other) {
|
||||
let a;
|
||||
let b;
|
||||
|
||||
if (!(other instanceof Dimension)) {
|
||||
if (this.unit.isEmpty() || other.unit.isEmpty()) {
|
||||
a = this;
|
||||
b = other;
|
||||
} else {
|
||||
a = this.unify();
|
||||
b = other.unify();
|
||||
if (a.unit.compare(b.unit) !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (this.unit.isEmpty() || other.unit.isEmpty()) {
|
||||
a = this;
|
||||
b = other;
|
||||
} else {
|
||||
a = this.unify();
|
||||
b = other.unify();
|
||||
if (a.unit.compare(b.unit) !== 0) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return Node.numericCompare(a.value, b.value);
|
||||
}
|
||||
|
||||
unify() {
|
||||
return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
|
||||
}
|
||||
return Node.numericCompare(a.value, b.value);
|
||||
};
|
||||
|
||||
convertTo(conversions) {
|
||||
let value = this.value;
|
||||
const unit = this.unit.clone();
|
||||
let i;
|
||||
let groupName;
|
||||
let group;
|
||||
let targetUnit;
|
||||
let derivedConversions = {};
|
||||
let applyUnit;
|
||||
Dimension.prototype.unify = function() {
|
||||
return this.convertTo({ length: 'px', duration: 's', angle: 'rad' });
|
||||
};
|
||||
|
||||
if (typeof conversions === 'string') {
|
||||
for (i in unitConversions) {
|
||||
if (unitConversions[i].hasOwnProperty(conversions)) {
|
||||
derivedConversions = {};
|
||||
derivedConversions[i] = conversions;
|
||||
}
|
||||
}
|
||||
conversions = derivedConversions;
|
||||
}
|
||||
applyUnit = (atomicUnit, denominator) => {
|
||||
/* jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit)) {
|
||||
if (denominator) {
|
||||
value = value / (group[atomicUnit] / group[targetUnit]);
|
||||
} else {
|
||||
value = value * (group[atomicUnit] / group[targetUnit]);
|
||||
}
|
||||
Dimension.prototype.convertTo = function(conversions) {
|
||||
let value = this.value;
|
||||
const unit = this.unit.clone();
|
||||
let i;
|
||||
let groupName;
|
||||
let group;
|
||||
let targetUnit;
|
||||
let derivedConversions = {};
|
||||
let applyUnit;
|
||||
|
||||
return targetUnit;
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in conversions) {
|
||||
if (conversions.hasOwnProperty(groupName)) {
|
||||
targetUnit = conversions[groupName];
|
||||
group = unitConversions[groupName];
|
||||
|
||||
unit.map(applyUnit);
|
||||
if (typeof conversions === 'string') {
|
||||
for (i in unitConversions) {
|
||||
if (unitConversions[i].hasOwnProperty(conversions)) {
|
||||
derivedConversions = {};
|
||||
derivedConversions[i] = conversions;
|
||||
}
|
||||
}
|
||||
|
||||
unit.cancel();
|
||||
|
||||
return new Dimension(value, unit);
|
||||
conversions = derivedConversions;
|
||||
}
|
||||
}
|
||||
applyUnit = (atomicUnit, denominator) => {
|
||||
/* jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit)) {
|
||||
if (denominator) {
|
||||
value = value / (group[atomicUnit] / group[targetUnit]);
|
||||
} else {
|
||||
value = value * (group[atomicUnit] / group[targetUnit]);
|
||||
}
|
||||
|
||||
return targetUnit;
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in conversions) {
|
||||
if (conversions.hasOwnProperty(groupName)) {
|
||||
targetUnit = conversions[groupName];
|
||||
group = unitConversions[groupName];
|
||||
|
||||
unit.map(applyUnit);
|
||||
}
|
||||
}
|
||||
|
||||
unit.cancel();
|
||||
|
||||
return new Dimension(value, unit);
|
||||
};
|
||||
|
||||
Dimension.prototype.type = 'Dimension';
|
||||
export default Dimension;
|
||||
|
||||
@@ -2,72 +2,70 @@ import Node from './node';
|
||||
import Paren from './paren';
|
||||
import Combinator from './combinator';
|
||||
|
||||
class Element extends Node {
|
||||
constructor(combinator, value, isVariable, index, currentFileInfo, visibilityInfo) {
|
||||
super();
|
||||
const Element = function(combinator, value, isVariable, index, currentFileInfo, visibilityInfo) {
|
||||
this.combinator = combinator instanceof Combinator ?
|
||||
combinator : new Combinator(combinator);
|
||||
|
||||
this.combinator = combinator instanceof Combinator ?
|
||||
combinator : new Combinator(combinator);
|
||||
|
||||
if (typeof value === 'string') {
|
||||
this.value = value.trim();
|
||||
} else if (value) {
|
||||
this.value = value;
|
||||
} else {
|
||||
this.value = '';
|
||||
}
|
||||
this.isVariable = isVariable;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.combinator, this);
|
||||
if (typeof value === 'string') {
|
||||
this.value = value.trim();
|
||||
} else if (value) {
|
||||
this.value = value;
|
||||
} else {
|
||||
this.value = '';
|
||||
}
|
||||
this.isVariable = isVariable;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.combinator, this);
|
||||
};
|
||||
|
||||
accept(visitor) {
|
||||
const value = this.value;
|
||||
this.combinator = visitor.visit(this.combinator);
|
||||
if (typeof value === 'object') {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
}
|
||||
Element.prototype = new Node();
|
||||
|
||||
eval(context) {
|
||||
return new Element(this.combinator,
|
||||
this.value.eval ? this.value.eval(context) : this.value,
|
||||
this.isVariable,
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
Element.prototype.accept = function(visitor) {
|
||||
const value = this.value;
|
||||
this.combinator = visitor.visit(this.combinator);
|
||||
if (typeof value === 'object') {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
};
|
||||
|
||||
clone() {
|
||||
return new Element(this.combinator,
|
||||
this.value,
|
||||
this.isVariable,
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
}
|
||||
Element.prototype.eval = function(context) {
|
||||
return new Element(this.combinator,
|
||||
this.value.eval ? this.value.eval(context) : this.value,
|
||||
this.isVariable,
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add(this.toCSS(context), this.fileInfo(), this.getIndex());
|
||||
}
|
||||
Element.prototype.clone = function() {
|
||||
return new Element(this.combinator,
|
||||
this.value,
|
||||
this.isVariable,
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
|
||||
toCSS(context = {}) {
|
||||
let value = this.value;
|
||||
const firstSelector = context.firstSelector;
|
||||
if (value instanceof Paren) {
|
||||
// selector in parens should not be affected by outer selector
|
||||
// flags (breaks only interpolated selectors - see #1973)
|
||||
context.firstSelector = true;
|
||||
}
|
||||
value = value.toCSS ? value.toCSS(context) : value;
|
||||
context.firstSelector = firstSelector;
|
||||
if (value === '' && this.combinator.value.charAt(0) === '&') {
|
||||
return '';
|
||||
} else {
|
||||
return this.combinator.toCSS(context) + value;
|
||||
}
|
||||
Element.prototype.genCSS = function(context, output) {
|
||||
output.add(this.toCSS(context), this.fileInfo(), this.getIndex());
|
||||
};
|
||||
|
||||
Element.prototype.toCSS = function(context = {}) {
|
||||
let value = this.value;
|
||||
const firstSelector = context.firstSelector;
|
||||
if (value instanceof Paren) {
|
||||
// selector in parens should not be affected by outer selector
|
||||
// flags (breaks only interpolated selectors - see #1973)
|
||||
context.firstSelector = true;
|
||||
}
|
||||
}
|
||||
value = value.toCSS ? value.toCSS(context) : value;
|
||||
context.firstSelector = firstSelector;
|
||||
if (value === '' && this.combinator.value.charAt(0) === '&') {
|
||||
return '';
|
||||
} else {
|
||||
return this.combinator.toCSS(context) + value;
|
||||
}
|
||||
};
|
||||
|
||||
Element.prototype.type = 'Element';
|
||||
export default Element;
|
||||
|
||||
@@ -5,70 +5,68 @@ import Dimension from './dimension';
|
||||
import * as Constants from '../constants';
|
||||
const MATH = Constants.Math;
|
||||
|
||||
class Expression extends Node {
|
||||
constructor(value, noSpacing) {
|
||||
super();
|
||||
|
||||
this.value = value;
|
||||
this.noSpacing = noSpacing;
|
||||
if (!value) {
|
||||
throw new Error('Expression requires an array parameter');
|
||||
}
|
||||
const Expression = function(value, noSpacing) {
|
||||
this.value = value;
|
||||
this.noSpacing = noSpacing;
|
||||
if (!value) {
|
||||
throw new Error('Expression requires an array parameter');
|
||||
}
|
||||
};
|
||||
|
||||
accept(visitor) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
Expression.prototype = new Node();
|
||||
|
||||
Expression.prototype.accept = function(visitor) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
};
|
||||
|
||||
Expression.prototype.eval = function(context) {
|
||||
let returnValue;
|
||||
const mathOn = context.isMathOn();
|
||||
|
||||
const inParenthesis = this.parens &&
|
||||
(context.math !== MATH.STRICT_LEGACY || !this.parensInOp);
|
||||
|
||||
let doubleParen = false;
|
||||
if (inParenthesis) {
|
||||
context.inParenthesis();
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let returnValue;
|
||||
const mathOn = context.isMathOn();
|
||||
|
||||
const inParenthesis = this.parens &&
|
||||
(context.math !== MATH.STRICT_LEGACY || !this.parensInOp);
|
||||
|
||||
let doubleParen = false;
|
||||
if (inParenthesis) {
|
||||
context.inParenthesis();
|
||||
}
|
||||
if (this.value.length > 1) {
|
||||
returnValue = new Expression(this.value.map(e => {
|
||||
if (!e.eval) {
|
||||
return e;
|
||||
}
|
||||
return e.eval(context);
|
||||
}), this.noSpacing);
|
||||
} else if (this.value.length === 1) {
|
||||
if (this.value[0].parens && !this.value[0].parensInOp && !context.inCalc) {
|
||||
doubleParen = true;
|
||||
if (this.value.length > 1) {
|
||||
returnValue = new Expression(this.value.map(e => {
|
||||
if (!e.eval) {
|
||||
return e;
|
||||
}
|
||||
returnValue = this.value[0].eval(context);
|
||||
} else {
|
||||
returnValue = this;
|
||||
return e.eval(context);
|
||||
}), this.noSpacing);
|
||||
} else if (this.value.length === 1) {
|
||||
if (this.value[0].parens && !this.value[0].parensInOp && !context.inCalc) {
|
||||
doubleParen = true;
|
||||
}
|
||||
if (inParenthesis) {
|
||||
context.outOfParenthesis();
|
||||
}
|
||||
if (this.parens && this.parensInOp && !mathOn && !doubleParen
|
||||
&& (!(returnValue instanceof Dimension))) {
|
||||
returnValue = new Paren(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
returnValue = this.value[0].eval(context);
|
||||
} else {
|
||||
returnValue = this;
|
||||
}
|
||||
if (inParenthesis) {
|
||||
context.outOfParenthesis();
|
||||
}
|
||||
if (this.parens && this.parensInOp && !mathOn && !doubleParen
|
||||
&& (!(returnValue instanceof Dimension))) {
|
||||
returnValue = new Paren(returnValue);
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
|
||||
genCSS(context, output) {
|
||||
for (let i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(context, output);
|
||||
if (!this.noSpacing && i + 1 < this.value.length) {
|
||||
output.add(' ');
|
||||
}
|
||||
Expression.prototype.genCSS = function(context, output) {
|
||||
for (let i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(context, output);
|
||||
if (!this.noSpacing && i + 1 < this.value.length) {
|
||||
output.add(' ');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
throwAwayComments() {
|
||||
this.value = this.value.filter(v => !(v instanceof Comment));
|
||||
}
|
||||
}
|
||||
Expression.prototype.throwAwayComments = function() {
|
||||
this.value = this.value.filter(v => !(v instanceof Comment));
|
||||
};
|
||||
|
||||
Expression.prototype.type = 'Expression';
|
||||
export default Expression;
|
||||
|
||||
@@ -1,66 +1,66 @@
|
||||
import Node from './node';
|
||||
import Selector from './selector';
|
||||
|
||||
class Extend extends Node {
|
||||
constructor(selector, option, index, currentFileInfo, visibilityInfo) {
|
||||
super();
|
||||
const Extend = function(selector, option, index, currentFileInfo, visibilityInfo) {
|
||||
this.selector = selector;
|
||||
this.option = option;
|
||||
this.object_id = Extend.next_id++;
|
||||
this.parent_ids = [this.object_id];
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
|
||||
this.selector = selector;
|
||||
this.option = option;
|
||||
this.object_id = Extend.next_id++;
|
||||
this.parent_ids = [this.object_id];
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
switch (option) {
|
||||
case 'all':
|
||||
this.allowBefore = true;
|
||||
this.allowAfter = true;
|
||||
break;
|
||||
default:
|
||||
this.allowBefore = false;
|
||||
this.allowAfter = false;
|
||||
break;
|
||||
}
|
||||
this.setParent(this.selector, this);
|
||||
};
|
||||
|
||||
switch (option) {
|
||||
case 'all':
|
||||
this.allowBefore = true;
|
||||
this.allowAfter = true;
|
||||
break;
|
||||
default:
|
||||
this.allowBefore = false;
|
||||
this.allowAfter = false;
|
||||
break;
|
||||
Extend.prototype = new Node();
|
||||
|
||||
Extend.prototype.accept = function(visitor) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
};
|
||||
|
||||
Extend.prototype.eval = function(context) {
|
||||
return new Extend(this.selector.eval(context), this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
|
||||
Extend.prototype.clone = function(context) {
|
||||
return new Extend(this.selector, this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
|
||||
// it concatenates (joins) all selectors in selector array
|
||||
Extend.prototype.findSelfSelectors = function(selectors) {
|
||||
let selfElements = [];
|
||||
let i;
|
||||
let selectorElements;
|
||||
|
||||
for (i = 0; i < selectors.length; i++) {
|
||||
selectorElements = selectors[i].elements;
|
||||
// duplicate the logic in genCSS function inside the selector node.
|
||||
// future TODO - move both logics into the selector joiner visitor
|
||||
if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === '') {
|
||||
selectorElements[0].combinator.value = ' ';
|
||||
}
|
||||
this.setParent(this.selector, this);
|
||||
selfElements = selfElements.concat(selectors[i].elements);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
return new Extend(this.selector.eval(context), this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
}
|
||||
|
||||
clone(context) {
|
||||
return new Extend(this.selector, this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
}
|
||||
|
||||
// it concatenates (joins) all selectors in selector array
|
||||
findSelfSelectors(selectors) {
|
||||
let selfElements = [];
|
||||
let i;
|
||||
let selectorElements;
|
||||
|
||||
for (i = 0; i < selectors.length; i++) {
|
||||
selectorElements = selectors[i].elements;
|
||||
// duplicate the logic in genCSS function inside the selector node.
|
||||
// future TODO - move both logics into the selector joiner visitor
|
||||
if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === '') {
|
||||
selectorElements[0].combinator.value = ' ';
|
||||
}
|
||||
selfElements = selfElements.concat(selectors[i].elements);
|
||||
}
|
||||
|
||||
this.selfSelectors = [new Selector(selfElements)];
|
||||
this.selfSelectors[0].copyVisibilityInfo(this.visibilityInfo());
|
||||
}
|
||||
}
|
||||
this.selfSelectors = [new Selector(selfElements)];
|
||||
this.selfSelectors[0].copyVisibilityInfo(this.visibilityInfo());
|
||||
};
|
||||
|
||||
/**
|
||||
* Used with the extend visitor
|
||||
*/
|
||||
Extend.next_id = 0;
|
||||
|
||||
Extend.prototype.type = 'Extend';
|
||||
export default Extend;
|
||||
|
||||
@@ -19,168 +19,166 @@ import LessError from '../less-error';
|
||||
// `import,push`, we also pass it a callback, which it'll call once
|
||||
// the file has been fetched, and parsed.
|
||||
//
|
||||
class Import extends Node {
|
||||
constructor(path, features, options, index, currentFileInfo, visibilityInfo) {
|
||||
super();
|
||||
const Import = function(path, features, options, index, currentFileInfo, visibilityInfo) {
|
||||
this.options = options;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.path = path;
|
||||
this.features = features;
|
||||
this.allowRoot = true;
|
||||
|
||||
this.options = options;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.path = path;
|
||||
this.features = features;
|
||||
this.allowRoot = true;
|
||||
|
||||
if (this.options.less !== undefined || this.options.inline) {
|
||||
this.css = !this.options.less || this.options.inline;
|
||||
} else {
|
||||
const pathValue = this.getPath();
|
||||
if (pathValue && /[#\.\&\?]css([\?;].*)?$/.test(pathValue)) {
|
||||
this.css = true;
|
||||
}
|
||||
}
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.features, this);
|
||||
this.setParent(this.path, this);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.isPlugin && !this.options.inline && this.root) {
|
||||
this.root = visitor.visit(this.root);
|
||||
}
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
if (this.css && this.path._fileInfo.reference === undefined) {
|
||||
output.add('@import ', this._fileInfo, this._index);
|
||||
this.path.genCSS(context, output);
|
||||
if (this.features) {
|
||||
output.add(' ');
|
||||
this.features.genCSS(context, output);
|
||||
}
|
||||
output.add(';');
|
||||
}
|
||||
}
|
||||
|
||||
getPath() {
|
||||
return (this.path instanceof URL) ?
|
||||
this.path.value.value : this.path.value;
|
||||
}
|
||||
|
||||
isVariableImport() {
|
||||
let path = this.path;
|
||||
if (path instanceof URL) {
|
||||
path = path.value;
|
||||
}
|
||||
if (path instanceof Quoted) {
|
||||
return path.containsVariables();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
evalForImport(context) {
|
||||
let path = this.path;
|
||||
|
||||
if (path instanceof URL) {
|
||||
path = path.value;
|
||||
}
|
||||
|
||||
return new Import(path.eval(context), this.features, this.options, this._index, this._fileInfo, this.visibilityInfo());
|
||||
}
|
||||
|
||||
evalPath(context) {
|
||||
const path = this.path.eval(context);
|
||||
const fileInfo = this._fileInfo;
|
||||
|
||||
if (!(path instanceof URL)) {
|
||||
// Add the rootpath if the URL requires a rewrite
|
||||
const pathValue = path.value;
|
||||
if (fileInfo &&
|
||||
pathValue &&
|
||||
context.pathRequiresRewrite(pathValue)) {
|
||||
path.value = context.rewritePath(pathValue, fileInfo.rootpath);
|
||||
} else {
|
||||
path.value = context.normalizePath(path.value);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
const result = this.doEval(context);
|
||||
if (this.options.reference || this.blocksVisibility()) {
|
||||
if (result.length || result.length === 0) {
|
||||
result.forEach(node => {
|
||||
node.addVisibilityBlock();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
result.addVisibilityBlock();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
doEval(context) {
|
||||
let ruleset;
|
||||
let registry;
|
||||
const features = this.features && this.features.eval(context);
|
||||
|
||||
if (this.options.isPlugin) {
|
||||
if (this.root && this.root.eval) {
|
||||
try {
|
||||
this.root.eval(context);
|
||||
}
|
||||
catch (e) {
|
||||
e.message = 'Plugin error during evaluation';
|
||||
throw new LessError(e, this.root.imports, this.root.filename);
|
||||
}
|
||||
}
|
||||
registry = context.frames[0] && context.frames[0].functionRegistry;
|
||||
if ( registry && this.root && this.root.functions ) {
|
||||
registry.addMultiple( this.root.functions );
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (this.skip) {
|
||||
if (typeof this.skip === 'function') {
|
||||
this.skip = this.skip();
|
||||
}
|
||||
if (this.skip) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
if (this.options.inline) {
|
||||
const contents = new Anonymous(this.root, 0,
|
||||
{
|
||||
filename: this.importedFilename,
|
||||
reference: this.path._fileInfo && this.path._fileInfo.reference
|
||||
}, true, true);
|
||||
|
||||
return this.features ? new Media([contents], this.features.value) : [contents];
|
||||
} else if (this.css) {
|
||||
const newImport = new Import(this.evalPath(context), features, this.options, this._index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else if (this.root) {
|
||||
ruleset = new Ruleset(null, utils.copyArray(this.root.rules));
|
||||
ruleset.evalImports(context);
|
||||
|
||||
return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
|
||||
} else {
|
||||
return [];
|
||||
if (this.options.less !== undefined || this.options.inline) {
|
||||
this.css = !this.options.less || this.options.inline;
|
||||
} else {
|
||||
const pathValue = this.getPath();
|
||||
if (pathValue && /[#\.\&\?]css([\?;].*)?$/.test(pathValue)) {
|
||||
this.css = true;
|
||||
}
|
||||
}
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.features, this);
|
||||
this.setParent(this.path, this);
|
||||
}
|
||||
|
||||
Import.prototype = new Node();
|
||||
|
||||
Import.prototype.accept = function(visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.isPlugin && !this.options.inline && this.root) {
|
||||
this.root = visitor.visit(this.root);
|
||||
}
|
||||
};
|
||||
|
||||
Import.prototype.genCSS = function(context, output) {
|
||||
if (this.css && this.path._fileInfo.reference === undefined) {
|
||||
output.add('@import ', this._fileInfo, this._index);
|
||||
this.path.genCSS(context, output);
|
||||
if (this.features) {
|
||||
output.add(' ');
|
||||
this.features.genCSS(context, output);
|
||||
}
|
||||
output.add(';');
|
||||
}
|
||||
};
|
||||
|
||||
Import.prototype.getPath = function() {
|
||||
return (this.path instanceof URL) ?
|
||||
this.path.value.value : this.path.value;
|
||||
};
|
||||
|
||||
Import.prototype.isVariableImport = function() {
|
||||
let path = this.path;
|
||||
if (path instanceof URL) {
|
||||
path = path.value;
|
||||
}
|
||||
if (path instanceof Quoted) {
|
||||
return path.containsVariables();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Import.prototype.evalForImport = function(context) {
|
||||
let path = this.path;
|
||||
|
||||
if (path instanceof URL) {
|
||||
path = path.value;
|
||||
}
|
||||
|
||||
return new Import(path.eval(context), this.features, this.options, this._index, this._fileInfo, this.visibilityInfo());
|
||||
};
|
||||
|
||||
Import.prototype.evalPath = function(context) {
|
||||
const path = this.path.eval(context);
|
||||
const fileInfo = this._fileInfo;
|
||||
|
||||
if (!(path instanceof URL)) {
|
||||
// Add the rootpath if the URL requires a rewrite
|
||||
const pathValue = path.value;
|
||||
if (fileInfo &&
|
||||
pathValue &&
|
||||
context.pathRequiresRewrite(pathValue)) {
|
||||
path.value = context.rewritePath(pathValue, fileInfo.rootpath);
|
||||
} else {
|
||||
path.value = context.normalizePath(path.value);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
Import.prototype.eval = function(context) {
|
||||
const result = this.doEval(context);
|
||||
if (this.options.reference || this.blocksVisibility()) {
|
||||
if (result.length || result.length === 0) {
|
||||
result.forEach(node => {
|
||||
node.addVisibilityBlock();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
result.addVisibilityBlock();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Import.prototype.doEval = function(context) {
|
||||
let ruleset;
|
||||
let registry;
|
||||
const features = this.features && this.features.eval(context);
|
||||
|
||||
if (this.options.isPlugin) {
|
||||
if (this.root && this.root.eval) {
|
||||
try {
|
||||
this.root.eval(context);
|
||||
}
|
||||
catch (e) {
|
||||
e.message = 'Plugin error during evaluation';
|
||||
throw new LessError(e, this.root.imports, this.root.filename);
|
||||
}
|
||||
}
|
||||
registry = context.frames[0] && context.frames[0].functionRegistry;
|
||||
if ( registry && this.root && this.root.functions ) {
|
||||
registry.addMultiple( this.root.functions );
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
if (this.skip) {
|
||||
if (typeof this.skip === 'function') {
|
||||
this.skip = this.skip();
|
||||
}
|
||||
if (this.skip) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
if (this.options.inline) {
|
||||
const contents = new Anonymous(this.root, 0,
|
||||
{
|
||||
filename: this.importedFilename,
|
||||
reference: this.path._fileInfo && this.path._fileInfo.reference
|
||||
}, true, true);
|
||||
|
||||
return this.features ? new Media([contents], this.features.value) : [contents];
|
||||
} else if (this.css) {
|
||||
const newImport = new Import(this.evalPath(context), features, this.options, this._index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else if (this.root) {
|
||||
ruleset = new Ruleset(null, utils.copyArray(this.root.rules));
|
||||
ruleset.evalImports(context);
|
||||
|
||||
return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
Import.prototype.type = 'Import';
|
||||
export default Import;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const tree = Object.create(null);
|
||||
|
||||
import Node from './node';
|
||||
import Color from './color';
|
||||
import AtRule from './atrule';
|
||||
|
||||
@@ -3,31 +3,29 @@ import Dimension from './dimension';
|
||||
import Quoted from './quoted';
|
||||
import Anonymous from './anonymous';
|
||||
|
||||
class JavaScript extends JsEvalNode {
|
||||
constructor(string, escaped, index, currentFileInfo) {
|
||||
super();
|
||||
const JavaScript = function(string, escaped, index, currentFileInfo) {
|
||||
this.escaped = escaped;
|
||||
this.expression = string;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
|
||||
this.escaped = escaped;
|
||||
this.expression = string;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
JavaScript.prototype = new JsEvalNode();
|
||||
|
||||
JavaScript.prototype.eval = function(context) {
|
||||
const result = this.evaluateJavaScript(this.expression, context);
|
||||
const type = typeof result;
|
||||
|
||||
if (type === 'number' && !isNaN(result)) {
|
||||
return new Dimension(result);
|
||||
} else if (type === 'string') {
|
||||
return new Quoted(`"${result}"`, result, this.escaped, this._index);
|
||||
} else if (Array.isArray(result)) {
|
||||
return new Anonymous(result.join(', '));
|
||||
} else {
|
||||
return new Anonymous(result);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
const result = this.evaluateJavaScript(this.expression, context);
|
||||
const type = typeof result;
|
||||
|
||||
if (type === 'number' && !isNaN(result)) {
|
||||
return new Dimension(result);
|
||||
} else if (type === 'string') {
|
||||
return new Quoted(`"${result}"`, result, this.escaped, this._index);
|
||||
} else if (Array.isArray(result)) {
|
||||
return new Anonymous(result.join(', '));
|
||||
} else {
|
||||
return new Anonymous(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
JavaScript.prototype.type = 'JavaScript';
|
||||
export default JavaScript;
|
||||
|
||||
@@ -1,58 +1,60 @@
|
||||
import Node from './node';
|
||||
import Variable from './variable';
|
||||
|
||||
class JsEvalNode extends Node {
|
||||
evaluateJavaScript(expression, context) {
|
||||
let result;
|
||||
const that = this;
|
||||
const evalContext = {};
|
||||
const JsEvalNode = function() {};
|
||||
|
||||
if (!context.javascriptEnabled) {
|
||||
throw { message: 'Inline JavaScript is not enabled. Is it set in your options?',
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
JsEvalNode.prototype = new Node();
|
||||
|
||||
expression = expression.replace(/@\{([\w-]+)\}/g, (_, name) => that.jsify(new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context)));
|
||||
JsEvalNode.prototype.evaluateJavaScript = function(expression, context) {
|
||||
let result;
|
||||
const that = this;
|
||||
const evalContext = {};
|
||||
|
||||
try {
|
||||
expression = new Function(`return (${expression})`);
|
||||
} catch (e) {
|
||||
throw { message: `JavaScript evaluation error: ${e.message} from \`${expression}\`` ,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
const variables = context.frames[0].variables();
|
||||
for (const k in variables) {
|
||||
if (variables.hasOwnProperty(k)) {
|
||||
/* jshint loopfunc:true */
|
||||
evalContext[k.slice(1)] = {
|
||||
value: variables[k].value,
|
||||
toJS: function () {
|
||||
return this.value.eval(context).toCSS();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
result = expression.call(evalContext);
|
||||
} catch (e) {
|
||||
throw { message: `JavaScript evaluation error: '${e.name}: ${e.message.replace(/["]/g, '\'')}'` ,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
return result;
|
||||
if (!context.javascriptEnabled) {
|
||||
throw { message: 'Inline JavaScript is not enabled. Is it set in your options?',
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
jsify(obj) {
|
||||
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
|
||||
return `[${obj.value.map(v => v.toCSS()).join(', ')}]`;
|
||||
} else {
|
||||
return obj.toCSS();
|
||||
expression = expression.replace(/@\{([\w-]+)\}/g, (_, name) => that.jsify(new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context)));
|
||||
|
||||
try {
|
||||
expression = new Function(`return (${expression})`);
|
||||
} catch (e) {
|
||||
throw { message: `JavaScript evaluation error: ${e.message} from \`${expression}\`` ,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
const variables = context.frames[0].variables();
|
||||
for (const k in variables) {
|
||||
if (variables.hasOwnProperty(k)) {
|
||||
/* jshint loopfunc:true */
|
||||
evalContext[k.slice(1)] = {
|
||||
value: variables[k].value,
|
||||
toJS: function () {
|
||||
return this.value.eval(context).toCSS();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
result = expression.call(evalContext);
|
||||
} catch (e) {
|
||||
throw { message: `JavaScript evaluation error: '${e.name}: ${e.message.replace(/["]/g, '\'')}'` ,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
JsEvalNode.prototype.jsify = function(obj) {
|
||||
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
|
||||
return `[${obj.value.map(v => v.toCSS()).join(', ')}]`;
|
||||
} else {
|
||||
return obj.toCSS();
|
||||
}
|
||||
};
|
||||
|
||||
export default JsEvalNode;
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
import Node from './node';
|
||||
|
||||
class Keyword extends Node {
|
||||
constructor(value) {
|
||||
super();
|
||||
const Keyword = function(value) {
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
Keyword.prototype = new Node();
|
||||
|
||||
genCSS(context, output) {
|
||||
if (this.value === '%') { throw { type: 'Syntax', message: 'Invalid % without number' }; }
|
||||
output.add(this.value);
|
||||
}
|
||||
}
|
||||
Keyword.prototype.genCSS = function(context, output) {
|
||||
if (this.value === '%') { throw { type: 'Syntax', message: 'Invalid % without number' }; }
|
||||
output.add(this.value);
|
||||
};
|
||||
|
||||
Keyword.prototype.type = 'Keyword';
|
||||
|
||||
|
||||
@@ -6,149 +6,147 @@ import Expression from './expression';
|
||||
import AtRule from './atrule';
|
||||
import * as utils from '../utils';
|
||||
|
||||
class Media extends AtRule {
|
||||
constructor(value, features, index, currentFileInfo, visibilityInfo) {
|
||||
super();
|
||||
const Media = function(value, features, index, currentFileInfo, visibilityInfo) {
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
const selectors = (new Selector([], null, null, this._index, this._fileInfo)).createEmptySelectors();
|
||||
|
||||
const selectors = (new Selector([], null, null, this._index, this._fileInfo)).createEmptySelectors();
|
||||
this.features = new Value(features);
|
||||
this.rules = [new Ruleset(selectors, value)];
|
||||
this.rules[0].allowImports = true;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
this.setParent(selectors, this);
|
||||
this.setParent(this.features, this);
|
||||
this.setParent(this.rules, this);
|
||||
}
|
||||
|
||||
this.features = new Value(features);
|
||||
this.rules = [new Ruleset(selectors, value)];
|
||||
this.rules[0].allowImports = true;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
this.setParent(selectors, this);
|
||||
this.setParent(this.features, this);
|
||||
this.setParent(this.rules, this);
|
||||
Media.prototype = new AtRule();
|
||||
|
||||
Media.prototype.isRulesetLike = function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
Media.prototype.accept = function(visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
if (this.rules) {
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
}
|
||||
};
|
||||
|
||||
Media.prototype.genCSS = function(context, output) {
|
||||
output.add('@media ', this._fileInfo, this._index);
|
||||
this.features.genCSS(context, output);
|
||||
this.outputRuleset(context, output, this.rules);
|
||||
};
|
||||
|
||||
Media.prototype.eval = function(context) {
|
||||
if (!context.mediaBlocks) {
|
||||
context.mediaBlocks = [];
|
||||
context.mediaPath = [];
|
||||
}
|
||||
|
||||
isRulesetLike() {
|
||||
return true;
|
||||
const media = new Media(null, [], this._index, this._fileInfo, this.visibilityInfo());
|
||||
if (this.debugInfo) {
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
}
|
||||
|
||||
media.features = this.features.eval(context);
|
||||
|
||||
context.mediaPath.push(media);
|
||||
context.mediaBlocks.push(media);
|
||||
|
||||
this.rules[0].functionRegistry = context.frames[0].functionRegistry.inherit();
|
||||
context.frames.unshift(this.rules[0]);
|
||||
media.rules = [this.rules[0].eval(context)];
|
||||
context.frames.shift();
|
||||
|
||||
context.mediaPath.pop();
|
||||
|
||||
return context.mediaPath.length === 0 ? media.evalTop(context) :
|
||||
media.evalNested(context);
|
||||
};
|
||||
|
||||
Media.prototype.evalTop = function(context) {
|
||||
let result = this;
|
||||
|
||||
// Render all dependent Media blocks.
|
||||
if (context.mediaBlocks.length > 1) {
|
||||
const selectors = (new Selector([], null, null, this.getIndex(), this.fileInfo())).createEmptySelectors();
|
||||
result = new Ruleset(selectors, context.mediaBlocks);
|
||||
result.multiMedia = true;
|
||||
result.copyVisibilityInfo(this.visibilityInfo());
|
||||
this.setParent(result, this);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
if (this.rules) {
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
}
|
||||
delete context.mediaBlocks;
|
||||
delete context.mediaPath;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Media.prototype.evalNested = function(context) {
|
||||
let i;
|
||||
let value;
|
||||
const path = context.mediaPath.concat([this]);
|
||||
|
||||
// Extract the media-query conditions separated with `,` (OR).
|
||||
for (i = 0; i < path.length; i++) {
|
||||
value = path[i].features instanceof Value ?
|
||||
path[i].features.value : path[i].features;
|
||||
path[i] = Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add('@media ', this._fileInfo, this._index);
|
||||
this.features.genCSS(context, output);
|
||||
this.outputRuleset(context, output, this.rules);
|
||||
}
|
||||
// Trace all permutations to generate the resulting media-query.
|
||||
//
|
||||
// (a, b and c) with nested (d, e) ->
|
||||
// a and d
|
||||
// a and e
|
||||
// b and c and d
|
||||
// b and c and e
|
||||
this.features = new Value(this.permute(path).map(path => {
|
||||
path = path.map(fragment => fragment.toCSS ? fragment : new Anonymous(fragment));
|
||||
|
||||
eval(context) {
|
||||
if (!context.mediaBlocks) {
|
||||
context.mediaBlocks = [];
|
||||
context.mediaPath = [];
|
||||
for (i = path.length - 1; i > 0; i--) {
|
||||
path.splice(i, 0, new Anonymous('and'));
|
||||
}
|
||||
|
||||
const media = new Media(null, [], this._index, this._fileInfo, this.visibilityInfo());
|
||||
if (this.debugInfo) {
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
return new Expression(path);
|
||||
}));
|
||||
this.setParent(this.features, this);
|
||||
|
||||
// Fake a tree-node that doesn't output anything.
|
||||
return new Ruleset([], []);
|
||||
};
|
||||
|
||||
Media.prototype.permute = function(arr) {
|
||||
if (arr.length === 0) {
|
||||
return [];
|
||||
} else if (arr.length === 1) {
|
||||
return arr[0];
|
||||
} else {
|
||||
const result = [];
|
||||
const rest = this.permute(arr.slice(1));
|
||||
for (let i = 0; i < rest.length; i++) {
|
||||
for (let j = 0; j < arr[0].length; j++) {
|
||||
result.push([arr[0][j]].concat(rest[i]));
|
||||
}
|
||||
}
|
||||
|
||||
media.features = this.features.eval(context);
|
||||
|
||||
context.mediaPath.push(media);
|
||||
context.mediaBlocks.push(media);
|
||||
|
||||
this.rules[0].functionRegistry = context.frames[0].functionRegistry.inherit();
|
||||
context.frames.unshift(this.rules[0]);
|
||||
media.rules = [this.rules[0].eval(context)];
|
||||
context.frames.shift();
|
||||
|
||||
context.mediaPath.pop();
|
||||
|
||||
return context.mediaPath.length === 0 ? media.evalTop(context) :
|
||||
media.evalNested(context);
|
||||
}
|
||||
|
||||
evalTop(context) {
|
||||
let result = this;
|
||||
|
||||
// Render all dependent Media blocks.
|
||||
if (context.mediaBlocks.length > 1) {
|
||||
const selectors = (new Selector([], null, null, this.getIndex(), this.fileInfo())).createEmptySelectors();
|
||||
result = new Ruleset(selectors, context.mediaBlocks);
|
||||
result.multiMedia = true;
|
||||
result.copyVisibilityInfo(this.visibilityInfo());
|
||||
this.setParent(result, this);
|
||||
}
|
||||
|
||||
delete context.mediaBlocks;
|
||||
delete context.mediaPath;
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
evalNested(context) {
|
||||
let i;
|
||||
let value;
|
||||
const path = context.mediaPath.concat([this]);
|
||||
|
||||
// Extract the media-query conditions separated with `,` (OR).
|
||||
for (i = 0; i < path.length; i++) {
|
||||
value = path[i].features instanceof Value ?
|
||||
path[i].features.value : path[i].features;
|
||||
path[i] = Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
// Trace all permutations to generate the resulting media-query.
|
||||
//
|
||||
// (a, b and c) with nested (d, e) ->
|
||||
// a and d
|
||||
// a and e
|
||||
// b and c and d
|
||||
// b and c and e
|
||||
this.features = new Value(this.permute(path).map(path => {
|
||||
path = path.map(fragment => fragment.toCSS ? fragment : new Anonymous(fragment));
|
||||
|
||||
for (i = path.length - 1; i > 0; i--) {
|
||||
path.splice(i, 0, new Anonymous('and'));
|
||||
}
|
||||
|
||||
return new Expression(path);
|
||||
}));
|
||||
this.setParent(this.features, this);
|
||||
|
||||
// Fake a tree-node that doesn't output anything.
|
||||
return new Ruleset([], []);
|
||||
Media.prototype.bubbleSelectors = function(selectors) {
|
||||
if (!selectors) {
|
||||
return;
|
||||
}
|
||||
|
||||
permute(arr) {
|
||||
if (arr.length === 0) {
|
||||
return [];
|
||||
} else if (arr.length === 1) {
|
||||
return arr[0];
|
||||
} else {
|
||||
const result = [];
|
||||
const rest = this.permute(arr.slice(1));
|
||||
for (let i = 0; i < rest.length; i++) {
|
||||
for (let j = 0; j < arr[0].length; j++) {
|
||||
result.push([arr[0][j]].concat(rest[i]));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
bubbleSelectors(selectors) {
|
||||
if (!selectors) {
|
||||
return;
|
||||
}
|
||||
this.rules = [new Ruleset(utils.copyArray(selectors), [this.rules[0]])];
|
||||
this.setParent(this.rules, this);
|
||||
}
|
||||
}
|
||||
this.rules = [new Ruleset(utils.copyArray(selectors), [this.rules[0]])];
|
||||
this.setParent(this.rules, this);
|
||||
};
|
||||
|
||||
Media.prototype.type = 'Media';
|
||||
export default Media;
|
||||
|
||||
@@ -3,213 +3,211 @@ import Selector from './selector';
|
||||
import MixinDefinition from './mixin-definition';
|
||||
import defaultFunc from '../functions/default';
|
||||
|
||||
class MixinCall extends Node {
|
||||
constructor(elements, args, index, currentFileInfo, important) {
|
||||
super();
|
||||
const MixinCall = function(elements, args, index, currentFileInfo, important) {
|
||||
this.selector = new Selector(elements);
|
||||
this.arguments = args || [];
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.important = important;
|
||||
this.allowRoot = true;
|
||||
this.setParent(this.selector, this);
|
||||
};
|
||||
|
||||
this.selector = new Selector(elements);
|
||||
this.arguments = args || [];
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.important = important;
|
||||
this.allowRoot = true;
|
||||
this.setParent(this.selector, this);
|
||||
MixinCall.prototype = new Node();
|
||||
|
||||
MixinCall.prototype.accept = function(visitor) {
|
||||
if (this.selector) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.selector) {
|
||||
this.selector = visitor.visit(this.selector);
|
||||
}
|
||||
if (this.arguments.length) {
|
||||
this.arguments = visitor.visitArray(this.arguments);
|
||||
}
|
||||
if (this.arguments.length) {
|
||||
this.arguments = visitor.visitArray(this.arguments);
|
||||
}
|
||||
};
|
||||
|
||||
eval(context) {
|
||||
let mixins;
|
||||
let mixin;
|
||||
let mixinPath;
|
||||
const args = [];
|
||||
let arg;
|
||||
let argValue;
|
||||
const rules = [];
|
||||
let match = false;
|
||||
let i;
|
||||
let m;
|
||||
MixinCall.prototype.eval = function(context) {
|
||||
let mixins;
|
||||
let mixin;
|
||||
let mixinPath;
|
||||
const args = [];
|
||||
let arg;
|
||||
let argValue;
|
||||
const rules = [];
|
||||
let match = false;
|
||||
let i;
|
||||
let m;
|
||||
let f;
|
||||
let isRecursive;
|
||||
let isOneFound;
|
||||
const candidates = [];
|
||||
let candidate;
|
||||
const conditionResult = [];
|
||||
let defaultResult;
|
||||
const defFalseEitherCase = -1;
|
||||
const defNone = 0;
|
||||
const defTrue = 1;
|
||||
const defFalse = 2;
|
||||
let count;
|
||||
let originalRuleset;
|
||||
let noArgumentsFilter;
|
||||
|
||||
this.selector = this.selector.eval(context);
|
||||
|
||||
function calcDefGroup(mixin, mixinPath) {
|
||||
let f;
|
||||
let isRecursive;
|
||||
let isOneFound;
|
||||
const candidates = [];
|
||||
let candidate;
|
||||
const conditionResult = [];
|
||||
let defaultResult;
|
||||
const defFalseEitherCase = -1;
|
||||
const defNone = 0;
|
||||
const defTrue = 1;
|
||||
const defFalse = 2;
|
||||
let count;
|
||||
let originalRuleset;
|
||||
let noArgumentsFilter;
|
||||
let p;
|
||||
let namespace;
|
||||
|
||||
this.selector = this.selector.eval(context);
|
||||
|
||||
function calcDefGroup(mixin, mixinPath) {
|
||||
let f;
|
||||
let p;
|
||||
let namespace;
|
||||
|
||||
for (f = 0; f < 2; f++) {
|
||||
conditionResult[f] = true;
|
||||
defaultFunc.value(f);
|
||||
for (p = 0; p < mixinPath.length && conditionResult[f]; p++) {
|
||||
namespace = mixinPath[p];
|
||||
if (namespace.matchCondition) {
|
||||
conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context);
|
||||
}
|
||||
}
|
||||
if (mixin.matchCondition) {
|
||||
conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context);
|
||||
for (f = 0; f < 2; f++) {
|
||||
conditionResult[f] = true;
|
||||
defaultFunc.value(f);
|
||||
for (p = 0; p < mixinPath.length && conditionResult[f]; p++) {
|
||||
namespace = mixinPath[p];
|
||||
if (namespace.matchCondition) {
|
||||
conditionResult[f] = conditionResult[f] && namespace.matchCondition(null, context);
|
||||
}
|
||||
}
|
||||
if (conditionResult[0] || conditionResult[1]) {
|
||||
if (conditionResult[0] != conditionResult[1]) {
|
||||
return conditionResult[1] ?
|
||||
defTrue : defFalse;
|
||||
}
|
||||
|
||||
return defNone;
|
||||
}
|
||||
return defFalseEitherCase;
|
||||
}
|
||||
|
||||
for (i = 0; i < this.arguments.length; i++) {
|
||||
arg = this.arguments[i];
|
||||
argValue = arg.value.eval(context);
|
||||
if (arg.expand && Array.isArray(argValue.value)) {
|
||||
argValue = argValue.value;
|
||||
for (m = 0; m < argValue.length; m++) {
|
||||
args.push({value: argValue[m]});
|
||||
}
|
||||
} else {
|
||||
args.push({name: arg.name, value: argValue});
|
||||
if (mixin.matchCondition) {
|
||||
conditionResult[f] = conditionResult[f] && mixin.matchCondition(args, context);
|
||||
}
|
||||
}
|
||||
|
||||
noArgumentsFilter = rule => rule.matchArgs(null, context);
|
||||
|
||||
for (i = 0; i < context.frames.length; i++) {
|
||||
if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) {
|
||||
isOneFound = true;
|
||||
|
||||
// To make `default()` function independent of definition order we have two "subpasses" here.
|
||||
// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
|
||||
// and build candidate list with corresponding flags. Then, when we know all possible matches,
|
||||
// we make a final decision.
|
||||
|
||||
for (m = 0; m < mixins.length; m++) {
|
||||
mixin = mixins[m].rule;
|
||||
mixinPath = mixins[m].path;
|
||||
isRecursive = false;
|
||||
for (f = 0; f < context.frames.length; f++) {
|
||||
if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) {
|
||||
isRecursive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRecursive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mixin.matchArgs(args, context)) {
|
||||
candidate = {mixin, group: calcDefGroup(mixin, mixinPath)};
|
||||
|
||||
if (candidate.group !== defFalseEitherCase) {
|
||||
candidates.push(candidate);
|
||||
}
|
||||
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
defaultFunc.reset();
|
||||
|
||||
count = [0, 0, 0];
|
||||
for (m = 0; m < candidates.length; m++) {
|
||||
count[candidates[m].group]++;
|
||||
}
|
||||
|
||||
if (count[defNone] > 0) {
|
||||
defaultResult = defFalse;
|
||||
} else {
|
||||
defaultResult = defTrue;
|
||||
if ((count[defTrue] + count[defFalse]) > 1) {
|
||||
throw { type: 'Runtime',
|
||||
message: `Ambiguous use of \`default()\` found when matching for \`${this.format(args)}\``,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
}
|
||||
|
||||
for (m = 0; m < candidates.length; m++) {
|
||||
candidate = candidates[m].group;
|
||||
if ((candidate === defNone) || (candidate === defaultResult)) {
|
||||
try {
|
||||
mixin = candidates[m].mixin;
|
||||
if (!(mixin instanceof MixinDefinition)) {
|
||||
originalRuleset = mixin.originalRuleset || mixin;
|
||||
mixin = new MixinDefinition('', [], mixin.rules, null, false, null, originalRuleset.visibilityInfo());
|
||||
mixin.originalRuleset = originalRuleset;
|
||||
}
|
||||
const newRules = mixin.evalCall(context, args, this.important).rules;
|
||||
this._setVisibilityToReplacement(newRules);
|
||||
Array.prototype.push.apply(rules, newRules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.getIndex(), filename: this.fileInfo().filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return rules;
|
||||
}
|
||||
if (conditionResult[0] || conditionResult[1]) {
|
||||
if (conditionResult[0] != conditionResult[1]) {
|
||||
return conditionResult[1] ?
|
||||
defTrue : defFalse;
|
||||
}
|
||||
|
||||
return defNone;
|
||||
}
|
||||
if (isOneFound) {
|
||||
throw { type: 'Runtime',
|
||||
message: `No matching definition was found for \`${this.format(args)}\``,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
return defFalseEitherCase;
|
||||
}
|
||||
|
||||
for (i = 0; i < this.arguments.length; i++) {
|
||||
arg = this.arguments[i];
|
||||
argValue = arg.value.eval(context);
|
||||
if (arg.expand && Array.isArray(argValue.value)) {
|
||||
argValue = argValue.value;
|
||||
for (m = 0; m < argValue.length; m++) {
|
||||
args.push({value: argValue[m]});
|
||||
}
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `${this.selector.toCSS().trim()} is undefined`,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
args.push({name: arg.name, value: argValue});
|
||||
}
|
||||
}
|
||||
|
||||
_setVisibilityToReplacement(replacement) {
|
||||
let i;
|
||||
let rule;
|
||||
if (this.blocksVisibility()) {
|
||||
for (i = 0; i < replacement.length; i++) {
|
||||
rule = replacement[i];
|
||||
rule.addVisibilityBlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
noArgumentsFilter = rule => rule.matchArgs(null, context);
|
||||
|
||||
format(args) {
|
||||
return `${this.selector.toCSS().trim()}(${args ? args.map(a => {
|
||||
let argValue = '';
|
||||
if (a.name) {
|
||||
argValue += `${a.name}:`;
|
||||
for (i = 0; i < context.frames.length; i++) {
|
||||
if ((mixins = context.frames[i].find(this.selector, null, noArgumentsFilter)).length > 0) {
|
||||
isOneFound = true;
|
||||
|
||||
// To make `default()` function independent of definition order we have two "subpasses" here.
|
||||
// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
|
||||
// and build candidate list with corresponding flags. Then, when we know all possible matches,
|
||||
// we make a final decision.
|
||||
|
||||
for (m = 0; m < mixins.length; m++) {
|
||||
mixin = mixins[m].rule;
|
||||
mixinPath = mixins[m].path;
|
||||
isRecursive = false;
|
||||
for (f = 0; f < context.frames.length; f++) {
|
||||
if ((!(mixin instanceof MixinDefinition)) && mixin === (context.frames[f].originalRuleset || context.frames[f])) {
|
||||
isRecursive = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isRecursive) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mixin.matchArgs(args, context)) {
|
||||
candidate = {mixin, group: calcDefGroup(mixin, mixinPath)};
|
||||
|
||||
if (candidate.group !== defFalseEitherCase) {
|
||||
candidates.push(candidate);
|
||||
}
|
||||
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
if (a.value.toCSS) {
|
||||
argValue += a.value.toCSS();
|
||||
|
||||
defaultFunc.reset();
|
||||
|
||||
count = [0, 0, 0];
|
||||
for (m = 0; m < candidates.length; m++) {
|
||||
count[candidates[m].group]++;
|
||||
}
|
||||
|
||||
if (count[defNone] > 0) {
|
||||
defaultResult = defFalse;
|
||||
} else {
|
||||
argValue += '???';
|
||||
defaultResult = defTrue;
|
||||
if ((count[defTrue] + count[defFalse]) > 1) {
|
||||
throw { type: 'Runtime',
|
||||
message: `Ambiguous use of \`default()\` found when matching for \`${this.format(args)}\``,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : ''})`;
|
||||
|
||||
for (m = 0; m < candidates.length; m++) {
|
||||
candidate = candidates[m].group;
|
||||
if ((candidate === defNone) || (candidate === defaultResult)) {
|
||||
try {
|
||||
mixin = candidates[m].mixin;
|
||||
if (!(mixin instanceof MixinDefinition)) {
|
||||
originalRuleset = mixin.originalRuleset || mixin;
|
||||
mixin = new MixinDefinition('', [], mixin.rules, null, false, null, originalRuleset.visibilityInfo());
|
||||
mixin.originalRuleset = originalRuleset;
|
||||
}
|
||||
const newRules = mixin.evalCall(context, args, this.important).rules;
|
||||
this._setVisibilityToReplacement(newRules);
|
||||
Array.prototype.push.apply(rules, newRules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.getIndex(), filename: this.fileInfo().filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
return rules;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isOneFound) {
|
||||
throw { type: 'Runtime',
|
||||
message: `No matching definition was found for \`${this.format(args)}\``,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `${this.selector.toCSS().trim()} is undefined`,
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
};
|
||||
|
||||
MixinCall.prototype._setVisibilityToReplacement = function(replacement) {
|
||||
let i;
|
||||
let rule;
|
||||
if (this.blocksVisibility()) {
|
||||
for (i = 0; i < replacement.length; i++) {
|
||||
rule = replacement[i];
|
||||
rule.addVisibilityBlock();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MixinCall.prototype.format = function(args) {
|
||||
return `${this.selector.toCSS().trim()}(${args ? args.map(a => {
|
||||
let argValue = '';
|
||||
if (a.name) {
|
||||
argValue += `${a.name}:`;
|
||||
}
|
||||
if (a.value.toCSS) {
|
||||
argValue += a.value.toCSS();
|
||||
} else {
|
||||
argValue += '???';
|
||||
}
|
||||
return argValue;
|
||||
}).join(', ') : ''})`;
|
||||
};
|
||||
|
||||
MixinCall.prototype.type = 'MixinCall';
|
||||
export default MixinCall;
|
||||
|
||||
@@ -7,222 +7,220 @@ import Expression from './expression';
|
||||
import contexts from '../contexts';
|
||||
import * as utils from '../utils';
|
||||
|
||||
class Definition extends Ruleset {
|
||||
constructor(name, params, rules, condition, variadic, frames, visibilityInfo) {
|
||||
super();
|
||||
const Definition = function(name, params, rules, condition, variadic, frames, visibilityInfo) {
|
||||
this.name = name || 'anonymous mixin';
|
||||
this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
|
||||
this.params = params;
|
||||
this.condition = condition;
|
||||
this.variadic = variadic;
|
||||
this.arity = params.length;
|
||||
this.rules = rules;
|
||||
this._lookups = {};
|
||||
const optionalParameters = [];
|
||||
this.required = params.reduce((count, p) => {
|
||||
if (!p.name || (p.name && !p.value)) {
|
||||
return count + 1;
|
||||
}
|
||||
else {
|
||||
optionalParameters.push(p.name);
|
||||
return count;
|
||||
}
|
||||
}, 0);
|
||||
this.optionalParameters = optionalParameters;
|
||||
this.frames = frames;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
};
|
||||
|
||||
this.name = name || 'anonymous mixin';
|
||||
this.selectors = [new Selector([new Element(null, name, false, this._index, this._fileInfo)])];
|
||||
this.params = params;
|
||||
this.condition = condition;
|
||||
this.variadic = variadic;
|
||||
this.arity = params.length;
|
||||
this.rules = rules;
|
||||
this._lookups = {};
|
||||
const optionalParameters = [];
|
||||
this.required = params.reduce((count, p) => {
|
||||
if (!p.name || (p.name && !p.value)) {
|
||||
return count + 1;
|
||||
}
|
||||
else {
|
||||
optionalParameters.push(p.name);
|
||||
return count;
|
||||
}
|
||||
}, 0);
|
||||
this.optionalParameters = optionalParameters;
|
||||
this.frames = frames;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
Definition.prototype = new Ruleset();
|
||||
|
||||
Definition.prototype.accept = function(visitor) {
|
||||
if (this.params && this.params.length) {
|
||||
this.params = visitor.visitArray(this.params);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.params && this.params.length) {
|
||||
this.params = visitor.visitArray(this.params);
|
||||
}
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
this.rules = visitor.visitArray(this.rules);
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
};
|
||||
|
||||
evalParams(context, mixinEnv, args, evaldArguments) {
|
||||
/* jshint boss:true */
|
||||
const frame = new Ruleset(null, null);
|
||||
Definition.prototype.evalParams = function(context, mixinEnv, args, evaldArguments) {
|
||||
/* jshint boss:true */
|
||||
const frame = new Ruleset(null, null);
|
||||
|
||||
let varargs;
|
||||
let arg;
|
||||
const params = utils.copyArray(this.params);
|
||||
let i;
|
||||
let j;
|
||||
let val;
|
||||
let name;
|
||||
let isNamedFound;
|
||||
let argIndex;
|
||||
let argsLength = 0;
|
||||
let varargs;
|
||||
let arg;
|
||||
const params = utils.copyArray(this.params);
|
||||
let i;
|
||||
let j;
|
||||
let val;
|
||||
let name;
|
||||
let isNamedFound;
|
||||
let argIndex;
|
||||
let argsLength = 0;
|
||||
|
||||
if (mixinEnv.frames && mixinEnv.frames[0] && mixinEnv.frames[0].functionRegistry) {
|
||||
frame.functionRegistry = mixinEnv.frames[0].functionRegistry.inherit();
|
||||
}
|
||||
mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
if (mixinEnv.frames && mixinEnv.frames[0] && mixinEnv.frames[0].functionRegistry) {
|
||||
frame.functionRegistry = mixinEnv.frames[0].functionRegistry.inherit();
|
||||
}
|
||||
mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
|
||||
if (args) {
|
||||
args = utils.copyArray(args);
|
||||
argsLength = args.length;
|
||||
if (args) {
|
||||
args = utils.copyArray(args);
|
||||
argsLength = args.length;
|
||||
|
||||
for (i = 0; i < argsLength; i++) {
|
||||
arg = args[i];
|
||||
if (name = (arg && arg.name)) {
|
||||
isNamedFound = false;
|
||||
for (j = 0; j < params.length; j++) {
|
||||
if (!evaldArguments[j] && name === params[j].name) {
|
||||
evaldArguments[j] = arg.value.eval(context);
|
||||
frame.prependRule(new Declaration(name, arg.value.eval(context)));
|
||||
isNamedFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isNamedFound) {
|
||||
args.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
} else {
|
||||
throw { type: 'Runtime', message: `Named argument for ${this.name} ${args[i].name} not found` };
|
||||
for (i = 0; i < argsLength; i++) {
|
||||
arg = args[i];
|
||||
if (name = (arg && arg.name)) {
|
||||
isNamedFound = false;
|
||||
for (j = 0; j < params.length; j++) {
|
||||
if (!evaldArguments[j] && name === params[j].name) {
|
||||
evaldArguments[j] = arg.value.eval(context);
|
||||
frame.prependRule(new Declaration(name, arg.value.eval(context)));
|
||||
isNamedFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
argIndex = 0;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
if (evaldArguments[i]) { continue; }
|
||||
|
||||
arg = args && args[argIndex];
|
||||
|
||||
if (name = params[i].name) {
|
||||
if (params[i].variadic) {
|
||||
varargs = [];
|
||||
for (j = argIndex; j < argsLength; j++) {
|
||||
varargs.push(args[j].value.eval(context));
|
||||
}
|
||||
frame.prependRule(new Declaration(name, new Expression(varargs).eval(context)));
|
||||
if (isNamedFound) {
|
||||
args.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
} else {
|
||||
val = arg && arg.value;
|
||||
if (val) {
|
||||
// This was a mixin call, pass in a detached ruleset of it's eval'd rules
|
||||
if (Array.isArray(val)) {
|
||||
val = new DetachedRuleset(new Ruleset('', val));
|
||||
}
|
||||
else {
|
||||
val = val.eval(context);
|
||||
}
|
||||
} else if (params[i].value) {
|
||||
val = params[i].value.eval(mixinEnv);
|
||||
frame.resetCache();
|
||||
} else {
|
||||
throw { type: 'Runtime', message: `wrong number of arguments for ${this.name} (${argsLength} for ${this.arity})` };
|
||||
}
|
||||
|
||||
frame.prependRule(new Declaration(name, val));
|
||||
evaldArguments[i] = val;
|
||||
throw { type: 'Runtime', message: `Named argument for ${this.name} ${args[i].name} not found` };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
argIndex = 0;
|
||||
for (i = 0; i < params.length; i++) {
|
||||
if (evaldArguments[i]) { continue; }
|
||||
|
||||
if (params[i].variadic && args) {
|
||||
arg = args && args[argIndex];
|
||||
|
||||
if (name = params[i].name) {
|
||||
if (params[i].variadic) {
|
||||
varargs = [];
|
||||
for (j = argIndex; j < argsLength; j++) {
|
||||
evaldArguments[j] = args[j].value.eval(context);
|
||||
varargs.push(args[j].value.eval(context));
|
||||
}
|
||||
}
|
||||
argIndex++;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
makeImportant() {
|
||||
const rules = !this.rules ? this.rules : this.rules.map(r => {
|
||||
if (r.makeImportant) {
|
||||
return r.makeImportant(true);
|
||||
frame.prependRule(new Declaration(name, new Expression(varargs).eval(context)));
|
||||
} else {
|
||||
return r;
|
||||
val = arg && arg.value;
|
||||
if (val) {
|
||||
// This was a mixin call, pass in a detached ruleset of it's eval'd rules
|
||||
if (Array.isArray(val)) {
|
||||
val = new DetachedRuleset(new Ruleset('', val));
|
||||
}
|
||||
else {
|
||||
val = val.eval(context);
|
||||
}
|
||||
} else if (params[i].value) {
|
||||
val = params[i].value.eval(mixinEnv);
|
||||
frame.resetCache();
|
||||
} else {
|
||||
throw { type: 'Runtime', message: `wrong number of arguments for ${this.name} (${argsLength} for ${this.arity})` };
|
||||
}
|
||||
|
||||
frame.prependRule(new Declaration(name, val));
|
||||
evaldArguments[i] = val;
|
||||
}
|
||||
});
|
||||
const result = new Definition(this.name, this.params, rules, this.condition, this.variadic, this.frames);
|
||||
return result;
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || utils.copyArray(context.frames));
|
||||
}
|
||||
|
||||
evalCall(context, args, important) {
|
||||
const _arguments = [];
|
||||
const mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames;
|
||||
const frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments);
|
||||
let rules;
|
||||
let ruleset;
|
||||
|
||||
frame.prependRule(new Declaration('@arguments', new Expression(_arguments).eval(context)));
|
||||
|
||||
rules = utils.copyArray(this.rules);
|
||||
|
||||
ruleset = new Ruleset(null, rules);
|
||||
ruleset.originalRuleset = this;
|
||||
ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames)));
|
||||
if (important) {
|
||||
ruleset = ruleset.makeImportant();
|
||||
}
|
||||
return ruleset;
|
||||
|
||||
if (params[i].variadic && args) {
|
||||
for (j = argIndex; j < argsLength; j++) {
|
||||
evaldArguments[j] = args[j].value.eval(context);
|
||||
}
|
||||
}
|
||||
argIndex++;
|
||||
}
|
||||
|
||||
matchCondition(args, context) {
|
||||
if (this.condition && !this.condition.eval(
|
||||
new contexts.Eval(context,
|
||||
[this.evalParams(context, /* the parameter variables */
|
||||
new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
|
||||
.concat(this.frames || []) // the parent namespace/mixin frames
|
||||
.concat(context.frames)))) { // the current environment frames
|
||||
return frame;
|
||||
};
|
||||
|
||||
Definition.prototype.makeImportant = function() {
|
||||
const rules = !this.rules ? this.rules : this.rules.map(r => {
|
||||
if (r.makeImportant) {
|
||||
return r.makeImportant(true);
|
||||
} else {
|
||||
return r;
|
||||
}
|
||||
});
|
||||
const result = new Definition(this.name, this.params, rules, this.condition, this.variadic, this.frames);
|
||||
return result;
|
||||
};
|
||||
|
||||
Definition.prototype.eval = function(context) {
|
||||
return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || utils.copyArray(context.frames));
|
||||
};
|
||||
|
||||
Definition.prototype.evalCall = function(context, args, important) {
|
||||
const _arguments = [];
|
||||
const mixinFrames = this.frames ? this.frames.concat(context.frames) : context.frames;
|
||||
const frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments);
|
||||
let rules;
|
||||
let ruleset;
|
||||
|
||||
frame.prependRule(new Declaration('@arguments', new Expression(_arguments).eval(context)));
|
||||
|
||||
rules = utils.copyArray(this.rules);
|
||||
|
||||
ruleset = new Ruleset(null, rules);
|
||||
ruleset.originalRuleset = this;
|
||||
ruleset = ruleset.eval(new contexts.Eval(context, [this, frame].concat(mixinFrames)));
|
||||
if (important) {
|
||||
ruleset = ruleset.makeImportant();
|
||||
}
|
||||
return ruleset;
|
||||
};
|
||||
|
||||
Definition.prototype.matchCondition = function(args, context) {
|
||||
if (this.condition && !this.condition.eval(
|
||||
new contexts.Eval(context,
|
||||
[this.evalParams(context, /* the parameter variables */
|
||||
new contexts.Eval(context, this.frames ? this.frames.concat(context.frames) : context.frames), args, [])]
|
||||
.concat(this.frames || []) // the parent namespace/mixin frames
|
||||
.concat(context.frames)))) { // the current environment frames
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Definition.prototype.matchArgs = function(args, context) {
|
||||
const allArgsCnt = (args && args.length) || 0;
|
||||
let len;
|
||||
const optionalParameters = this.optionalParameters;
|
||||
const requiredArgsCnt = !args ? 0 : args.reduce((count, p) => {
|
||||
if (optionalParameters.indexOf(p.name) < 0) {
|
||||
return count + 1;
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
}, 0);
|
||||
|
||||
if (!this.variadic) {
|
||||
if (requiredArgsCnt < this.required) {
|
||||
return false;
|
||||
}
|
||||
if (allArgsCnt > this.params.length) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (requiredArgsCnt < (this.required - 1)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
matchArgs(args, context) {
|
||||
const allArgsCnt = (args && args.length) || 0;
|
||||
let len;
|
||||
const optionalParameters = this.optionalParameters;
|
||||
const requiredArgsCnt = !args ? 0 : args.reduce((count, p) => {
|
||||
if (optionalParameters.indexOf(p.name) < 0) {
|
||||
return count + 1;
|
||||
} else {
|
||||
return count;
|
||||
}
|
||||
}, 0);
|
||||
// check patterns
|
||||
len = Math.min(requiredArgsCnt, this.arity);
|
||||
|
||||
if (!this.variadic) {
|
||||
if (requiredArgsCnt < this.required) {
|
||||
return false;
|
||||
}
|
||||
if (allArgsCnt > this.params.length) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (requiredArgsCnt < (this.required - 1)) {
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (!this.params[i].name && !this.params[i].variadic) {
|
||||
if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// check patterns
|
||||
len = Math.min(requiredArgsCnt, this.arity);
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (!this.params[i].name && !this.params[i].variadic) {
|
||||
if (args[i].value.eval(context).toCSS() != this.params[i].value.eval(context).toCSS()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
Definition.prototype.type = 'MixinDefinition';
|
||||
Definition.prototype.evalFirst = true;
|
||||
|
||||
@@ -3,84 +3,82 @@ import Variable from './variable';
|
||||
import Ruleset from './ruleset';
|
||||
import Selector from './selector';
|
||||
|
||||
class NamespaceValue extends Node {
|
||||
constructor(ruleCall, lookups, index, fileInfo) {
|
||||
super();
|
||||
const NamespaceValue = function(ruleCall, lookups, index, fileInfo) {
|
||||
this.value = ruleCall;
|
||||
this.lookups = lookups;
|
||||
this._index = index;
|
||||
this._fileInfo = fileInfo;
|
||||
}
|
||||
|
||||
this.value = ruleCall;
|
||||
this.lookups = lookups;
|
||||
this._index = index;
|
||||
this._fileInfo = fileInfo;
|
||||
}
|
||||
NamespaceValue.prototype = new Node();
|
||||
|
||||
eval(context) {
|
||||
let i;
|
||||
let j;
|
||||
let name;
|
||||
let rules = this.value.eval(context);
|
||||
NamespaceValue.prototype.eval = function(context) {
|
||||
let i;
|
||||
let j;
|
||||
let name;
|
||||
let rules = this.value.eval(context);
|
||||
|
||||
for (i = 0; i < this.lookups.length; i++) {
|
||||
name = this.lookups[i];
|
||||
for (i = 0; i < this.lookups.length; i++) {
|
||||
name = this.lookups[i];
|
||||
|
||||
/**
|
||||
* Eval'd DRs return rulesets.
|
||||
* Eval'd mixins return rules, so let's make a ruleset if we need it.
|
||||
* We need to do this because of late parsing of values
|
||||
*/
|
||||
if (Array.isArray(rules)) {
|
||||
rules = new Ruleset([new Selector()], rules);
|
||||
/**
|
||||
* Eval'd DRs return rulesets.
|
||||
* Eval'd mixins return rules, so let's make a ruleset if we need it.
|
||||
* We need to do this because of late parsing of values
|
||||
*/
|
||||
if (Array.isArray(rules)) {
|
||||
rules = new Ruleset([new Selector()], rules);
|
||||
}
|
||||
|
||||
if (name === '') {
|
||||
rules = rules.lastDeclaration();
|
||||
}
|
||||
else if (name.charAt(0) === '@') {
|
||||
if (name.charAt(1) === '@') {
|
||||
name = `@${new Variable(name.substr(1)).eval(context).value}`;
|
||||
}
|
||||
|
||||
if (name === '') {
|
||||
rules = rules.lastDeclaration();
|
||||
if (rules.variables) {
|
||||
rules = rules.variable(name);
|
||||
}
|
||||
else if (name.charAt(0) === '@') {
|
||||
if (name.charAt(1) === '@') {
|
||||
name = `@${new Variable(name.substr(1)).eval(context).value}`;
|
||||
}
|
||||
if (rules.variables) {
|
||||
rules = rules.variable(name);
|
||||
}
|
||||
|
||||
if (!rules) {
|
||||
throw { type: 'Name',
|
||||
message: `variable ${name} not found`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (name.substring(0, 2) === '$@') {
|
||||
name = `$${new Variable(name.substr(1)).eval(context).value}`;
|
||||
}
|
||||
else {
|
||||
name = name.charAt(0) === '$' ? name : `$${name}`;
|
||||
}
|
||||
if (rules.properties) {
|
||||
rules = rules.property(name);
|
||||
}
|
||||
|
||||
if (!rules) {
|
||||
throw { type: 'Name',
|
||||
message: `property "${name.substr(1)}" not found`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
// Properties are an array of values, since a ruleset can have multiple props.
|
||||
// We pick the last one (the "cascaded" value)
|
||||
rules = rules[rules.length - 1];
|
||||
}
|
||||
|
||||
if (rules.value) {
|
||||
rules = rules.eval(context).value;
|
||||
}
|
||||
if (rules.ruleset) {
|
||||
rules = rules.ruleset.eval(context);
|
||||
if (!rules) {
|
||||
throw { type: 'Name',
|
||||
message: `variable ${name} not found`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
else {
|
||||
if (name.substring(0, 2) === '$@') {
|
||||
name = `$${new Variable(name.substr(1)).eval(context).value}`;
|
||||
}
|
||||
else {
|
||||
name = name.charAt(0) === '$' ? name : `$${name}`;
|
||||
}
|
||||
if (rules.properties) {
|
||||
rules = rules.property(name);
|
||||
}
|
||||
|
||||
if (!rules) {
|
||||
throw { type: 'Name',
|
||||
message: `property "${name.substr(1)}" not found`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
// Properties are an array of values, since a ruleset can have multiple props.
|
||||
// We pick the last one (the "cascaded" value)
|
||||
rules = rules[rules.length - 1];
|
||||
}
|
||||
|
||||
if (rules.value) {
|
||||
rules = rules.eval(context).value;
|
||||
}
|
||||
if (rules.ruleset) {
|
||||
rules = rules.ruleset.eval(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
};
|
||||
|
||||
NamespaceValue.prototype.type = 'NamespaceValue';
|
||||
export default NamespaceValue;
|
||||
|
||||
@@ -2,25 +2,23 @@ import Node from './node';
|
||||
import Operation from './operation';
|
||||
import Dimension from './dimension';
|
||||
|
||||
class Negative extends Node {
|
||||
constructor(node) {
|
||||
super();
|
||||
|
||||
this.value = node;
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add('-');
|
||||
this.value.genCSS(context, output);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
if (context.isMathOn()) {
|
||||
return (new Operation('*', [new Dimension(-1), this.value])).eval(context);
|
||||
}
|
||||
return new Negative(this.value.eval(context));
|
||||
}
|
||||
const Negative = function(node) {
|
||||
this.value = node;
|
||||
}
|
||||
|
||||
Negative.prototype = new Node();
|
||||
|
||||
Negative.prototype.genCSS = function(context, output) {
|
||||
output.add('-');
|
||||
this.value.genCSS(context, output);
|
||||
};
|
||||
|
||||
Negative.prototype.eval = function(context) {
|
||||
if (context.isMathOn()) {
|
||||
return (new Operation('*', [new Dimension(-1), this.value])).eval(context);
|
||||
}
|
||||
return new Negative(this.value.eval(context));
|
||||
};
|
||||
|
||||
Negative.prototype.type = 'Negative';
|
||||
export default Negative;
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* The reason why Node is a class and other nodes simply do not extend
|
||||
* from Node (since we're transpiling) is due to this issue:
|
||||
*
|
||||
* https://github.com/less/less.js/issues/3434
|
||||
*/
|
||||
class Node {
|
||||
constructor() {
|
||||
this.parent = null;
|
||||
|
||||
@@ -4,59 +4,56 @@ import Dimension from './dimension';
|
||||
import * as Constants from '../constants';
|
||||
const MATH = Constants.Math;
|
||||
|
||||
const Operation = function(op, operands, isSpaced) {
|
||||
this.op = op.trim();
|
||||
this.operands = operands;
|
||||
this.isSpaced = isSpaced;
|
||||
};
|
||||
|
||||
class Operation extends Node {
|
||||
constructor(op, operands, isSpaced) {
|
||||
super();
|
||||
Operation.prototype = new Node();
|
||||
|
||||
this.op = op.trim();
|
||||
this.operands = operands;
|
||||
this.isSpaced = isSpaced;
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
this.operands = visitor.visitArray(this.operands);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let a = this.operands[0].eval(context);
|
||||
let b = this.operands[1].eval(context);
|
||||
let op;
|
||||
|
||||
if (context.isMathOn(this.op)) {
|
||||
op = this.op === './' ? '/' : this.op;
|
||||
if (a instanceof Dimension && b instanceof Color) {
|
||||
a = a.toColor();
|
||||
}
|
||||
if (b instanceof Dimension && a instanceof Color) {
|
||||
b = b.toColor();
|
||||
}
|
||||
if (!a.operate) {
|
||||
if (a instanceof Operation && a.op === '/' && context.math === MATH.PARENS_DIVISION) {
|
||||
return new Operation(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
throw { type: 'Operation',
|
||||
message: 'Operation on an invalid type' };
|
||||
}
|
||||
|
||||
return a.operate(context, op, b);
|
||||
} else {
|
||||
return new Operation(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
this.operands[0].genCSS(context, output);
|
||||
if (this.isSpaced) {
|
||||
output.add(' ');
|
||||
}
|
||||
output.add(this.op);
|
||||
if (this.isSpaced) {
|
||||
output.add(' ');
|
||||
}
|
||||
this.operands[1].genCSS(context, output);
|
||||
}
|
||||
Operation.prototype.accept = function(visitor) {
|
||||
this.operands = visitor.visitArray(this.operands);
|
||||
}
|
||||
|
||||
Operation.prototype.eval = function(context) {
|
||||
let a = this.operands[0].eval(context);
|
||||
let b = this.operands[1].eval(context);
|
||||
let op;
|
||||
|
||||
if (context.isMathOn(this.op)) {
|
||||
op = this.op === './' ? '/' : this.op;
|
||||
if (a instanceof Dimension && b instanceof Color) {
|
||||
a = a.toColor();
|
||||
}
|
||||
if (b instanceof Dimension && a instanceof Color) {
|
||||
b = b.toColor();
|
||||
}
|
||||
if (!a.operate) {
|
||||
if (a instanceof Operation && a.op === '/' && context.math === MATH.PARENS_DIVISION) {
|
||||
return new Operation(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
throw { type: 'Operation',
|
||||
message: 'Operation on an invalid type' };
|
||||
}
|
||||
|
||||
return a.operate(context, op, b);
|
||||
} else {
|
||||
return new Operation(this.op, [a, b], this.isSpaced);
|
||||
}
|
||||
};
|
||||
|
||||
Operation.prototype.genCSS = function(context, output) {
|
||||
this.operands[0].genCSS(context, output);
|
||||
if (this.isSpaced) {
|
||||
output.add(' ');
|
||||
}
|
||||
output.add(this.op);
|
||||
if (this.isSpaced) {
|
||||
output.add(' ');
|
||||
}
|
||||
this.operands[1].genCSS(context, output);
|
||||
};
|
||||
|
||||
Operation.prototype.type = 'Operation';
|
||||
export default Operation;
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
import Node from './node';
|
||||
|
||||
class Paren extends Node {
|
||||
constructor(node) {
|
||||
super();
|
||||
const Paren = function(node) {
|
||||
this.value = node;
|
||||
};
|
||||
|
||||
this.value = node;
|
||||
}
|
||||
Paren.prototype = new Node();
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add('(');
|
||||
this.value.genCSS(context, output);
|
||||
output.add(')');
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
return new Paren(this.value.eval(context));
|
||||
}
|
||||
Paren.prototype.genCSS = function(context, output) {
|
||||
output.add('(');
|
||||
this.value.genCSS(context, output);
|
||||
output.add(')');
|
||||
}
|
||||
|
||||
Paren.prototype.eval = function(context) {
|
||||
return new Paren(this.value.eval(context));
|
||||
};
|
||||
|
||||
Paren.prototype.type = 'Paren';
|
||||
export default Paren;
|
||||
|
||||
@@ -1,77 +1,75 @@
|
||||
import Node from './node';
|
||||
import Declaration from './declaration';
|
||||
|
||||
class Property extends Node {
|
||||
constructor(name, index, currentFileInfo) {
|
||||
super();
|
||||
const Property = function(name, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
|
||||
this.name = name;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
Property.prototype = new Node();
|
||||
|
||||
Property.prototype.eval = function(context) {
|
||||
let property;
|
||||
const name = this.name;
|
||||
// TODO: shorten this reference
|
||||
const mergeRules = context.pluginManager.less.visitors.ToCSSVisitor.prototype._mergeRules;
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: `Recursive property reference for ${name}`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let property;
|
||||
const name = this.name;
|
||||
// TODO: shorten this reference
|
||||
const mergeRules = context.pluginManager.less.visitors.ToCSSVisitor.prototype._mergeRules;
|
||||
this.evaluating = true;
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: `Recursive property reference for ${name}`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
property = this.find(context.frames, frame => {
|
||||
let v;
|
||||
const vArr = frame.property(name);
|
||||
if (vArr) {
|
||||
for (let i = 0; i < vArr.length; i++) {
|
||||
v = vArr[i];
|
||||
|
||||
this.evaluating = true;
|
||||
|
||||
property = this.find(context.frames, frame => {
|
||||
let v;
|
||||
const vArr = frame.property(name);
|
||||
if (vArr) {
|
||||
for (let i = 0; i < vArr.length; i++) {
|
||||
v = vArr[i];
|
||||
|
||||
vArr[i] = new Declaration(v.name,
|
||||
v.value,
|
||||
v.important,
|
||||
v.merge,
|
||||
v.index,
|
||||
v.currentFileInfo,
|
||||
v.inline,
|
||||
v.variable
|
||||
);
|
||||
}
|
||||
mergeRules(vArr);
|
||||
|
||||
v = vArr[vArr.length - 1];
|
||||
if (v.important) {
|
||||
const importantScope = context.importantScope[context.importantScope.length - 1];
|
||||
importantScope.important = v.important;
|
||||
}
|
||||
v = v.value.eval(context);
|
||||
return v;
|
||||
vArr[i] = new Declaration(v.name,
|
||||
v.value,
|
||||
v.important,
|
||||
v.merge,
|
||||
v.index,
|
||||
v.currentFileInfo,
|
||||
v.inline,
|
||||
v.variable
|
||||
);
|
||||
}
|
||||
});
|
||||
if (property) {
|
||||
this.evaluating = false;
|
||||
return property;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `Property '${name}' is undefined`,
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
}
|
||||
}
|
||||
mergeRules(vArr);
|
||||
|
||||
find(obj, fun) {
|
||||
for (let i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
v = vArr[vArr.length - 1];
|
||||
if (v.important) {
|
||||
const importantScope = context.importantScope[context.importantScope.length - 1];
|
||||
importantScope.important = v.important;
|
||||
}
|
||||
v = v.value.eval(context);
|
||||
return v;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
if (property) {
|
||||
this.evaluating = false;
|
||||
return property;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `Property '${name}' is undefined`,
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Property.prototype.find = function(obj, fun) {
|
||||
for (let i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Property.prototype.type = 'Property';
|
||||
export default Property;
|
||||
|
||||
@@ -2,69 +2,66 @@ import Node from './node';
|
||||
import Variable from './variable';
|
||||
import Property from './property';
|
||||
|
||||
const Quoted = function(str, content, escaped, index, currentFileInfo) {
|
||||
this.escaped = (escaped == null) ? true : escaped;
|
||||
this.value = content || '';
|
||||
this.quote = str.charAt(0);
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.variableRegex = /@\{([\w-]+)\}/g;
|
||||
this.propRegex = /\$\{([\w-]+)\}/g;
|
||||
this.allowRoot = escaped;
|
||||
};
|
||||
|
||||
class Quoted extends Node {
|
||||
constructor(str, content, escaped, index, currentFileInfo) {
|
||||
super();
|
||||
Quoted.prototype = new Node();
|
||||
|
||||
this.escaped = (escaped == null) ? true : escaped;
|
||||
this.value = content || '';
|
||||
this.quote = str.charAt(0);
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.variableRegex = /@\{([\w-]+)\}/g;
|
||||
this.propRegex = /\$\{([\w-]+)\}/g;
|
||||
this.allowRoot = escaped;
|
||||
Quoted.prototype.genCSS = function(context, output) {
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote, this.fileInfo(), this.getIndex());
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote, this.fileInfo(), this.getIndex());
|
||||
}
|
||||
output.add(this.value);
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote);
|
||||
}
|
||||
output.add(this.value);
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote);
|
||||
}
|
||||
};
|
||||
|
||||
containsVariables() {
|
||||
return this.value.match(this.variableRegex);
|
||||
Quoted.prototype.containsVariables = function() {
|
||||
return this.value.match(this.variableRegex);
|
||||
};
|
||||
|
||||
Quoted.prototype.eval = function(context) {
|
||||
const that = this;
|
||||
let value = this.value;
|
||||
const variableReplacement = (_, name) => {
|
||||
const v = new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
const propertyReplacement = (_, name) => {
|
||||
const v = new Property(`$${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
function iterativeReplace(value, regexp, replacementFnc) {
|
||||
let evaluatedValue = value;
|
||||
do {
|
||||
value = evaluatedValue.toString();
|
||||
evaluatedValue = value.replace(regexp, replacementFnc);
|
||||
} while (value !== evaluatedValue);
|
||||
return evaluatedValue;
|
||||
}
|
||||
value = iterativeReplace(value, this.variableRegex, variableReplacement);
|
||||
value = iterativeReplace(value, this.propRegex, propertyReplacement);
|
||||
|
||||
eval(context) {
|
||||
const that = this;
|
||||
let value = this.value;
|
||||
const variableReplacement = (_, name) => {
|
||||
const v = new Variable(`@${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
const propertyReplacement = (_, name) => {
|
||||
const v = new Property(`$${name}`, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
function iterativeReplace(value, regexp, replacementFnc) {
|
||||
let evaluatedValue = value;
|
||||
do {
|
||||
value = evaluatedValue.toString();
|
||||
evaluatedValue = value.replace(regexp, replacementFnc);
|
||||
} while (value !== evaluatedValue);
|
||||
return evaluatedValue;
|
||||
}
|
||||
value = iterativeReplace(value, this.variableRegex, variableReplacement);
|
||||
value = iterativeReplace(value, this.propRegex, propertyReplacement);
|
||||
return new Quoted(this.quote + value + this.quote, value, this.escaped, this.getIndex(), this.fileInfo());
|
||||
};
|
||||
|
||||
return new Quoted(this.quote + value + this.quote, value, this.escaped, this.getIndex(), this.fileInfo());
|
||||
Quoted.prototype.compare = function(other) {
|
||||
// when comparing quoted strings allow the quote to differ
|
||||
if (other.type === 'Quoted' && !this.escaped && !other.escaped) {
|
||||
return Node.numericCompare(this.value, other.value);
|
||||
} else {
|
||||
return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
|
||||
}
|
||||
|
||||
compare(other) {
|
||||
// when comparing quoted strings allow the quote to differ
|
||||
if (other.type === 'Quoted' && !this.escaped && !other.escaped) {
|
||||
return Node.numericCompare(this.value, other.value);
|
||||
} else {
|
||||
return other.toCSS && this.toCSS() === other.toCSS() ? 0 : undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Quoted.prototype.type = 'Quoted';
|
||||
export default Quoted;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,145 +2,143 @@ import Node from './node';
|
||||
import Element from './element';
|
||||
import LessError from '../less-error';
|
||||
|
||||
class Selector extends Node {
|
||||
constructor(elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
|
||||
super();
|
||||
|
||||
this.extendList = extendList;
|
||||
this.condition = condition;
|
||||
this.evaldCondition = !condition;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.elements = this.getElements(elements);
|
||||
this.mixinElements_ = undefined;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.elements, this);
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.elements) {
|
||||
this.elements = visitor.visitArray(this.elements);
|
||||
}
|
||||
if (this.extendList) {
|
||||
this.extendList = visitor.visitArray(this.extendList);
|
||||
}
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
}
|
||||
|
||||
createDerived(elements, extendList, evaldCondition) {
|
||||
elements = this.getElements(elements);
|
||||
const newSelector = new Selector(elements, extendList || this.extendList,
|
||||
null, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
newSelector.evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
|
||||
newSelector.mediaEmpty = this.mediaEmpty;
|
||||
return newSelector;
|
||||
}
|
||||
|
||||
getElements(els) {
|
||||
if (!els) {
|
||||
return [new Element('', '&', false, this._index, this._fileInfo)];
|
||||
}
|
||||
if (typeof els === 'string') {
|
||||
this.parse.parseNode(
|
||||
els,
|
||||
['selector'],
|
||||
this._index,
|
||||
this._fileInfo,
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
throw new LessError({
|
||||
index: err.index,
|
||||
message: err.message
|
||||
}, this.parse.imports, this._fileInfo.filename);
|
||||
}
|
||||
els = result[0].elements;
|
||||
});
|
||||
}
|
||||
return els;
|
||||
}
|
||||
|
||||
createEmptySelectors() {
|
||||
const el = new Element('', '&', false, this._index, this._fileInfo);
|
||||
const sels = [new Selector([el], null, null, this._index, this._fileInfo)];
|
||||
sels[0].mediaEmpty = true;
|
||||
return sels;
|
||||
}
|
||||
|
||||
match(other) {
|
||||
const elements = this.elements;
|
||||
const len = elements.length;
|
||||
let olen;
|
||||
let i;
|
||||
|
||||
other = other.mixinElements();
|
||||
olen = other.length;
|
||||
if (olen === 0 || len < olen) {
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < olen; i++) {
|
||||
if (elements[i].value !== other[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return olen; // return number of matched elements
|
||||
}
|
||||
|
||||
mixinElements() {
|
||||
if (this.mixinElements_) {
|
||||
return this.mixinElements_;
|
||||
}
|
||||
|
||||
let elements = this.elements.map( v => v.combinator.value + (v.value.value || v.value)).join('').match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
|
||||
|
||||
if (elements) {
|
||||
if (elements[0] === '&') {
|
||||
elements.shift();
|
||||
}
|
||||
} else {
|
||||
elements = [];
|
||||
}
|
||||
|
||||
return (this.mixinElements_ = elements);
|
||||
}
|
||||
|
||||
isJustParentSelector() {
|
||||
return !this.mediaEmpty &&
|
||||
this.elements.length === 1 &&
|
||||
this.elements[0].value === '&' &&
|
||||
(this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
const evaldCondition = this.condition && this.condition.eval(context);
|
||||
let elements = this.elements;
|
||||
let extendList = this.extendList;
|
||||
|
||||
elements = elements && elements.map(e => e.eval(context));
|
||||
extendList = extendList && extendList.map(extend => extend.eval(context));
|
||||
|
||||
return this.createDerived(elements, extendList, evaldCondition);
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
let i;
|
||||
let element;
|
||||
if ((!context || !context.firstSelector) && this.elements[0].combinator.value === '') {
|
||||
output.add(' ', this.fileInfo(), this.getIndex());
|
||||
}
|
||||
for (i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(context, output);
|
||||
}
|
||||
}
|
||||
|
||||
getIsOutput() {
|
||||
return this.evaldCondition;
|
||||
}
|
||||
const Selector = function(elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
|
||||
this.extendList = extendList;
|
||||
this.condition = condition;
|
||||
this.evaldCondition = !condition;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.elements = this.getElements(elements);
|
||||
this.mixinElements_ = undefined;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.elements, this);
|
||||
}
|
||||
|
||||
Selector.prototype = new Node();
|
||||
|
||||
Selector.prototype.accept = function(visitor) {
|
||||
if (this.elements) {
|
||||
this.elements = visitor.visitArray(this.elements);
|
||||
}
|
||||
if (this.extendList) {
|
||||
this.extendList = visitor.visitArray(this.extendList);
|
||||
}
|
||||
if (this.condition) {
|
||||
this.condition = visitor.visit(this.condition);
|
||||
}
|
||||
};
|
||||
|
||||
Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
|
||||
elements = this.getElements(elements);
|
||||
const newSelector = new Selector(elements, extendList || this.extendList,
|
||||
null, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
newSelector.evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
|
||||
newSelector.mediaEmpty = this.mediaEmpty;
|
||||
return newSelector;
|
||||
};
|
||||
|
||||
Selector.prototype.getElements = function(els) {
|
||||
if (!els) {
|
||||
return [new Element('', '&', false, this._index, this._fileInfo)];
|
||||
}
|
||||
if (typeof els === 'string') {
|
||||
this.parse.parseNode(
|
||||
els,
|
||||
['selector'],
|
||||
this._index,
|
||||
this._fileInfo,
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
throw new LessError({
|
||||
index: err.index,
|
||||
message: err.message
|
||||
}, this.parse.imports, this._fileInfo.filename);
|
||||
}
|
||||
els = result[0].elements;
|
||||
});
|
||||
}
|
||||
return els;
|
||||
};
|
||||
|
||||
Selector.prototype.createEmptySelectors = function() {
|
||||
const el = new Element('', '&', false, this._index, this._fileInfo);
|
||||
const sels = [new Selector([el], null, null, this._index, this._fileInfo)];
|
||||
sels[0].mediaEmpty = true;
|
||||
return sels;
|
||||
};
|
||||
|
||||
Selector.prototype.match = function(other) {
|
||||
const elements = this.elements;
|
||||
const len = elements.length;
|
||||
let olen;
|
||||
let i;
|
||||
|
||||
other = other.mixinElements();
|
||||
olen = other.length;
|
||||
if (olen === 0 || len < olen) {
|
||||
return 0;
|
||||
} else {
|
||||
for (i = 0; i < olen; i++) {
|
||||
if (elements[i].value !== other[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return olen; // return number of matched elements
|
||||
};
|
||||
|
||||
Selector.prototype.mixinElements = function() {
|
||||
if (this.mixinElements_) {
|
||||
return this.mixinElements_;
|
||||
}
|
||||
|
||||
let elements = this.elements.map( v => v.combinator.value + (v.value.value || v.value)).join('').match(/[,&#\*\.\w-]([\w-]|(\\.))*/g);
|
||||
|
||||
if (elements) {
|
||||
if (elements[0] === '&') {
|
||||
elements.shift();
|
||||
}
|
||||
} else {
|
||||
elements = [];
|
||||
}
|
||||
|
||||
return (this.mixinElements_ = elements);
|
||||
};
|
||||
|
||||
Selector.prototype.isJustParentSelector = function() {
|
||||
return !this.mediaEmpty &&
|
||||
this.elements.length === 1 &&
|
||||
this.elements[0].value === '&' &&
|
||||
(this.elements[0].combinator.value === ' ' || this.elements[0].combinator.value === '');
|
||||
};
|
||||
|
||||
Selector.prototype.eval = function(context) {
|
||||
const evaldCondition = this.condition && this.condition.eval(context);
|
||||
let elements = this.elements;
|
||||
let extendList = this.extendList;
|
||||
|
||||
elements = elements && elements.map(e => e.eval(context));
|
||||
extendList = extendList && extendList.map(extend => extend.eval(context));
|
||||
|
||||
return this.createDerived(elements, extendList, evaldCondition);
|
||||
};
|
||||
|
||||
Selector.prototype.genCSS = function(context, output) {
|
||||
let i;
|
||||
let element;
|
||||
if ((!context || !context.firstSelector) && this.elements[0].combinator.value === '') {
|
||||
output.add(' ', this.fileInfo(), this.getIndex());
|
||||
}
|
||||
for (i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(context, output);
|
||||
}
|
||||
};
|
||||
|
||||
Selector.prototype.getIsOutput = function() {
|
||||
return this.evaldCondition;
|
||||
};
|
||||
|
||||
Selector.prototype.type = 'Selector';
|
||||
export default Selector;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import Node from './node';
|
||||
|
||||
class UnicodeDescriptor extends Node {
|
||||
constructor(value) {
|
||||
super();
|
||||
|
||||
this.value = value;
|
||||
}
|
||||
const UnicodeDescriptor = function(value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
UnicodeDescriptor.prototype = new Node();
|
||||
UnicodeDescriptor.prototype.type = 'UnicodeDescriptor';
|
||||
|
||||
export default UnicodeDescriptor;
|
||||
|
||||
@@ -2,140 +2,138 @@ import Node from './node';
|
||||
import unitConversions from '../data/unit-conversions';
|
||||
import * as utils from '../utils';
|
||||
|
||||
class Unit extends Node {
|
||||
constructor(numerator, denominator, backupUnit) {
|
||||
super();
|
||||
const Unit = function(numerator, denominator, backupUnit) {
|
||||
this.numerator = numerator ? utils.copyArray(numerator).sort() : [];
|
||||
this.denominator = denominator ? utils.copyArray(denominator).sort() : [];
|
||||
if (backupUnit) {
|
||||
this.backupUnit = backupUnit;
|
||||
} else if (numerator && numerator.length) {
|
||||
this.backupUnit = numerator[0];
|
||||
}
|
||||
};
|
||||
|
||||
this.numerator = numerator ? utils.copyArray(numerator).sort() : [];
|
||||
this.denominator = denominator ? utils.copyArray(denominator).sort() : [];
|
||||
if (backupUnit) {
|
||||
this.backupUnit = backupUnit;
|
||||
} else if (numerator && numerator.length) {
|
||||
this.backupUnit = numerator[0];
|
||||
Unit.prototype = new Node();
|
||||
|
||||
Unit.prototype.clone = function() {
|
||||
return new Unit(utils.copyArray(this.numerator), utils.copyArray(this.denominator), this.backupUnit);
|
||||
};
|
||||
|
||||
Unit.prototype.genCSS = function(context, output) {
|
||||
// Dimension checks the unit is singular and throws an error if in strict math mode.
|
||||
const strictUnits = context && context.strictUnits;
|
||||
if (this.numerator.length === 1) {
|
||||
output.add(this.numerator[0]); // the ideal situation
|
||||
} else if (!strictUnits && this.backupUnit) {
|
||||
output.add(this.backupUnit);
|
||||
} else if (!strictUnits && this.denominator.length) {
|
||||
output.add(this.denominator[0]);
|
||||
}
|
||||
};
|
||||
|
||||
Unit.prototype.toString = function() {
|
||||
let i;
|
||||
let returnStr = this.numerator.join('*');
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
returnStr += `/${this.denominator[i]}`;
|
||||
}
|
||||
return returnStr;
|
||||
};
|
||||
|
||||
Unit.prototype.compare = function(other) {
|
||||
return this.is(other.toString()) ? 0 : undefined;
|
||||
};
|
||||
|
||||
Unit.prototype.is = function(unitString) {
|
||||
return this.toString().toUpperCase() === unitString.toUpperCase();
|
||||
};
|
||||
|
||||
Unit.prototype.isLength = function() {
|
||||
return RegExp('^(px|em|ex|ch|rem|in|cm|mm|pc|pt|ex|vw|vh|vmin|vmax)$', 'gi').test(this.toCSS());
|
||||
};
|
||||
|
||||
Unit.prototype.isEmpty = function() {
|
||||
return this.numerator.length === 0 && this.denominator.length === 0;
|
||||
};
|
||||
|
||||
Unit.prototype.isSingular = function() {
|
||||
return this.numerator.length <= 1 && this.denominator.length === 0;
|
||||
};
|
||||
|
||||
Unit.prototype.map = function(callback) {
|
||||
let i;
|
||||
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
this.numerator[i] = callback(this.numerator[i], false);
|
||||
}
|
||||
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
this.denominator[i] = callback(this.denominator[i], true);
|
||||
}
|
||||
};
|
||||
|
||||
Unit.prototype.usedUnits = function() {
|
||||
let group;
|
||||
const result = {};
|
||||
let mapUnit;
|
||||
let groupName;
|
||||
|
||||
mapUnit = atomicUnit => {
|
||||
/* jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
|
||||
result[groupName] = atomicUnit;
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in unitConversions) {
|
||||
if (unitConversions.hasOwnProperty(groupName)) {
|
||||
group = unitConversions[groupName];
|
||||
|
||||
this.map(mapUnit);
|
||||
}
|
||||
}
|
||||
|
||||
clone() {
|
||||
return new Unit(utils.copyArray(this.numerator), utils.copyArray(this.denominator), this.backupUnit);
|
||||
return result;
|
||||
};
|
||||
|
||||
Unit.prototype.cancel = function() {
|
||||
const counter = {};
|
||||
let atomicUnit;
|
||||
let i;
|
||||
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
atomicUnit = this.numerator[i];
|
||||
counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
// Dimension checks the unit is singular and throws an error if in strict math mode.
|
||||
const strictUnits = context && context.strictUnits;
|
||||
if (this.numerator.length === 1) {
|
||||
output.add(this.numerator[0]); // the ideal situation
|
||||
} else if (!strictUnits && this.backupUnit) {
|
||||
output.add(this.backupUnit);
|
||||
} else if (!strictUnits && this.denominator.length) {
|
||||
output.add(this.denominator[0]);
|
||||
}
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
atomicUnit = this.denominator[i];
|
||||
counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
|
||||
}
|
||||
|
||||
toString() {
|
||||
let i;
|
||||
let returnStr = this.numerator.join('*');
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
returnStr += `/${this.denominator[i]}`;
|
||||
}
|
||||
return returnStr;
|
||||
}
|
||||
this.numerator = [];
|
||||
this.denominator = [];
|
||||
|
||||
compare(other) {
|
||||
return this.is(other.toString()) ? 0 : undefined;
|
||||
}
|
||||
for (atomicUnit in counter) {
|
||||
if (counter.hasOwnProperty(atomicUnit)) {
|
||||
const count = counter[atomicUnit];
|
||||
|
||||
is(unitString) {
|
||||
return this.toString().toUpperCase() === unitString.toUpperCase();
|
||||
}
|
||||
|
||||
isLength() {
|
||||
return RegExp('^(px|em|ex|ch|rem|in|cm|mm|pc|pt|ex|vw|vh|vmin|vmax)$', 'gi').test(this.toCSS());
|
||||
}
|
||||
|
||||
isEmpty() {
|
||||
return this.numerator.length === 0 && this.denominator.length === 0;
|
||||
}
|
||||
|
||||
isSingular() {
|
||||
return this.numerator.length <= 1 && this.denominator.length === 0;
|
||||
}
|
||||
|
||||
map(callback) {
|
||||
let i;
|
||||
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
this.numerator[i] = callback(this.numerator[i], false);
|
||||
}
|
||||
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
this.denominator[i] = callback(this.denominator[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
usedUnits() {
|
||||
let group;
|
||||
const result = {};
|
||||
let mapUnit;
|
||||
let groupName;
|
||||
|
||||
mapUnit = atomicUnit => {
|
||||
/* jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
|
||||
result[groupName] = atomicUnit;
|
||||
}
|
||||
|
||||
return atomicUnit;
|
||||
};
|
||||
|
||||
for (groupName in unitConversions) {
|
||||
if (unitConversions.hasOwnProperty(groupName)) {
|
||||
group = unitConversions[groupName];
|
||||
|
||||
this.map(mapUnit);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
cancel() {
|
||||
const counter = {};
|
||||
let atomicUnit;
|
||||
let i;
|
||||
|
||||
for (i = 0; i < this.numerator.length; i++) {
|
||||
atomicUnit = this.numerator[i];
|
||||
counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < this.denominator.length; i++) {
|
||||
atomicUnit = this.denominator[i];
|
||||
counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
|
||||
}
|
||||
|
||||
this.numerator = [];
|
||||
this.denominator = [];
|
||||
|
||||
for (atomicUnit in counter) {
|
||||
if (counter.hasOwnProperty(atomicUnit)) {
|
||||
const count = counter[atomicUnit];
|
||||
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
this.numerator.push(atomicUnit);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
for (i = 0; i < -count; i++) {
|
||||
this.denominator.push(atomicUnit);
|
||||
}
|
||||
if (count > 0) {
|
||||
for (i = 0; i < count; i++) {
|
||||
this.numerator.push(atomicUnit);
|
||||
}
|
||||
} else if (count < 0) {
|
||||
for (i = 0; i < -count; i++) {
|
||||
this.denominator.push(atomicUnit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.numerator.sort();
|
||||
this.denominator.sort();
|
||||
}
|
||||
}
|
||||
|
||||
this.numerator.sort();
|
||||
this.denominator.sort();
|
||||
};
|
||||
|
||||
Unit.prototype.type = 'Unit';
|
||||
export default Unit;
|
||||
|
||||
@@ -1,60 +1,58 @@
|
||||
import Node from './node';
|
||||
|
||||
class URL extends Node {
|
||||
constructor(val, index, currentFileInfo, isEvald) {
|
||||
super();
|
||||
const URL = function(val, index, currentFileInfo, isEvald) {
|
||||
this.value = val;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.isEvald = isEvald;
|
||||
};
|
||||
|
||||
this.value = val;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.isEvald = isEvald;
|
||||
}
|
||||
URL.prototype = new Node();
|
||||
|
||||
accept(visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
}
|
||||
URL.prototype.accept = function(visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
|
||||
genCSS(context, output) {
|
||||
output.add('url(');
|
||||
this.value.genCSS(context, output);
|
||||
output.add(')');
|
||||
}
|
||||
URL.prototype.genCSS = function(context, output) {
|
||||
output.add('url(');
|
||||
this.value.genCSS(context, output);
|
||||
output.add(')');
|
||||
};
|
||||
|
||||
eval(context) {
|
||||
const val = this.value.eval(context);
|
||||
let rootpath;
|
||||
URL.prototype.eval = function(context) {
|
||||
const val = this.value.eval(context);
|
||||
let rootpath;
|
||||
|
||||
if (!this.isEvald) {
|
||||
// Add the rootpath if the URL requires a rewrite
|
||||
rootpath = this.fileInfo() && this.fileInfo().rootpath;
|
||||
if (typeof rootpath === 'string' &&
|
||||
typeof val.value === 'string' &&
|
||||
context.pathRequiresRewrite(val.value)) {
|
||||
if (!val.quote) {
|
||||
rootpath = escapePath(rootpath);
|
||||
}
|
||||
val.value = context.rewritePath(val.value, rootpath);
|
||||
} else {
|
||||
val.value = context.normalizePath(val.value);
|
||||
if (!this.isEvald) {
|
||||
// Add the rootpath if the URL requires a rewrite
|
||||
rootpath = this.fileInfo() && this.fileInfo().rootpath;
|
||||
if (typeof rootpath === 'string' &&
|
||||
typeof val.value === 'string' &&
|
||||
context.pathRequiresRewrite(val.value)) {
|
||||
if (!val.quote) {
|
||||
rootpath = escapePath(rootpath);
|
||||
}
|
||||
val.value = context.rewritePath(val.value, rootpath);
|
||||
} else {
|
||||
val.value = context.normalizePath(val.value);
|
||||
}
|
||||
|
||||
// Add url args if enabled
|
||||
if (context.urlArgs) {
|
||||
if (!val.value.match(/^\s*data:/)) {
|
||||
const delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
|
||||
const urlArgs = delimiter + context.urlArgs;
|
||||
if (val.value.indexOf('#') !== -1) {
|
||||
val.value = val.value.replace('#', `${urlArgs}#`);
|
||||
} else {
|
||||
val.value += urlArgs;
|
||||
}
|
||||
// Add url args if enabled
|
||||
if (context.urlArgs) {
|
||||
if (!val.value.match(/^\s*data:/)) {
|
||||
const delimiter = val.value.indexOf('?') === -1 ? '?' : '&';
|
||||
const urlArgs = delimiter + context.urlArgs;
|
||||
if (val.value.indexOf('#') !== -1) {
|
||||
val.value = val.value.replace('#', `${urlArgs}#`);
|
||||
} else {
|
||||
val.value += urlArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new URL(val, this.getIndex(), this.fileInfo(), true);
|
||||
}
|
||||
}
|
||||
|
||||
return new URL(val, this.getIndex(), this.fileInfo(), true);
|
||||
};
|
||||
|
||||
URL.prototype.type = 'Url';
|
||||
|
||||
|
||||
@@ -1,44 +1,42 @@
|
||||
import Node from './node';
|
||||
|
||||
class Value extends Node {
|
||||
constructor(value) {
|
||||
super();
|
||||
const Value = function(value) {
|
||||
if (!value) {
|
||||
throw new Error('Value requires an array argument');
|
||||
}
|
||||
if (!Array.isArray(value)) {
|
||||
this.value = [ value ];
|
||||
}
|
||||
else {
|
||||
this.value = value;
|
||||
}
|
||||
};
|
||||
|
||||
if (!value) {
|
||||
throw new Error('Value requires an array argument');
|
||||
}
|
||||
if (!Array.isArray(value)) {
|
||||
this.value = [ value ];
|
||||
}
|
||||
else {
|
||||
this.value = value;
|
||||
Value.prototype = new Node();
|
||||
|
||||
Value.prototype.accept = function(visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
};
|
||||
|
||||
Value.prototype.eval = function(context) {
|
||||
if (this.value.length === 1) {
|
||||
return this.value[0].eval(context);
|
||||
} else {
|
||||
return new Value(this.value.map(v => v.eval(context)));
|
||||
}
|
||||
};
|
||||
|
||||
Value.prototype.genCSS = function(context, output) {
|
||||
let i;
|
||||
for (i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(context, output);
|
||||
if (i + 1 < this.value.length) {
|
||||
output.add((context && context.compress) ? ',' : ', ');
|
||||
}
|
||||
}
|
||||
|
||||
accept(visitor) {
|
||||
if (this.value) {
|
||||
this.value = visitor.visitArray(this.value);
|
||||
}
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
if (this.value.length === 1) {
|
||||
return this.value[0].eval(context);
|
||||
} else {
|
||||
return new Value(this.value.map(v => v.eval(context)));
|
||||
}
|
||||
}
|
||||
|
||||
genCSS(context, output) {
|
||||
let i;
|
||||
for (i = 0; i < this.value.length; i++) {
|
||||
this.value[i].genCSS(context, output);
|
||||
if (i + 1 < this.value.length) {
|
||||
output.add((context && context.compress) ? ',' : ', ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Value.prototype.type = 'Value';
|
||||
export default Value;
|
||||
|
||||
@@ -4,43 +4,41 @@ import Ruleset from './ruleset';
|
||||
import DetachedRuleset from './detached-ruleset';
|
||||
import LessError from '../less-error';
|
||||
|
||||
class VariableCall extends Node {
|
||||
constructor(variable, index, currentFileInfo) {
|
||||
super();
|
||||
const VariableCall = function(variable, index, currentFileInfo) {
|
||||
this.variable = variable;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
};
|
||||
|
||||
this.variable = variable;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
VariableCall.prototype = new Node();
|
||||
|
||||
VariableCall.prototype.eval = function(context) {
|
||||
let rules;
|
||||
let detachedRuleset = new Variable(this.variable, this.getIndex(), this.fileInfo()).eval(context);
|
||||
const error = new LessError({message: `Could not evaluate variable call ${this.variable}`});
|
||||
|
||||
if (!detachedRuleset.ruleset) {
|
||||
if (detachedRuleset.rules) {
|
||||
rules = detachedRuleset;
|
||||
}
|
||||
else if (Array.isArray(detachedRuleset)) {
|
||||
rules = new Ruleset('', detachedRuleset);
|
||||
}
|
||||
else if (Array.isArray(detachedRuleset.value)) {
|
||||
rules = new Ruleset('', detachedRuleset.value);
|
||||
}
|
||||
else {
|
||||
throw error;
|
||||
}
|
||||
detachedRuleset = new DetachedRuleset(rules);
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let rules;
|
||||
let detachedRuleset = new Variable(this.variable, this.getIndex(), this.fileInfo()).eval(context);
|
||||
const error = new LessError({message: `Could not evaluate variable call ${this.variable}`});
|
||||
|
||||
if (!detachedRuleset.ruleset) {
|
||||
if (detachedRuleset.rules) {
|
||||
rules = detachedRuleset;
|
||||
}
|
||||
else if (Array.isArray(detachedRuleset)) {
|
||||
rules = new Ruleset('', detachedRuleset);
|
||||
}
|
||||
else if (Array.isArray(detachedRuleset.value)) {
|
||||
rules = new Ruleset('', detachedRuleset.value);
|
||||
}
|
||||
else {
|
||||
throw error;
|
||||
}
|
||||
detachedRuleset = new DetachedRuleset(rules);
|
||||
}
|
||||
|
||||
if (detachedRuleset.ruleset) {
|
||||
return detachedRuleset.callEval(context);
|
||||
}
|
||||
throw error;
|
||||
if (detachedRuleset.ruleset) {
|
||||
return detachedRuleset.callEval(context);
|
||||
}
|
||||
}
|
||||
throw error;
|
||||
};
|
||||
|
||||
VariableCall.prototype.type = 'VariableCall';
|
||||
export default VariableCall;
|
||||
|
||||
@@ -1,67 +1,65 @@
|
||||
import Node from './node';
|
||||
import Call from './call';
|
||||
|
||||
class Variable extends Node {
|
||||
constructor(name, index, currentFileInfo) {
|
||||
super();
|
||||
const Variable = function(name, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
|
||||
this.name = name;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
Variable.prototype = new Node();
|
||||
|
||||
Variable.prototype.eval = function(context) {
|
||||
let variable;
|
||||
let name = this.name;
|
||||
|
||||
if (name.indexOf('@@') === 0) {
|
||||
name = `@${new Variable(name.slice(1), this.getIndex(), this.fileInfo()).eval(context).value}`;
|
||||
}
|
||||
|
||||
eval(context) {
|
||||
let variable;
|
||||
let name = this.name;
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: `Recursive variable definition for ${name}`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
if (name.indexOf('@@') === 0) {
|
||||
name = `@${new Variable(name.slice(1), this.getIndex(), this.fileInfo()).eval(context).value}`;
|
||||
}
|
||||
this.evaluating = true;
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: `Recursive variable definition for ${name}`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
this.evaluating = true;
|
||||
|
||||
variable = this.find(context.frames, frame => {
|
||||
const v = frame.variable(name);
|
||||
if (v) {
|
||||
if (v.important) {
|
||||
const importantScope = context.importantScope[context.importantScope.length - 1];
|
||||
importantScope.important = v.important;
|
||||
}
|
||||
// If in calc, wrap vars in a function call to cascade evaluate args first
|
||||
if (context.inCalc) {
|
||||
return (new Call('_SELF', [v.value])).eval(context);
|
||||
}
|
||||
else {
|
||||
return v.value.eval(context);
|
||||
}
|
||||
variable = this.find(context.frames, frame => {
|
||||
const v = frame.variable(name);
|
||||
if (v) {
|
||||
if (v.important) {
|
||||
const importantScope = context.importantScope[context.importantScope.length - 1];
|
||||
importantScope.important = v.important;
|
||||
}
|
||||
// If in calc, wrap vars in a function call to cascade evaluate args first
|
||||
if (context.inCalc) {
|
||||
return (new Call('_SELF', [v.value])).eval(context);
|
||||
}
|
||||
else {
|
||||
return v.value.eval(context);
|
||||
}
|
||||
});
|
||||
if (variable) {
|
||||
this.evaluating = false;
|
||||
return variable;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `variable ${name} is undefined`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
});
|
||||
if (variable) {
|
||||
this.evaluating = false;
|
||||
return variable;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: `variable ${name} is undefined`,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
};
|
||||
|
||||
find(obj, fun) {
|
||||
for (let i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
return null;
|
||||
Variable.prototype.find = function(obj, fun) {
|
||||
for (let i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Variable.prototype.type = 'Variable';
|
||||
export default Variable;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"version": "3.12.2",
|
||||
"version": "3.13.0",
|
||||
"description": "Less files and CSS results",
|
||||
"author": {
|
||||
"name": "Alexis Sellier",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@less/test-import-module",
|
||||
"private": true,
|
||||
"version": "3.12.0",
|
||||
"version": "3.13.0",
|
||||
"description": "Less files to be included in node_modules directory for testing import from node_modules",
|
||||
"author": {
|
||||
"name": "Alexis Sellier",
|
||||
|
||||
Reference in New Issue
Block a user