mirror of
https://github.com/less/less.js.git
synced 2026-05-01 03:00:22 -04:00
Merge branch '3.x'
This commit is contained in:
88
.eslintrc.json
Normal file
88
.eslintrc.json
Normal file
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"globals": {},
|
||||
"rules": {
|
||||
"no-eval": 2,
|
||||
"no-use-before-define": [
|
||||
2,
|
||||
{
|
||||
"functions": false
|
||||
}
|
||||
],
|
||||
"no-undef": 0,
|
||||
"no-unused-vars": 1,
|
||||
"no-caller": 2,
|
||||
"no-eq-null": 1,
|
||||
"guard-for-in": 2,
|
||||
"no-implicit-coercion": [
|
||||
2,
|
||||
{
|
||||
"boolean": false,
|
||||
"string": true,
|
||||
"number": true
|
||||
}
|
||||
],
|
||||
"no-with": 2,
|
||||
"no-mixed-spaces-and-tabs": 2,
|
||||
"no-multiple-empty-lines": 2,
|
||||
"dot-location": [
|
||||
2,
|
||||
"property"
|
||||
],
|
||||
"operator-linebreak": [
|
||||
0,
|
||||
"after"
|
||||
],
|
||||
"keyword-spacing": [
|
||||
2,
|
||||
{}
|
||||
],
|
||||
"space-unary-ops": [
|
||||
2,
|
||||
{
|
||||
"words": false,
|
||||
"nonwords": false
|
||||
}
|
||||
],
|
||||
"no-spaced-func": 2,
|
||||
"space-before-function-paren": [
|
||||
1,
|
||||
{
|
||||
"anonymous": "ignore",
|
||||
"named": "never"
|
||||
}
|
||||
],
|
||||
"comma-dangle": [
|
||||
2,
|
||||
"never"
|
||||
],
|
||||
"no-trailing-spaces": 0,
|
||||
"max-len": [
|
||||
2,
|
||||
160
|
||||
],
|
||||
"comma-style": [
|
||||
2,
|
||||
"last"
|
||||
],
|
||||
"curly": [
|
||||
2,
|
||||
"all"
|
||||
],
|
||||
"space-infix-ops": 2,
|
||||
"spaced-comment": 1,
|
||||
"space-before-blocks": [
|
||||
2,
|
||||
"always"
|
||||
],
|
||||
"indent": [
|
||||
2,
|
||||
4,
|
||||
{
|
||||
"SwitchCase": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
17
.github/stale.yml
vendored
Normal file
17
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 120
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 14
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- up-for-grabs
|
||||
- bug
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
73
.jscsrc
73
.jscsrc
@@ -1,73 +0,0 @@
|
||||
{
|
||||
"disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"],
|
||||
"disallowKeywords": ["with"],
|
||||
"disallowMixedSpacesAndTabs": true,
|
||||
"disallowMultipleLineBreaks": true,
|
||||
"disallowOperatorBeforeLineBreak": ["."],
|
||||
"disallowSpaceAfterKeywords": [
|
||||
"void"//,
|
||||
//"typeof"
|
||||
],
|
||||
"disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~"],
|
||||
"disallowSpaceBeforePostfixUnaryOperators": ["++", "--"],
|
||||
"disallowSpacesInCallExpression": true,
|
||||
"disallowSpacesInNamedFunctionExpression": {
|
||||
"beforeOpeningRoundBrace": true},
|
||||
"disallowTrailingComma": true,
|
||||
"disallowTrailingWhitespace": true,
|
||||
"maximumLineLength": 160,
|
||||
"requireCommaBeforeLineBreak": true,
|
||||
"requireCurlyBraces": [ "if",
|
||||
"else",
|
||||
"for",
|
||||
"while",
|
||||
"do",
|
||||
"try",
|
||||
"catch"],
|
||||
"requireOperatorBeforeLineBreak": [ "?",
|
||||
"=",
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"*",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!==",
|
||||
">",
|
||||
">=",
|
||||
"<",
|
||||
"<="],
|
||||
"requireSpaceAfterBinaryOperators": true,
|
||||
"requireSpaceAfterKeywords": [
|
||||
"else",
|
||||
"case",
|
||||
"try",
|
||||
"typeof",
|
||||
"return",
|
||||
"if",
|
||||
"for",
|
||||
"while",
|
||||
"do"
|
||||
],
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireSpaceBeforeBinaryOperators": [
|
||||
"=",
|
||||
"+",
|
||||
"-",
|
||||
"/",
|
||||
"*",
|
||||
"==",
|
||||
"===",
|
||||
"!=",
|
||||
"!=="
|
||||
],
|
||||
"requireSpaceBeforeBlockStatements": true,
|
||||
"requireSpaceBetweenArguments": true,
|
||||
"requireSpacesInConditionalExpression": true,
|
||||
"requireSpacesInForStatement": true,
|
||||
"requireSpacesInNamedFunctionExpression": {
|
||||
"beforeOpeningCurlyBrace": true
|
||||
},
|
||||
"validateIndentation": 4
|
||||
}
|
||||
11
.jshintrc
11
.jshintrc
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"evil": true,
|
||||
"latedef": true,
|
||||
"node": true,
|
||||
"undef": true,
|
||||
"unused": "vars",
|
||||
"noarg": true,
|
||||
"eqnull": true,
|
||||
"forin": true,
|
||||
"predef": ["Promise"]
|
||||
}
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,12 +1,31 @@
|
||||
language: node_js
|
||||
cache:
|
||||
directories:
|
||||
- travis-phantomjs
|
||||
node_js:
|
||||
- "9"
|
||||
- "8"
|
||||
- "6"
|
||||
- "4"
|
||||
- "0.12"
|
||||
- "0.10"
|
||||
before_install:
|
||||
# from https://github.com/travis-ci/travis-ci/issues/3225#issuecomment-177592725
|
||||
# and also from https://github.com/travis-ci/travis-ci/issues/3225#issuecomment-200965782
|
||||
- phantomjs --version
|
||||
- export PATH=$PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64/bin:$PATH
|
||||
- phantomjs --version
|
||||
# Clear cache and download new copy of PhantomJS if the current version doesn't match 2.1.1.
|
||||
- "if [ $(phantomjs --version) != '2.1.1' ]; then rm -rf $PWD/travis-phantomjs; mkdir -p $PWD/travis-phantomjs; fi"
|
||||
- "if [ $(phantomjs --version) != '2.1.1' ]; then wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2; fi"
|
||||
- "if [ $(phantomjs --version) != '2.1.1' ]; then tar -xvf $PWD/travis-phantomjs/phantomjs-2.1.1-linux-x86_64.tar.bz2 -C $PWD/travis-phantomjs; fi"
|
||||
- phantomjs --version
|
||||
install:
|
||||
- npm install -g grunt-cli
|
||||
- npm install
|
||||
# node 0.10 & 0.12 have race condition issues when running custom install scripts
|
||||
# this can cause phantomjs-prebuilt install script to fail with the error:
|
||||
# <Cannot find module 'boom'>
|
||||
# Seems related to: https://github.com/npm/npm/issues/8152
|
||||
# using <travis_retry> solves this.
|
||||
- travis_retry npm install
|
||||
env:
|
||||
global:
|
||||
- PHANTOMJS_CDNURL=http://cnpmjs.org/downloads
|
||||
|
||||
25
CHANGELOG.md
25
CHANGELOG.md
@@ -1,8 +1,29 @@
|
||||
# 2.7.2
|
||||
# 3.0.0
|
||||
2018-02-04
|
||||
- Fix `calc()` function to not do math operations on compile
|
||||
- Rename Directive -> AtRule & Rule -> Declaration
|
||||
- Cross-platform `@plugin` loading! (Node & Browser)
|
||||
- Numerous changes / improvements to plugin architecture
|
||||
- Simplified API calls in plugins (`less.atrule()` vs `new less.tree.AtRule()`)
|
||||
- Property accessors (`$width` to refer to `width: 300px` value)
|
||||
- Inline JavaScript disabled by default for security reasons (use `@plugin`)
|
||||
- Improvements in Less error reporting
|
||||
- Added feature: returning `null` / `false` from Less functions will remove that line
|
||||
- Simple `boolean()` and `if()` functions added
|
||||
- Bug fixes
|
||||
- Removal of unnecessary nodes from API (like IE's `alpha()`)
|
||||
|
||||
# 2.7.3
|
||||
2017-10-23
|
||||
|
||||
- Bump `request` dependency
|
||||
|
||||
# 2.7.2
|
||||
2017-01-04
|
||||
|
||||
- Revert changes to contrast() function in 2.7.0
|
||||
- Revert breaking changes to contrast() function
|
||||
- Fix error reporting of lessc executable
|
||||
- Changed octals to hex for ES6 strict mode
|
||||
|
||||
# 2.7.1 HOTFIX
|
||||
|
||||
|
||||
281
Gruntfile.js
281
Gruntfile.js
@@ -2,10 +2,154 @@
|
||||
|
||||
module.exports = function (grunt) {
|
||||
|
||||
|
||||
grunt.option('stack', true)
|
||||
|
||||
// Report the elapsed execution time of tasks.
|
||||
require('time-grunt')(grunt);
|
||||
|
||||
var COMPRESS_FOR_TESTS = true;
|
||||
var COMPRESS_FOR_TESTS = false;
|
||||
var git = require('git-rev');
|
||||
|
||||
// Sauce Labs browser
|
||||
var browsers = [
|
||||
// Desktop browsers
|
||||
{
|
||||
browserName: "chrome",
|
||||
version: 'latest',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
{
|
||||
browserName: "firefox",
|
||||
version: 'latest',
|
||||
platform: 'Linux'
|
||||
},
|
||||
{
|
||||
browserName: 'safari',
|
||||
version: '9',
|
||||
platform: 'OS X 10.11'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '8',
|
||||
platform: 'Windows XP'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '11',
|
||||
platform: 'Windows 8.1'
|
||||
},
|
||||
{
|
||||
browserName: "edge",
|
||||
version: '13',
|
||||
platform: 'Windows 10'
|
||||
},
|
||||
// Mobile browsers
|
||||
{
|
||||
browserName: 'ipad',
|
||||
deviceName: 'iPad Air Simulator',
|
||||
deviceOrientation: 'portrait',
|
||||
version: '8.4',
|
||||
platform: 'OS X 10.9'
|
||||
},
|
||||
{
|
||||
browserName: 'iphone',
|
||||
deviceName: 'iPhone 5 Simulator',
|
||||
deviceOrientation: 'portrait',
|
||||
version: '9.3',
|
||||
platform: 'OS X 10.11'
|
||||
},
|
||||
{
|
||||
browserName: 'android',
|
||||
deviceName: 'Google Nexus 7 HD Emulator',
|
||||
deviceOrientation: 'portrait',
|
||||
version: '4.4',
|
||||
platform: 'Linux'
|
||||
}
|
||||
];
|
||||
|
||||
var sauceJobs = {};
|
||||
|
||||
var browserTests = [ "filemanager-plugin",
|
||||
"visitor-plugin",
|
||||
"global-vars",
|
||||
"modify-vars",
|
||||
"production",
|
||||
"rootpath-relative",
|
||||
"rootpath",
|
||||
"relative-urls",
|
||||
"browser",
|
||||
"no-js-errors",
|
||||
"legacy"
|
||||
];
|
||||
|
||||
function makeJob(testName) {
|
||||
sauceJobs[testName] = {
|
||||
options: {
|
||||
urls: testName === 'all' ?
|
||||
browserTests.map(function(name) {
|
||||
return "http://localhost:8081/tmp/browser/test-runner-" + name + ".html";
|
||||
}) :
|
||||
["http://localhost:8081/tmp/browser/test-runner-" + testName + ".html"],
|
||||
testname: testName === 'all' ? 'Unit Tests for Less.js' : testName,
|
||||
browsers: browsers,
|
||||
public: 'public',
|
||||
recordVideo: false,
|
||||
videoUploadOnPass: false,
|
||||
recordScreenshots: process.env.TRAVIS_BRANCH !== "master",
|
||||
build: process.env.TRAVIS_BRANCH === "master" ? process.env.TRAVIS_JOB_ID : undefined,
|
||||
tags: [process.env.TRAVIS_BUILD_NUMBER, process.env.TRAVIS_PULL_REQUEST, process.env.TRAVIS_BRANCH],
|
||||
statusCheckAttempts: -1,
|
||||
sauceConfig: {
|
||||
'idle-timeout': 100
|
||||
},
|
||||
throttled: 5,
|
||||
onTestComplete: function(result, callback) {
|
||||
// Called after a unit test is done, per page, per browser
|
||||
// 'result' param is the object returned by the test framework's reporter
|
||||
// 'callback' is a Node.js style callback function. You must invoke it after you
|
||||
// finish your work.
|
||||
// Pass a non-null value as the callback's first parameter if you want to throw an
|
||||
// exception. If your function is synchronous you can also throw exceptions
|
||||
// directly.
|
||||
// Passing true or false as the callback's second parameter passes or fails the
|
||||
// test. Passing undefined does not alter the test result. Please note that this
|
||||
// only affects the grunt task's result. You have to explicitly update the Sauce
|
||||
// Labs job's status via its REST API, if you want so.
|
||||
|
||||
// This should be the encrypted value in Travis
|
||||
var user = process.env.SAUCE_USERNAME;
|
||||
var pass = process.env.SAUCE_ACCESS_KEY;
|
||||
|
||||
git.short(function(hash) {
|
||||
require('phin')({
|
||||
method: 'PUT',
|
||||
url: ['https://saucelabs.com/rest/v1', user, 'jobs', result.job_id].join('/'),
|
||||
auth: { user: user, pass: pass },
|
||||
data: {
|
||||
passed: result.passed,
|
||||
build: 'build-' + hash
|
||||
}
|
||||
}, function (error, response) {
|
||||
if (error) {
|
||||
console.log(error);
|
||||
callback(error);
|
||||
} else if (response.statusCode !== 200) {
|
||||
console.log(response);
|
||||
callback(new Error('Unexpected response status'));
|
||||
} else {
|
||||
callback(null, result.passed);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Make the SauceLabs jobs
|
||||
(['all'].concat(browserTests)).map(makeJob);
|
||||
|
||||
// Project configuration.
|
||||
grunt.initConfig({
|
||||
@@ -29,9 +173,15 @@ module.exports = function (grunt) {
|
||||
},
|
||||
|
||||
shell: {
|
||||
options: {stdout: true, failOnError: true},
|
||||
options: {
|
||||
stdout: true,
|
||||
failOnError: true,
|
||||
execOptions: {
|
||||
maxBuffer: Infinity
|
||||
}
|
||||
},
|
||||
test: {
|
||||
command: 'node test'
|
||||
command: 'node test/index.js'
|
||||
},
|
||||
benchmark: {
|
||||
command: 'node benchmark/index.js'
|
||||
@@ -107,24 +257,16 @@ module.exports = function (grunt) {
|
||||
}
|
||||
},
|
||||
|
||||
jshint: {
|
||||
options: {jshintrc: '.jshintrc'},
|
||||
files: {
|
||||
src: [
|
||||
'Gruntfile.js',
|
||||
'lib/less/**/*.js',
|
||||
'lib/less-node/**/*.js',
|
||||
'lib/less-browser/**/*.js',
|
||||
'lib/less-rhino/**/*.js',
|
||||
'bin/lessc'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
jscs: {
|
||||
src: ["test/**/*.js", "lib/less*/**/*.js", "bin/lessc"],
|
||||
eslint: {
|
||||
target: ["Gruntfile.js",
|
||||
"test/**/*.js",
|
||||
"lib/less*/**/*.js",
|
||||
"bin/lessc",
|
||||
"!test/browser/jasmine-jsreporter.js",
|
||||
"!test/less/errors/plugin/plugin-error.js"
|
||||
],
|
||||
options: {
|
||||
config: ".jscsrc"
|
||||
configFile: ".eslintrc.json"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -145,7 +287,15 @@ module.exports = function (grunt) {
|
||||
},
|
||||
main: {
|
||||
// src is used to build list of less files to compile
|
||||
src: ['test/less/*.less', '!test/less/javascript.less', '!test/less/urls.less', '!test/less/empty.less'],
|
||||
src: [
|
||||
'test/less/*.less',
|
||||
// Don't test NPM import, obviously
|
||||
'!test/less/plugin-module.less',
|
||||
'!test/less/import-module.less',
|
||||
'!test/less/javascript.less',
|
||||
'!test/less/urls.less',
|
||||
'!test/less/empty.less'
|
||||
],
|
||||
options: {
|
||||
helpers: 'test/browser/runner-main-options.js',
|
||||
specs: 'test/browser/runner-main-spec.js',
|
||||
@@ -186,7 +336,7 @@ module.exports = function (grunt) {
|
||||
}
|
||||
},
|
||||
browser: {
|
||||
src: ['test/browser/less/*.less'],
|
||||
src: ['test/browser/less/*.less', 'test/browser/less/plugin/*.less'],
|
||||
options: {
|
||||
helpers: 'test/browser/runner-browser-options.js',
|
||||
specs: 'test/browser/runner-browser-spec.js',
|
||||
@@ -241,14 +391,6 @@ module.exports = function (grunt) {
|
||||
outfile: 'tmp/browser/test-runner-global-vars.html'
|
||||
}
|
||||
},
|
||||
postProcessor: {
|
||||
src: ['test/browser/less/postProcessor/*.less'],
|
||||
options: {
|
||||
helpers: 'test/browser/runner-postProcessor-options.js',
|
||||
specs: 'test/browser/runner-postProcessor.js',
|
||||
outfile: 'tmp/browser/test-runner-post-processor.html'
|
||||
}
|
||||
},
|
||||
postProcessorPlugin: {
|
||||
src: ['test/less/postProcessorPlugin/*.less'],
|
||||
options: {
|
||||
@@ -283,62 +425,8 @@ module.exports = function (grunt) {
|
||||
}
|
||||
},
|
||||
|
||||
'saucelabs-jasmine': {
|
||||
all: {
|
||||
options: {
|
||||
urls: ["filemanager-plugin","visitor-plugin","pre-processor-plugin","post-processor-plugin","post-processor", "global-vars", "modify-vars", "production", "rootpath-relative",
|
||||
"rootpath", "relative-urls", "browser", "no-js-errors", "legacy", "strict-units"
|
||||
].map(function(testName) {
|
||||
return "http://localhost:8081/tmp/browser/test-runner-" + testName + ".html";
|
||||
}),
|
||||
testname: 'Sauce Unit Test for less.js',
|
||||
browsers: [{
|
||||
browserName: "chrome",
|
||||
version: '',
|
||||
platform: 'Windows 8'
|
||||
},
|
||||
{
|
||||
browserName: "firefox",
|
||||
version: '33',
|
||||
platform: 'Linux'
|
||||
},
|
||||
{
|
||||
browserName: "iPad",
|
||||
version: '8.0',
|
||||
platform: 'OS X 10.9',
|
||||
'device-orientation': 'portrait'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '8',
|
||||
platform: 'Windows XP'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '9',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '10',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
{
|
||||
browserName: "internet explorer",
|
||||
version: '11',
|
||||
platform: 'Windows 8.1'
|
||||
}],
|
||||
sauceConfig: {
|
||||
'record-video': process.env.TRAVIS_BRANCH !== "master",
|
||||
'record-screenshots': process.env.TRAVIS_BRANCH !== "master",
|
||||
'idle-timeout': 100, 'max-duration': 120,
|
||||
build: process.env.TRAVIS_BRANCH === "master" ? process.env.TRAVIS_JOB_ID : undefined,
|
||||
tags: [process.env.TRAVIS_BUILD_NUMBER, process.env.TRAVIS_PULL_REQUEST, process.env.TRAVIS_BRANCH]
|
||||
},
|
||||
throttled: 3
|
||||
}
|
||||
}
|
||||
},
|
||||
'saucelabs-jasmine': sauceJobs,
|
||||
|
||||
|
||||
// Clean the version of less built for the tests
|
||||
clean: {
|
||||
@@ -349,6 +437,8 @@ module.exports = function (grunt) {
|
||||
});
|
||||
|
||||
// Load these plugins to provide the necessary tasks
|
||||
grunt.loadNpmTasks('grunt-saucelabs');
|
||||
|
||||
require('jit-grunt')(grunt);
|
||||
|
||||
// Actually load this plugin's task(s).
|
||||
@@ -366,7 +456,7 @@ module.exports = function (grunt) {
|
||||
'uglify:dist'
|
||||
]);
|
||||
|
||||
// Release Rhino Version
|
||||
// Release Rhino Version (UNSUPPORTED)
|
||||
grunt.registerTask('rhino', [
|
||||
'browserify:rhino',
|
||||
'concat:rhino',
|
||||
@@ -415,23 +505,26 @@ module.exports = function (grunt) {
|
||||
'sauce-after-setup'
|
||||
]);
|
||||
|
||||
// setup a web server to run the browser tests in a browser rather than phantom
|
||||
// var sauceTests = [];
|
||||
// browserTests.map(function(testName) {
|
||||
// sauceTests.push('saucelabs-jasmine:' + testName);
|
||||
// });
|
||||
|
||||
grunt.registerTask('sauce-after-setup', [
|
||||
'saucelabs-jasmine',
|
||||
'saucelabs-jasmine:all',
|
||||
'clean:sauce_log'
|
||||
]);
|
||||
|
||||
var testTasks = [
|
||||
'clean',
|
||||
'jshint',
|
||||
'jscs',
|
||||
'eslint',
|
||||
'shell:test',
|
||||
'browsertest'
|
||||
];
|
||||
|
||||
if (isNaN(Number(process.env.TRAVIS_PULL_REQUEST, 10)) &&
|
||||
Number(process.env.TRAVIS_NODE_VERSION) === 0.11 &&
|
||||
(process.env.TRAVIS_BRANCH === "master" || process.env.TRAVIS_BRANCH === "sauce")) {
|
||||
Number(process.env.TRAVIS_NODE_VERSION) === 4 &&
|
||||
(process.env.TRAVIS_BRANCH === "master" || process.env.TRAVIS_BRANCH === "3.x")) {
|
||||
testTasks.push("force:on");
|
||||
testTasks.push("sauce-after-setup");
|
||||
testTasks.push("force:off");
|
||||
@@ -441,7 +534,7 @@ module.exports = function (grunt) {
|
||||
grunt.registerTask('test', testTasks);
|
||||
|
||||
// Run all tests
|
||||
grunt.registerTask('quicktest', testTasks.slice(0, testTasks.length -1));
|
||||
grunt.registerTask('quicktest', testTasks.slice(0, -1));
|
||||
|
||||
// generate a good test environment for testing sourcemaps
|
||||
grunt.registerTask('sourcemap-test', [
|
||||
|
||||
13
README.md
13
README.md
@@ -1,6 +1,9 @@
|
||||
[](http://badge.fury.io/js/less) [](https://travis-ci.org/less/less.js)
|
||||
[](https://david-dm.org/less/less.js) [](https://david-dm.org/less/less.js#info=devDependencies) [](https://david-dm.org/less/less.js#info=optionalDependencies)
|
||||
[](https://saucelabs.com/u/less) [](https://ci.appveyor.com/project/lukeapage/less-js/branch/master)
|
||||
### This is the Less 3.0 Edge branch. For the stable (2.x) branch of Less, go [here](https://github.com/less/less.js/tree/master).
|
||||
|
||||
|
||||
[](http://badge.fury.io/js/less) [](https://travis-ci.org/less/less.js) [](https://ci.appveyor.com/project/lukeapage/less-js/branch/3.x) [](https://david-dm.org/less/less.js) [](https://david-dm.org/less/less.js#info=devDependencies) [](https://david-dm.org/less/less.js#info=optionalDependencies) [](https://twitter.com/lesstocss) [](https://gitter.im/less/less.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <sup>Chat with Less.js users</sup>
|
||||
|
||||
[](https://saucelabs.com/u/less)
|
||||
|
||||
# [Less.js](http://lesscss.org)
|
||||
|
||||
@@ -8,8 +11,6 @@
|
||||
|
||||
This is the JavaScript, official, stable version of Less.
|
||||
|
||||
###### :point_right: [](https://gitter.im/less/less.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) <sup>Chat with Less.js users</sup>
|
||||
|
||||
|
||||
## Getting Started
|
||||
|
||||
@@ -47,7 +48,7 @@ See the [changelog](CHANGELOG.md)
|
||||
|
||||
## [License](LICENSE)
|
||||
|
||||
Copyright (c) 2009-2016 [Alexis Sellier](http://cloudhead.io) & The Core Less Team
|
||||
Copyright (c) 2009-2017 [Alexis Sellier](http://cloudhead.io) & The Core Less Team
|
||||
Licensed under the [Apache License](LICENSE).
|
||||
|
||||
|
||||
|
||||
19
appveyor.yml
19
appveyor.yml
@@ -1,21 +1,20 @@
|
||||
# Test against these versions of Node.js.
|
||||
environment:
|
||||
matrix:
|
||||
- nodejs_version: "0.10"
|
||||
- nodejs_version: "0.12"
|
||||
- nodejs_version: "4"
|
||||
- nodejs_version: "6"
|
||||
- nodejs_version: "8"
|
||||
- nodejs_version: "9"
|
||||
|
||||
# Install scripts. (runs after repo cloning)
|
||||
install:
|
||||
# Get the latest stable version of Node 0.STABLE.latest
|
||||
- ps: Install-Product node $env:nodejs_version
|
||||
# Use npm v2
|
||||
- npm -g install npm@2
|
||||
- set PATH=%APPDATA%\npm;%PATH%
|
||||
- npm -v
|
||||
# Typical npm stuff.
|
||||
- npm install
|
||||
# node 0.10 & 0.12 have race condition issues when running custom install scripts
|
||||
# this can cause phantomjs-prebuilt install script to fail with the error:
|
||||
# <Cannot find module 'boom'>
|
||||
# Seems related to: https://github.com/npm/npm/issues/8152
|
||||
# using <appveyor_retry> solves this.
|
||||
- appveyor-retry call npm install
|
||||
|
||||
# Grunt-specific stuff.
|
||||
- npm install -g grunt-cli
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
var path = require('path'),
|
||||
fs = require('fs');
|
||||
fs = require('fs'),
|
||||
now = require("performance-now");
|
||||
|
||||
var less = require('../lib/less-node');
|
||||
var file = path.join(__dirname, 'benchmark.less');
|
||||
@@ -7,49 +8,86 @@ var file = path.join(__dirname, 'benchmark.less');
|
||||
if (process.argv[2]) { file = path.join(process.cwd(), process.argv[2]) }
|
||||
|
||||
fs.readFile(file, 'utf8', function (e, data) {
|
||||
var start, end, total;
|
||||
var start, total;
|
||||
|
||||
console.log("Benchmarking...\n", path.basename(file) + " (" +
|
||||
parseInt(data.length / 1024) + " KB)", "");
|
||||
|
||||
var benchMarkData = [];
|
||||
var renderBenchmark = []
|
||||
, parserBenchmark = []
|
||||
, evalBenchmark = [];
|
||||
|
||||
var totalruns = 100;
|
||||
var ignoreruns = 30;
|
||||
var totalruns = 30;
|
||||
var ignoreruns = 5;
|
||||
|
||||
for(var i = 0; i < totalruns; i++) {
|
||||
start = new Date();
|
||||
var i = 0;
|
||||
|
||||
less.render(data, function (err) {
|
||||
end = new Date();
|
||||
nextRun();
|
||||
|
||||
benchMarkData.push(end - start);
|
||||
function nextRun() {
|
||||
var start, renderEnd, parserEnd;
|
||||
|
||||
start = now();
|
||||
|
||||
less.parse(data, {}, function(err, root, imports, options) {
|
||||
if (err) {
|
||||
less.writeError(err);
|
||||
process.exit(3);
|
||||
}
|
||||
parserEnd = now();
|
||||
|
||||
var tree, result;
|
||||
tree = new less.ParseTree(root, imports);
|
||||
result = tree.toCSS(options);
|
||||
|
||||
renderEnd = now();
|
||||
|
||||
renderBenchmark.push(renderEnd - start);
|
||||
parserBenchmark.push(parserEnd - start);
|
||||
evalBenchmark.push(renderEnd - parserEnd);
|
||||
|
||||
i += 1;
|
||||
//console.log('Less Run #: ' + i);
|
||||
if(i < totalruns) {
|
||||
nextRun();
|
||||
}
|
||||
else {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var totalTime = 0;
|
||||
var mintime = Infinity;
|
||||
var maxtime = 0;
|
||||
for(var i = ignoreruns; i < totalruns; i++) {
|
||||
totalTime += benchMarkData[i];
|
||||
mintime = Math.min(mintime, benchMarkData[i]);
|
||||
maxtime = Math.max(maxtime, benchMarkData[i]);
|
||||
}
|
||||
var avgtime = totalTime / (totalruns - ignoreruns);
|
||||
var variation = maxtime - mintime;
|
||||
var variationperc = (variation / avgtime) * 100;
|
||||
function finish() {
|
||||
function analyze(benchmark, benchMarkData) {
|
||||
console.log('----------------------');
|
||||
console.log(benchmark);
|
||||
console.log('----------------------');
|
||||
var totalTime = 0;
|
||||
var mintime = Infinity;
|
||||
var maxtime = 0;
|
||||
for(var i = ignoreruns; i < totalruns; i++) {
|
||||
totalTime += benchMarkData[i];
|
||||
mintime = Math.min(mintime, benchMarkData[i]);
|
||||
maxtime = Math.max(maxtime, benchMarkData[i]);
|
||||
}
|
||||
var avgtime = totalTime / (totalruns - ignoreruns);
|
||||
var variation = maxtime - mintime;
|
||||
var variationperc = (variation / avgtime) * 100;
|
||||
|
||||
console.log("Min. Time: "+mintime + " ms");
|
||||
console.log("Max. Time: "+maxtime + " ms");
|
||||
console.log("Total Average Time: " + avgtime + " ms (" +
|
||||
parseInt(1000 / avgtime *
|
||||
data.length / 1024) + " KB\/s)");
|
||||
console.log("+/- " + variationperc + "%");
|
||||
console.log("Min. Time: " + Math.round(mintime) + " ms");
|
||||
console.log("Max. Time: " + Math.round(maxtime) + " ms");
|
||||
console.log("Total Average Time: " + Math.round(avgtime) + " ms (" +
|
||||
parseInt(1000 / avgtime *
|
||||
data.length / 1024) + " KB\/s)");
|
||||
console.log("+/- " + Math.round(variationperc) + "%");
|
||||
console.log("");
|
||||
}
|
||||
|
||||
analyze('Parsing', parserBenchmark);
|
||||
analyze('Evaluation', evalBenchmark);
|
||||
analyze('Render Time', renderBenchmark);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
449
bin/lessc
449
bin/lessc
@@ -14,31 +14,15 @@ try {
|
||||
|
||||
var less = require('../lib/less-node'),
|
||||
pluginLoader = new less.PluginLoader(less),
|
||||
plugin,
|
||||
plugins = [];
|
||||
|
||||
var args = process.argv.slice(1);
|
||||
var silent = false,
|
||||
plugins = [],
|
||||
queuePlugins = [],
|
||||
args = process.argv.slice(1),
|
||||
silent = false,
|
||||
verbose = false,
|
||||
options = {
|
||||
depends: false,
|
||||
compress: false,
|
||||
max_line_len: -1,
|
||||
lint: false,
|
||||
paths: [],
|
||||
color: true,
|
||||
strictImports: false,
|
||||
insecure: false,
|
||||
rootpath: '',
|
||||
relativeUrls: false,
|
||||
ieCompat: true,
|
||||
strictMath: false,
|
||||
strictUnits: false,
|
||||
globalVars: null,
|
||||
modifyVars: null,
|
||||
urlArgs: '',
|
||||
plugins: plugins
|
||||
};
|
||||
options = less.options;
|
||||
|
||||
options.plugins = plugins;
|
||||
|
||||
var sourceMapOptions = {};
|
||||
var continueProcessing = true;
|
||||
|
||||
@@ -94,194 +78,7 @@ function printUsage() {
|
||||
pluginLoader.printUsage(plugins);
|
||||
continueProcessing = false;
|
||||
}
|
||||
|
||||
// self executing function so we can return
|
||||
(function() {
|
||||
args = args.filter(function (arg) {
|
||||
var match;
|
||||
|
||||
match = arg.match(/^-I(.+)$/);
|
||||
if (match) {
|
||||
options.paths.push(match[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
|
||||
if (match) {
|
||||
arg = match[1];
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
|
||||
switch (arg) {
|
||||
case 'v':
|
||||
case 'version':
|
||||
console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
|
||||
continueProcessing = false;
|
||||
break;
|
||||
case 'verbose':
|
||||
verbose = true;
|
||||
break;
|
||||
case 's':
|
||||
case 'silent':
|
||||
silent = true;
|
||||
break;
|
||||
case 'l':
|
||||
case 'lint':
|
||||
options.lint = true;
|
||||
break;
|
||||
case 'strict-imports':
|
||||
options.strictImports = true;
|
||||
break;
|
||||
case 'h':
|
||||
case 'help':
|
||||
printUsage();
|
||||
break;
|
||||
case 'x':
|
||||
case 'compress':
|
||||
options.compress = true;
|
||||
break;
|
||||
case 'insecure':
|
||||
options.insecure = true;
|
||||
break;
|
||||
case 'M':
|
||||
case 'depends':
|
||||
options.depends = true;
|
||||
break;
|
||||
case 'max-line-len':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.maxLineLen = parseInt(match[2], 10);
|
||||
if (options.maxLineLen <= 0) {
|
||||
options.maxLineLen = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'no-color':
|
||||
options.color = false;
|
||||
break;
|
||||
case 'no-ie-compat':
|
||||
options.ieCompat = false;
|
||||
break;
|
||||
case 'no-js':
|
||||
options.javascriptEnabled = false;
|
||||
break;
|
||||
case 'include-path':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
// ; supported on windows.
|
||||
// : supported on windows and linux, excluding a drive letter like C:\ so C:\file:D:\file parses to 2
|
||||
options.paths = match[2]
|
||||
.split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
|
||||
.map(function(p) {
|
||||
if (p) {
|
||||
return path.resolve(process.cwd(), p);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'line-numbers':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.dumpLineNumbers = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map':
|
||||
options.sourceMap = true;
|
||||
if (match[2]) {
|
||||
sourceMapOptions.sourceMapFullFilename = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-rootpath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapRootpath = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-basepath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapBasepath = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-map-inline':
|
||||
sourceMapFileInline = true;
|
||||
options.sourceMap = true;
|
||||
break;
|
||||
case 'source-map-less-inline':
|
||||
sourceMapOptions.outputSourceFiles = true;
|
||||
break;
|
||||
case 'source-map-url':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapURL = match[2];
|
||||
}
|
||||
break;
|
||||
case 'rp':
|
||||
case 'rootpath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.rootpath = match[2].replace(/\\/g, '/');
|
||||
}
|
||||
break;
|
||||
case "ru":
|
||||
case "relative-urls":
|
||||
options.relativeUrls = true;
|
||||
break;
|
||||
case "sm":
|
||||
case "strict-math":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.strictMath = checkBooleanArg(match[2]);
|
||||
}
|
||||
break;
|
||||
case "su":
|
||||
case "strict-units":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.strictUnits = checkBooleanArg(match[2]);
|
||||
}
|
||||
break;
|
||||
case "global-var":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
if (!options.globalVars) {
|
||||
options.globalVars = {};
|
||||
}
|
||||
parseVariableOption(match[2], options.globalVars);
|
||||
}
|
||||
break;
|
||||
case "modify-var":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
if (!options.modifyVars) {
|
||||
options.modifyVars = {};
|
||||
}
|
||||
|
||||
parseVariableOption(match[2], options.modifyVars);
|
||||
}
|
||||
break;
|
||||
case 'url-args':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.urlArgs = match[2];
|
||||
}
|
||||
break;
|
||||
case 'plugin':
|
||||
var splitupArg = match[2].match(/^([^=]+)(=(.*))?/),
|
||||
name = splitupArg[1],
|
||||
pluginOptions = splitupArg[3];
|
||||
|
||||
plugin = pluginLoader.tryLoadPlugin(name, pluginOptions);
|
||||
if (plugin) {
|
||||
plugins.push(plugin);
|
||||
} else {
|
||||
console.error("Unable to load plugin " + name +
|
||||
" please make sure that it is installed under or at the same level as less");
|
||||
process.exitCode = 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
plugin = pluginLoader.tryLoadPlugin("less-plugin-" + arg, match[2]);
|
||||
if (plugin) {
|
||||
plugins.push(plugin);
|
||||
} else {
|
||||
console.error("Unable to interpret argument " + arg +
|
||||
" - if it is a plugin (less-plugin-" + arg + "), make sure that it is installed under or at" +
|
||||
" the same level as less");
|
||||
process.exitCode = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
function render() {
|
||||
|
||||
if (!continueProcessing) {
|
||||
return;
|
||||
@@ -340,7 +137,7 @@ function printUsage() {
|
||||
sourceMapOptions.sourceMapRootpath = path.relative(pathToMap, pathToInput);
|
||||
}
|
||||
|
||||
if (! input) {
|
||||
if (!input) {
|
||||
console.error("lessc: no input files");
|
||||
console.error("");
|
||||
printUsage();
|
||||
@@ -355,7 +152,7 @@ function printUsage() {
|
||||
if (!existsSync(dir)) {
|
||||
if (mkdirp === undefined) {
|
||||
try {mkdirp = require('mkdirp');}
|
||||
catch(e) { mkdirp = null; }
|
||||
catch (e) { mkdirp = null; }
|
||||
}
|
||||
cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
|
||||
cmd(dir);
|
||||
@@ -489,7 +286,11 @@ function printUsage() {
|
||||
}
|
||||
},
|
||||
function(err) {
|
||||
less.writeError(err, options);
|
||||
if (!options.silent) {
|
||||
console.error(err.toString({
|
||||
stylize: options.color && less.lesscHelper.stylize
|
||||
}));
|
||||
}
|
||||
process.exitCode = 1;
|
||||
});
|
||||
};
|
||||
@@ -509,4 +310,222 @@ function printUsage() {
|
||||
parseLessFile(false, buffer);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function processPluginQueue() {
|
||||
var x = 0;
|
||||
|
||||
function pluginError(name) {
|
||||
console.error("Unable to load plugin " + name +
|
||||
" please make sure that it is installed under or at the same level as less");
|
||||
process.exitCode = 1;
|
||||
}
|
||||
function pluginFinished(plugin) {
|
||||
x++;
|
||||
plugins.push(plugin);
|
||||
if (x === queuePlugins.length) {
|
||||
render();
|
||||
}
|
||||
}
|
||||
queuePlugins.forEach(function(queue) {
|
||||
pluginLoader.tryLoadPlugin(queue.name, function(err, data) {
|
||||
if (err) {
|
||||
pluginError(queue.name);
|
||||
}
|
||||
else {
|
||||
pluginFinished({
|
||||
fileContent: data.contents,
|
||||
filename: data.filename,
|
||||
options: queue.options
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// self executing function so we can return
|
||||
(function() {
|
||||
args = args.filter(function (arg) {
|
||||
var match;
|
||||
|
||||
match = arg.match(/^-I(.+)$/);
|
||||
if (match) {
|
||||
options.paths.push(match[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
match = arg.match(/^--?([a-z][0-9a-z-]*)(?:=(.*))?$/i);
|
||||
if (match) {
|
||||
arg = match[1];
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
|
||||
switch (arg) {
|
||||
case 'v':
|
||||
case 'version':
|
||||
console.log("lessc " + less.version.join('.') + " (Less Compiler) [JavaScript]");
|
||||
continueProcessing = false;
|
||||
break;
|
||||
case 'verbose':
|
||||
verbose = true;
|
||||
break;
|
||||
case 's':
|
||||
case 'silent':
|
||||
silent = true;
|
||||
break;
|
||||
case 'l':
|
||||
case 'lint':
|
||||
options.lint = true;
|
||||
break;
|
||||
case 'strict-imports':
|
||||
options.strictImports = true;
|
||||
break;
|
||||
case 'h':
|
||||
case 'help':
|
||||
printUsage();
|
||||
break;
|
||||
case 'x':
|
||||
case 'compress':
|
||||
options.compress = true;
|
||||
break;
|
||||
case 'insecure':
|
||||
options.insecure = true;
|
||||
break;
|
||||
case 'M':
|
||||
case 'depends':
|
||||
options.depends = true;
|
||||
break;
|
||||
case 'max-line-len':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.maxLineLen = parseInt(match[2], 10);
|
||||
if (options.maxLineLen <= 0) {
|
||||
options.maxLineLen = -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'no-color':
|
||||
options.color = false;
|
||||
break;
|
||||
case 'ie-compat':
|
||||
options.ieCompat = true;
|
||||
break;
|
||||
case 'js':
|
||||
options.javascriptEnabled = true;
|
||||
break;
|
||||
case 'no-js':
|
||||
console.error('The "--no-js" argument is deprecated, as inline JavaScript ' +
|
||||
'is disabled by default. Use "--js" to enable inline JavaScript (not recommended).');
|
||||
break;
|
||||
case 'include-path':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
// ; supported on windows.
|
||||
// : supported on windows and linux, excluding a drive letter like C:\ so C:\file:D:\file parses to 2
|
||||
options.paths = match[2]
|
||||
.split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
|
||||
.map(function(p) {
|
||||
if (p) {
|
||||
return path.resolve(process.cwd(), p);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'line-numbers':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.dumpLineNumbers = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map':
|
||||
options.sourceMap = true;
|
||||
if (match[2]) {
|
||||
sourceMapOptions.sourceMapFullFilename = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-rootpath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapRootpath = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-basepath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapBasepath = match[2];
|
||||
}
|
||||
break;
|
||||
case 'source-map-inline':
|
||||
case 'source-map-map-inline':
|
||||
sourceMapFileInline = true;
|
||||
options.sourceMap = true;
|
||||
break;
|
||||
case 'source-map-include-source':
|
||||
case 'source-map-less-inline':
|
||||
sourceMapOptions.outputSourceFiles = true;
|
||||
break;
|
||||
case 'source-map-url':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
sourceMapOptions.sourceMapURL = match[2];
|
||||
}
|
||||
break;
|
||||
case 'rp':
|
||||
case 'rootpath':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.rootpath = match[2].replace(/\\/g, '/');
|
||||
}
|
||||
break;
|
||||
case "ru":
|
||||
case "relative-urls":
|
||||
options.relativeUrls = true;
|
||||
break;
|
||||
case "sm":
|
||||
case "strict-math":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.strictMath = checkBooleanArg(match[2]);
|
||||
}
|
||||
break;
|
||||
case "su":
|
||||
case "strict-units":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.strictUnits = checkBooleanArg(match[2]);
|
||||
}
|
||||
break;
|
||||
case "global-var":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
if (!options.globalVars) {
|
||||
options.globalVars = {};
|
||||
}
|
||||
parseVariableOption(match[2], options.globalVars);
|
||||
}
|
||||
break;
|
||||
case "modify-var":
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
if (!options.modifyVars) {
|
||||
options.modifyVars = {};
|
||||
}
|
||||
|
||||
parseVariableOption(match[2], options.modifyVars);
|
||||
}
|
||||
break;
|
||||
case 'url-args':
|
||||
if (checkArgFunc(arg, match[2])) {
|
||||
options.urlArgs = match[2];
|
||||
}
|
||||
break;
|
||||
case 'plugin':
|
||||
var splitupArg = match[2].match(/^([^=]+)(=(.*))?/),
|
||||
name = splitupArg[1],
|
||||
pluginOptions = splitupArg[3];
|
||||
queuePlugins.push({ name: name, options: pluginOptions });
|
||||
break;
|
||||
default:
|
||||
queuePlugins.push({ name: arg, options: match[2], default: true });
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (queuePlugins.length > 0) {
|
||||
processPluginQueue();
|
||||
}
|
||||
else {
|
||||
render();
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
@@ -43,4 +43,6 @@ module.exports = function(window, options) {
|
||||
options.onReady = true;
|
||||
}
|
||||
|
||||
options.javascriptEnabled = (options.javascriptEnabled || options.inlineJavaScript) ? true : false;
|
||||
|
||||
};
|
||||
|
||||
26
lib/less-browser/bootstrap.js
vendored
26
lib/less-browser/bootstrap.js
vendored
@@ -3,14 +3,30 @@
|
||||
* used in the browser distributed version of less
|
||||
* to kick-start less using the browser api
|
||||
*/
|
||||
/*global window, document */
|
||||
/* global window, document */
|
||||
|
||||
// shim Promise if required
|
||||
require('promise/polyfill.js');
|
||||
// TODO - consider switching this out for a recommendation for this polyfill?
|
||||
// <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
|
||||
// Browsers have good Promise support
|
||||
require("promise/polyfill");
|
||||
|
||||
var options = window.less || {};
|
||||
var options = require('../less/default-options')();
|
||||
|
||||
if (window.less) {
|
||||
for (key in window.less) {
|
||||
if (window.less.hasOwnProperty(key)) {
|
||||
options[key] = window.less[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
require("./add-default-options")(window, options);
|
||||
|
||||
options.plugins = options.plugins || [];
|
||||
|
||||
if (window.LESS_PLUGINS) {
|
||||
options.plugins = options.plugins.concat(window.LESS_PLUGINS);
|
||||
}
|
||||
|
||||
var less = module.exports = require("./index")(window, options);
|
||||
|
||||
window.less = less;
|
||||
@@ -31,7 +47,7 @@ if (options.onReady) {
|
||||
if (/!watch/.test(window.location.hash)) {
|
||||
less.watch();
|
||||
}
|
||||
// Simulate synchronous stylesheet loading by blocking page rendering
|
||||
// Simulate synchronous stylesheet loading by hiding page rendering
|
||||
if (!options.async) {
|
||||
css = 'body { display: none !important }';
|
||||
head = document.head || document.getElementsByTagName('head')[0];
|
||||
|
||||
@@ -17,8 +17,8 @@ module.exports = function(window, options, logger) {
|
||||
if (modifyVars) {
|
||||
cache.setItem(path + ':vars', JSON.stringify(modifyVars));
|
||||
}
|
||||
} catch(e) {
|
||||
//TODO - could do with adding more robust error handling
|
||||
} catch (e) {
|
||||
// TODO - could do with adding more robust error handling
|
||||
logger.error('failed to save "' + path + '" to local storage for caching.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ module.exports = function(window, less, options) {
|
||||
}
|
||||
};
|
||||
|
||||
if (e.extract) {
|
||||
if (e.line) {
|
||||
errorline(e, 0, '');
|
||||
errorline(e, 1, 'line');
|
||||
errorline(e, 2, '');
|
||||
@@ -112,7 +112,7 @@ module.exports = function(window, less, options) {
|
||||
}
|
||||
|
||||
function removeErrorConsole(path) {
|
||||
//no action
|
||||
// no action
|
||||
}
|
||||
|
||||
function removeError(path) {
|
||||
@@ -130,7 +130,7 @@ module.exports = function(window, less, options) {
|
||||
var filename = e.filename || rootHref;
|
||||
var errors = [];
|
||||
var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
|
||||
" in " + filename + " ";
|
||||
" in " + filename;
|
||||
|
||||
var errorline = function (e, i, classname) {
|
||||
if (e.extract[i] !== undefined) {
|
||||
@@ -140,11 +140,11 @@ module.exports = function(window, less, options) {
|
||||
}
|
||||
};
|
||||
|
||||
if (e.extract) {
|
||||
if (e.line) {
|
||||
errorline(e, 0, '');
|
||||
errorline(e, 1, 'line');
|
||||
errorline(e, 2, '');
|
||||
content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
|
||||
content += ' on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
|
||||
errors.join('\n');
|
||||
}
|
||||
if (e.stack && (e.extract || options.logLevel >= 4)) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/*global window, XMLHttpRequest */
|
||||
/* global window, XMLHttpRequest */
|
||||
|
||||
module.exports = function(options, logger) {
|
||||
|
||||
@@ -6,22 +6,7 @@ module.exports = function(options, logger) {
|
||||
|
||||
var fileCache = {};
|
||||
|
||||
//TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
|
||||
|
||||
function getXMLHttpRequest() {
|
||||
if (window.XMLHttpRequest && (window.location.protocol !== "file:" || !("ActiveXObject" in window))) {
|
||||
return new XMLHttpRequest();
|
||||
} else {
|
||||
try {
|
||||
/*global ActiveXObject */
|
||||
return new ActiveXObject("Microsoft.XMLHTTP");
|
||||
} catch (e) {
|
||||
logger.error("browser doesn't support AJAX.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
|
||||
var FileManager = function() {
|
||||
};
|
||||
|
||||
@@ -38,7 +23,7 @@ module.exports = function(options, logger) {
|
||||
};
|
||||
FileManager.prototype.doXHR = function doXHR(url, type, callback, errback) {
|
||||
|
||||
var xhr = getXMLHttpRequest();
|
||||
var xhr = new XMLHttpRequest();
|
||||
var async = options.isFileProtocol ? options.fileAsync : true;
|
||||
|
||||
if (typeof xhr.overrideMimeType === 'function') {
|
||||
@@ -82,36 +67,43 @@ module.exports = function(options, logger) {
|
||||
fileCache = {};
|
||||
};
|
||||
|
||||
FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment, callback) {
|
||||
FileManager.prototype.loadFile = function loadFile(filename, currentDirectory, options, environment) {
|
||||
// TODO: Add prefix support like less-node?
|
||||
// What about multiple paths?
|
||||
|
||||
if (currentDirectory && !this.isPathAbsolute(filename)) {
|
||||
filename = currentDirectory + filename;
|
||||
}
|
||||
|
||||
filename = options.ext ? this.tryAppendExtension(filename, options.ext) : filename;
|
||||
|
||||
options = options || {};
|
||||
|
||||
// sheet may be set to the stylesheet for the initial load or a collection of properties including
|
||||
// some context variables for imports
|
||||
var hrefParts = this.extractUrlParts(filename, window.location.href);
|
||||
var href = hrefParts.url;
|
||||
|
||||
if (options.useFileCache && fileCache[href]) {
|
||||
try {
|
||||
var lessText = fileCache[href];
|
||||
callback(null, { contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
|
||||
} catch (e) {
|
||||
callback({filename: href, message: "Error loading file " + href + " error was " + e.message});
|
||||
var self = this;
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (options.useFileCache && fileCache[href]) {
|
||||
try {
|
||||
var lessText = fileCache[href];
|
||||
return resolve({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
|
||||
} catch (e) {
|
||||
return reject({ filename: href, message: "Error loading file " + href + " error was " + e.message });
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
|
||||
// per file cache
|
||||
fileCache[href] = data;
|
||||
self.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
|
||||
// per file cache
|
||||
fileCache[href] = data;
|
||||
|
||||
// Use remote copy (re-parse)
|
||||
callback(null, { contents: data, filename: href, webInfo: { lastModified: lastModified }});
|
||||
}, function doXHRError(status, url) {
|
||||
callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href });
|
||||
// Use remote copy (re-parse)
|
||||
resolve({ contents: data, filename: href, webInfo: { lastModified: lastModified }});
|
||||
}, function doXHRError(status, url) {
|
||||
reject({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")", href: href });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -8,42 +8,29 @@ var addDataAttr = require("./utils").addDataAttr,
|
||||
module.exports = function(window, options) {
|
||||
var document = window.document;
|
||||
var less = require('../less')();
|
||||
|
||||
//module.exports = less;
|
||||
|
||||
less.options = options;
|
||||
var environment = less.environment,
|
||||
FileManager = require("./file-manager")(options, less.logger),
|
||||
fileManager = new FileManager();
|
||||
environment.addFileManager(fileManager);
|
||||
less.FileManager = FileManager;
|
||||
less.PluginLoader = require("./plugin-loader");
|
||||
|
||||
require("./log-listener")(less, options);
|
||||
var errors = require("./error-reporting")(window, less, options);
|
||||
var cache = less.cache = options.cache || require("./cache")(window, options, less.logger);
|
||||
require('./image-size')(less.environment);
|
||||
|
||||
//Setup user functions
|
||||
// Setup user functions - Deprecate?
|
||||
if (options.functions) {
|
||||
less.functions.functionRegistry.addMultiple(options.functions);
|
||||
}
|
||||
|
||||
var typePattern = /^text\/(x-)?less$/;
|
||||
|
||||
function postProcessCSS(styles) { // deprecated, use a plugin for postprocesstasks
|
||||
if (options.postProcessor && typeof options.postProcessor === 'function') {
|
||||
styles = options.postProcessor.call(styles, styles) || styles;
|
||||
}
|
||||
return styles;
|
||||
}
|
||||
|
||||
function clone(obj) {
|
||||
var cloned = {};
|
||||
for (var prop in obj) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
cloned[prop] = obj[prop];
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
return JSON.parse(JSON.stringify(obj || {}));
|
||||
}
|
||||
|
||||
// only really needed for phantom
|
||||
@@ -67,7 +54,7 @@ module.exports = function(window, options) {
|
||||
var lessText = style.innerHTML || '';
|
||||
instanceOptions.filename = document.location.href.replace(/#.*$/, '');
|
||||
|
||||
/*jshint loopfunc:true */
|
||||
/* jshint loopfunc:true */
|
||||
// use closure to store current style
|
||||
less.render(lessText, instanceOptions,
|
||||
bind(function(style, e, result) {
|
||||
@@ -123,7 +110,7 @@ module.exports = function(window, options) {
|
||||
|
||||
}
|
||||
|
||||
//TODO add tests around how this behaves when reloading
|
||||
// TODO add tests around how this behaves when reloading
|
||||
errors.remove(path);
|
||||
|
||||
instanceOptions.rootFileInfo = newFileInfo;
|
||||
@@ -132,20 +119,20 @@ module.exports = function(window, options) {
|
||||
e.href = path;
|
||||
callback(e);
|
||||
} else {
|
||||
result.css = postProcessCSS(result.css);
|
||||
cache.setCSS(sheet.href, webInfo.lastModified, instanceOptions.modifyVars, result.css);
|
||||
callback(null, result.css, data, sheet, webInfo, path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fileManager.loadFile(sheet.href, null, instanceOptions, environment, function(e, loadedFile) {
|
||||
if (e) {
|
||||
callback(e);
|
||||
return;
|
||||
}
|
||||
loadInitialFileCallback(loadedFile);
|
||||
});
|
||||
fileManager.loadFile(sheet.href, null, instanceOptions, environment)
|
||||
.then(function(loadedFile) {
|
||||
loadInitialFileCallback(loadedFile);
|
||||
}).catch(function(err) {
|
||||
console.log(err);
|
||||
callback(err);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function loadStyleSheets(callback, reload, modifyVars) {
|
||||
|
||||
25
lib/less-browser/plugin-loader.js
Normal file
25
lib/less-browser/plugin-loader.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// TODO: Add tests for browser @plugin
|
||||
/*global window */
|
||||
|
||||
var AbstractPluginLoader = require("../less/environment/abstract-plugin-loader.js");
|
||||
|
||||
/**
|
||||
* Browser Plugin Loader
|
||||
*/
|
||||
var PluginLoader = function(less) {
|
||||
this.less = less;
|
||||
// shim for browser require?
|
||||
this.require = require;
|
||||
};
|
||||
|
||||
PluginLoader.prototype = new AbstractPluginLoader();
|
||||
|
||||
PluginLoader.prototype.loadPlugin = function(filename, basePath, context, environment, fileManager) {
|
||||
return new Promise(function(fulfill, reject) {
|
||||
fileManager.loadFile(filename, basePath, context, environment)
|
||||
.then(fulfill).catch(reject);
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = PluginLoader;
|
||||
|
||||
@@ -16,7 +16,7 @@ module.exports = {
|
||||
try {
|
||||
options[opt] = JSON.parse(tag.dataset[opt]);
|
||||
}
|
||||
catch(_) {}
|
||||
catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
var path = require('path'),
|
||||
fs = require('./fs'),
|
||||
PromiseConstructor,
|
||||
PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
|
||||
AbstractFileManager = require("../less/environment/abstract-file-manager.js");
|
||||
|
||||
try {
|
||||
PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
|
||||
} catch(e) {
|
||||
}
|
||||
|
||||
var FileManager = function() {
|
||||
this.files = {};
|
||||
};
|
||||
|
||||
FileManager.prototype = new AbstractFileManager();
|
||||
@@ -21,88 +17,122 @@ FileManager.prototype.supportsSync = function(filename, currentDirectory, option
|
||||
};
|
||||
|
||||
FileManager.prototype.loadFile = function(filename, currentDirectory, options, environment, callback) {
|
||||
|
||||
var fullFilename,
|
||||
data,
|
||||
isAbsoluteFilename = this.isPathAbsolute(filename),
|
||||
filenamesTried = [];
|
||||
filenamesTried = [],
|
||||
self = this,
|
||||
prefix = filename.slice(0, 1),
|
||||
explicit = prefix === "." || prefix === "/",
|
||||
result = null;
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (options.syncImport || !PromiseConstructor) {
|
||||
data = this.loadFileSync(filename, currentDirectory, options, environment, 'utf-8');
|
||||
callback(data.error, data);
|
||||
return;
|
||||
}
|
||||
var paths = isAbsoluteFilename ? [''] : [currentDirectory];
|
||||
|
||||
var paths = isAbsoluteFilename ? [""] : [currentDirectory];
|
||||
if (options.paths) { paths.push.apply(paths, options.paths); }
|
||||
|
||||
// Search node_modules
|
||||
if (!explicit) { paths.push.apply(paths, this.modulePaths); }
|
||||
|
||||
if (!isAbsoluteFilename && paths.indexOf('.') === -1) { paths.push('.'); }
|
||||
|
||||
// promise is guaranteed to be asyncronous
|
||||
// which helps as it allows the file handle
|
||||
// to be closed before it continues with the next file
|
||||
return new PromiseConstructor(function(fulfill, reject) {
|
||||
var prefixes = options.prefixes || [''];
|
||||
var fileParts = this.extractUrlParts(filename);
|
||||
|
||||
if (options.syncImport) {
|
||||
getFileData(returnData, returnData);
|
||||
if (callback) {
|
||||
callback(result.error, result);
|
||||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// promise is guaranteed to be asyncronous
|
||||
// which helps as it allows the file handle
|
||||
// to be closed before it continues with the next file
|
||||
return new PromiseConstructor(getFileData);
|
||||
}
|
||||
|
||||
function returnData(data) {
|
||||
if (!data.filename) {
|
||||
result = { error: data };
|
||||
}
|
||||
else {
|
||||
result = data;
|
||||
}
|
||||
}
|
||||
|
||||
function getFileData(fulfill, reject) {
|
||||
(function tryPathIndex(i) {
|
||||
if (i < paths.length) {
|
||||
fullFilename = filename;
|
||||
if (paths[i]) {
|
||||
fullFilename = path.join(paths[i], fullFilename);
|
||||
}
|
||||
fs.stat(fullFilename, function (err) {
|
||||
if (err) {
|
||||
filenamesTried.push(fullFilename);
|
||||
tryPathIndex(i + 1);
|
||||
} else {
|
||||
fs.readFile(fullFilename, 'utf-8', function(e, data) {
|
||||
if (e) { reject(e); return; }
|
||||
(function tryPrefix(j) {
|
||||
if (j < prefixes.length) {
|
||||
fullFilename = fileParts.rawPath + prefixes[j] + fileParts.filename;
|
||||
|
||||
if (paths[i]) {
|
||||
fullFilename = path.join(paths[i], fullFilename);
|
||||
}
|
||||
|
||||
if (paths[i].indexOf('node_modules') > -1) {
|
||||
try {
|
||||
fullFilename = require.resolve(fullFilename);
|
||||
}
|
||||
catch (e) {}
|
||||
}
|
||||
|
||||
fullFilename = options.ext ? self.tryAppendExtension(fullFilename, options.ext) : fullFilename;
|
||||
|
||||
if (self.files[fullFilename]) {
|
||||
fulfill({ contents: self.files[fullFilename], filename: fullFilename});
|
||||
}
|
||||
else {
|
||||
var readFileArgs = [fullFilename];
|
||||
if (!options.rawBuffer) {
|
||||
readFileArgs.push('utf-8');
|
||||
}
|
||||
if (options.syncImport) {
|
||||
try {
|
||||
var data = fs.readFileSync.apply(this, readFileArgs);
|
||||
self.files[fullFilename] = data;
|
||||
fulfill({ contents: data, filename: fullFilename});
|
||||
}
|
||||
catch (e) {
|
||||
filenamesTried.push(fullFilename);
|
||||
return tryPrefix(j + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
readFileArgs.push(function(e, data) {
|
||||
if (e) {
|
||||
filenamesTried.push(fullFilename);
|
||||
return tryPrefix(j + 1);
|
||||
}
|
||||
self.files[fullFilename] = data;
|
||||
fulfill({ contents: data, filename: fullFilename});
|
||||
});
|
||||
fs.readFile.apply(this, readFileArgs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fulfill({ contents: data, filename: fullFilename});
|
||||
});
|
||||
}
|
||||
});
|
||||
else {
|
||||
tryPathIndex(i + 1);
|
||||
}
|
||||
})(0);
|
||||
} else {
|
||||
reject({ type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") });
|
||||
}
|
||||
}(0));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment, encoding) {
|
||||
var fullFilename, paths, filenamesTried = [], isAbsoluteFilename = this.isPathAbsolute(filename) , data;
|
||||
options = options || {};
|
||||
|
||||
paths = isAbsoluteFilename ? [""] : [currentDirectory];
|
||||
if (options.paths) {
|
||||
paths.push.apply(paths, options.paths);
|
||||
}
|
||||
if (!isAbsoluteFilename && paths.indexOf('.') === -1) {
|
||||
paths.push('.');
|
||||
}
|
||||
|
||||
var err, result;
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
try {
|
||||
fullFilename = filename;
|
||||
if (paths[i]) {
|
||||
fullFilename = path.join(paths[i], fullFilename);
|
||||
}
|
||||
filenamesTried.push(fullFilename);
|
||||
fs.statSync(fullFilename);
|
||||
break;
|
||||
} catch (e) {
|
||||
fullFilename = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fullFilename) {
|
||||
err = { type: 'File', message: "'" + filename + "' wasn't found. Tried - " + filenamesTried.join(",") };
|
||||
result = { error: err };
|
||||
} else {
|
||||
data = fs.readFileSync(fullFilename, encoding);
|
||||
result = { contents: data, filename: fullFilename};
|
||||
}
|
||||
|
||||
return result;
|
||||
FileManager.prototype.loadFileSync = function(filename, currentDirectory, options, environment) {
|
||||
options.syncImport = true;
|
||||
return this.loadFile(filename, currentDirectory, options, environment);
|
||||
};
|
||||
|
||||
module.exports = FileManager;
|
||||
|
||||
@@ -3,7 +3,7 @@ try
|
||||
{
|
||||
fs = require("graceful-fs");
|
||||
}
|
||||
catch(e)
|
||||
catch (e)
|
||||
{
|
||||
fs = require("fs");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = function(environment) {
|
||||
var Dimension = require("../less/tree/dimension"),
|
||||
Expression = require("../less/tree/expression"),
|
||||
functionRegistry = require("./../less/functions/function-registry");
|
||||
Expression = require("../less/tree/expression"),
|
||||
functionRegistry = require("./../less/functions/function-registry");
|
||||
|
||||
function imageSize(functionContext, filePathNode) {
|
||||
var filePath = filePathNode.value;
|
||||
|
||||
@@ -3,7 +3,8 @@ var environment = require("./environment"),
|
||||
UrlFileManager = require("./url-file-manager"),
|
||||
createFromEnvironment = require("../less"),
|
||||
less = createFromEnvironment(environment, [new FileManager(), new UrlFileManager()]),
|
||||
lesscHelper = require('./lessc-helper');
|
||||
lesscHelper = require('./lessc-helper'),
|
||||
path = require('path');
|
||||
|
||||
// allow people to create less with their own environment
|
||||
less.createFromEnvironment = createFromEnvironment;
|
||||
@@ -12,61 +13,12 @@ less.PluginLoader = require("./plugin-loader");
|
||||
less.fs = require("./fs");
|
||||
less.FileManager = FileManager;
|
||||
less.UrlFileManager = UrlFileManager;
|
||||
less.formatError = function(ctx, options) {
|
||||
options = options || {};
|
||||
|
||||
var message = "";
|
||||
var extract = ctx.extract;
|
||||
var error = [];
|
||||
var stylize = options.color ? lesscHelper.stylize : function (str) { return str; };
|
||||
|
||||
// only output a stack if it isn't a less error
|
||||
if (ctx.stack && !ctx.type) { return stylize(ctx.stack, 'red'); }
|
||||
|
||||
if (!ctx.hasOwnProperty('index') || !extract) {
|
||||
return ctx.stack || ctx.message;
|
||||
}
|
||||
|
||||
if (typeof extract[0] === 'string') {
|
||||
error.push(stylize((ctx.line - 1) + ' ' + extract[0], 'grey'));
|
||||
}
|
||||
|
||||
if (typeof extract[1] === 'string') {
|
||||
var errorTxt = ctx.line + ' ';
|
||||
if (extract[1]) {
|
||||
errorTxt += extract[1].slice(0, ctx.column) +
|
||||
stylize(stylize(stylize(extract[1].substr(ctx.column, 1), 'bold') +
|
||||
extract[1].slice(ctx.column + 1), 'red'), 'inverse');
|
||||
}
|
||||
error.push(errorTxt);
|
||||
}
|
||||
|
||||
if (typeof extract[2] === 'string') {
|
||||
error.push(stylize((ctx.line + 1) + ' ' + extract[2], 'grey'));
|
||||
}
|
||||
error = error.join('\n') + stylize('', 'reset') + '\n';
|
||||
|
||||
message += stylize(ctx.type + 'Error: ' + ctx.message, 'red');
|
||||
if (ctx.filename) {
|
||||
message += stylize(' in ', 'red') + ctx.filename +
|
||||
stylize(' on line ' + ctx.line + ', column ' + (ctx.column + 1) + ':', 'grey');
|
||||
}
|
||||
|
||||
message += '\n' + error;
|
||||
|
||||
if (ctx.callLine) {
|
||||
message += stylize('from ', 'red') + (ctx.filename || '') + '/n';
|
||||
message += stylize(ctx.callLine, 'grey') + ' ' + ctx.callExtract + '/n';
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
less.writeError = function (ctx, options) {
|
||||
options = options || {};
|
||||
if (options.silent) { return; }
|
||||
console.error(less.formatError(ctx, options));
|
||||
};
|
||||
// Set up options
|
||||
less.options = require('../less/default-options')();
|
||||
less.options.paths = [
|
||||
path.join(process.cwd(), "node_modules")
|
||||
];
|
||||
|
||||
// provide image-size functionality
|
||||
require('./image-size')(less.environment);
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
// helper functions for lessc
|
||||
var lessc_helper = {
|
||||
|
||||
//Stylize a string
|
||||
// Stylize a string
|
||||
stylize : function(str, style) {
|
||||
var styles = {
|
||||
'reset' : [0, 0],
|
||||
@@ -19,49 +19,49 @@ var lessc_helper = {
|
||||
'\x1b[' + styles[style][1] + 'm';
|
||||
},
|
||||
|
||||
//Print command line options
|
||||
// Print command line options
|
||||
printUsage: function() {
|
||||
console.log("usage: lessc [option option=parameter ...] <source> [destination]");
|
||||
console.log("");
|
||||
console.log("If source is set to `-' (dash or hyphen-minus), input is read from stdin.");
|
||||
console.log("");
|
||||
console.log("options:");
|
||||
console.log(" -h, --help Prints help (this message) and exit.");
|
||||
console.log(" --include-path=PATHS Sets include paths. Separated by `:'. `;' also supported on windows.");
|
||||
console.log(" -M, --depends Outputs a makefile import dependency list to stdout.");
|
||||
console.log(" --no-color Disables colorized output.");
|
||||
console.log(" --no-ie-compat Disables IE compatibility checks.");
|
||||
console.log(" --no-js Disables JavaScript in less files");
|
||||
console.log(" -l, --lint Syntax check only (lint).");
|
||||
console.log(" -s, --silent Suppresses output of error messages.");
|
||||
console.log(" --strict-imports Forces evaluation of imports.");
|
||||
console.log(" --insecure Allows imports from insecure https hosts.");
|
||||
console.log(" -v, --version Prints version number and exit.");
|
||||
console.log(" --verbose Be verbose.");
|
||||
console.log(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map).");
|
||||
console.log(" --source-map-rootpath=X Adds this path onto the sourcemap filename and less file paths.");
|
||||
console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory.");
|
||||
console.log(" --source-map-less-inline Puts the less files into the map instead of referencing them.");
|
||||
console.log(" --source-map-map-inline Puts the map (and any less files) as a base64 data uri into the output css file.");
|
||||
console.log(" --source-map-url=URL Sets a custom URL to map file, for sourceMappingURL comment");
|
||||
console.log(" in generated CSS file.");
|
||||
console.log(" -rp, --rootpath=URL Sets rootpath for url rewriting in relative imports and urls");
|
||||
console.log(" Works with or without the relative-urls option.");
|
||||
console.log(" -ru, --relative-urls Re-writes relative urls to the base less file.");
|
||||
console.log(" -sm=on|off Turns on or off strict math, where in strict mode, math.");
|
||||
console.log(" --strict-math=on|off Requires brackets. This option may default to on and then");
|
||||
console.log(" be removed in the future.");
|
||||
console.log(" -su=on|off Allows mixed units, e.g. 1px+1em or 1px*1px which have units");
|
||||
console.log(" --strict-units=on|off that cannot be represented.");
|
||||
console.log(" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file.");
|
||||
console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file.");
|
||||
console.log(" --url-args='QUERYSTRING' Adds params into url tokens (e.g. 42, cb=42 or 'a=1&b=2')");
|
||||
console.log(" --plugin=PLUGIN=OPTIONS Loads a plugin. You can also omit the --plugin= if the plugin begins");
|
||||
console.log(" less-plugin. E.g. the clean css plugin is called less-plugin-clean-css");
|
||||
console.log(" once installed (npm install less-plugin-clean-css), use either with");
|
||||
console.log(" --plugin=less-plugin-clean-css or just --clean-css");
|
||||
console.log(" specify options afterwards e.g. --plugin=less-plugin-clean-css=\"advanced\"");
|
||||
console.log(" or --clean-css=\"advanced\"");
|
||||
console.log(" -h, --help Prints help (this message) and exit.");
|
||||
console.log(" --include-path=PATHS Sets include paths. Separated by `:'. `;' also supported on windows.");
|
||||
console.log(" -M, --depends Outputs a makefile import dependency list to stdout.");
|
||||
console.log(" --no-color Disables colorized output.");
|
||||
console.log(" --ie-compat Enables IE8 compatibility checks.");
|
||||
console.log(" --js Enables inline JavaScript in less files");
|
||||
console.log(" -l, --lint Syntax check only (lint).");
|
||||
console.log(" -s, --silent Suppresses output of error messages.");
|
||||
console.log(" --strict-imports Forces evaluation of imports.");
|
||||
console.log(" --insecure Allows imports from insecure https hosts.");
|
||||
console.log(" -v, --version Prints version number and exit.");
|
||||
console.log(" --verbose Be verbose.");
|
||||
console.log(" --source-map[=FILENAME] Outputs a v3 sourcemap to the filename (or output filename.map).");
|
||||
console.log(" --source-map-rootpath=X Adds this path onto the sourcemap filename and less file paths.");
|
||||
console.log(" --source-map-basepath=X Sets sourcemap base path, defaults to current working directory.");
|
||||
console.log(" --source-map-include-source Puts the less files into the map instead of referencing them.");
|
||||
console.log(" --source-map-inline Puts the map (and any less files) as a base64 data uri into the output css file.");
|
||||
console.log(" --source-map-url=URL Sets a custom URL to map file, for sourceMappingURL comment");
|
||||
console.log(" in generated CSS file.");
|
||||
console.log(" -rp, --rootpath=URL Sets rootpath for url rewriting in relative imports and urls");
|
||||
console.log(" Works with or without the relative-urls option.");
|
||||
console.log(" -ru, --relative-urls Re-writes relative urls to the base less file.");
|
||||
console.log(" -sm=on|off Turns on or off strict math, where in strict mode, math.");
|
||||
console.log(" --strict-math=on|off Requires brackets. This option may default to on and then");
|
||||
console.log(" be removed in the future.");
|
||||
console.log(" -su=on|off Allows mixed units, e.g. 1px+1em or 1px*1px which have units");
|
||||
console.log(" --strict-units=on|off that cannot be represented.");
|
||||
console.log(" --global-var='VAR=VALUE' Defines a variable that can be referenced by the file.");
|
||||
console.log(" --modify-var='VAR=VALUE' Modifies a variable already declared in the file.");
|
||||
console.log(" --url-args='QUERYSTRING' Adds params into url tokens (e.g. 42, cb=42 or 'a=1&b=2')");
|
||||
console.log(" --plugin=PLUGIN=OPTIONS Loads a plugin. You can also omit the --plugin= if the plugin begins");
|
||||
console.log(" less-plugin. E.g. the clean css plugin is called less-plugin-clean-css");
|
||||
console.log(" once installed (npm install less-plugin-clean-css), use either with");
|
||||
console.log(" --plugin=less-plugin-clean-css or just --clean-css");
|
||||
console.log(" specify options afterwards e.g. --plugin=less-plugin-clean-css=\"advanced\"");
|
||||
console.log(" or --clean-css=\"advanced\"");
|
||||
console.log("");
|
||||
console.log("-------------------------- Deprecated ----------------");
|
||||
console.log(" --line-numbers=TYPE Outputs filename and line numbers.");
|
||||
|
||||
@@ -1,91 +1,54 @@
|
||||
var path = require("path");
|
||||
var path = require("path"),
|
||||
PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise,
|
||||
AbstractPluginLoader = require("../less/environment/abstract-plugin-loader.js");
|
||||
|
||||
/**
|
||||
* Node Plugin Loader
|
||||
*/
|
||||
var PluginLoader = function(less) {
|
||||
this.less = less;
|
||||
};
|
||||
PluginLoader.prototype.tryLoadPlugin = function(name, argument) {
|
||||
var plugin = this.tryRequirePlugin(name);
|
||||
if (plugin) {
|
||||
// support plugins being a function
|
||||
// so that the plugin can be more usable programmatically
|
||||
if (typeof plugin === "function") {
|
||||
plugin = new plugin();
|
||||
}
|
||||
if (plugin.minVersion) {
|
||||
if (this.compareVersion(plugin.minVersion, this.less.version) < 0) {
|
||||
console.log("plugin " + name + " requires version " + this.versionToString(plugin.minVersion));
|
||||
return null;
|
||||
this.require = require;
|
||||
this.requireRelative = function(prefix) {
|
||||
prefix = path.dirname(prefix);
|
||||
return function(id) {
|
||||
var str = id.substr(0, 2);
|
||||
if (str === '..' || str === './') {
|
||||
return require(path.join(prefix, id));
|
||||
}
|
||||
}
|
||||
if (argument) {
|
||||
if (!plugin.setOptions) {
|
||||
console.log("options have been provided but the plugin " + name + "does not support any options");
|
||||
return null;
|
||||
else {
|
||||
return require(id);
|
||||
}
|
||||
try {
|
||||
plugin.setOptions(argument);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
PluginLoader.prototype = new AbstractPluginLoader();
|
||||
|
||||
PluginLoader.prototype.loadPlugin = function(filename, basePath, context, environment, fileManager) {
|
||||
var self = this;
|
||||
var prefix = filename.slice(0, 1);
|
||||
var explicit = prefix === "." || prefix === "/" || filename.slice(-3).toLowerCase() === ".js";
|
||||
if (!explicit) {
|
||||
context.prefixes = ['less-plugin-', ''];
|
||||
}
|
||||
|
||||
return new PromiseConstructor(function(fulfill, reject) {
|
||||
fileManager.loadFile(filename, basePath, context, environment).then(
|
||||
function(data) {
|
||||
try {
|
||||
self.require = self.requireRelative(data.filename);
|
||||
fulfill(data);
|
||||
}
|
||||
catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
console.log("Error setting options on plugin " + name);
|
||||
console.log(e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
PluginLoader.prototype.compareVersion = function(aVersion, bVersion) {
|
||||
for (var i = 0; i < aVersion.length; i++) {
|
||||
if (aVersion[i] !== bVersion[i]) {
|
||||
return parseInt(aVersion[i]) > parseInt(bVersion[i]) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
PluginLoader.prototype.versionToString = function(version) {
|
||||
var versionString = "";
|
||||
for (var i = 0; i < version.length; i++) {
|
||||
versionString += (versionString ? "." : "") + version[i];
|
||||
}
|
||||
return versionString;
|
||||
};
|
||||
PluginLoader.prototype.tryRequirePlugin = function(name) {
|
||||
// is at the same level as the less.js module
|
||||
try {
|
||||
return require("../../../" + name);
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
// is installed as a sub dependency of the current folder
|
||||
try {
|
||||
return require(path.join(process.cwd(), "node_modules", name));
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
// is referenced relative to the current directory
|
||||
try {
|
||||
return require(path.join(process.cwd(), name));
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
// unlikely - would have to be a dependency of where this code was running (less.js)...
|
||||
if (name[0] !== '.') {
|
||||
try {
|
||||
return require(name);
|
||||
}
|
||||
catch(e) {
|
||||
}
|
||||
}
|
||||
};
|
||||
PluginLoader.prototype.printUsage = function(plugins) {
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
var plugin = plugins[i];
|
||||
if (plugin.printUsage) {
|
||||
plugin.printUsage();
|
||||
}
|
||||
}
|
||||
).catch(function(err) {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
module.exports = PluginLoader;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ UrlFileManager.prototype.loadFile = function(filename, currentDirectory, options
|
||||
return new PromiseConstructor(function(fulfill, reject) {
|
||||
if (request === undefined) {
|
||||
try { request = require('request'); }
|
||||
catch(e) { request = null; }
|
||||
catch (e) { request = null; }
|
||||
}
|
||||
if (!request) {
|
||||
reject({ type: 'File', message: "optional dependency 'request' required to import over http(s)\n" });
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
/* jshint rhino:true, unused: false */
|
||||
/* jscs:disable validateIndentation */
|
||||
/*global name:true, less, loadStyleSheet, os */
|
||||
/* global name:true, less, loadStyleSheet, os */
|
||||
|
||||
function formatError(ctx, options) {
|
||||
options = options || {};
|
||||
@@ -9,7 +8,7 @@ function formatError(ctx, options) {
|
||||
var extract = ctx.extract;
|
||||
var error = [];
|
||||
|
||||
// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
|
||||
// var stylize = options.color ? require('./lessc_helper').stylize : function (str) { return str; };
|
||||
var stylize = function (str) { return str; };
|
||||
|
||||
// only output a stack if it isn't a less error
|
||||
@@ -81,7 +80,7 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
|
||||
}
|
||||
try {
|
||||
callback(e, root, input, sheet, { local: false, lastModified: 0, remaining: remaining }, sheetName);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
writeError(e);
|
||||
}
|
||||
});
|
||||
@@ -146,25 +145,10 @@ function writeFile(filename, content) {
|
||||
// Command line integration via Rhino
|
||||
(function (args) {
|
||||
|
||||
var options = {
|
||||
depends: false,
|
||||
compress: false,
|
||||
cleancss: false,
|
||||
max_line_len: -1,
|
||||
silent: false,
|
||||
verbose: false,
|
||||
lint: false,
|
||||
paths: [],
|
||||
color: true,
|
||||
strictImports: false,
|
||||
rootpath: '',
|
||||
relativeUrls: false,
|
||||
ieCompat: true,
|
||||
strictMath: false,
|
||||
strictUnits: false
|
||||
};
|
||||
var options = require('../default-options');
|
||||
|
||||
var continueProcessing = true,
|
||||
currentErrorcode;
|
||||
currentErrorcode;
|
||||
|
||||
var checkArgFunc = function(arg, option) {
|
||||
if (!option) {
|
||||
@@ -226,8 +210,8 @@ function writeFile(filename, content) {
|
||||
break;
|
||||
case 'h':
|
||||
case 'help':
|
||||
//TODO
|
||||
// require('../lib/less/lessc_helper').printUsage();
|
||||
// TODO
|
||||
// require('../lib/less/lessc_helper').printUsage();
|
||||
continueProcessing = false;
|
||||
break;
|
||||
case 'x':
|
||||
@@ -270,7 +254,7 @@ function writeFile(filename, content) {
|
||||
.split(os.type().match(/Windows/) ? /:(?!\\)|;/ : ':')
|
||||
.map(function(p) {
|
||||
if (p) {
|
||||
// return path.resolve(process.cwd(), p);
|
||||
// return path.resolve(process.cwd(), p);
|
||||
return p;
|
||||
}
|
||||
});
|
||||
@@ -351,20 +335,20 @@ function writeFile(filename, content) {
|
||||
|
||||
var name = args[0];
|
||||
if (name && name != '-') {
|
||||
// name = path.resolve(process.cwd(), name);
|
||||
// name = path.resolve(process.cwd(), name);
|
||||
}
|
||||
var output = args[1];
|
||||
var outputbase = args[1];
|
||||
if (output) {
|
||||
options.sourceMapOutputFilename = output;
|
||||
// output = path.resolve(process.cwd(), output);
|
||||
// output = path.resolve(process.cwd(), output);
|
||||
if (warningMessages) {
|
||||
console.log(warningMessages);
|
||||
}
|
||||
}
|
||||
|
||||
// options.sourceMapBasepath = process.cwd();
|
||||
// options.sourceMapBasepath = '';
|
||||
// options.sourceMapBasepath = process.cwd();
|
||||
// options.sourceMapBasepath = '';
|
||||
|
||||
if (options.sourceMap === true) {
|
||||
console.log("output: " + output);
|
||||
@@ -382,24 +366,25 @@ function writeFile(filename, content) {
|
||||
console.log("lessc: no inout files");
|
||||
console.log("");
|
||||
// TODO
|
||||
// require('../lib/less/lessc_helper').printUsage();
|
||||
// require('../lib/less/lessc_helper').printUsage();
|
||||
currentErrorcode = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// var ensureDirectory = function (filepath) {
|
||||
// var dir = path.dirname(filepath),
|
||||
// cmd,
|
||||
// existsSync = fs.existsSync || path.existsSync;
|
||||
// if (!existsSync(dir)) {
|
||||
// if (mkdirp === undefined) {
|
||||
// try {mkdirp = require('mkdirp');}
|
||||
// catch(e) { mkdirp = null; }
|
||||
// }
|
||||
// cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
|
||||
// cmd(dir);
|
||||
// }
|
||||
// };
|
||||
/*
|
||||
var ensureDirectory = function (filepath) {
|
||||
var dir = path.dirname(filepath),
|
||||
cmd,
|
||||
existsSync = fs.existsSync || path.existsSync;
|
||||
if (!existsSync(dir)) {
|
||||
if (mkdirp === undefined) {
|
||||
try {mkdirp = require('mkdirp');}
|
||||
catch(e) { mkdirp = null; }
|
||||
}
|
||||
cmd = mkdirp && mkdirp.sync || fs.mkdirSync;
|
||||
cmd(dir);
|
||||
}
|
||||
}; */
|
||||
|
||||
if (options.depends) {
|
||||
if (!outputbase) {
|
||||
@@ -443,7 +428,7 @@ function writeFile(filename, content) {
|
||||
}
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
catch (e) {
|
||||
writeError(e, options);
|
||||
quit(1);
|
||||
}
|
||||
|
||||
@@ -40,18 +40,18 @@ contexts.Parse = function(options) {
|
||||
};
|
||||
|
||||
var evalCopyProperties = [
|
||||
'paths', // additional include paths
|
||||
'compress', // whether to compress
|
||||
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
|
||||
'strictMath', // whether math has to be within parenthesis
|
||||
'strictUnits', // whether units need to evaluate correctly
|
||||
'sourceMap', // whether to output a source map
|
||||
'importMultiple', // whether we are currently importing multiple copies
|
||||
'urlArgs', // whether to add args into url tokens
|
||||
'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
|
||||
'pluginManager', // Used as the plugin manager for the session
|
||||
'importantScope' // used to bubble up !important statements
|
||||
];
|
||||
'paths', // additional include paths
|
||||
'compress', // whether to compress
|
||||
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
|
||||
'strictMath', // whether math has to be within parenthesis
|
||||
'strictUnits', // whether units need to evaluate correctly
|
||||
'sourceMap', // whether to output a source map
|
||||
'importMultiple', // whether we are currently importing multiple copies
|
||||
'urlArgs', // whether to add args into url tokens
|
||||
'javascriptEnabled', // option - whether Inline JavaScript is enabled. if undefined, defaults to false
|
||||
'pluginManager', // Used as the plugin manager for the session
|
||||
'importantScope' // used to bubble up !important statements
|
||||
];
|
||||
|
||||
contexts.Eval = function(options, frames) {
|
||||
copyFromOriginal(options, this, evalCopyProperties);
|
||||
@@ -73,7 +73,11 @@ contexts.Eval.prototype.outOfParenthesis = function () {
|
||||
this.parensStack.pop();
|
||||
};
|
||||
|
||||
contexts.Eval.prototype.mathOn = true;
|
||||
contexts.Eval.prototype.isMathOn = function () {
|
||||
if (!this.mathOn) {
|
||||
return false;
|
||||
}
|
||||
return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
|
||||
};
|
||||
|
||||
@@ -83,13 +87,13 @@ contexts.Eval.prototype.isPathRelative = function (path) {
|
||||
|
||||
contexts.Eval.prototype.normalizePath = function( path ) {
|
||||
var
|
||||
segments = path.split("/").reverse(),
|
||||
segment;
|
||||
segments = path.split("/").reverse(),
|
||||
segment;
|
||||
|
||||
path = [];
|
||||
while (segments.length !== 0 ) {
|
||||
segment = segments.pop();
|
||||
switch( segment ) {
|
||||
switch ( segment ) {
|
||||
case ".":
|
||||
break;
|
||||
case "..":
|
||||
@@ -108,4 +112,4 @@ contexts.Eval.prototype.normalizePath = function( path ) {
|
||||
return path.join("/");
|
||||
};
|
||||
|
||||
//todo - do the same for the toCSS ?
|
||||
// todo - do the same for the toCSS ?
|
||||
|
||||
65
lib/less/default-options.js
Normal file
65
lib/less/default-options.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// Export a new default each time
|
||||
module.exports = function() {
|
||||
return {
|
||||
/* Outputs a makefile import dependency list to stdout. */
|
||||
depends: false,
|
||||
|
||||
/* Compress using less built-in compression.
|
||||
* This does an okay job but does not utilise all the tricks of
|
||||
* dedicated css compression. */
|
||||
compress: false,
|
||||
|
||||
/* Runs the less parser and just reports errors without any output. */
|
||||
lint: false,
|
||||
|
||||
/* Sets available include paths.
|
||||
* If the file in an @import rule does not exist at that exact location,
|
||||
* less will look for it at the location(s) passed to this option.
|
||||
* You might use this for instance to specify a path to a library which
|
||||
* you want to be referenced simply and relatively in the less files. */
|
||||
paths: [],
|
||||
|
||||
/* color output in the terminal */
|
||||
color: true,
|
||||
|
||||
/* The strictImports controls whether the compiler will allow an @import inside of either
|
||||
* @media blocks or (a later addition) other selector blocks.
|
||||
* See: https://github.com/less/less.js/issues/656 */
|
||||
strictImports: false,
|
||||
|
||||
/* Allow Imports from Insecure HTTPS Hosts */
|
||||
insecure: false,
|
||||
|
||||
/* Allows you to add a path to every generated import and url in your css.
|
||||
* This does not affect less import statements that are processed, just ones
|
||||
* that are left in the output css. */
|
||||
rootpath: '',
|
||||
|
||||
/* By default URLs are kept as-is, so if you import a file in a sub-directory
|
||||
* that references an image, exactly the same URL will be output in the css.
|
||||
* This option allows you to re-write URL's in imported files so that the
|
||||
* URL is always relative to the base imported file */
|
||||
relativeUrls: false,
|
||||
|
||||
/* Compatibility with IE8. Used for limiting data-uri length */
|
||||
ieCompat: false, // true until 3.0
|
||||
|
||||
/* Without this option on, Less will try and process all math in your css */
|
||||
strictMath: false,
|
||||
|
||||
/* Without this option, less attempts to guess at the output unit when it does maths. */
|
||||
strictUnits: false,
|
||||
|
||||
/* Effectively the declaration is put at the top of your base Less file,
|
||||
* meaning it can be used but it also can be overridden if this variable
|
||||
* is defined in the file. */
|
||||
globalVars: null,
|
||||
|
||||
/* As opposed to the global variable option, this puts the declaration at the
|
||||
* end of your base file, meaning it will override anything defined in your Less file. */
|
||||
modifyVars: null,
|
||||
|
||||
/* This option allows you to specify a argument to go on to every URL. */
|
||||
urlArgs: ''
|
||||
}
|
||||
}
|
||||
@@ -35,13 +35,14 @@ abstractFileManager.prototype.alwaysMakePathsAbsolute = function() {
|
||||
abstractFileManager.prototype.isPathAbsolute = function(filename) {
|
||||
return (/^(?:[a-z-]+:|\/|\\|#)/i).test(filename);
|
||||
};
|
||||
|
||||
// TODO: pull out / replace?
|
||||
abstractFileManager.prototype.join = function(basePath, laterPath) {
|
||||
if (!basePath) {
|
||||
return laterPath;
|
||||
}
|
||||
return basePath + laterPath;
|
||||
};
|
||||
|
||||
abstractFileManager.prototype.pathDiff = function pathDiff(url, baseUrl) {
|
||||
// diff between two paths to create a relative path
|
||||
|
||||
@@ -75,7 +76,7 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
|
||||
|
||||
var urlPartsRegex = /^((?:[a-z-]+:)?\/{2}(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
|
||||
urlParts = url.match(urlPartsRegex),
|
||||
returner = {}, directories = [], i, baseUrlParts;
|
||||
returner = {}, rawDirectories = [], directories = [], i, baseUrlParts;
|
||||
|
||||
if (!urlParts) {
|
||||
throw new Error("Could not parse sheet href - '" + url + "'");
|
||||
@@ -94,27 +95,26 @@ abstractFileManager.prototype.extractUrlParts = function extractUrlParts(url, ba
|
||||
}
|
||||
|
||||
if (urlParts[3]) {
|
||||
directories = urlParts[3].replace(/\\/g, "/").split("/");
|
||||
rawDirectories = urlParts[3].replace(/\\/g, "/").split("/");
|
||||
|
||||
// extract out . before .. so .. doesn't absorb a non-directory
|
||||
for (i = 0; i < directories.length; i++) {
|
||||
if (directories[i] === ".") {
|
||||
directories.splice(i, 1);
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
// collapse '..' and skip '.'
|
||||
for (i = 0; i < rawDirectories.length; i++) {
|
||||
|
||||
for (i = 0; i < directories.length; i++) {
|
||||
if (directories[i] === ".." && i > 0) {
|
||||
directories.splice(i - 1, 2);
|
||||
i -= 2;
|
||||
if (rawDirectories[i] === "..") {
|
||||
directories.pop();
|
||||
}
|
||||
else if (rawDirectories[i] !== ".") {
|
||||
directories.push(rawDirectories[i]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
returner.hostPart = urlParts[1];
|
||||
returner.directories = directories;
|
||||
returner.rawPath = (urlParts[1] || "") + rawDirectories.join("/");
|
||||
returner.path = (urlParts[1] || "") + directories.join("/");
|
||||
returner.filename = urlParts[4];
|
||||
returner.fileUrl = returner.path + (urlParts[4] || "");
|
||||
returner.url = returner.fileUrl + (urlParts[5] || "");
|
||||
return returner;
|
||||
|
||||
169
lib/less/environment/abstract-plugin-loader.js
Normal file
169
lib/less/environment/abstract-plugin-loader.js
Normal file
@@ -0,0 +1,169 @@
|
||||
var functionRegistry = require("../functions/function-registry"),
|
||||
LessError = require('../less-error');
|
||||
|
||||
var AbstractPluginLoader = function() {
|
||||
};
|
||||
|
||||
function error(msg, type) {
|
||||
throw new LessError(
|
||||
{
|
||||
type: type || 'Syntax',
|
||||
message: msg
|
||||
}
|
||||
);
|
||||
}
|
||||
AbstractPluginLoader.prototype.evalPlugin = function(contents, context, imports, pluginOptions, fileInfo) {
|
||||
|
||||
var loader,
|
||||
registry,
|
||||
pluginObj,
|
||||
localModule,
|
||||
pluginManager,
|
||||
filename;
|
||||
|
||||
pluginManager = context.pluginManager;
|
||||
|
||||
if (fileInfo) {
|
||||
if (typeof fileInfo === "string") {
|
||||
filename = fileInfo;
|
||||
}
|
||||
else {
|
||||
filename = fileInfo.filename;
|
||||
}
|
||||
}
|
||||
var shortname = (new this.less.FileManager()).extractUrlParts(filename).filename;
|
||||
|
||||
if (filename) {
|
||||
pluginObj = pluginManager.get(filename);
|
||||
|
||||
if (pluginObj) {
|
||||
this.trySetOptions(pluginObj, filename, shortname, pluginOptions);
|
||||
try {
|
||||
if (pluginObj.use) {
|
||||
pluginObj.use.call(this.context, pluginObj);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
e.message = 'Error during @plugin call';
|
||||
return new this.less.LessError(e, imports, filename);
|
||||
}
|
||||
return pluginObj;
|
||||
}
|
||||
}
|
||||
localModule = {
|
||||
exports: {},
|
||||
pluginManager: pluginManager,
|
||||
fileInfo: fileInfo
|
||||
};
|
||||
registry = functionRegistry.create();
|
||||
|
||||
var registerPlugin = function(obj) {
|
||||
pluginObj = obj;
|
||||
};
|
||||
|
||||
try {
|
||||
loader = new Function("module", "require", "registerPlugin", "functions", "tree", "less", "fileInfo", contents);
|
||||
loader(localModule, this.require, registerPlugin, registry, this.less.tree, this.less, fileInfo);
|
||||
} catch (e) {
|
||||
return new this.less.LessError(e, imports, filename);
|
||||
}
|
||||
|
||||
if (!pluginObj) {
|
||||
pluginObj = localModule.exports;
|
||||
}
|
||||
pluginObj = this.validatePlugin(pluginObj, filename, shortname);
|
||||
|
||||
if (pluginObj) {
|
||||
// Run on first load
|
||||
pluginManager.addPlugin(pluginObj, fileInfo.filename, registry);
|
||||
pluginObj.functions = registry.getLocalFunctions();
|
||||
pluginObj.imports = imports;
|
||||
pluginObj.filename = filename;
|
||||
|
||||
this.trySetOptions(pluginObj, filename, shortname, pluginOptions);
|
||||
|
||||
// Run every @plugin call
|
||||
try {
|
||||
if (pluginObj.use) {
|
||||
pluginObj.use.call(this.context, pluginObj);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
e.message = 'Error during @plugin call';
|
||||
return new this.less.LessError(e, imports, filename);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
return new this.less.LessError({ message: "Not a valid plugin" });
|
||||
}
|
||||
|
||||
return pluginObj;
|
||||
|
||||
};
|
||||
|
||||
AbstractPluginLoader.prototype.trySetOptions = function(plugin, filename, name, options) {
|
||||
if (options) {
|
||||
if (!plugin.setOptions) {
|
||||
error("Options have been provided but the plugin " + name + " does not support any options.");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
plugin.setOptions(options);
|
||||
}
|
||||
catch (e) {
|
||||
error("Error setting options on plugin " + name + '\n' + e.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AbstractPluginLoader.prototype.validatePlugin = function(plugin, filename, name) {
|
||||
if (plugin) {
|
||||
// support plugins being a function
|
||||
// so that the plugin can be more usable programmatically
|
||||
if (typeof plugin === "function") {
|
||||
plugin = new plugin();
|
||||
}
|
||||
|
||||
if (plugin.minVersion) {
|
||||
if (this.compareVersion(plugin.minVersion, this.less.version) < 0) {
|
||||
error("Plugin " + name + " requires version " + this.versionToString(plugin.minVersion));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return plugin;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
AbstractPluginLoader.prototype.compareVersion = function(aVersion, bVersion) {
|
||||
if (typeof aVersion === "string") {
|
||||
aVersion = aVersion.match(/^(\d+)\.?(\d+)?\.?(\d+)?/);
|
||||
aVersion.shift();
|
||||
}
|
||||
for (var i = 0; i < aVersion.length; i++) {
|
||||
if (aVersion[i] !== bVersion[i]) {
|
||||
return parseInt(aVersion[i]) > parseInt(bVersion[i]) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
AbstractPluginLoader.prototype.versionToString = function(version) {
|
||||
var versionString = "";
|
||||
for (var i = 0; i < version.length; i++) {
|
||||
versionString += (versionString ? "." : "") + version[i];
|
||||
}
|
||||
return versionString;
|
||||
};
|
||||
AbstractPluginLoader.prototype.printUsage = function(plugins) {
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
var plugin = plugins[i];
|
||||
if (plugin.printUsage) {
|
||||
plugin.printUsage();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = AbstractPluginLoader;
|
||||
|
||||
15
lib/less/functions/boolean.js
Normal file
15
lib/less/functions/boolean.js
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
var functionRegistry = require("./function-registry"),
|
||||
Anonymous = require("../tree/anonymous"),
|
||||
Keyword = require("../tree/keyword");
|
||||
|
||||
functionRegistry.addMultiple({
|
||||
boolean: function(condition) {
|
||||
return condition ? Keyword.True : Keyword.False;
|
||||
},
|
||||
|
||||
'if': function(condition, trueValue, falseValue) {
|
||||
return condition ? trueValue
|
||||
: (falseValue || new Anonymous);
|
||||
}
|
||||
});
|
||||
@@ -278,7 +278,7 @@ colorFunctions = {
|
||||
if (typeof dark === 'undefined') {
|
||||
dark = colorFunctions.rgba(0, 0, 0, 1.0);
|
||||
}
|
||||
//Figure out which is actually light and dark!
|
||||
// Figure out which is actually light and dark:
|
||||
if (dark.luma() > light.luma()) {
|
||||
var t = light;
|
||||
light = dark;
|
||||
@@ -295,6 +295,44 @@ colorFunctions = {
|
||||
return dark;
|
||||
}
|
||||
},
|
||||
// Changes made in 2.7.0 - Reverted in 3.0.0
|
||||
// contrast: function (color, color1, color2, threshold) {
|
||||
// // Return which of `color1` and `color2` has the greatest contrast with `color`
|
||||
// // according to the standard WCAG contrast ratio calculation.
|
||||
// // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
||||
// // The threshold param is no longer used, in line with SASS.
|
||||
// // filter: contrast(3.2);
|
||||
// // should be kept as is, so check for color
|
||||
// if (!color.rgb) {
|
||||
// return null;
|
||||
// }
|
||||
// if (typeof color1 === 'undefined') {
|
||||
// color1 = colorFunctions.rgba(0, 0, 0, 1.0);
|
||||
// }
|
||||
// if (typeof color2 === 'undefined') {
|
||||
// color2 = colorFunctions.rgba(255, 255, 255, 1.0);
|
||||
// }
|
||||
// var contrast1, contrast2;
|
||||
// var luma = color.luma();
|
||||
// var luma1 = color1.luma();
|
||||
// var luma2 = color2.luma();
|
||||
// // Calculate contrast ratios for each color
|
||||
// if (luma > luma1) {
|
||||
// contrast1 = (luma + 0.05) / (luma1 + 0.05);
|
||||
// } else {
|
||||
// contrast1 = (luma1 + 0.05) / (luma + 0.05);
|
||||
// }
|
||||
// if (luma > luma2) {
|
||||
// contrast2 = (luma + 0.05) / (luma2 + 0.05);
|
||||
// } else {
|
||||
// contrast2 = (luma2 + 0.05) / (luma + 0.05);
|
||||
// }
|
||||
// if (contrast1 > contrast2) {
|
||||
// return color1;
|
||||
// } else {
|
||||
// return color2;
|
||||
// }
|
||||
// },
|
||||
argb: function (color) {
|
||||
return new Anonymous(color.toARGB());
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module.exports = function(environment) {
|
||||
var Quoted = require("../tree/quoted"),
|
||||
URL = require("../tree/url"),
|
||||
utils = require('../utils'),
|
||||
functionRegistry = require("./function-registry"),
|
||||
fallback = function(functionThis, node) {
|
||||
return new URL(node, functionThis.index, functionThis.currentFileInfo).eval(functionThis.context);
|
||||
@@ -26,8 +27,10 @@ module.exports = function(environment) {
|
||||
fragment = filePath.slice(fragmentStart);
|
||||
filePath = filePath.slice(0, fragmentStart);
|
||||
}
|
||||
var context = utils.clone(this.context);
|
||||
context.rawBuffer = true;
|
||||
|
||||
var fileManager = environment.getFileManager(filePath, currentDirectory, this.context, environment, true);
|
||||
var fileManager = environment.getFileManager(filePath, currentDirectory, context, environment, true);
|
||||
|
||||
if (!fileManager) {
|
||||
return fallback(this, filePathNode);
|
||||
@@ -53,7 +56,7 @@ module.exports = function(environment) {
|
||||
useBase64 = /;base64$/.test(mimetype);
|
||||
}
|
||||
|
||||
var fileSync = fileManager.loadFileSync(filePath, currentDirectory, this.context, environment);
|
||||
var fileSync = fileManager.loadFileSync(filePath, currentDirectory, context, environment);
|
||||
if (!fileSync.contents) {
|
||||
logger.warn("Skipped data-uri embedding of " + filePath + " because file not found");
|
||||
return fallback(this, filePathNode || mimetypeNode);
|
||||
|
||||
@@ -11,8 +11,8 @@ var functionCaller = function(name, context, index, currentFileInfo) {
|
||||
functionCaller.prototype.isValid = function() {
|
||||
return Boolean(this.func);
|
||||
};
|
||||
functionCaller.prototype.call = function(args) {
|
||||
|
||||
functionCaller.prototype.call = function(args) {
|
||||
// This code is terrible and should be replaced as per this issue...
|
||||
// https://github.com/less/less.js/issues/2477
|
||||
if (Array.isArray(args)) {
|
||||
|
||||
@@ -7,7 +7,7 @@ function makeRegistry( base ) {
|
||||
name = name.toLowerCase();
|
||||
|
||||
if (this._data.hasOwnProperty(name)) {
|
||||
//TODO warn
|
||||
// TODO warn
|
||||
}
|
||||
this._data[name] = func;
|
||||
},
|
||||
@@ -20,8 +20,14 @@ function makeRegistry( base ) {
|
||||
get: function(name) {
|
||||
return this._data[name] || ( base && base.get( name ));
|
||||
},
|
||||
inherit : function() {
|
||||
getLocalFunctions: function() {
|
||||
return this._data;
|
||||
},
|
||||
inherit: function() {
|
||||
return makeRegistry( this );
|
||||
},
|
||||
create: function(base) {
|
||||
return makeRegistry(base);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ module.exports = function(environment) {
|
||||
functionCaller: require("./function-caller")
|
||||
};
|
||||
|
||||
//register functions
|
||||
// register functions
|
||||
require("./boolean");
|
||||
require("./default");
|
||||
require("./color");
|
||||
require("./color-blending");
|
||||
|
||||
@@ -5,7 +5,7 @@ var Dimension = require("../tree/dimension"),
|
||||
|
||||
var minMax = function (isMin, args) {
|
||||
args = Array.prototype.slice.call(args);
|
||||
switch(args.length) {
|
||||
switch (args.length) {
|
||||
case 0: throw { type: "Argument", message: "one or more arguments required" };
|
||||
}
|
||||
var i, j, current, currentUnified, referenceUnified, unit, unitStatic, unitClone,
|
||||
@@ -27,7 +27,7 @@ var minMax = function (isMin, args) {
|
||||
j = values[""] !== undefined && unit !== "" && unit === unitStatic ? values[""] : values[unit];
|
||||
if (j === undefined) {
|
||||
if (unitStatic !== undefined && unit !== unitStatic) {
|
||||
throw{ type: "Argument", message: "incompatible types" };
|
||||
throw { type: "Argument", message: "incompatible types" };
|
||||
}
|
||||
values[unit] = order.length;
|
||||
order.push(current);
|
||||
|
||||
@@ -19,12 +19,12 @@ functionRegistry.addMultiple({
|
||||
result = result.replace(new RegExp(pattern.value, flags ? flags.value : ''), replacement);
|
||||
return new Quoted(string.quote || '', result, string.escaped);
|
||||
},
|
||||
'%': function (string /* arg, arg, ...*/) {
|
||||
'%': function (string /* arg, arg, ... */) {
|
||||
var args = Array.prototype.slice.call(arguments, 1),
|
||||
result = string.value;
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
/*jshint loopfunc:true */
|
||||
/* jshint loopfunc:true */
|
||||
result = result.replace(/%[sda]/i, function(token) {
|
||||
var value = ((args[i].type === "Quoted") &&
|
||||
token.match(/s/i)) ? args[i].value : args[i].toCSS();
|
||||
|
||||
@@ -15,12 +15,12 @@ module.exports = function(environment) {
|
||||
renderEnv = {compress: false},
|
||||
returner,
|
||||
directionValue = direction.toCSS(renderEnv),
|
||||
i, color, position, positionValue, alpha;
|
||||
i, color, position, positionValue, alpha;
|
||||
|
||||
function throwArgumentDescriptor() {
|
||||
throw { type: "Argument",
|
||||
message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
|
||||
" end_color [end_position] or direction, color list" };
|
||||
message: "svg-gradient expects direction, start_color [start_position], [color position,]...," +
|
||||
" end_color [end_position] or direction, color list" };
|
||||
}
|
||||
|
||||
if (arguments.length == 2) {
|
||||
@@ -61,7 +61,7 @@ module.exports = function(environment) {
|
||||
'<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' +
|
||||
'<' + gradientType + 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' + gradientDirectionSvg + '>';
|
||||
|
||||
for (i = 0; i < stops.length; i+= 1) {
|
||||
for (i = 0; i < stops.length; i += 1) {
|
||||
if (stops[i] instanceof Expression) {
|
||||
color = stops[i].value[0];
|
||||
position = stops[i].value[1];
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
var contexts = require("./contexts"),
|
||||
Parser = require('./parser/parser'),
|
||||
FunctionImporter = require('./plugins/function-importer');
|
||||
LessError = require('./less-error'),
|
||||
utils = require('./utils'),
|
||||
PromiseConstructor = typeof Promise === 'undefined' ? require('promise') : Promise;
|
||||
|
||||
module.exports = function(environment) {
|
||||
|
||||
@@ -13,7 +15,8 @@ module.exports = function(environment) {
|
||||
// 'entryPath' - absolute path to the entry file
|
||||
// 'reference' - whether the file should not be output and only output parts that are referenced
|
||||
|
||||
var ImportManager = function(context, rootFileInfo) {
|
||||
var ImportManager = function(less, context, rootFileInfo) {
|
||||
this.less = less;
|
||||
this.rootFilename = rootFileInfo.filename;
|
||||
this.paths = context.paths || []; // Search paths, when importing
|
||||
this.contents = {}; // map - filename to contents of all the files
|
||||
@@ -25,16 +28,19 @@ module.exports = function(environment) {
|
||||
this.queue = []; // Files which haven't been imported yet
|
||||
this.files = {}; // Holds the imported parse trees.
|
||||
};
|
||||
|
||||
/**
|
||||
* Add an import to be imported
|
||||
* @param path - the raw path
|
||||
* @param tryAppendLessExtension - whether to try appending the less extension (if the path has no extension)
|
||||
* @param tryAppendExtension - whether to try appending a file extension (.less or .js if the path has no extension)
|
||||
* @param currentFileInfo - the current file info (used for instance to work out relative paths)
|
||||
* @param importOptions - import options
|
||||
* @param callback - callback for when it is imported
|
||||
*/
|
||||
ImportManager.prototype.push = function (path, tryAppendLessExtension, currentFileInfo, importOptions, callback) {
|
||||
var importManager = this;
|
||||
ImportManager.prototype.push = function (path, tryAppendExtension, currentFileInfo, importOptions, callback) {
|
||||
var importManager = this,
|
||||
pluginLoader = this.context.pluginManager.Loader;
|
||||
|
||||
this.queue.push(path);
|
||||
|
||||
var fileParsedFunc = function (e, root, fullPath) {
|
||||
@@ -45,7 +51,9 @@ module.exports = function(environment) {
|
||||
callback(null, {rules:[]}, false, null);
|
||||
}
|
||||
else {
|
||||
importManager.files[fullPath] = root;
|
||||
if (!importManager.files[fullPath]) {
|
||||
importManager.files[fullPath] = { root: root, options: importOptions };
|
||||
}
|
||||
if (e && !importManager.error) { importManager.error = e; }
|
||||
callback(e, root, importedEqualsRoot, fullPath);
|
||||
}
|
||||
@@ -65,12 +73,9 @@ module.exports = function(environment) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tryAppendLessExtension) {
|
||||
path = fileManager.tryAppendExtension(path, importOptions.plugin ? ".js" : ".less");
|
||||
}
|
||||
|
||||
var loadFileCallback = function(loadedFile) {
|
||||
var resolvedFilename = loadedFile.filename,
|
||||
var plugin,
|
||||
resolvedFilename = loadedFile.filename,
|
||||
contents = loadedFile.contents.replace(/^\uFEFF/, '');
|
||||
|
||||
// Pass on an updated rootpath if path of imported file is relative and file
|
||||
@@ -102,30 +107,56 @@ module.exports = function(environment) {
|
||||
newFileInfo.reference = true;
|
||||
}
|
||||
|
||||
if (importOptions.plugin) {
|
||||
new FunctionImporter(newEnv, newFileInfo).eval(contents, function (e, root) {
|
||||
fileParsedFunc(e, root, resolvedFilename);
|
||||
});
|
||||
if (importOptions.isPlugin) {
|
||||
plugin = pluginLoader.evalPlugin(contents, newEnv, importManager, importOptions.pluginArgs, newFileInfo);
|
||||
if (plugin instanceof LessError) {
|
||||
fileParsedFunc(plugin, null, resolvedFilename);
|
||||
}
|
||||
else {
|
||||
fileParsedFunc(null, plugin, resolvedFilename);
|
||||
}
|
||||
} else if (importOptions.inline) {
|
||||
fileParsedFunc(null, contents, resolvedFilename);
|
||||
} else {
|
||||
new Parser(newEnv, importManager, newFileInfo).parse(contents, function (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);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
var promise, context = utils.clone(this.context);
|
||||
|
||||
var promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, this.context, environment,
|
||||
function(err, loadedFile) {
|
||||
if (err) {
|
||||
fileParsedFunc(err);
|
||||
} else {
|
||||
loadFileCallback(loadedFile);
|
||||
}
|
||||
});
|
||||
if (tryAppendExtension) {
|
||||
context.ext = importOptions.isPlugin ? ".js" : ".less";
|
||||
}
|
||||
|
||||
if (importOptions.isPlugin) {
|
||||
promise = pluginLoader.loadPlugin(path, currentFileInfo.currentDirectory, context, environment, fileManager);
|
||||
}
|
||||
else {
|
||||
promise = fileManager.loadFile(path, currentFileInfo.currentDirectory, context, environment,
|
||||
function(err, loadedFile) {
|
||||
if (err) {
|
||||
fileParsedFunc(err);
|
||||
} else {
|
||||
loadFileCallback(loadedFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (promise) {
|
||||
promise.then(loadFileCallback, fileParsedFunc);
|
||||
}
|
||||
|
||||
};
|
||||
return ImportManager;
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
module.exports = function(environment, fileManagers) {
|
||||
var SourceMapOutput, SourceMapBuilder, ParseTree, ImportManager, Environment;
|
||||
|
||||
var less = {
|
||||
version: [2, 7, 3],
|
||||
var initial = {
|
||||
version: [3, 0, 0],
|
||||
data: require('./data'),
|
||||
tree: require('./tree'),
|
||||
Environment: (Environment = require("./environment/environment")),
|
||||
AbstractFileManager: require("./environment/abstract-file-manager"),
|
||||
AbstractPluginLoader: require("./environment/abstract-plugin-loader"),
|
||||
environment: (environment = new Environment(environment, fileManagers)),
|
||||
visitors: require('./visitors'),
|
||||
Parser: require('./parser/parser'),
|
||||
@@ -25,5 +26,30 @@ module.exports = function(environment, fileManagers) {
|
||||
logger: require('./logger')
|
||||
};
|
||||
|
||||
return less;
|
||||
// Create a public API
|
||||
|
||||
var ctor = function(t) {
|
||||
return function() {
|
||||
var obj = Object.create(t.prototype);
|
||||
t.apply(obj, Array.prototype.slice.call(arguments, 0));
|
||||
return obj;
|
||||
};
|
||||
};
|
||||
var t, api = Object.create(initial);
|
||||
for (var n in initial.tree) {
|
||||
/* eslint guard-for-in: 0 */
|
||||
t = initial.tree[n];
|
||||
if (typeof t === "function") {
|
||||
api[n.toLowerCase()] = ctor(t);
|
||||
}
|
||||
else {
|
||||
api[n] = Object.create(null);
|
||||
for (var o in t) {
|
||||
/* eslint guard-for-in: 0 */
|
||||
api[n][o.toLowerCase()] = ctor(t[o]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return api;
|
||||
};
|
||||
|
||||
@@ -1,11 +1,34 @@
|
||||
var utils = require("./utils");
|
||||
|
||||
var utils = require('./utils');
|
||||
/**
|
||||
* This is a centralized class of any error that could be thrown internally (mostly by the parser).
|
||||
* Besides standard .message it keeps some additional data like a path to the file where the error
|
||||
* occurred along with line and column numbers.
|
||||
*
|
||||
* @class
|
||||
* @extends Error
|
||||
* @type {module.LessError}
|
||||
*
|
||||
* @prop {string} type
|
||||
* @prop {string} filename
|
||||
* @prop {number} index
|
||||
* @prop {number} line
|
||||
* @prop {number} column
|
||||
* @prop {number} callLine
|
||||
* @prop {number} callExtract
|
||||
* @prop {string[]} extract
|
||||
*
|
||||
* @param {Object} e - An error object to wrap around or just a descriptive object
|
||||
* @param {Object} importManager - An instance of ImportManager (see import-manager.js)
|
||||
* @param {string} [currentFilename]
|
||||
*/
|
||||
var LessError = module.exports = function LessError(e, importManager, currentFilename) {
|
||||
|
||||
Error.call(this);
|
||||
|
||||
var filename = e.filename || currentFilename;
|
||||
|
||||
this.message = e.message;
|
||||
this.stack = e.stack;
|
||||
|
||||
if (importManager && filename) {
|
||||
var input = importManager.contents[filename],
|
||||
loc = utils.getLocation(e.index, input),
|
||||
@@ -18,17 +41,32 @@ var LessError = module.exports = function LessError(e, importManager, currentFil
|
||||
this.filename = filename;
|
||||
this.index = e.index;
|
||||
this.line = typeof line === 'number' ? line + 1 : null;
|
||||
this.column = col;
|
||||
|
||||
if (!this.line && this.stack) {
|
||||
var found = this.stack.match(/(<anonymous>|Function):(\d+):(\d+)/);
|
||||
|
||||
if (found) {
|
||||
if (found[2]) {
|
||||
this.line = parseInt(found[2]) - 2;
|
||||
}
|
||||
if (found[3]) {
|
||||
this.column = parseInt(found[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.callLine = callLine + 1;
|
||||
this.callExtract = lines[callLine];
|
||||
this.column = col;
|
||||
|
||||
this.extract = [
|
||||
lines[line - 1],
|
||||
lines[line],
|
||||
lines[line + 1]
|
||||
lines[this.line - 2],
|
||||
lines[this.line - 1],
|
||||
lines[this.line]
|
||||
];
|
||||
|
||||
}
|
||||
this.message = e.message;
|
||||
this.stack = e.stack;
|
||||
|
||||
};
|
||||
|
||||
if (typeof Object.create === 'undefined') {
|
||||
@@ -40,3 +78,64 @@ if (typeof Object.create === 'undefined') {
|
||||
}
|
||||
|
||||
LessError.prototype.constructor = LessError;
|
||||
|
||||
/**
|
||||
* An overridden version of the default Object.prototype.toString
|
||||
* which uses additional information to create a helpful message.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @returns {string}
|
||||
*/
|
||||
LessError.prototype.toString = function(options) {
|
||||
options = options || {};
|
||||
|
||||
var message = '';
|
||||
var extract = this.extract || [];
|
||||
var error = [];
|
||||
var stylize = function (str) { return str; };
|
||||
if (options.stylize) {
|
||||
var type = typeof options.stylize;
|
||||
if (type !== 'function') {
|
||||
throw Error('options.stylize should be a function, got a ' + type + '!');
|
||||
}
|
||||
stylize = options.stylize;
|
||||
}
|
||||
|
||||
if (this.line !== null) {
|
||||
if (typeof extract[0] === 'string') {
|
||||
error.push(stylize((this.line - 1) + ' ' + extract[0], 'grey'));
|
||||
}
|
||||
|
||||
if (typeof extract[1] === 'string') {
|
||||
var errorTxt = this.line + ' ';
|
||||
if (extract[1]) {
|
||||
errorTxt += extract[1].slice(0, this.column) +
|
||||
stylize(stylize(stylize(extract[1].substr(this.column, 1), 'bold') +
|
||||
extract[1].slice(this.column + 1), 'red'), 'inverse');
|
||||
}
|
||||
error.push(errorTxt);
|
||||
}
|
||||
|
||||
if (typeof extract[2] === 'string') {
|
||||
error.push(stylize((this.line + 1) + ' ' + extract[2], 'grey'));
|
||||
}
|
||||
error = error.join('\n') + stylize('', 'reset') + '\n';
|
||||
}
|
||||
|
||||
message += stylize(this.type + 'Error: ' + this.message, 'red');
|
||||
if (this.filename) {
|
||||
message += stylize(' in ', 'red') + this.filename;
|
||||
}
|
||||
if (this.line) {
|
||||
message += stylize(' on line ' + this.line + ', column ' + (this.column + 1) + ':', 'grey');
|
||||
}
|
||||
|
||||
message += '\n' + error;
|
||||
|
||||
if (this.callLine) {
|
||||
message += stylize('from ', 'red') + (this.filename || '') + '/n';
|
||||
message += stylize(this.callLine, 'grey') + ' ' + this.callExtract + '/n';
|
||||
}
|
||||
|
||||
return message;
|
||||
};
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
var PromiseConstructor,
|
||||
contexts = require("./contexts"),
|
||||
Parser = require('./parser/parser'),
|
||||
PluginManager = require('./plugin-manager');
|
||||
PluginManager = require('./plugin-manager'),
|
||||
LessError = require('./less-error'),
|
||||
utils = require('./utils');
|
||||
|
||||
module.exports = function(environment, ParseTree, ImportManager) {
|
||||
var parse = function (input, options, callback) {
|
||||
options = options || {};
|
||||
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
options = utils.defaults(this.options, {});
|
||||
}
|
||||
else {
|
||||
options = utils.defaults(this.options, options || {});
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
@@ -29,9 +33,8 @@ module.exports = function(environment, ParseTree, ImportManager) {
|
||||
} else {
|
||||
var context,
|
||||
rootFileInfo,
|
||||
pluginManager = new PluginManager(this);
|
||||
pluginManager = new PluginManager(this, true);
|
||||
|
||||
pluginManager.addPlugins(options.plugins);
|
||||
options.pluginManager = pluginManager;
|
||||
|
||||
context = new contexts.Parse(options);
|
||||
@@ -55,13 +58,33 @@ module.exports = function(environment, ParseTree, ImportManager) {
|
||||
}
|
||||
}
|
||||
|
||||
var imports = new ImportManager(context, rootFileInfo);
|
||||
var imports = new ImportManager(this, context, rootFileInfo);
|
||||
this.importManager = imports;
|
||||
|
||||
// TODO: allow the plugins to be just a list of paths or names
|
||||
// Do an async plugin queue like lessc
|
||||
|
||||
if (options.plugins) {
|
||||
options.plugins.forEach(function(plugin) {
|
||||
var evalResult, contents;
|
||||
if (plugin.fileContent) {
|
||||
contents = plugin.fileContent.replace(/^\uFEFF/, '');
|
||||
evalResult = pluginManager.Loader.evalPlugin(contents, context, imports, plugin.options, plugin.filename);
|
||||
if (evalResult instanceof LessError) {
|
||||
return callback(evalResult);
|
||||
}
|
||||
}
|
||||
else {
|
||||
pluginManager.addPlugin(plugin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
new Parser(context, imports, rootFileInfo)
|
||||
.parse(input, function (e, root) {
|
||||
if (e) { return callback(e); }
|
||||
callback(null, root, imports, options);
|
||||
}, options);
|
||||
if (e) { return callback(e); }
|
||||
callback(null, root, imports, options);
|
||||
}, options);
|
||||
}
|
||||
};
|
||||
return parse;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
var chunker = require('./chunker');
|
||||
|
||||
module.exports = function() {
|
||||
var input, // LeSS input string
|
||||
var input, // Less input string
|
||||
j, // current chunk
|
||||
saveStack = [], // holds state for backtracking
|
||||
furthest, // furthest index the parser has gone to
|
||||
furthestPossibleErrorMessage,// if this is furthest we got to, this is the probably cause
|
||||
furthestPossibleErrorMessage, // if this is furthest we got to, this is the probably cause
|
||||
chunks, // chunkified input
|
||||
current, // current chunk
|
||||
currentPos, // index of current chunk, in `input`
|
||||
@@ -156,7 +156,7 @@ module.exports = function() {
|
||||
|
||||
for (var i = 1; i + currentPosition < length; i++) {
|
||||
var nextChar = input.charAt(i + currentPosition);
|
||||
switch(nextChar) {
|
||||
switch (nextChar) {
|
||||
case "\\":
|
||||
i++;
|
||||
continue;
|
||||
@@ -209,7 +209,7 @@ module.exports = function() {
|
||||
|
||||
parserInput.peekNotNumeric = function() {
|
||||
var c = input.charCodeAt(parserInput.i);
|
||||
//Is the first char of the dimension 0-9, '.', '+' or '-'
|
||||
// Is the first char of the dimension 0-9, '.', '+' or '-'
|
||||
return (c > CHARCODE_9 || c < CHARCODE_PLUS) || c === CHARCODE_FORWARD_SLASH || c === CHARCODE_COMMA;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@ var LessError = require('../less-error'),
|
||||
tree = require("../tree"),
|
||||
visitors = require("../visitors"),
|
||||
getParserInput = require("./parser-input"),
|
||||
utils = require("../utils");
|
||||
utils = require("../utils"),
|
||||
functionRegistry = require('../functions/function-registry');
|
||||
|
||||
//
|
||||
// less.js - parser
|
||||
@@ -35,8 +36,8 @@ var LessError = require('../less-error'),
|
||||
// Token matching is done with the `$` function, which either takes
|
||||
// a terminal string or regexp, or a non-terminal function to call.
|
||||
// It also takes care of moving all the indices forwards.
|
||||
//`
|
||||
//
|
||||
|
||||
var Parser = function Parser(context, imports, fileInfo) {
|
||||
var parsers,
|
||||
parserInput = getParserInput();
|
||||
@@ -80,11 +81,61 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Used after initial parsing to create nodes on the fly
|
||||
*
|
||||
* @param {String} str - string to parse
|
||||
* @param {Array} parseList - array of parsers to run input through e.g. ["value", "important"]
|
||||
* @param {Number} currentIndex - start number to begin indexing
|
||||
* @param {Object} fileInfo - fileInfo to attach to created nodes
|
||||
*/
|
||||
function parseNode(str, parseList, currentIndex, fileInfo, callback) {
|
||||
var result, returnNodes = [];
|
||||
var parser = parserInput;
|
||||
|
||||
try {
|
||||
parser.start(str, false, function fail(msg, index) {
|
||||
callback({
|
||||
message: msg,
|
||||
index: index + currentIndex
|
||||
});
|
||||
});
|
||||
for (var x = 0, p, i; (p = parseList[x]); x++) {
|
||||
i = parser.i;
|
||||
result = parsers[p]();
|
||||
if (result) {
|
||||
result._index = i + currentIndex;
|
||||
result._fileInfo = fileInfo;
|
||||
returnNodes.push(result);
|
||||
}
|
||||
else {
|
||||
returnNodes.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
var endInfo = parser.end();
|
||||
if (endInfo.isFinished) {
|
||||
callback(null, returnNodes);
|
||||
}
|
||||
else {
|
||||
callback(true, null);
|
||||
}
|
||||
} catch (e) {
|
||||
throw new LessError({
|
||||
index: e.index + currentIndex,
|
||||
message: e.message
|
||||
}, imports, fileInfo.filename);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// The Parser
|
||||
//
|
||||
return {
|
||||
|
||||
parserInput: parserInput,
|
||||
imports: imports,
|
||||
fileInfo: fileInfo,
|
||||
parseNode: parseNode,
|
||||
//
|
||||
// Parse an input string into an abstract syntax tree,
|
||||
// @param str A string containing 'less' markup
|
||||
@@ -130,9 +181,13 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
}, imports);
|
||||
});
|
||||
|
||||
root = new(tree.Ruleset)(null, this.parsers.primary());
|
||||
tree.Node.prototype.parse = this;
|
||||
root = new tree.Ruleset(null, this.parsers.primary());
|
||||
tree.Node.prototype.rootNode = root;
|
||||
root.root = true;
|
||||
root.firstRoot = true;
|
||||
root.functionRegistry = functionRegistry.inherit();
|
||||
|
||||
} catch (e) {
|
||||
return callback(new LessError(e, imports, fileInfo.filename));
|
||||
}
|
||||
@@ -197,7 +252,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
//
|
||||
// The basic structure of the syntax tree generated is as follows:
|
||||
//
|
||||
// Ruleset -> Rule -> Value -> Expression -> Entity
|
||||
// Ruleset -> Declaration -> Value -> Expression -> Entity
|
||||
//
|
||||
// Here's some Less code:
|
||||
//
|
||||
@@ -211,9 +266,9 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// And here's what the parse tree might look like:
|
||||
//
|
||||
// Ruleset (Selector '.class', [
|
||||
// Rule ("color", Value ([Expression [Color #fff]]))
|
||||
// Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
|
||||
// Rule ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
|
||||
// Declaration ("color", Value ([Expression [Color #fff]]))
|
||||
// Declaration ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
|
||||
// Declaration ("width", Value ([Expression [Operation " + " [Variable "@w"][Dimension 4px]]]))
|
||||
// Ruleset (Selector [Element '>', '.child'], [...])
|
||||
// ])
|
||||
//
|
||||
@@ -230,7 +285,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
|
||||
// as represented by this simplified grammar:
|
||||
//
|
||||
// primary → (ruleset | rule)+
|
||||
// primary → (ruleset | declaration)+
|
||||
// ruleset → selector+ block
|
||||
// block → '{' primary '}'
|
||||
//
|
||||
@@ -260,8 +315,8 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
continue;
|
||||
}
|
||||
|
||||
node = mixin.definition() || this.rule() || this.ruleset() ||
|
||||
mixin.call() || this.rulesetCall() || this.entities.call() || this.directive();
|
||||
node = mixin.definition() || this.declaration() || this.ruleset() ||
|
||||
mixin.call() || this.variableCall() || this.entities.call() || this.atrule();
|
||||
if (node) {
|
||||
root.push(node);
|
||||
} else {
|
||||
@@ -319,7 +374,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// black border-collapse
|
||||
//
|
||||
keyword: function () {
|
||||
var k = parserInput.$char("%") || parserInput.$re(/^[_A-Za-z-][_A-Za-z0-9-]*/);
|
||||
var k = parserInput.$char("%") || parserInput.$re(/^\[?[_A-Za-z-][_A-Za-z0-9-]*\]?/);
|
||||
if (k) {
|
||||
return tree.Color.fromKeyword(k) || new(tree.Keyword)(k);
|
||||
}
|
||||
@@ -330,13 +385,10 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
//
|
||||
// rgb(255, 0, 255)
|
||||
//
|
||||
// We also try to catch IE's `alpha()`, but let the `alpha` parser
|
||||
// deal with the details.
|
||||
//
|
||||
// The arguments are parsed with the `entities.arguments` parser.
|
||||
//
|
||||
call: function () {
|
||||
var name, nameLC, args, alpha, index = parserInput.i;
|
||||
var name, args, func, index = parserInput.i;
|
||||
|
||||
// http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
|
||||
if (parserInput.peek(/^url\(/i)) {
|
||||
@@ -346,70 +398,98 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.save();
|
||||
|
||||
name = parserInput.$re(/^([\w-]+|%|progid:[\w\.]+)\(/);
|
||||
if (!name) { parserInput.forget(); return; }
|
||||
if (!name) {
|
||||
parserInput.forget();
|
||||
return;
|
||||
}
|
||||
|
||||
name = name[1];
|
||||
nameLC = name.toLowerCase();
|
||||
|
||||
if (nameLC === 'alpha') {
|
||||
alpha = parsers.alpha();
|
||||
if (alpha) {
|
||||
func = this.customFuncCall(name);
|
||||
if (func) {
|
||||
args = func.parse();
|
||||
if (args && func.stop) {
|
||||
parserInput.forget();
|
||||
return alpha;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
args = this.arguments();
|
||||
args = this.arguments(args);
|
||||
|
||||
if (! parserInput.$char(')')) {
|
||||
if (!parserInput.$char(')')) {
|
||||
parserInput.restore("Could not parse call arguments or missing ')'");
|
||||
return;
|
||||
}
|
||||
|
||||
parserInput.forget();
|
||||
|
||||
return new(tree.Call)(name, args, index, fileInfo);
|
||||
},
|
||||
arguments: function () {
|
||||
var argsSemiColon = [], argsComma = [],
|
||||
expressions = [],
|
||||
isSemiColonSeparated, value, arg;
|
||||
|
||||
//
|
||||
// Parsing rules for functions with non-standard args, e.g.:
|
||||
//
|
||||
// boolean(not(2 > 1))
|
||||
//
|
||||
// This is a quick prototype, to be modified/improved when
|
||||
// more custom-parsed funcs come (e.g. `selector(...)`)
|
||||
//
|
||||
|
||||
customFuncCall: function (name) {
|
||||
/* Ideally the table is to be moved out of here for faster perf.,
|
||||
but it's quite tricky since it relies on all these `parsers`
|
||||
and `expect` available only here */
|
||||
return {
|
||||
alpha: f(parsers.ieAlpha, true),
|
||||
boolean: f(condition),
|
||||
'if': f(condition)
|
||||
}[name.toLowerCase()];
|
||||
|
||||
function f(parse, stop) {
|
||||
return {
|
||||
parse: parse, // parsing function
|
||||
stop: stop // when true - stop after parse() and return its result,
|
||||
// otherwise continue for plain args
|
||||
};
|
||||
}
|
||||
|
||||
function condition() {
|
||||
return [expect(parsers.condition, 'expected condition')];
|
||||
}
|
||||
},
|
||||
|
||||
arguments: function (prevArgs) {
|
||||
var argsComma = prevArgs || [],
|
||||
argsSemiColon = [],
|
||||
isSemiColonSeparated, value;
|
||||
|
||||
parserInput.save();
|
||||
|
||||
while (true) {
|
||||
if (prevArgs) {
|
||||
prevArgs = false;
|
||||
} else {
|
||||
value = parsers.detachedRuleset() || this.assignment() || parsers.expression();
|
||||
if (!value) {
|
||||
break;
|
||||
}
|
||||
|
||||
arg = parsers.detachedRuleset() || this.assignment() || parsers.expression();
|
||||
if (value.value && value.value.length == 1) {
|
||||
value = value.value[0];
|
||||
}
|
||||
|
||||
if (!arg) {
|
||||
break;
|
||||
argsComma.push(value);
|
||||
}
|
||||
|
||||
value = arg;
|
||||
|
||||
if (arg.value && arg.value.length == 1) {
|
||||
value = arg.value[0];
|
||||
}
|
||||
|
||||
if (value) {
|
||||
expressions.push(value);
|
||||
}
|
||||
|
||||
argsComma.push(value);
|
||||
|
||||
if (parserInput.$char(',')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (parserInput.$char(';') || isSemiColonSeparated) {
|
||||
|
||||
isSemiColonSeparated = true;
|
||||
|
||||
if (expressions.length > 1) {
|
||||
value = new(tree.Value)(expressions);
|
||||
}
|
||||
value = (argsComma.length < 1) ? argsComma[0]
|
||||
: new tree.Value(argsComma);
|
||||
argsSemiColon.push(value);
|
||||
|
||||
expressions = [];
|
||||
argsComma = [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,15 +547,17 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
value = this.quoted() || this.variable() ||
|
||||
value = this.quoted() || this.variable() || this.property() ||
|
||||
parserInput.$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
|
||||
|
||||
parserInput.autoCommentAbsorb = true;
|
||||
|
||||
expectChar(')');
|
||||
|
||||
return new(tree.URL)((value.value != null || value instanceof tree.Variable) ?
|
||||
value : new(tree.Anonymous)(value), index, fileInfo);
|
||||
return new(tree.URL)((value.value != null ||
|
||||
value instanceof tree.Variable ||
|
||||
value instanceof tree.Property) ?
|
||||
value : new(tree.Anonymous)(value, index), index, fileInfo);
|
||||
},
|
||||
|
||||
//
|
||||
@@ -502,7 +584,27 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
return new(tree.Variable)("@" + curly[1], index, fileInfo);
|
||||
}
|
||||
},
|
||||
//
|
||||
// A Property accessor, such as `$color`, in
|
||||
//
|
||||
// background-color: $color
|
||||
//
|
||||
property: function () {
|
||||
var name, index = parserInput.i;
|
||||
|
||||
if (parserInput.currentChar() === '$' && (name = parserInput.$re(/^\$[\w-]+/))) {
|
||||
return new(tree.Property)(name, index, fileInfo);
|
||||
}
|
||||
},
|
||||
|
||||
// A property entity useing the protective {} e.g. ${prop}
|
||||
propertyCurly: function () {
|
||||
var curly, index = parserInput.i;
|
||||
|
||||
if (parserInput.currentChar() === '$' && (curly = parserInput.$re(/^\$\{([\w-]+)\}/))) {
|
||||
return new(tree.Property)("$" + curly[1], index, fileInfo);
|
||||
}
|
||||
},
|
||||
//
|
||||
// A Hexadecimal color
|
||||
//
|
||||
@@ -612,15 +714,17 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
},
|
||||
|
||||
//
|
||||
// The variable part of a variable definition. Used in the `rule` parser
|
||||
// Call a variable value
|
||||
//
|
||||
// @fink();
|
||||
// @fink()
|
||||
//
|
||||
rulesetCall: function () {
|
||||
variableCall: function () {
|
||||
var name;
|
||||
|
||||
if (parserInput.currentChar() === '@' && (name = parserInput.$re(/^(@[\w-]+)\(\s*\)\s*;/))) {
|
||||
return new tree.RulesetCall(name[1]);
|
||||
if (parserInput.currentChar() === '@'
|
||||
&& (name = parserInput.$re(/^(@[\w-]+)\(\s*\)/))
|
||||
&& parsers.end()) {
|
||||
return new tree.VariableCall(name[1]);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -637,7 +741,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
do {
|
||||
option = null;
|
||||
elements = null;
|
||||
while (! (option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
|
||||
while (!(option = parserInput.$re(/^(all)(?=\s*(\)|,))/))) {
|
||||
e = this.element();
|
||||
if (!e) {
|
||||
break;
|
||||
@@ -756,7 +860,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
.push({ variadic: true });
|
||||
break;
|
||||
}
|
||||
arg = entities.variable() || entities.literal() || entities.keyword();
|
||||
arg = entities.variable() || entities.property() || entities.literal() || entities.keyword();
|
||||
}
|
||||
|
||||
if (!arg) {
|
||||
@@ -779,7 +883,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
val = arg;
|
||||
}
|
||||
|
||||
if (val && val instanceof tree.Variable) {
|
||||
if (val && (val instanceof tree.Variable || val instanceof tree.Property)) {
|
||||
if (parserInput.$char(':')) {
|
||||
if (expressions.length > 0) {
|
||||
if (isSemiColonSeparated) {
|
||||
@@ -925,11 +1029,11 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
var entities = this.entities;
|
||||
|
||||
return this.comment() || entities.literal() || entities.variable() || entities.url() ||
|
||||
entities.call() || entities.keyword() || entities.javascript();
|
||||
entities.property() || entities.call() || entities.keyword() || entities.javascript();
|
||||
},
|
||||
|
||||
//
|
||||
// A Rule terminator. Note that we use `peek()` to check for '}',
|
||||
// A Declaration terminator. Note that we use `peek()` to check for '}',
|
||||
// because the `block` rule will be expecting it, but we still need to make sure
|
||||
// it's there, if ';' was omitted.
|
||||
//
|
||||
@@ -942,17 +1046,18 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
//
|
||||
// alpha(opacity=88)
|
||||
//
|
||||
alpha: function () {
|
||||
ieAlpha: function () {
|
||||
var value;
|
||||
|
||||
// http://jsperf.com/case-insensitive-regex-vs-strtolower-then-regex/18
|
||||
if (! parserInput.$re(/^opacity=/i)) { return; }
|
||||
if (!parserInput.$re(/^opacity=/i)) { return; }
|
||||
value = parserInput.$re(/^\d+/);
|
||||
if (!value) {
|
||||
value = expect(this.entities.variable, "Could not parse alpha");
|
||||
value = expect(parsers.entities.variable, "Could not parse alpha");
|
||||
value = '@{' + value.name.slice(1) + '}';
|
||||
}
|
||||
expectChar(')');
|
||||
return new(tree.Alpha)(value);
|
||||
return new tree.Quoted('', 'alpha(opacity=' + value + ')');
|
||||
},
|
||||
|
||||
//
|
||||
@@ -978,10 +1083,10 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.$re(/^\([^&()@]+\)/) || parserInput.$re(/^[\.#:](?=@)/) ||
|
||||
this.entities.variableCurly();
|
||||
|
||||
if (! e) {
|
||||
if (!e) {
|
||||
parserInput.save();
|
||||
if (parserInput.$char('(')) {
|
||||
if ((v = this.selector()) && parserInput.$char(')')) {
|
||||
if ((v = this.selector(false)) && parserInput.$char(')')) {
|
||||
e = new(tree.Paren)(v);
|
||||
parserInput.forget();
|
||||
} else {
|
||||
@@ -1032,14 +1137,8 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
},
|
||||
//
|
||||
// A CSS selector (see selector below)
|
||||
// with less extensions e.g. the ability to extend and guard
|
||||
//
|
||||
lessSelector: function () {
|
||||
return this.selector(true);
|
||||
},
|
||||
//
|
||||
// A CSS Selector
|
||||
// with less extensions e.g. the ability to extend and guard
|
||||
//
|
||||
// .class > div + h1
|
||||
// li a:hover
|
||||
@@ -1048,7 +1147,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
//
|
||||
selector: function (isLess) {
|
||||
var index = parserInput.i, elements, extendList, c, e, allExtends, when, condition;
|
||||
|
||||
isLess = isLess !== false;
|
||||
while ((isLess && (extendList = this.extend())) || (isLess && (when = parserInput.$str("when"))) || (e = this.element())) {
|
||||
if (when) {
|
||||
condition = expect(this.conditions, 'expected condition');
|
||||
@@ -1079,7 +1178,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
if (allExtends) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
|
||||
},
|
||||
attribute: function () {
|
||||
if (! parserInput.$char('[')) { return; }
|
||||
if (!parserInput.$char('[')) { return; }
|
||||
|
||||
var entities = this.entities,
|
||||
key, val, op;
|
||||
@@ -1138,7 +1237,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
s = this.lessSelector();
|
||||
s = this.selector();
|
||||
if (!s) {
|
||||
break;
|
||||
}
|
||||
@@ -1151,7 +1250,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
if (s.condition && selectors.length > 1) {
|
||||
error("Guards are only currently allowed on a single selector.");
|
||||
}
|
||||
if (! parserInput.$char(',')) { break; }
|
||||
if (!parserInput.$char(',')) { break; }
|
||||
if (s.condition) {
|
||||
error("Guards are only currently allowed on a single selector.");
|
||||
}
|
||||
@@ -1169,7 +1268,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
parserInput.restore();
|
||||
}
|
||||
},
|
||||
rule: function (tryAnonymous) {
|
||||
declaration: function () {
|
||||
var name, value, startOfRule = parserInput.i, c = parserInput.currentChar(), important, merge, isVariable;
|
||||
|
||||
if (c === '.' || c === '#' || c === '&' || c === ':') { return; }
|
||||
@@ -1191,22 +1290,16 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// where each item is a tree.Keyword or tree.Variable
|
||||
merge = !isVariable && name.length > 1 && name.pop().value;
|
||||
|
||||
// prefer to try to parse first if its a variable or we are compressing
|
||||
// but always fallback on the other one
|
||||
var tryValueFirst = !tryAnonymous && (context.compress || isVariable);
|
||||
// Try to store values as anonymous
|
||||
// If we need the value later we'll re-parse it in ruleset.parseValue
|
||||
value = this.anonymousValue();
|
||||
if (value) {
|
||||
parserInput.forget();
|
||||
// anonymous values absorb the end ';' which is required for them to work
|
||||
return new (tree.Declaration)(name, value, false, merge, startOfRule, fileInfo);
|
||||
}
|
||||
|
||||
if (tryValueFirst) {
|
||||
value = this.value();
|
||||
}
|
||||
if (!value) {
|
||||
value = this.anonymousValue();
|
||||
if (value) {
|
||||
parserInput.forget();
|
||||
// anonymous values absorb the end ';' which is required for them to work
|
||||
return new (tree.Rule)(name, value, false, merge, startOfRule, fileInfo);
|
||||
}
|
||||
}
|
||||
if (!tryValueFirst && !value) {
|
||||
value = this.value();
|
||||
}
|
||||
|
||||
@@ -1215,26 +1308,25 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (value && this.end()) {
|
||||
parserInput.forget();
|
||||
return new (tree.Rule)(name, value, important, merge, startOfRule, fileInfo);
|
||||
} else {
|
||||
return new (tree.Declaration)(name, value, important, merge, startOfRule, fileInfo);
|
||||
}
|
||||
else {
|
||||
parserInput.restore();
|
||||
if (value && !tryAnonymous) {
|
||||
return this.rule(true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parserInput.forget();
|
||||
parserInput.restore();
|
||||
}
|
||||
},
|
||||
anonymousValue: function () {
|
||||
var match = parserInput.$re(/^([^@+\/'"*`(;{}-]*);/);
|
||||
var index = parserInput.i;
|
||||
var match = parserInput.$re(/^([^@\$+\/'"*`(;{}-]*);/);
|
||||
if (match) {
|
||||
return new(tree.Anonymous)(match[1]);
|
||||
return new(tree.Anonymous)(match[1], index);
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// An @import directive
|
||||
// An @import atrule
|
||||
//
|
||||
// @import "lib";
|
||||
//
|
||||
@@ -1272,13 +1364,13 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
var o, options = {}, optionName, value;
|
||||
|
||||
// list of options, surrounded by parens
|
||||
if (! parserInput.$char('(')) { return null; }
|
||||
if (!parserInput.$char('(')) { return null; }
|
||||
do {
|
||||
o = this.importOption();
|
||||
if (o) {
|
||||
optionName = o;
|
||||
value = true;
|
||||
switch(optionName) {
|
||||
switch (optionName) {
|
||||
case "css":
|
||||
optionName = "less";
|
||||
value = false;
|
||||
@@ -1289,7 +1381,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
break;
|
||||
}
|
||||
options[optionName] = value;
|
||||
if (! parserInput.$char(',')) { break; }
|
||||
if (!parserInput.$char(',')) { break; }
|
||||
}
|
||||
} while (o);
|
||||
expectChar(')');
|
||||
@@ -1315,7 +1407,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
e = this.value();
|
||||
if (parserInput.$char(')')) {
|
||||
if (p && e) {
|
||||
nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, parserInput.i, fileInfo, true)));
|
||||
nodes.push(new(tree.Paren)(new(tree.Declaration)(p, e, null, null, parserInput.i, fileInfo, true)));
|
||||
} else if (e) {
|
||||
nodes.push(new(tree.Paren)(e));
|
||||
} else {
|
||||
@@ -1339,12 +1431,12 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
e = this.mediaFeature();
|
||||
if (e) {
|
||||
features.push(e);
|
||||
if (! parserInput.$char(',')) { break; }
|
||||
if (!parserInput.$char(',')) { break; }
|
||||
} else {
|
||||
e = entities.variable();
|
||||
if (e) {
|
||||
features.push(e);
|
||||
if (! parserInput.$char(',')) { break; }
|
||||
if (!parserInput.$char(',')) { break; }
|
||||
}
|
||||
}
|
||||
} while (e);
|
||||
@@ -1384,45 +1476,68 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
},
|
||||
|
||||
//
|
||||
// A @plugin directive, used to import compiler extensions dynamically.
|
||||
|
||||
// A @plugin directive, used to import plugins dynamically.
|
||||
//
|
||||
// @plugin "lib";
|
||||
//
|
||||
// Depending on our environment, importing is done differently:
|
||||
// In the browser, it's an XHR request, in Node, it would be a
|
||||
// file-system operation. The function used for importing is
|
||||
// stored in `import`, which we pass to the Import constructor.
|
||||
// @plugin (args) "lib";
|
||||
//
|
||||
plugin: function () {
|
||||
var path,
|
||||
var path, args, options,
|
||||
index = parserInput.i,
|
||||
dir = parserInput.$re(/^@plugin?\s+/);
|
||||
|
||||
if (dir) {
|
||||
var options = { plugin : true };
|
||||
args = this.pluginArgs();
|
||||
|
||||
if (args) {
|
||||
options = {
|
||||
pluginArgs: args,
|
||||
isPlugin: true
|
||||
};
|
||||
}
|
||||
else {
|
||||
options = { isPlugin: true };
|
||||
}
|
||||
|
||||
if ((path = this.entities.quoted() || this.entities.url())) {
|
||||
|
||||
if (!parserInput.$char(';')) {
|
||||
parserInput.i = index;
|
||||
error("missing semi-colon on plugin");
|
||||
error("missing semi-colon on @plugin");
|
||||
}
|
||||
|
||||
return new(tree.Import)(path, null, options, index, fileInfo);
|
||||
}
|
||||
else {
|
||||
parserInput.i = index;
|
||||
error("malformed plugin statement");
|
||||
error("malformed @plugin statement");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
pluginArgs: function() {
|
||||
// list of options, surrounded by parens
|
||||
parserInput.save();
|
||||
if (!parserInput.$char('(')) {
|
||||
parserInput.restore();
|
||||
return null;
|
||||
}
|
||||
var args = parserInput.$re(/^\s*([^\);]+)\)\s*/);
|
||||
if (args[1]) {
|
||||
parserInput.forget();
|
||||
return args[1].trim();
|
||||
}
|
||||
else {
|
||||
parserInput.restore();
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
//
|
||||
// A CSS Directive
|
||||
// A CSS AtRule
|
||||
//
|
||||
// @charset "utf-8";
|
||||
//
|
||||
directive: function () {
|
||||
atrule: function () {
|
||||
var index = parserInput.i, name, value, rules, nonVendorSpecificName,
|
||||
hasIdentifier, hasExpression, hasUnknown, hasBlock = true, isRooted = true;
|
||||
|
||||
@@ -1444,7 +1559,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
|
||||
}
|
||||
|
||||
switch(nonVendorSpecificName) {
|
||||
switch (nonVendorSpecificName) {
|
||||
case "@charset":
|
||||
hasIdentifier = true;
|
||||
hasBlock = false;
|
||||
@@ -1493,13 +1608,13 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
if (rules || (!hasBlock && value && parserInput.$char(';'))) {
|
||||
parserInput.forget();
|
||||
return new (tree.Directive)(name, value, rules, index, fileInfo,
|
||||
return new (tree.AtRule)(name, value, rules, index, fileInfo,
|
||||
context.dumpLineNumbers ? getDebugInfo(index) : null,
|
||||
isRooted
|
||||
);
|
||||
}
|
||||
|
||||
parserInput.restore("directive options not recognised");
|
||||
parserInput.restore("at-rule options not recognised");
|
||||
},
|
||||
|
||||
//
|
||||
@@ -1511,18 +1626,18 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// and before the `;`.
|
||||
//
|
||||
value: function () {
|
||||
var e, expressions = [];
|
||||
var e, expressions = [], index = parserInput.i;
|
||||
|
||||
do {
|
||||
e = this.expression();
|
||||
if (e) {
|
||||
expressions.push(e);
|
||||
if (! parserInput.$char(',')) { break; }
|
||||
if (!parserInput.$char(',')) { break; }
|
||||
}
|
||||
} while (e);
|
||||
|
||||
if (expressions.length > 0) {
|
||||
return new(tree.Value)(expressions);
|
||||
return new(tree.Value)(expressions, index);
|
||||
}
|
||||
},
|
||||
important: function () {
|
||||
@@ -1761,13 +1876,14 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
operand: function () {
|
||||
var entities = this.entities, negate;
|
||||
|
||||
if (parserInput.peek(/^-[@\(]/)) {
|
||||
if (parserInput.peek(/^-[@\$\(]/)) {
|
||||
negate = parserInput.$char('-');
|
||||
}
|
||||
|
||||
var o = this.sub() || entities.dimension() ||
|
||||
entities.color() || entities.variable() ||
|
||||
entities.call() || entities.colorKeyword();
|
||||
entities.property() || entities.call() ||
|
||||
entities.colorKeyword();
|
||||
|
||||
if (negate) {
|
||||
o.parensInOp = true;
|
||||
@@ -1785,7 +1901,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
// @var * 2
|
||||
//
|
||||
expression: function () {
|
||||
var entities = [], e, delim;
|
||||
var entities = [], e, delim, index = parserInput.i;
|
||||
|
||||
do {
|
||||
e = this.comment();
|
||||
@@ -1800,7 +1916,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
if (!parserInput.peek(/^\/[\/*]/)) {
|
||||
delim = parserInput.$char('/');
|
||||
if (delim) {
|
||||
entities.push(new(tree.Anonymous)(delim));
|
||||
entities.push(new(tree.Anonymous)(delim, index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1838,7 +1954,7 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
|
||||
match(/^(\*?)/);
|
||||
while (true) {
|
||||
if (!match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)) {
|
||||
if (!match(/^((?:[\w-]+)|(?:[@\$]\{[\w-]+\}))/)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1854,10 +1970,11 @@ var Parser = function Parser(context, imports, fileInfo) {
|
||||
}
|
||||
for (k = 0; k < name.length; k++) {
|
||||
s = name[k];
|
||||
name[k] = (s.charAt(0) !== '@') ?
|
||||
name[k] = (s.charAt(0) !== '@' && s.charAt(0) !== '$') ?
|
||||
new(tree.Keyword)(s) :
|
||||
new(tree.Variable)('@' + s.slice(2, -1),
|
||||
index[k], fileInfo);
|
||||
(s.charAt(0) === '@' ?
|
||||
new(tree.Variable)('@' + s.slice(2, -1), index[k], fileInfo) :
|
||||
new(tree.Property)('$' + s.slice(2, -1), index[k], fileInfo));
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,18 @@ var PluginManager = function(less) {
|
||||
this.postProcessors = [];
|
||||
this.installedPlugins = [];
|
||||
this.fileManagers = [];
|
||||
this.iterator = -1;
|
||||
this.pluginCache = {};
|
||||
this.Loader = new less.PluginLoader(less);
|
||||
};
|
||||
|
||||
var pm, PluginManagerFactory = function(less, newFactory) {
|
||||
if (newFactory || !pm) {
|
||||
pm = new PluginManager(less);
|
||||
}
|
||||
return pm;
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds all the plugins in the array
|
||||
* @param {Array} plugins
|
||||
@@ -23,11 +34,25 @@ PluginManager.prototype.addPlugins = function(plugins) {
|
||||
/**
|
||||
*
|
||||
* @param plugin
|
||||
* @param {String} filename
|
||||
*/
|
||||
PluginManager.prototype.addPlugin = function(plugin) {
|
||||
PluginManager.prototype.addPlugin = function(plugin, filename, functionRegistry) {
|
||||
this.installedPlugins.push(plugin);
|
||||
plugin.install(this.less, this);
|
||||
if (filename) {
|
||||
this.pluginCache[filename] = plugin;
|
||||
}
|
||||
if (plugin.install) {
|
||||
plugin.install(this.less, this, functionRegistry || this.less.functions.functionRegistry);
|
||||
}
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param filename
|
||||
*/
|
||||
PluginManager.prototype.get = function(filename) {
|
||||
return this.pluginCache[filename];
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds a visitor. The visitor object has options on itself to determine
|
||||
* when it should run.
|
||||
@@ -103,6 +128,20 @@ PluginManager.prototype.getPostProcessors = function() {
|
||||
PluginManager.prototype.getVisitors = function() {
|
||||
return this.visitors;
|
||||
};
|
||||
|
||||
PluginManager.prototype.visitor = function() {
|
||||
var self = this;
|
||||
return {
|
||||
first: function() {
|
||||
self.iterator = -1;
|
||||
return self.visitors[self.iterator];
|
||||
},
|
||||
get: function() {
|
||||
self.iterator += 1;
|
||||
return self.visitors[self.iterator];
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @returns {Array}
|
||||
@@ -111,4 +150,6 @@ PluginManager.prototype.getVisitors = function() {
|
||||
PluginManager.prototype.getFileManagers = function() {
|
||||
return this.fileManagers;
|
||||
};
|
||||
module.exports = PluginManager;
|
||||
|
||||
//
|
||||
module.exports = PluginManagerFactory;
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
var LessError = require('../less-error'),
|
||||
tree = require("../tree");
|
||||
|
||||
var FunctionImporter = module.exports = function FunctionImporter(context, fileInfo) {
|
||||
this.fileInfo = fileInfo;
|
||||
};
|
||||
|
||||
FunctionImporter.prototype.eval = function(contents, callback) {
|
||||
var loaded = {},
|
||||
loader,
|
||||
registry;
|
||||
|
||||
registry = {
|
||||
add: function(name, func) {
|
||||
loaded[name] = func;
|
||||
},
|
||||
addMultiple: function(functions) {
|
||||
Object.keys(functions).forEach(function(name) {
|
||||
loaded[name] = functions[name];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
loader = new Function("functions", "tree", "fileInfo", contents);
|
||||
loader(registry, tree, this.fileInfo);
|
||||
} catch(e) {
|
||||
callback(new LessError({
|
||||
message: "Plugin evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
|
||||
filename: this.fileInfo.filename
|
||||
}), null );
|
||||
}
|
||||
|
||||
callback(null, { functions: loaded });
|
||||
};
|
||||
@@ -1,10 +1,14 @@
|
||||
var PromiseConstructor;
|
||||
var PromiseConstructor,
|
||||
utils = require('./utils');
|
||||
|
||||
module.exports = function(environment, ParseTree, ImportManager) {
|
||||
var render = function (input, options, callback) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
options = utils.defaults(this.options, {});
|
||||
}
|
||||
else {
|
||||
options = utils.defaults(this.options, options || {});
|
||||
}
|
||||
|
||||
if (!callback) {
|
||||
|
||||
@@ -26,6 +26,9 @@ module.exports = function (SourceMapOutput, environment) {
|
||||
if (this.options.sourceMapInputFilename) {
|
||||
this.sourceMapInputFilename = sourceMapOutput.normalizeFilename(this.options.sourceMapInputFilename);
|
||||
}
|
||||
if (this.options.sourceMapBasepath !== undefined && this.sourceMapURL !== undefined) {
|
||||
this.sourceMapURL = sourceMapOutput.removeBasepath(this.sourceMapURL);
|
||||
}
|
||||
return css + this.getCSSAppendage();
|
||||
};
|
||||
|
||||
|
||||
@@ -28,21 +28,26 @@ module.exports = function (environment) {
|
||||
this._column = 0;
|
||||
};
|
||||
|
||||
SourceMapOutput.prototype.normalizeFilename = function(filename) {
|
||||
filename = filename.replace(/\\/g, '/');
|
||||
|
||||
if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
|
||||
filename = filename.substring(this._sourceMapBasepath.length);
|
||||
if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
|
||||
filename = filename.substring(1);
|
||||
SourceMapOutput.prototype.removeBasepath = function(path) {
|
||||
if (this._sourceMapBasepath && path.indexOf(this._sourceMapBasepath) === 0) {
|
||||
path = path.substring(this._sourceMapBasepath.length);
|
||||
if (path.charAt(0) === '\\' || path.charAt(0) === '/') {
|
||||
path = path.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
};
|
||||
|
||||
SourceMapOutput.prototype.normalizeFilename = function(filename) {
|
||||
filename = filename.replace(/\\/g, '/');
|
||||
filename = this.removeBasepath(filename);
|
||||
return (this._sourceMapRootpath || "") + filename;
|
||||
};
|
||||
|
||||
SourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
|
||||
|
||||
//ignore adding empty strings
|
||||
// ignore adding empty strings
|
||||
if (!chunk) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ module.exports = function(root, options) {
|
||||
//
|
||||
// `{ color: new tree.Color('#f01') }` will become:
|
||||
//
|
||||
// new tree.Rule('@color',
|
||||
// new tree.Declaration('@color',
|
||||
// new tree.Value([
|
||||
// new tree.Expression([
|
||||
// new tree.Color('#f01')
|
||||
@@ -25,50 +25,49 @@ module.exports = function(root, options) {
|
||||
variables = Object.keys(variables).map(function (k) {
|
||||
var value = variables[k];
|
||||
|
||||
if (! (value instanceof tree.Value)) {
|
||||
if (! (value instanceof tree.Expression)) {
|
||||
if (!(value instanceof tree.Value)) {
|
||||
if (!(value instanceof tree.Expression)) {
|
||||
value = new tree.Expression([value]);
|
||||
}
|
||||
value = new tree.Value([value]);
|
||||
}
|
||||
return new tree.Rule('@' + k, value, false, null, 0);
|
||||
return new tree.Declaration('@' + k, value, false, null, 0);
|
||||
});
|
||||
evalEnv.frames = [new tree.Ruleset(null, variables)];
|
||||
}
|
||||
|
||||
var preEvalVisitors = [],
|
||||
visitors = [
|
||||
var visitors = [
|
||||
new visitor.JoinSelectorVisitor(),
|
||||
new visitor.MarkVisibleSelectorsVisitor(true),
|
||||
new visitor.ExtendVisitor(),
|
||||
new visitor.ToCSSVisitor({compress: Boolean(options.compress)})
|
||||
], i;
|
||||
], v, visitorIterator;
|
||||
|
||||
// first() / get() allows visitors to be added while visiting
|
||||
if (options.pluginManager) {
|
||||
var pluginVisitors = options.pluginManager.getVisitors();
|
||||
for (i = 0; i < pluginVisitors.length; i++) {
|
||||
var pluginVisitor = pluginVisitors[i];
|
||||
if (pluginVisitor.isPreEvalVisitor) {
|
||||
preEvalVisitors.push(pluginVisitor);
|
||||
} else {
|
||||
if (pluginVisitor.isPreVisitor) {
|
||||
visitors.splice(0, 0, pluginVisitor);
|
||||
} else {
|
||||
visitors.push(pluginVisitor);
|
||||
}
|
||||
visitorIterator = options.pluginManager.visitor();
|
||||
visitorIterator.first();
|
||||
while ((v = visitorIterator.get())) {
|
||||
if (v.isPreEvalVisitor) {
|
||||
v.run(root);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < preEvalVisitors.length; i++) {
|
||||
preEvalVisitors[i].run(root);
|
||||
}
|
||||
|
||||
evaldRoot = root.eval(evalEnv);
|
||||
|
||||
for (i = 0; i < visitors.length; i++) {
|
||||
for (var i = 0; i < visitors.length; i++) {
|
||||
visitors[i].run(evaldRoot);
|
||||
}
|
||||
|
||||
if (options.pluginManager) {
|
||||
visitorIterator.first();
|
||||
while ((v = visitorIterator.get())) {
|
||||
if (!v.isPreEvalVisitor) {
|
||||
v.run(evaldRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return evaldRoot;
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
var Node = require("./node");
|
||||
|
||||
var Alpha = function (val) {
|
||||
this.value = val;
|
||||
};
|
||||
Alpha.prototype = new Node();
|
||||
Alpha.prototype.type = "Alpha";
|
||||
|
||||
Alpha.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(this.value);
|
||||
};
|
||||
Alpha.prototype.eval = function (context) {
|
||||
if (this.value.eval) { return new Alpha(this.value.eval(context)); }
|
||||
return this;
|
||||
};
|
||||
Alpha.prototype.genCSS = function (context, output) {
|
||||
output.add("alpha(opacity=");
|
||||
|
||||
if (this.value.genCSS) {
|
||||
this.value.genCSS(context, output);
|
||||
} else {
|
||||
output.add(this.value);
|
||||
}
|
||||
|
||||
output.add(")");
|
||||
};
|
||||
|
||||
module.exports = Alpha;
|
||||
@@ -2,9 +2,9 @@ var Node = require("./node");
|
||||
|
||||
var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike, visibilityInfo) {
|
||||
this.value = value;
|
||||
this.index = index;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.mapLines = mapLines;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.rulesetLike = (typeof rulesetLike === 'undefined') ? false : rulesetLike;
|
||||
this.allowRoot = true;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
@@ -12,7 +12,7 @@ var Anonymous = function (value, index, currentFileInfo, mapLines, rulesetLike,
|
||||
Anonymous.prototype = new Node();
|
||||
Anonymous.prototype.type = "Anonymous";
|
||||
Anonymous.prototype.eval = function () {
|
||||
return new Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines, this.rulesetLike, this.visibilityInfo());
|
||||
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;
|
||||
@@ -21,6 +21,9 @@ Anonymous.prototype.isRulesetLike = function() {
|
||||
return this.rulesetLike;
|
||||
};
|
||||
Anonymous.prototype.genCSS = function (context, output) {
|
||||
output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
|
||||
this.nodeVisible = Boolean(this.value);
|
||||
if (this.nodeVisible) {
|
||||
output.add(this.value, this._fileInfo, this._index, this.mapLines);
|
||||
}
|
||||
};
|
||||
module.exports = Anonymous;
|
||||
|
||||
@@ -1,34 +1,36 @@
|
||||
var Node = require("./node"),
|
||||
Selector = require("./selector"),
|
||||
Ruleset = require("./ruleset");
|
||||
Ruleset = require("./ruleset"),
|
||||
Anonymous = require('./anonymous');
|
||||
|
||||
var Directive = function (name, value, rules, index, currentFileInfo, debugInfo, isRooted, visibilityInfo) {
|
||||
var AtRule = function (name, value, rules, index, currentFileInfo, debugInfo, isRooted, visibilityInfo) {
|
||||
var i;
|
||||
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
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, this.index, currentFileInfo)).createEmptySelectors();
|
||||
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.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.debugInfo = debugInfo;
|
||||
this.isRooted = isRooted || false;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
};
|
||||
|
||||
Directive.prototype = new Node();
|
||||
Directive.prototype.type = "Directive";
|
||||
Directive.prototype.accept = function (visitor) {
|
||||
AtRule.prototype = new Node();
|
||||
AtRule.prototype.type = "AtRule";
|
||||
AtRule.prototype.accept = function (visitor) {
|
||||
var value = this.value, rules = this.rules;
|
||||
if (rules) {
|
||||
this.rules = visitor.visitArray(rules);
|
||||
@@ -37,15 +39,15 @@ Directive.prototype.accept = function (visitor) {
|
||||
this.value = visitor.visit(value);
|
||||
}
|
||||
};
|
||||
Directive.prototype.isRulesetLike = function() {
|
||||
AtRule.prototype.isRulesetLike = function() {
|
||||
return this.rules || !this.isCharset();
|
||||
};
|
||||
Directive.prototype.isCharset = function() {
|
||||
AtRule.prototype.isCharset = function() {
|
||||
return "@charset" === this.name;
|
||||
};
|
||||
Directive.prototype.genCSS = function (context, output) {
|
||||
AtRule.prototype.genCSS = function (context, output) {
|
||||
var value = this.value, rules = this.rules;
|
||||
output.add(this.name, this.currentFileInfo, this.index);
|
||||
output.add(this.name, this.fileInfo(), this.getIndex());
|
||||
if (value) {
|
||||
output.add(' ');
|
||||
value.genCSS(context, output);
|
||||
@@ -56,14 +58,14 @@ Directive.prototype.genCSS = function (context, output) {
|
||||
output.add(';');
|
||||
}
|
||||
};
|
||||
Directive.prototype.eval = function (context) {
|
||||
AtRule.prototype.eval = function (context) {
|
||||
var mediaPathBackup, mediaBlocksBackup, value = this.value, rules = this.rules;
|
||||
|
||||
//media stored inside other directive should not bubble over it
|
||||
//backpup media bubbling information
|
||||
// media stored inside other atrule should not bubble over it
|
||||
// backpup media bubbling information
|
||||
mediaPathBackup = context.mediaPath;
|
||||
mediaBlocksBackup = context.mediaBlocks;
|
||||
//deleted media bubbling information
|
||||
// deleted media bubbling information
|
||||
context.mediaPath = [];
|
||||
context.mediaBlocks = [];
|
||||
|
||||
@@ -75,32 +77,32 @@ Directive.prototype.eval = function (context) {
|
||||
rules = [rules[0].eval(context)];
|
||||
rules[0].root = true;
|
||||
}
|
||||
//restore media bubbling information
|
||||
// restore media bubbling information
|
||||
context.mediaPath = mediaPathBackup;
|
||||
context.mediaBlocks = mediaBlocksBackup;
|
||||
|
||||
return new Directive(this.name, value, rules,
|
||||
this.index, this.currentFileInfo, this.debugInfo, this.isRooted, this.visibilityInfo());
|
||||
return new AtRule(this.name, value, rules,
|
||||
this.getIndex(), this.fileInfo(), this.debugInfo, this.isRooted, this.visibilityInfo());
|
||||
};
|
||||
Directive.prototype.variable = function (name) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
Directive.prototype.find = function () {
|
||||
AtRule.prototype.find = function () {
|
||||
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], arguments);
|
||||
}
|
||||
};
|
||||
Directive.prototype.rulesets = function () {
|
||||
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]);
|
||||
}
|
||||
};
|
||||
Directive.prototype.outputRuleset = function (context, output, rules) {
|
||||
AtRule.prototype.outputRuleset = function (context, output, rules) {
|
||||
var ruleCnt = rules.length, i;
|
||||
context.tabLevel = (context.tabLevel | 0) + 1;
|
||||
|
||||
@@ -131,4 +133,4 @@ Directive.prototype.outputRuleset = function (context, output, rules) {
|
||||
|
||||
context.tabLevel--;
|
||||
};
|
||||
module.exports = Directive;
|
||||
module.exports = AtRule;
|
||||
@@ -1,4 +1,5 @@
|
||||
var Node = require("./node"),
|
||||
Anonymous = require("./anonymous"),
|
||||
FunctionCaller = require("../functions/function-caller");
|
||||
//
|
||||
// A function call node.
|
||||
@@ -6,8 +7,9 @@ var Node = require("./node"),
|
||||
var Call = function (name, args, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this.args = args;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.mathOn = name === 'calc' ? false : true;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
Call.prototype = new Node();
|
||||
Call.prototype.type = "Call";
|
||||
@@ -28,30 +30,55 @@ Call.prototype.accept = function (visitor) {
|
||||
// The function should receive the value, not the variable.
|
||||
//
|
||||
Call.prototype.eval = function (context) {
|
||||
var args = this.args.map(function (a) { return a.eval(context); }),
|
||||
result, funcCaller = new FunctionCaller(this.name, context, this.index, this.currentFileInfo);
|
||||
|
||||
/**
|
||||
* Turn off math for calc(), and switch back on for evaluating nested functions
|
||||
*/
|
||||
var currentMathContext = context.mathOn;
|
||||
context.mathOn = this.mathOn;
|
||||
var args = this.args.map(function (a) { return a.eval(context); });
|
||||
context.mathOn = currentMathContext;
|
||||
|
||||
var result, funcCaller = new FunctionCaller(this.name, context, this.getIndex(), this.fileInfo());
|
||||
|
||||
if (funcCaller.isValid()) {
|
||||
try {
|
||||
result = funcCaller.call(args);
|
||||
} catch (e) {
|
||||
throw { type: e.type || "Runtime",
|
||||
message: "error evaluating function `" + this.name + "`" +
|
||||
(e.message ? ': ' + e.message : ''),
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
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.index = this.index;
|
||||
result.currentFileInfo = this.currentFileInfo;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new Call(this.name, args, this.index, this.currentFileInfo);
|
||||
return new Call(this.name, args, this.getIndex(), this.fileInfo());
|
||||
};
|
||||
Call.prototype.genCSS = function (context, output) {
|
||||
output.add(this.name + "(", this.currentFileInfo, this.index);
|
||||
output.add(this.name + "(", this.fileInfo(), this.getIndex());
|
||||
|
||||
for (var i = 0; i < this.args.length; i++) {
|
||||
this.args[i].genCSS(context, output);
|
||||
|
||||
@@ -99,7 +99,7 @@ Color.prototype.toCSS = function (context, doNotCompress) {
|
||||
// we create a new Color node to hold the result.
|
||||
//
|
||||
Color.prototype.operate = function (context, op, other) {
|
||||
var rgb = [];
|
||||
var rgb = new Array(3);
|
||||
var alpha = this.alpha * (1 - other.alpha) + other.alpha;
|
||||
for (var c = 0; c < 3; c++) {
|
||||
rgb[c] = this._operate(context, op, this.rgb[c], other.rgb[c]);
|
||||
@@ -132,7 +132,7 @@ Color.prototype.toHSL = function () {
|
||||
}
|
||||
return { h: h * 360, s: s, l: l, a: a };
|
||||
};
|
||||
//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
// Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
|
||||
Color.prototype.toHSV = function () {
|
||||
var r = this.rgb[0] / 255,
|
||||
g = this.rgb[1] / 255,
|
||||
@@ -152,7 +152,7 @@ Color.prototype.toHSV = function () {
|
||||
if (max === min) {
|
||||
h = 0;
|
||||
} else {
|
||||
switch(max) {
|
||||
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;
|
||||
|
||||
@@ -4,15 +4,15 @@ var Node = require("./node"),
|
||||
var Comment = function (value, isLineComment, index, currentFileInfo) {
|
||||
this.value = value;
|
||||
this.isLineComment = isLineComment;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
};
|
||||
Comment.prototype = new Node();
|
||||
Comment.prototype.type = "Comment";
|
||||
Comment.prototype.genCSS = function (context, output) {
|
||||
if (this.debugInfo) {
|
||||
output.add(getDebugInfo(context, this), this.currentFileInfo, this.index);
|
||||
output.add(getDebugInfo(context, this), this.fileInfo(), this.getIndex());
|
||||
}
|
||||
output.add(this.value);
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ var Condition = function (op, l, r, i, negate) {
|
||||
this.op = op.trim();
|
||||
this.lvalue = l;
|
||||
this.rvalue = r;
|
||||
this.index = i;
|
||||
this._index = i;
|
||||
this.negate = negate;
|
||||
};
|
||||
Condition.prototype = new Node();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
var debugInfo = function(context, ctx, lineSeparator) {
|
||||
var result = "";
|
||||
if (context.dumpLineNumbers && !context.compress) {
|
||||
switch(context.dumpLineNumbers) {
|
||||
switch (context.dumpLineNumbers) {
|
||||
case 'comments':
|
||||
result = debugInfo.asComment(ctx);
|
||||
break;
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
var Node = require("./node"),
|
||||
Value = require("./value"),
|
||||
Keyword = require("./keyword");
|
||||
Keyword = require("./keyword"),
|
||||
Anonymous = require("./anonymous");
|
||||
|
||||
var Rule = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
|
||||
var Declaration = function (name, value, important, merge, index, currentFileInfo, inline, variable) {
|
||||
this.name = name;
|
||||
this.value = (value instanceof Node) ? value : new Value([value]); //value instanceof tree.Value || value instanceof tree.Ruleset ??
|
||||
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.currentFileInfo = currentFileInfo;
|
||||
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);
|
||||
};
|
||||
|
||||
function evalName(context, name) {
|
||||
@@ -24,21 +26,21 @@ function evalName(context, name) {
|
||||
return value;
|
||||
}
|
||||
|
||||
Rule.prototype = new Node();
|
||||
Rule.prototype.type = "Rule";
|
||||
Rule.prototype.genCSS = function (context, output) {
|
||||
output.add(this.name + (context.compress ? ':' : ': '), this.currentFileInfo, this.index);
|
||||
Declaration.prototype = new Node();
|
||||
Declaration.prototype.type = "Declaration";
|
||||
Declaration.prototype.genCSS = function (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.currentFileInfo.filename;
|
||||
catch (e) {
|
||||
e.index = this._index;
|
||||
e.filename = this._fileInfo.filename;
|
||||
throw e;
|
||||
}
|
||||
output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this.currentFileInfo, this.index);
|
||||
output.add(this.important + ((this.inline || (context.lastRule && context.compress)) ? "" : ";"), this._fileInfo, this._index);
|
||||
};
|
||||
Rule.prototype.eval = function (context) {
|
||||
Declaration.prototype.eval = function (context) {
|
||||
var strictMathBypass = false, name = this.name, evaldValue, variable = this.variable;
|
||||
if (typeof name !== "string") {
|
||||
// expand 'primitive' name directly to get
|
||||
@@ -57,7 +59,7 @@ Rule.prototype.eval = function (context) {
|
||||
|
||||
if (!this.variable && evaldValue.type === "DetachedRuleset") {
|
||||
throw { message: "Rulesets cannot be evaluated on a property.",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
var important = this.important,
|
||||
importantResult = context.importantScope.pop();
|
||||
@@ -65,17 +67,17 @@ Rule.prototype.eval = function (context) {
|
||||
important = importantResult.important;
|
||||
}
|
||||
|
||||
return new Rule(name,
|
||||
return new Declaration(name,
|
||||
evaldValue,
|
||||
important,
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline,
|
||||
this.getIndex(), this.fileInfo(), this.inline,
|
||||
variable);
|
||||
}
|
||||
catch(e) {
|
||||
catch (e) {
|
||||
if (typeof e.index !== 'number') {
|
||||
e.index = this.index;
|
||||
e.filename = this.currentFileInfo.filename;
|
||||
e.index = this.getIndex();
|
||||
e.filename = this.fileInfo().filename;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -85,12 +87,12 @@ Rule.prototype.eval = function (context) {
|
||||
}
|
||||
}
|
||||
};
|
||||
Rule.prototype.makeImportant = function () {
|
||||
return new Rule(this.name,
|
||||
Declaration.prototype.makeImportant = function () {
|
||||
return new Declaration(this.name,
|
||||
this.value,
|
||||
"!important",
|
||||
this.merge,
|
||||
this.index, this.currentFileInfo, this.inline);
|
||||
this.getIndex(), this.fileInfo(), this.inline);
|
||||
};
|
||||
|
||||
module.exports = Rule;
|
||||
module.exports = Declaration;
|
||||
@@ -1,9 +1,11 @@
|
||||
var Node = require("./node"),
|
||||
contexts = require("../contexts");
|
||||
contexts = require("../contexts"),
|
||||
utils = require("../utils");
|
||||
|
||||
var DetachedRuleset = function (ruleset, frames) {
|
||||
this.ruleset = ruleset;
|
||||
this.frames = frames;
|
||||
this.setParent(this.ruleset, this);
|
||||
};
|
||||
DetachedRuleset.prototype = new Node();
|
||||
DetachedRuleset.prototype.type = "DetachedRuleset";
|
||||
@@ -12,7 +14,7 @@ DetachedRuleset.prototype.accept = function (visitor) {
|
||||
this.ruleset = visitor.visit(this.ruleset);
|
||||
};
|
||||
DetachedRuleset.prototype.eval = function (context) {
|
||||
var frames = this.frames || context.frames.slice(0);
|
||||
var frames = this.frames || utils.copyArray(context.frames);
|
||||
return new DetachedRuleset(this.ruleset, frames);
|
||||
};
|
||||
DetachedRuleset.prototype.callEval = function (context) {
|
||||
|
||||
@@ -8,8 +8,12 @@ var Node = require("./node"),
|
||||
//
|
||||
var 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);
|
||||
};
|
||||
|
||||
Dimension.prototype = new Node();
|
||||
@@ -57,7 +61,7 @@ Dimension.prototype.genCSS = function (context, output) {
|
||||
// we default to the first Dimension's unit,
|
||||
// so `1px + 2` will yield `3px`.
|
||||
Dimension.prototype.operate = function (context, op, other) {
|
||||
/*jshint noempty:false */
|
||||
/* jshint noempty:false */
|
||||
var value = this._operate(context, op, this.value, other.value),
|
||||
unit = this.unit.clone();
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ var Node = require("./node"),
|
||||
Paren = require("./paren"),
|
||||
Combinator = require("./combinator");
|
||||
|
||||
var Element = function (combinator, value, index, currentFileInfo, info) {
|
||||
var Element = function (combinator, value, index, currentFileInfo, visibilityInfo) {
|
||||
this.combinator = combinator instanceof Combinator ?
|
||||
combinator : new Combinator(combinator);
|
||||
|
||||
@@ -13,9 +13,10 @@ var Element = function (combinator, value, index, currentFileInfo, info) {
|
||||
} else {
|
||||
this.value = "";
|
||||
}
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(info);
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.combinator, this);
|
||||
};
|
||||
Element.prototype = new Node();
|
||||
Element.prototype.type = "Element";
|
||||
@@ -29,17 +30,17 @@ Element.prototype.accept = function (visitor) {
|
||||
Element.prototype.eval = function (context) {
|
||||
return new Element(this.combinator,
|
||||
this.value.eval ? this.value.eval(context) : this.value,
|
||||
this.index,
|
||||
this.currentFileInfo, this.visibilityInfo());
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
Element.prototype.clone = function () {
|
||||
return new Element(this.combinator,
|
||||
this.value,
|
||||
this.index,
|
||||
this.currentFileInfo, this.visibilityInfo());
|
||||
this.getIndex(),
|
||||
this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
Element.prototype.genCSS = function (context, output) {
|
||||
output.add(this.toCSS(context), this.currentFileInfo, this.index);
|
||||
output.add(this.toCSS(context), this.fileInfo(), this.getIndex());
|
||||
};
|
||||
Element.prototype.toCSS = function (context) {
|
||||
context = context || {};
|
||||
|
||||
@@ -4,14 +4,14 @@ var Node = require("./node"),
|
||||
var Extend = function Extend(selector, option, index, currentFileInfo, visibilityInfo) {
|
||||
this.selector = selector;
|
||||
this.option = option;
|
||||
this.index = index;
|
||||
this.object_id = Extend.next_id++;
|
||||
this.parent_ids = [this.object_id];
|
||||
this.currentFileInfo = currentFileInfo || {};
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
|
||||
switch(option) {
|
||||
switch (option) {
|
||||
case "all":
|
||||
this.allowBefore = true;
|
||||
this.allowAfter = true;
|
||||
@@ -21,6 +21,7 @@ var Extend = function Extend(selector, option, index, currentFileInfo, visibilit
|
||||
this.allowAfter = false;
|
||||
break;
|
||||
}
|
||||
this.setParent(this.selector, this);
|
||||
};
|
||||
Extend.next_id = 0;
|
||||
|
||||
@@ -30,12 +31,12 @@ 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.index, this.currentFileInfo, this.visibilityInfo());
|
||||
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.index, this.currentFileInfo, this.visibilityInfo());
|
||||
return new Extend(this.selector, this.option, this.getIndex(), this.fileInfo(), this.visibilityInfo());
|
||||
};
|
||||
//it concatenates (joins) all selectors in selector array
|
||||
// it concatenates (joins) all selectors in selector array
|
||||
Extend.prototype.findSelfSelectors = function (selectors) {
|
||||
var selfElements = [],
|
||||
i,
|
||||
|
||||
@@ -3,7 +3,9 @@ var Node = require("./node"),
|
||||
URL = require("./url"),
|
||||
Quoted = require("./quoted"),
|
||||
Ruleset = require("./ruleset"),
|
||||
Anonymous = require("./anonymous");
|
||||
Anonymous = require("./anonymous"),
|
||||
utils = require("../utils"),
|
||||
LessError = require("../less-error");
|
||||
|
||||
//
|
||||
// CSS @import node
|
||||
@@ -19,21 +21,23 @@ var Node = require("./node"),
|
||||
//
|
||||
var Import = function (path, features, options, index, currentFileInfo, visibilityInfo) {
|
||||
this.options = options;
|
||||
this.index = index;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.path = path;
|
||||
this.features = features;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.allowRoot = true;
|
||||
|
||||
if (this.options.less !== undefined || this.options.inline) {
|
||||
this.css = !this.options.less || this.options.inline;
|
||||
} else {
|
||||
var pathValue = this.getPath();
|
||||
if (pathValue && /[#\.\&\?\/]css([\?;].*)?$/.test(pathValue)) {
|
||||
if (pathValue && /[#\.\&\?]css([\?;].*)?$/.test(pathValue)) {
|
||||
this.css = true;
|
||||
}
|
||||
}
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.setParent(this.features, this);
|
||||
this.setParent(this.path, this);
|
||||
};
|
||||
|
||||
//
|
||||
@@ -52,13 +56,13 @@ Import.prototype.accept = function (visitor) {
|
||||
this.features = visitor.visit(this.features);
|
||||
}
|
||||
this.path = visitor.visit(this.path);
|
||||
if (!this.options.plugin && !this.options.inline && this.root) {
|
||||
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.currentFileInfo.reference === undefined) {
|
||||
output.add("@import ", this.currentFileInfo, this.index);
|
||||
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(" ");
|
||||
@@ -89,11 +93,11 @@ Import.prototype.evalForImport = function (context) {
|
||||
path = path.value;
|
||||
}
|
||||
|
||||
return new Import(path.eval(context), this.features, this.options, this.index, this.currentFileInfo, this.visibilityInfo());
|
||||
return new Import(path.eval(context), this.features, this.options, this._index, this._fileInfo, this.visibilityInfo());
|
||||
};
|
||||
Import.prototype.evalPath = function (context) {
|
||||
var path = this.path.eval(context);
|
||||
var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
var rootpath = this._fileInfo && this._fileInfo.rootpath;
|
||||
|
||||
if (!(path instanceof URL)) {
|
||||
if (rootpath) {
|
||||
@@ -113,8 +117,8 @@ Import.prototype.eval = function (context) {
|
||||
if (this.options.reference || this.blocksVisibility()) {
|
||||
if (result.length || result.length === 0) {
|
||||
result.forEach(function (node) {
|
||||
node.addVisibilityBlock();
|
||||
}
|
||||
node.addVisibilityBlock();
|
||||
}
|
||||
);
|
||||
} else {
|
||||
result.addVisibilityBlock();
|
||||
@@ -126,11 +130,21 @@ Import.prototype.doEval = function (context) {
|
||||
var ruleset, registry,
|
||||
features = this.features && this.features.eval(context);
|
||||
|
||||
if (this.options.plugin) {
|
||||
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 [];
|
||||
}
|
||||
|
||||
@@ -144,20 +158,20 @@ Import.prototype.doEval = function (context) {
|
||||
}
|
||||
if (this.options.inline) {
|
||||
var contents = new Anonymous(this.root, 0,
|
||||
{
|
||||
filename: this.importedFilename,
|
||||
reference: this.path.currentFileInfo && this.path.currentFileInfo.reference
|
||||
}, true, true);
|
||||
{
|
||||
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) {
|
||||
var newImport = new Import(this.evalPath(context), features, this.options, this.index);
|
||||
var newImport = new Import(this.evalPath(context), features, this.options, this._index);
|
||||
if (!newImport.css && this.error) {
|
||||
throw this.error;
|
||||
}
|
||||
return newImport;
|
||||
} else {
|
||||
ruleset = new Ruleset(null, this.root.rules.slice(0));
|
||||
ruleset = new Ruleset(null, utils.copyArray(this.root.rules));
|
||||
ruleset.evalImports(context);
|
||||
|
||||
return this.features ? new Media(ruleset.rules, this.features.value) : ruleset.rules;
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
var tree = {};
|
||||
var tree = Object.create(null);
|
||||
|
||||
tree.Node = require('./node');
|
||||
tree.Alpha = require('./alpha');
|
||||
tree.Color = require('./color');
|
||||
tree.Directive = require('./directive');
|
||||
tree.AtRule = require('./atrule');
|
||||
tree.DetachedRuleset = require('./detached-ruleset');
|
||||
tree.Operation = require('./operation');
|
||||
tree.Dimension = require('./dimension');
|
||||
tree.Unit = require('./unit');
|
||||
tree.Keyword = require('./keyword');
|
||||
tree.Variable = require('./variable');
|
||||
tree.Property = require('./property');
|
||||
tree.Ruleset = require('./ruleset');
|
||||
tree.Element = require('./element');
|
||||
tree.Attribute = require('./attribute');
|
||||
@@ -17,7 +17,7 @@ tree.Combinator = require('./combinator');
|
||||
tree.Selector = require('./selector');
|
||||
tree.Quoted = require('./quoted');
|
||||
tree.Expression = require('./expression');
|
||||
tree.Rule = require('./rule');
|
||||
tree.Declaration = require('./declaration');
|
||||
tree.Call = require('./call');
|
||||
tree.URL = require('./url');
|
||||
tree.Import = require('./import');
|
||||
@@ -36,6 +36,6 @@ tree.Media = require('./media');
|
||||
tree.UnicodeDescriptor = require('./unicode-descriptor');
|
||||
tree.Negative = require('./negative');
|
||||
tree.Extend = require('./extend');
|
||||
tree.RulesetCall = require('./ruleset-call');
|
||||
tree.VariableCall = require('./variable-call');
|
||||
|
||||
module.exports = tree;
|
||||
|
||||
@@ -6,8 +6,8 @@ var JsEvalNode = require("./js-eval-node"),
|
||||
var JavaScript = function (string, escaped, index, currentFileInfo) {
|
||||
this.escaped = escaped;
|
||||
this.expression = string;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
JavaScript.prototype = new JsEvalNode();
|
||||
JavaScript.prototype.type = "JavaScript";
|
||||
@@ -17,7 +17,7 @@ JavaScript.prototype.eval = function(context) {
|
||||
if (typeof result === 'number') {
|
||||
return new Dimension(result);
|
||||
} else if (typeof result === 'string') {
|
||||
return new Quoted('"' + result + '"', result, this.escaped, this.index);
|
||||
return new Quoted('"' + result + '"', result, this.escaped, this._index);
|
||||
} else if (Array.isArray(result)) {
|
||||
return new Anonymous(result.join(', '));
|
||||
} else {
|
||||
|
||||
@@ -10,28 +10,28 @@ JsEvalNode.prototype.evaluateJavaScript = function (expression, context) {
|
||||
that = this,
|
||||
evalContext = {};
|
||||
|
||||
if (context.javascriptEnabled !== undefined && !context.javascriptEnabled) {
|
||||
throw { message: "You are using JavaScript, which has been disabled.",
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
if (!context.javascriptEnabled) {
|
||||
throw { message: "Inline JavaScript is not enabled. Is it set in your options?",
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
expression = expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
|
||||
return that.jsify(new Variable('@' + name, that.index, that.currentFileInfo).eval(context));
|
||||
return 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.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
var variables = context.frames[0].variables();
|
||||
for (var k in variables) {
|
||||
if (variables.hasOwnProperty(k)) {
|
||||
/*jshint loopfunc:true */
|
||||
/* jshint loopfunc:true */
|
||||
evalContext[k.slice(1)] = {
|
||||
value: variables[k].value,
|
||||
toJS: function () {
|
||||
@@ -45,8 +45,8 @@ JsEvalNode.prototype.evaluateJavaScript = function (expression, context) {
|
||||
result = expression.call(evalContext);
|
||||
} catch (e) {
|
||||
throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message.replace(/["]/g, "'") + "'" ,
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -3,23 +3,27 @@ var Ruleset = require("./ruleset"),
|
||||
Selector = require("./selector"),
|
||||
Anonymous = require("./anonymous"),
|
||||
Expression = require("./expression"),
|
||||
Directive = require("./directive");
|
||||
AtRule = require("./atrule"),
|
||||
utils = require("../utils");
|
||||
|
||||
var Media = function (value, features, index, currentFileInfo, visibilityInfo) {
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
|
||||
var selectors = (new Selector([], null, null, this.index, this.currentFileInfo)).createEmptySelectors();
|
||||
var 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);
|
||||
};
|
||||
Media.prototype = new Directive();
|
||||
Media.prototype = new AtRule();
|
||||
Media.prototype.type = "Media";
|
||||
Media.prototype.isRulesetLike = true;
|
||||
Media.prototype.isRulesetLike = function() { return true; };
|
||||
Media.prototype.accept = function (visitor) {
|
||||
if (this.features) {
|
||||
this.features = visitor.visit(this.features);
|
||||
@@ -29,7 +33,7 @@ Media.prototype.accept = function (visitor) {
|
||||
}
|
||||
};
|
||||
Media.prototype.genCSS = function (context, output) {
|
||||
output.add('@media ', this.currentFileInfo, this.index);
|
||||
output.add('@media ', this._fileInfo, this._index);
|
||||
this.features.genCSS(context, output);
|
||||
this.outputRuleset(context, output, this.rules);
|
||||
};
|
||||
@@ -39,24 +43,13 @@ Media.prototype.eval = function (context) {
|
||||
context.mediaPath = [];
|
||||
}
|
||||
|
||||
var media = new Media(null, [], this.index, this.currentFileInfo, this.visibilityInfo());
|
||||
var media = new Media(null, [], this._index, this._fileInfo, this.visibilityInfo());
|
||||
if (this.debugInfo) {
|
||||
this.rules[0].debugInfo = this.debugInfo;
|
||||
media.debugInfo = this.debugInfo;
|
||||
}
|
||||
var strictMathBypass = false;
|
||||
if (!context.strictMath) {
|
||||
strictMathBypass = true;
|
||||
context.strictMath = true;
|
||||
}
|
||||
try {
|
||||
media.features = this.features.eval(context);
|
||||
}
|
||||
finally {
|
||||
if (strictMathBypass) {
|
||||
context.strictMath = false;
|
||||
}
|
||||
}
|
||||
|
||||
media.features = this.features.eval(context);
|
||||
|
||||
context.mediaPath.push(media);
|
||||
context.mediaBlocks.push(media);
|
||||
@@ -76,10 +69,11 @@ Media.prototype.evalTop = function (context) {
|
||||
|
||||
// Render all dependent Media blocks.
|
||||
if (context.mediaBlocks.length > 1) {
|
||||
var selectors = (new Selector([], null, null, this.index, this.currentFileInfo)).createEmptySelectors();
|
||||
var 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;
|
||||
@@ -116,6 +110,7 @@ Media.prototype.evalNested = function (context) {
|
||||
|
||||
return new Expression(path);
|
||||
}));
|
||||
this.setParent(this.features, this);
|
||||
|
||||
// Fake a tree-node that doesn't output anything.
|
||||
return new Ruleset([], []);
|
||||
@@ -140,6 +135,7 @@ Media.prototype.bubbleSelectors = function (selectors) {
|
||||
if (!selectors) {
|
||||
return;
|
||||
}
|
||||
this.rules = [new Ruleset(selectors.slice(0), [this.rules[0]])];
|
||||
this.rules = [new Ruleset(utils.copyArray(selectors), [this.rules[0]])];
|
||||
this.setParent(this.rules, this);
|
||||
};
|
||||
module.exports = Media;
|
||||
|
||||
@@ -6,10 +6,11 @@ var Node = require("./node"),
|
||||
var MixinCall = function (elements, args, index, currentFileInfo, important) {
|
||||
this.selector = new Selector(elements);
|
||||
this.arguments = args || [];
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.important = important;
|
||||
this.allowRoot = true;
|
||||
this.setParent(this.selector, this);
|
||||
};
|
||||
MixinCall.prototype = new Node();
|
||||
MixinCall.prototype.type = "MixinCall";
|
||||
@@ -117,7 +118,7 @@ MixinCall.prototype.eval = function (context) {
|
||||
if ((count[defTrue] + count[defFalse]) > 1) {
|
||||
throw { type: 'Runtime',
|
||||
message: 'Ambiguous use of `default()` found when matching for `' + this.format(args) + '`',
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ MixinCall.prototype.eval = function (context) {
|
||||
this._setVisibilityToReplacement(newRules);
|
||||
Array.prototype.push.apply(rules, newRules);
|
||||
} catch (e) {
|
||||
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
|
||||
throw { message: e.message, index: this.getIndex(), filename: this.fileInfo().filename, stack: e.stack };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,11 +149,11 @@ MixinCall.prototype.eval = function (context) {
|
||||
if (isOneFound) {
|
||||
throw { type: 'Runtime',
|
||||
message: 'No matching definition was found for `' + this.format(args) + '`',
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: this.selector.toCSS().trim() + " is undefined",
|
||||
index: this.index, filename: this.currentFileInfo.filename };
|
||||
index: this.getIndex(), filename: this.fileInfo().filename };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
var Selector = require("./selector"),
|
||||
Element = require("./element"),
|
||||
Ruleset = require("./ruleset"),
|
||||
Rule = require("./rule"),
|
||||
Declaration = require("./declaration"),
|
||||
Expression = require("./expression"),
|
||||
contexts = require("../contexts");
|
||||
contexts = require("../contexts"),
|
||||
utils = require("../utils");
|
||||
|
||||
var Definition = function (name, params, rules, condition, variadic, frames, visibilityInfo) {
|
||||
this.name = name;
|
||||
this.selectors = [new Selector([new Element(null, name, this.index, this.currentFileInfo)])];
|
||||
this.selectors = [new Selector([new Element(null, name, this._index, this._fileInfo)])];
|
||||
this.params = params;
|
||||
this.condition = condition;
|
||||
this.variadic = variadic;
|
||||
@@ -42,10 +43,10 @@ Definition.prototype.accept = function (visitor) {
|
||||
}
|
||||
};
|
||||
Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArguments) {
|
||||
/*jshint boss:true */
|
||||
/* jshint boss:true */
|
||||
var frame = new Ruleset(null, null),
|
||||
varargs, arg,
|
||||
params = this.params.slice(0),
|
||||
params = utils.copyArray(this.params),
|
||||
i, j, val, name, isNamedFound, argIndex, argsLength = 0;
|
||||
|
||||
if (mixinEnv.frames && mixinEnv.frames[0] && mixinEnv.frames[0].functionRegistry) {
|
||||
@@ -54,7 +55,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
|
||||
mixinEnv = new contexts.Eval(mixinEnv, [frame].concat(mixinEnv.frames));
|
||||
|
||||
if (args) {
|
||||
args = args.slice(0);
|
||||
args = utils.copyArray(args);
|
||||
argsLength = args.length;
|
||||
|
||||
for (i = 0; i < argsLength; i++) {
|
||||
@@ -64,7 +65,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
|
||||
for (j = 0; j < params.length; j++) {
|
||||
if (!evaldArguments[j] && name === params[j].name) {
|
||||
evaldArguments[j] = arg.value.eval(context);
|
||||
frame.prependRule(new Rule(name, arg.value.eval(context)));
|
||||
frame.prependRule(new Declaration(name, arg.value.eval(context)));
|
||||
isNamedFound = true;
|
||||
break;
|
||||
}
|
||||
@@ -92,7 +93,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
|
||||
for (j = argIndex; j < argsLength; j++) {
|
||||
varargs.push(args[j].value.eval(context));
|
||||
}
|
||||
frame.prependRule(new Rule(name, new Expression(varargs).eval(context)));
|
||||
frame.prependRule(new Declaration(name, new Expression(varargs).eval(context)));
|
||||
} else {
|
||||
val = arg && arg.value;
|
||||
if (val) {
|
||||
@@ -105,7 +106,7 @@ Definition.prototype.evalParams = function (context, mixinEnv, args, evaldArgume
|
||||
' (' + argsLength + ' for ' + this.arity + ')' };
|
||||
}
|
||||
|
||||
frame.prependRule(new Rule(name, val));
|
||||
frame.prependRule(new Declaration(name, val));
|
||||
evaldArguments[i] = val;
|
||||
}
|
||||
}
|
||||
@@ -132,7 +133,7 @@ Definition.prototype.makeImportant = function() {
|
||||
return result;
|
||||
};
|
||||
Definition.prototype.eval = function (context) {
|
||||
return new Definition(this.name, this.params, this.rules, this.condition, this.variadic, this.frames || context.frames.slice(0));
|
||||
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) {
|
||||
var _arguments = [],
|
||||
@@ -140,9 +141,9 @@ Definition.prototype.evalCall = function (context, args, important) {
|
||||
frame = this.evalParams(context, new contexts.Eval(context, mixinFrames), args, _arguments),
|
||||
rules, ruleset;
|
||||
|
||||
frame.prependRule(new Rule('@arguments', new Expression(_arguments).eval(context)));
|
||||
frame.prependRule(new Declaration('@arguments', new Expression(_arguments).eval(context)));
|
||||
|
||||
rules = this.rules.slice(0);
|
||||
rules = utils.copyArray(this.rules);
|
||||
|
||||
ruleset = new Ruleset(null, rules);
|
||||
ruleset.originalRuleset = this;
|
||||
@@ -155,7 +156,7 @@ Definition.prototype.evalCall = function (context, args, important) {
|
||||
Definition.prototype.matchCondition = function (args, context) {
|
||||
if (this.condition && !this.condition.eval(
|
||||
new contexts.Eval(context,
|
||||
[this.evalParams(context, /* the parameter variables*/
|
||||
[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
|
||||
@@ -173,7 +174,7 @@ Definition.prototype.matchArgs = function (args, context) {
|
||||
}
|
||||
}, 0);
|
||||
|
||||
if (! this.variadic) {
|
||||
if (!this.variadic) {
|
||||
if (requiredArgsCnt < this.required) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,39 @@
|
||||
var Node = function() {
|
||||
this.parent = null;
|
||||
this.visibilityBlocks = undefined;
|
||||
this.nodeVisible = undefined;
|
||||
this.rootNode = null;
|
||||
this.parsed = null;
|
||||
|
||||
var self = this;
|
||||
Object.defineProperty(this, "currentFileInfo", {
|
||||
get: function() { return self.fileInfo(); }
|
||||
});
|
||||
Object.defineProperty(this, "index", {
|
||||
get: function() { return self.getIndex(); }
|
||||
});
|
||||
|
||||
};
|
||||
Node.prototype.setParent = function(nodes, parent) {
|
||||
function set(node) {
|
||||
if (node && node instanceof Node) {
|
||||
node.parent = parent;
|
||||
}
|
||||
}
|
||||
if (Array.isArray(nodes)) {
|
||||
nodes.forEach(set);
|
||||
}
|
||||
else {
|
||||
set(nodes);
|
||||
}
|
||||
};
|
||||
Node.prototype.getIndex = function() {
|
||||
return this._index || (this.parent && this.parent.getIndex()) || 0;
|
||||
};
|
||||
Node.prototype.fileInfo = function() {
|
||||
return this._fileInfo || (this.parent && this.parent.fileInfo()) || {};
|
||||
};
|
||||
Node.prototype.isRulesetLike = function() { return false; };
|
||||
Node.prototype.toCSS = function (context) {
|
||||
var strs = [];
|
||||
this.genCSS(context, {
|
||||
@@ -29,8 +63,8 @@ Node.prototype._operate = function (context, op, a, b) {
|
||||
};
|
||||
Node.prototype.fround = function(context, value) {
|
||||
var precision = context && context.numPrecision;
|
||||
//add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999....) are properly rounded...
|
||||
return (precision == null) ? value : Number((value + 2e-16).toFixed(precision));
|
||||
// add "epsilon" to ensure numbers like 1.000000005 (represented as 1.000000004999...) are properly rounded:
|
||||
return (precision) ? Number((value + 2e-16).toFixed(precision)) : value;
|
||||
};
|
||||
Node.compare = function (a, b) {
|
||||
/* returns:
|
||||
@@ -90,13 +124,13 @@ Node.prototype.removeVisibilityBlock = function () {
|
||||
}
|
||||
this.visibilityBlocks = this.visibilityBlocks - 1;
|
||||
};
|
||||
//Turns on node visibility - if called node will be shown in output regardless
|
||||
//of whether it comes from import by reference or not
|
||||
// Turns on node visibility - if called node will be shown in output regardless
|
||||
// of whether it comes from import by reference or not
|
||||
Node.prototype.ensureVisibility = function () {
|
||||
this.nodeVisible = true;
|
||||
};
|
||||
//Turns off node visibility - if called node will NOT be shown in output regardless
|
||||
//of whether it comes from import by reference or not
|
||||
// Turns off node visibility - if called node will NOT be shown in output regardless
|
||||
// of whether it comes from import by reference or not
|
||||
Node.prototype.ensureInvisibility = function () {
|
||||
this.nodeVisible = false;
|
||||
};
|
||||
|
||||
@@ -25,7 +25,7 @@ Operation.prototype.eval = function (context) {
|
||||
}
|
||||
if (!a.operate) {
|
||||
throw { type: "Operation",
|
||||
message: "Operation on an invalid type" };
|
||||
message: "Operation on an invalid type" };
|
||||
}
|
||||
|
||||
return a.operate(context, this.op, b);
|
||||
|
||||
70
lib/less/tree/property.js
Normal file
70
lib/less/tree/property.js
Normal file
@@ -0,0 +1,70 @@
|
||||
var Node = require("./node"),
|
||||
Declaration = require("./declaration");
|
||||
|
||||
var Property = function (name, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
Property.prototype = new Node();
|
||||
Property.prototype.type = "Property";
|
||||
Property.prototype.eval = function (context) {
|
||||
var property, name = this.name;
|
||||
// TODO: shorten this reference
|
||||
var 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() };
|
||||
}
|
||||
|
||||
this.evaluating = true;
|
||||
|
||||
property = this.find(context.frames, function (frame) {
|
||||
|
||||
var v, vArr = frame.property(name);
|
||||
if (vArr) {
|
||||
for (var 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) {
|
||||
var importantScope = context.importantScope[context.importantScope.length - 1];
|
||||
importantScope.important = v.important;
|
||||
}
|
||||
v = v.value.eval(context);
|
||||
return v;
|
||||
}
|
||||
});
|
||||
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 (var i = 0, r; i < obj.length; i++) {
|
||||
r = fun.call(obj, obj[i]);
|
||||
if (r) { return r; }
|
||||
}
|
||||
return null;
|
||||
};
|
||||
module.exports = Property;
|
||||
@@ -1,19 +1,19 @@
|
||||
var Node = require("./node"),
|
||||
JsEvalNode = require("./js-eval-node"),
|
||||
Variable = require("./variable");
|
||||
Variable = require("./variable"),
|
||||
Property = require("./property");
|
||||
|
||||
var 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.currentFileInfo = currentFileInfo;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
Quoted.prototype = new JsEvalNode();
|
||||
Quoted.prototype = new Node();
|
||||
Quoted.prototype.type = "Quoted";
|
||||
Quoted.prototype.genCSS = function (context, output) {
|
||||
if (!this.escaped) {
|
||||
output.add(this.quote, this.currentFileInfo, this.index);
|
||||
output.add(this.quote, this.fileInfo(), this.getIndex());
|
||||
}
|
||||
output.add(this.value);
|
||||
if (!this.escaped) {
|
||||
@@ -21,15 +21,16 @@ Quoted.prototype.genCSS = function (context, output) {
|
||||
}
|
||||
};
|
||||
Quoted.prototype.containsVariables = function() {
|
||||
return this.value.match(/(`([^`]+)`)|@\{([\w-]+)\}/);
|
||||
return this.value.match(/@\{([\w-]+)\}/);
|
||||
};
|
||||
Quoted.prototype.eval = function (context) {
|
||||
var that = this, value = this.value;
|
||||
var javascriptReplacement = function (_, exp) {
|
||||
return String(that.evaluateJavaScript(exp, context));
|
||||
var variableReplacement = function (_, name) {
|
||||
var v = new Variable('@' + name, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
var interpolationReplacement = function (_, name) {
|
||||
var v = new Variable('@' + name, that.index, that.currentFileInfo).eval(context, true);
|
||||
var propertyReplacement = function (_, name) {
|
||||
var v = new Property('$' + name, that.getIndex(), that.fileInfo()).eval(context, true);
|
||||
return (v instanceof Quoted) ? v.value : v.toCSS();
|
||||
};
|
||||
function iterativeReplace(value, regexp, replacementFnc) {
|
||||
@@ -40,9 +41,9 @@ Quoted.prototype.eval = function (context) {
|
||||
} while (value !== evaluatedValue);
|
||||
return evaluatedValue;
|
||||
}
|
||||
value = iterativeReplace(value, /`([^`]+)`/g, javascriptReplacement);
|
||||
value = iterativeReplace(value, /@\{([\w-]+)\}/g, interpolationReplacement);
|
||||
return new Quoted(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
|
||||
value = iterativeReplace(value, /@\{([\w-]+)\}/g, variableReplacement);
|
||||
value = iterativeReplace(value, /\$\{([\w-]+)\}/g, propertyReplacement);
|
||||
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
|
||||
|
||||
@@ -1,25 +1,35 @@
|
||||
var Node = require("./node"),
|
||||
Rule = require("./rule"),
|
||||
Declaration = require("./declaration"),
|
||||
Keyword = require("./keyword"),
|
||||
Comment = require("./comment"),
|
||||
Paren = require("./paren"),
|
||||
Selector = require("./selector"),
|
||||
Element = require("./element"),
|
||||
Paren = require("./paren"),
|
||||
Anonymous = require("./anonymous"),
|
||||
contexts = require("../contexts"),
|
||||
globalFunctionRegistry = require("../functions/function-registry"),
|
||||
defaultFunc = require("../functions/default"),
|
||||
getDebugInfo = require("./debug-info");
|
||||
getDebugInfo = require("./debug-info"),
|
||||
utils = require("../utils");
|
||||
|
||||
var Ruleset = function (selectors, rules, strictImports, visibilityInfo) {
|
||||
this.selectors = selectors;
|
||||
this.rules = rules;
|
||||
this._lookups = {};
|
||||
this._variables = null;
|
||||
this._properties = null;
|
||||
this.strictImports = strictImports;
|
||||
this.copyVisibilityInfo(visibilityInfo);
|
||||
this.allowRoot = true;
|
||||
|
||||
this.setParent(this.selectors, this);
|
||||
this.setParent(this.rules, this);
|
||||
|
||||
};
|
||||
Ruleset.prototype = new Node();
|
||||
Ruleset.prototype.type = "Ruleset";
|
||||
Ruleset.prototype.isRuleset = true;
|
||||
Ruleset.prototype.isRulesetLike = true;
|
||||
Ruleset.prototype.isRulesetLike = function() { return true; };
|
||||
Ruleset.prototype.accept = function (visitor) {
|
||||
if (this.paths) {
|
||||
this.paths = visitor.visitArray(this.paths, true);
|
||||
@@ -35,14 +45,14 @@ Ruleset.prototype.eval = function (context) {
|
||||
selCnt, selector, i, hasOnePassingSelector = false;
|
||||
|
||||
if (thisSelectors && (selCnt = thisSelectors.length)) {
|
||||
selectors = [];
|
||||
selectors = new Array(selCnt);
|
||||
defaultFunc.error({
|
||||
type: "Syntax",
|
||||
message: "it is currently only allowed in parametric mixin guards,"
|
||||
});
|
||||
for (i = 0; i < selCnt; i++) {
|
||||
selector = thisSelectors[i].eval(context);
|
||||
selectors.push(selector);
|
||||
selectors[i] = selector;
|
||||
if (selector.evaldCondition) {
|
||||
hasOnePassingSelector = true;
|
||||
}
|
||||
@@ -52,7 +62,7 @@ Ruleset.prototype.eval = function (context) {
|
||||
hasOnePassingSelector = true;
|
||||
}
|
||||
|
||||
var rules = this.rules ? this.rules.slice(0) : null,
|
||||
var rules = this.rules ? utils.copyArray(this.rules) : null,
|
||||
ruleset = new Ruleset(selectors, rules, this.strictImports, this.visibilityInfo()),
|
||||
rule, subRule;
|
||||
|
||||
@@ -100,21 +110,21 @@ Ruleset.prototype.eval = function (context) {
|
||||
|
||||
// Store the frames around mixin definitions,
|
||||
// so they can be evaluated like closures when the time comes.
|
||||
var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
|
||||
for (i = 0; i < rsRuleCnt; i++) {
|
||||
if (rsRules[i].evalFirst) {
|
||||
rsRules[i] = rsRules[i].eval(context);
|
||||
var rsRules = ruleset.rules;
|
||||
for (i = 0; (rule = rsRules[i]); i++) {
|
||||
if (rule.evalFirst) {
|
||||
rsRules[i] = rule.eval(context);
|
||||
}
|
||||
}
|
||||
|
||||
var mediaBlockCount = (context.mediaBlocks && context.mediaBlocks.length) || 0;
|
||||
|
||||
// Evaluate mixin calls.
|
||||
for (i = 0; i < rsRuleCnt; i++) {
|
||||
if (rsRules[i].type === "MixinCall") {
|
||||
/*jshint loopfunc:true */
|
||||
rules = rsRules[i].eval(context).filter(function(r) {
|
||||
if ((r instanceof Rule) && r.variable) {
|
||||
for (i = 0; (rule = rsRules[i]); i++) {
|
||||
if (rule.type === "MixinCall") {
|
||||
/* jshint loopfunc:true */
|
||||
rules = rule.eval(context).filter(function(r) {
|
||||
if ((r instanceof Declaration) && r.variable) {
|
||||
// do not pollute the scope if the variable is
|
||||
// already there. consider returning false here
|
||||
// but we need a way to "return" variable from mixins
|
||||
@@ -123,47 +133,44 @@ Ruleset.prototype.eval = function (context) {
|
||||
return true;
|
||||
});
|
||||
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
|
||||
rsRuleCnt += rules.length - 1;
|
||||
i += rules.length - 1;
|
||||
ruleset.resetCache();
|
||||
} else if (rsRules[i].type === "RulesetCall") {
|
||||
/*jshint loopfunc:true */
|
||||
rules = rsRules[i].eval(context).rules.filter(function(r) {
|
||||
if ((r instanceof Rule) && r.variable) {
|
||||
} else if (rule.type === "VariableCall") {
|
||||
/* jshint loopfunc:true */
|
||||
rules = rule.eval(context).rules.filter(function(r) {
|
||||
if ((r instanceof Declaration) && r.variable) {
|
||||
// do not pollute the scope at all
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
|
||||
rsRuleCnt += rules.length - 1;
|
||||
i += rules.length - 1;
|
||||
ruleset.resetCache();
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate everything else
|
||||
for (i = 0; i < rsRules.length; i++) {
|
||||
rule = rsRules[i];
|
||||
for (i = 0; (rule = rsRules[i]); i++) {
|
||||
if (!rule.evalFirst) {
|
||||
rsRules[i] = rule = rule.eval ? rule.eval(context) : rule;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate everything else
|
||||
for (i = 0; i < rsRules.length; i++) {
|
||||
rule = rsRules[i];
|
||||
for (i = 0; (rule = rsRules[i]); i++) {
|
||||
// for rulesets, check if it is a css guard and can be removed
|
||||
if (rule instanceof Ruleset && rule.selectors && rule.selectors.length === 1) {
|
||||
// check if it can be folded in (e.g. & where)
|
||||
if (rule.selectors[0].isJustParentSelector()) {
|
||||
rsRules.splice(i--, 1);
|
||||
|
||||
for (var j = 0; j < rule.rules.length; j++) {
|
||||
subRule = rule.rules[j];
|
||||
subRule.copyVisibilityInfo(rule.visibilityInfo());
|
||||
if (!(subRule instanceof Rule) || !subRule.variable) {
|
||||
rsRules.splice(++i, 0, subRule);
|
||||
for (var j = 0; (subRule = rule.rules[j]); j++) {
|
||||
if (subRule instanceof Node) {
|
||||
subRule.copyVisibilityInfo(rule.visibilityInfo());
|
||||
if (!(subRule instanceof Declaration) || !subRule.variable) {
|
||||
rsRules.splice(++i, 0, subRule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -191,7 +198,7 @@ Ruleset.prototype.evalImports = function(context) {
|
||||
importRules = rules[i].eval(context);
|
||||
if (importRules && (importRules.length || importRules.length === 0)) {
|
||||
rules.splice.apply(rules, [i, 1].concat(importRules));
|
||||
i+= importRules.length - 1;
|
||||
i += importRules.length - 1;
|
||||
} else {
|
||||
rules.splice(i, 1, importRules);
|
||||
}
|
||||
@@ -230,12 +237,13 @@ Ruleset.prototype.matchCondition = function (args, context) {
|
||||
Ruleset.prototype.resetCache = function () {
|
||||
this._rulesets = null;
|
||||
this._variables = null;
|
||||
this._properties = null;
|
||||
this._lookups = {};
|
||||
};
|
||||
Ruleset.prototype.variables = function () {
|
||||
if (!this._variables) {
|
||||
this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
|
||||
if (r instanceof Rule && r.variable === true) {
|
||||
if (r instanceof Declaration && r.variable === true) {
|
||||
hash[r.name] = r;
|
||||
}
|
||||
// when evaluating variables in an import statement, imports have not been eval'd
|
||||
@@ -254,17 +262,81 @@ Ruleset.prototype.variables = function () {
|
||||
}
|
||||
return this._variables;
|
||||
};
|
||||
Ruleset.prototype.properties = function () {
|
||||
if (!this._properties) {
|
||||
this._properties = !this.rules ? {} : this.rules.reduce(function (hash, r) {
|
||||
if (r instanceof Declaration && r.variable !== true) {
|
||||
var name = (r.name.length === 1) && (r.name[0] instanceof Keyword) ?
|
||||
r.name[0].value : r.name;
|
||||
// Properties don't overwrite as they can merge
|
||||
if (!hash['$' + name]) {
|
||||
hash['$' + name] = [ r ];
|
||||
}
|
||||
else {
|
||||
hash['$' + name].push(r);
|
||||
}
|
||||
}
|
||||
return hash;
|
||||
}, {});
|
||||
}
|
||||
return this._properties;
|
||||
};
|
||||
Ruleset.prototype.variable = function (name) {
|
||||
return this.variables()[name];
|
||||
var decl = this.variables()[name];
|
||||
if (decl) {
|
||||
return this.parseValue(decl);
|
||||
}
|
||||
};
|
||||
Ruleset.prototype.property = function (name) {
|
||||
var decl = this.properties()[name];
|
||||
if (decl) {
|
||||
return this.parseValue(decl);
|
||||
}
|
||||
};
|
||||
Ruleset.prototype.parseValue = function(toParse) {
|
||||
var self = this;
|
||||
function transformDeclaration(decl) {
|
||||
if (decl.value instanceof Anonymous && !decl.parsed) {
|
||||
this.parse.parseNode(
|
||||
decl.value.value,
|
||||
["value", "important"],
|
||||
decl.value.getIndex(),
|
||||
decl.fileInfo(),
|
||||
function(err, result) {
|
||||
if (err) {
|
||||
decl.parsed = true;
|
||||
}
|
||||
if (result) {
|
||||
decl.value = result[0];
|
||||
decl.important = result[1] || '';
|
||||
decl.parsed = true;
|
||||
}
|
||||
});
|
||||
|
||||
return decl;
|
||||
}
|
||||
else {
|
||||
return decl;
|
||||
}
|
||||
}
|
||||
if (!Array.isArray(toParse)) {
|
||||
return transformDeclaration.call(self, toParse);
|
||||
}
|
||||
else {
|
||||
var nodes = [];
|
||||
toParse.forEach(function(n) {
|
||||
nodes.push(transformDeclaration.call(self, n));
|
||||
});
|
||||
return nodes;
|
||||
}
|
||||
};
|
||||
Ruleset.prototype.rulesets = function () {
|
||||
if (!this.rules) { return []; }
|
||||
|
||||
var filtRules = [], rules = this.rules, cnt = rules.length,
|
||||
var filtRules = [], rules = this.rules,
|
||||
i, rule;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
rule = rules[i];
|
||||
for (i = 0; (rule = rules[i]); i++) {
|
||||
if (rule.isRuleset) {
|
||||
filtRules.push(rule);
|
||||
}
|
||||
@@ -279,6 +351,7 @@ Ruleset.prototype.prependRule = function (rule) {
|
||||
} else {
|
||||
this.rules = [ rule ];
|
||||
}
|
||||
this.setParent(rule, this);
|
||||
};
|
||||
Ruleset.prototype.find = function (selector, self, filter) {
|
||||
self = self || this;
|
||||
@@ -329,25 +402,10 @@ Ruleset.prototype.genCSS = function (context, output) {
|
||||
tabSetStr = context.compress ? '' : Array(context.tabLevel).join(" "),
|
||||
sep;
|
||||
|
||||
function isRulesetLikeNode(rule) {
|
||||
// if it has nested rules, then it should be treated like a ruleset
|
||||
// medias and comments do not have nested rules, but should be treated like rulesets anyway
|
||||
// some directives and anonymous nodes are ruleset like, others are not
|
||||
if (typeof rule.isRulesetLike === "boolean") {
|
||||
return rule.isRulesetLike;
|
||||
} else if (typeof rule.isRulesetLike === "function") {
|
||||
return rule.isRulesetLike();
|
||||
}
|
||||
|
||||
//anything else is assumed to be a rule
|
||||
return false;
|
||||
}
|
||||
|
||||
var charsetNodeIndex = 0;
|
||||
var importNodeIndex = 0;
|
||||
for (i = 0; i < this.rules.length; i++) {
|
||||
rule = this.rules[i];
|
||||
if (rule.type === "Comment") {
|
||||
for (i = 0; (rule = this.rules[i]); i++) {
|
||||
if (rule instanceof Comment) {
|
||||
if (importNodeIndex === i) {
|
||||
importNodeIndex++;
|
||||
}
|
||||
@@ -398,15 +456,14 @@ Ruleset.prototype.genCSS = function (context, output) {
|
||||
}
|
||||
|
||||
// Compile rules and rulesets
|
||||
for (i = 0; i < ruleNodes.length; i++) {
|
||||
rule = ruleNodes[i];
|
||||
for (i = 0; (rule = ruleNodes[i]); i++) {
|
||||
|
||||
if (i + 1 === ruleNodes.length) {
|
||||
context.lastRule = true;
|
||||
}
|
||||
|
||||
var currentLastRule = context.lastRule;
|
||||
if (isRulesetLikeNode(rule)) {
|
||||
if (rule.isRulesetLike(rule)) {
|
||||
context.lastRule = false;
|
||||
}
|
||||
|
||||
@@ -418,7 +475,7 @@ Ruleset.prototype.genCSS = function (context, output) {
|
||||
|
||||
context.lastRule = currentLastRule;
|
||||
|
||||
if (!context.lastRule) {
|
||||
if (!context.lastRule && rule.isVisible()) {
|
||||
output.add(context.compress ? '' : ('\n' + tabRuleStr));
|
||||
} else {
|
||||
context.lastRule = false;
|
||||
@@ -448,9 +505,9 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
if (elementsToPak.length === 0) {
|
||||
replacementParen = new Paren(elementsToPak[0]);
|
||||
} else {
|
||||
var insideParent = [];
|
||||
var insideParent = new Array(elementsToPak.length);
|
||||
for (j = 0; j < elementsToPak.length; j++) {
|
||||
insideParent.push(new Element(null, elementsToPak[j], originalElement.index, originalElement.currentFileInfo));
|
||||
insideParent[j] = new Element(null, elementsToPak[j], originalElement._index, originalElement._fileInfo);
|
||||
}
|
||||
replacementParen = new Paren(new Selector(insideParent));
|
||||
}
|
||||
@@ -459,7 +516,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
|
||||
function createSelector(containedElement, originalElement) {
|
||||
var element, selector;
|
||||
element = new Element(null, containedElement, originalElement.index, originalElement.currentFileInfo);
|
||||
element = new Element(null, containedElement, originalElement._index, originalElement._fileInfo);
|
||||
selector = new Selector([element]);
|
||||
return selector;
|
||||
}
|
||||
@@ -472,12 +529,12 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
// our new selector path
|
||||
newSelectorPath = [];
|
||||
|
||||
//construct the joined selector - if & is the first thing this will be empty,
|
||||
// construct the joined selector - if & is the first thing this will be empty,
|
||||
// if not newJoinedSelector will be the last set of elements in the selector
|
||||
if (beginningPath.length > 0) {
|
||||
newSelectorPath = beginningPath.slice(0);
|
||||
newSelectorPath = utils.copyArray(beginningPath);
|
||||
lastSelector = newSelectorPath.pop();
|
||||
newJoinedSelector = originalSelector.createDerived(lastSelector.elements.slice(0));
|
||||
newJoinedSelector = originalSelector.createDerived(utils.copyArray(lastSelector.elements));
|
||||
}
|
||||
else {
|
||||
newJoinedSelector = originalSelector.createDerived([]);
|
||||
@@ -493,7 +550,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
combinator = parentEl.combinator;
|
||||
}
|
||||
// join the elements so far with the first part of the parent
|
||||
newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement.index, replacedElement.currentFileInfo));
|
||||
newJoinedSelector.elements.push(new Element(combinator, parentEl.value, replacedElement._index, replacedElement._fileInfo));
|
||||
newJoinedSelector.elements = newJoinedSelector.elements.concat(addPath[0].elements.slice(1));
|
||||
}
|
||||
|
||||
@@ -502,7 +559,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
newSelectorPath.push(newJoinedSelector);
|
||||
}
|
||||
|
||||
//put together the parent selectors after the join (e.g. the rest of the parent)
|
||||
// put together the parent selectors after the join (e.g. the rest of the parent)
|
||||
if (addPath.length > 1) {
|
||||
var restOfPath = addPath.slice(1);
|
||||
restOfPath = restOfPath.map(function (selector) {
|
||||
@@ -536,9 +593,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < selectors.length; i++) {
|
||||
sel = selectors[i];
|
||||
|
||||
for (i = 0; (sel = selectors[i]); i++) {
|
||||
// if the previous thing in sel is a parent this needs to join on to it
|
||||
if (sel.length > 0) {
|
||||
sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
|
||||
@@ -566,12 +621,12 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
var i, j, k, currentElements, newSelectors, selectorsMultiplied, sel, el, hadParentSelector = false, length, lastSelector;
|
||||
function findNestedSelector(element) {
|
||||
var maybeSelector;
|
||||
if (element.value.type !== 'Paren') {
|
||||
if (!(element.value instanceof Paren)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
maybeSelector = element.value.value;
|
||||
if (maybeSelector.type !== 'Selector') {
|
||||
if (!(maybeSelector instanceof Selector)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -587,8 +642,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
[]
|
||||
];
|
||||
|
||||
for (i = 0; i < inSelector.elements.length; i++) {
|
||||
el = inSelector.elements[i];
|
||||
for (i = 0; (el = inSelector.elements[i]); i++) {
|
||||
// non parent reference elements just get added
|
||||
if (el.value !== "&") {
|
||||
var nestedSelector = findNestedSelector(el);
|
||||
@@ -600,7 +654,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
var nestedPaths = [], replaced, replacedNewSelectors = [];
|
||||
replaced = replaceParentSelector(nestedPaths, context, nestedSelector);
|
||||
hadParentSelector = hadParentSelector || replaced;
|
||||
//the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
|
||||
// the nestedPaths array should have only one member - replaceParentSelector does not multiply selectors
|
||||
for (k = 0; k < nestedPaths.length; k++) {
|
||||
var replacementSelector = createSelector(createParenthesis(nestedPaths[k], el), el);
|
||||
addAllReplacementsIntoPath(newSelectors, [replacementSelector], el, inSelector, replacedNewSelectors);
|
||||
@@ -630,7 +684,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
// the combinator used on el should now be applied to the next element instead so that
|
||||
// it is not lost
|
||||
if (sel.length > 0) {
|
||||
sel[0].elements.push(new Element(el.combinator, '', el.index, el.currentFileInfo));
|
||||
sel[0].elements.push(new Element(el.combinator, '', el._index, el._fileInfo));
|
||||
}
|
||||
selectorsMultiplied.push(sel);
|
||||
}
|
||||
@@ -662,7 +716,6 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
paths.push(newSelectors[i]);
|
||||
lastSelector = newSelectors[i][length - 1];
|
||||
newSelectors[i][length - 1] = lastSelector.createDerived(lastSelector.elements, inSelector.extendList);
|
||||
//newSelectors[i][length - 1].copyVisibilityInfo(inSelector.visibilityInfo());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,12 +738,7 @@ Ruleset.prototype.joinSelector = function (paths, context, selector) {
|
||||
if (context.length > 0) {
|
||||
newPaths = [];
|
||||
for (i = 0; i < context.length; i++) {
|
||||
//var concatenated = [];
|
||||
//context[i].forEach(function(entry) {
|
||||
// var newEntry = entry.createDerived(entry.elements, entry.extendList, entry.evaldCondition);
|
||||
// newEntry.copyVisibilityInfo(selector.visibilityInfo());
|
||||
// concatenated.push(newEntry);
|
||||
//}, this);
|
||||
|
||||
var concatenated = context[i].map(deriveSelector.bind(this, selector.visibilityInfo()));
|
||||
|
||||
concatenated.push(selector);
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
var Node = require("./node"),
|
||||
Element = require("./element");
|
||||
Element = require("./element"),
|
||||
LessError = require("../less-error");
|
||||
|
||||
var Selector = function (elements, extendList, condition, index, currentFileInfo, visibilityInfo) {
|
||||
this.elements = elements;
|
||||
this.extendList = extendList;
|
||||
this.condition = condition;
|
||||
this.currentFileInfo = currentFileInfo || {};
|
||||
if (!condition) {
|
||||
this.evaldCondition = true;
|
||||
}
|
||||
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.type = "Selector";
|
||||
@@ -25,16 +27,35 @@ Selector.prototype.accept = function (visitor) {
|
||||
}
|
||||
};
|
||||
Selector.prototype.createDerived = function(elements, extendList, evaldCondition) {
|
||||
var info = this.visibilityInfo();
|
||||
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
|
||||
var newSelector = new Selector(elements, extendList || this.extendList, null, this.index, this.currentFileInfo, info);
|
||||
newSelector.evaldCondition = evaldCondition;
|
||||
elements = this.getElements(elements);
|
||||
var 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 (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() {
|
||||
var el = new Element('', '&', this.index, this.currentFileInfo),
|
||||
sels = [new Selector([el], null, null, this.index, this.currentFileInfo)];
|
||||
var el = new Element('', '&', this._index, this._fileInfo),
|
||||
sels = [new Selector([el], null, null, this._index, this._fileInfo)];
|
||||
sels[0].mediaEmpty = true;
|
||||
return sels;
|
||||
};
|
||||
@@ -43,14 +64,13 @@ Selector.prototype.match = function (other) {
|
||||
len = elements.length,
|
||||
olen, i;
|
||||
|
||||
other.CacheElements();
|
||||
|
||||
olen = other._elements.length;
|
||||
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._elements[i]) {
|
||||
if (elements[i].value !== other[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -58,9 +78,9 @@ Selector.prototype.match = function (other) {
|
||||
|
||||
return olen; // return number of matched elements
|
||||
};
|
||||
Selector.prototype.CacheElements = function() {
|
||||
if (this._elements) {
|
||||
return;
|
||||
Selector.prototype.mixinElements = function() {
|
||||
if (this.mixinElements_) {
|
||||
return this.mixinElements_;
|
||||
}
|
||||
|
||||
var elements = this.elements.map( function(v) {
|
||||
@@ -75,7 +95,7 @@ Selector.prototype.CacheElements = function() {
|
||||
elements = [];
|
||||
}
|
||||
|
||||
this._elements = elements;
|
||||
return (this.mixinElements_ = elements);
|
||||
};
|
||||
Selector.prototype.isJustParentSelector = function() {
|
||||
return !this.mediaEmpty &&
|
||||
@@ -95,14 +115,11 @@ Selector.prototype.eval = function (context) {
|
||||
Selector.prototype.genCSS = function (context, output) {
|
||||
var i, element;
|
||||
if ((!context || !context.firstSelector) && this.elements[0].combinator.value === "") {
|
||||
output.add(' ', this.currentFileInfo, this.index);
|
||||
output.add(' ', this.fileInfo(), this.getIndex());
|
||||
}
|
||||
if (!this._css) {
|
||||
//TODO caching? speed comparison?
|
||||
for (i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(context, output);
|
||||
}
|
||||
for (i = 0; i < this.elements.length; i++) {
|
||||
element = this.elements[i];
|
||||
element.genCSS(context, output);
|
||||
}
|
||||
};
|
||||
Selector.prototype.getIsOutput = function() {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
var Node = require("./node"),
|
||||
unitConversions = require("../data/unit-conversions");
|
||||
unitConversions = require("../data/unit-conversions"),
|
||||
utils = require("../utils");
|
||||
|
||||
var Unit = function (numerator, denominator, backupUnit) {
|
||||
this.numerator = numerator ? numerator.slice(0).sort() : [];
|
||||
this.denominator = denominator ? denominator.slice(0).sort() : [];
|
||||
this.numerator = numerator ? utils.copyArray(numerator).sort() : [];
|
||||
this.denominator = denominator ? utils.copyArray(denominator).sort() : [];
|
||||
if (backupUnit) {
|
||||
this.backupUnit = backupUnit;
|
||||
} else if (numerator && numerator.length) {
|
||||
@@ -14,7 +15,7 @@ var Unit = function (numerator, denominator, backupUnit) {
|
||||
Unit.prototype = new Node();
|
||||
Unit.prototype.type = "Unit";
|
||||
Unit.prototype.clone = function () {
|
||||
return new Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
|
||||
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.
|
||||
@@ -64,7 +65,7 @@ Unit.prototype.usedUnits = function() {
|
||||
var group, result = {}, mapUnit, groupName;
|
||||
|
||||
mapUnit = function (atomicUnit) {
|
||||
/*jshint loopfunc:true */
|
||||
/* jshint loopfunc:true */
|
||||
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
|
||||
result[groupName] = atomicUnit;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ var Node = require("./node");
|
||||
|
||||
var URL = function (val, index, currentFileInfo, isEvald) {
|
||||
this.value = val;
|
||||
this.currentFileInfo = currentFileInfo;
|
||||
this.index = index;
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
this.isEvald = isEvald;
|
||||
};
|
||||
URL.prototype = new Node();
|
||||
@@ -22,7 +22,7 @@ URL.prototype.eval = function (context) {
|
||||
|
||||
if (!this.isEvald) {
|
||||
// Add the base path if the URL is relative
|
||||
rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
|
||||
rootpath = this.fileInfo() && this.fileInfo().rootpath;
|
||||
if (rootpath &&
|
||||
typeof val.value === "string" &&
|
||||
context.isPathRelative(val.value)) {
|
||||
@@ -49,6 +49,6 @@ URL.prototype.eval = function (context) {
|
||||
}
|
||||
}
|
||||
|
||||
return new URL(val, this.index, this.currentFileInfo, true);
|
||||
return new URL(val, this.getIndex(), this.fileInfo(), true);
|
||||
};
|
||||
module.exports = URL;
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
var Node = require("./node");
|
||||
|
||||
var Value = function (value) {
|
||||
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.type = "Value";
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
var Node = require("./node"),
|
||||
Variable = require("./variable");
|
||||
|
||||
var RulesetCall = function (variable) {
|
||||
var VariableCall = function (variable) {
|
||||
this.variable = variable;
|
||||
this.allowRoot = true;
|
||||
};
|
||||
RulesetCall.prototype = new Node();
|
||||
RulesetCall.prototype.type = "RulesetCall";
|
||||
RulesetCall.prototype.eval = function (context) {
|
||||
VariableCall.prototype = new Node();
|
||||
VariableCall.prototype.type = "VariableCall";
|
||||
VariableCall.prototype.eval = function (context) {
|
||||
var detachedRuleset = new Variable(this.variable).eval(context);
|
||||
return detachedRuleset.callEval(context);
|
||||
};
|
||||
module.exports = RulesetCall;
|
||||
module.exports = VariableCall;
|
||||
@@ -2,8 +2,8 @@ var Node = require("./node");
|
||||
|
||||
var Variable = function (name, index, currentFileInfo) {
|
||||
this.name = name;
|
||||
this.index = index;
|
||||
this.currentFileInfo = currentFileInfo || {};
|
||||
this._index = index;
|
||||
this._fileInfo = currentFileInfo;
|
||||
};
|
||||
Variable.prototype = new Node();
|
||||
Variable.prototype.type = "Variable";
|
||||
@@ -11,14 +11,14 @@ Variable.prototype.eval = function (context) {
|
||||
var variable, name = this.name;
|
||||
|
||||
if (name.indexOf('@@') === 0) {
|
||||
name = '@' + new Variable(name.slice(1), this.index, this.currentFileInfo).eval(context).value;
|
||||
name = '@' + new Variable(name.slice(1), this.getIndex(), this.fileInfo()).eval(context).value;
|
||||
}
|
||||
|
||||
if (this.evaluating) {
|
||||
throw { type: 'Name',
|
||||
message: "Recursive variable definition for " + name,
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
message: "Recursive variable definition for " + name,
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
|
||||
this.evaluating = true;
|
||||
@@ -38,9 +38,9 @@ Variable.prototype.eval = function (context) {
|
||||
return variable;
|
||||
} else {
|
||||
throw { type: 'Name',
|
||||
message: "variable " + name + " is undefined",
|
||||
filename: this.currentFileInfo.filename,
|
||||
index: this.index };
|
||||
message: "variable " + name + " is undefined",
|
||||
filename: this.fileInfo().filename,
|
||||
index: this.getIndex() };
|
||||
}
|
||||
};
|
||||
Variable.prototype.find = function (obj, fun) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* jshint proto: true */
|
||||
module.exports = {
|
||||
getLocation: function(index, inputStream) {
|
||||
var n = index + 1,
|
||||
@@ -16,5 +17,53 @@ module.exports = {
|
||||
line: line,
|
||||
column: column
|
||||
};
|
||||
},
|
||||
copyArray: function(arr) {
|
||||
var i, length = arr.length,
|
||||
copy = new Array(length);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
copy[i] = arr[i];
|
||||
}
|
||||
return copy;
|
||||
},
|
||||
clone: function (obj) {
|
||||
var cloned = {};
|
||||
for (var prop in obj) {
|
||||
if (obj.hasOwnProperty(prop)) {
|
||||
cloned[prop] = obj[prop];
|
||||
}
|
||||
}
|
||||
return cloned;
|
||||
},
|
||||
defaults: function(obj1, obj2) {
|
||||
if (!obj2._defaults || obj2._defaults !== obj1) {
|
||||
for (var prop in obj1) {
|
||||
if (obj1.hasOwnProperty(prop)) {
|
||||
if (!obj2.hasOwnProperty(prop)) {
|
||||
obj2[prop] = obj1[prop];
|
||||
}
|
||||
else if (Array.isArray(obj1[prop])
|
||||
&& Array.isArray(obj2[prop])) {
|
||||
|
||||
obj1[prop].forEach(function(p) {
|
||||
if (obj2[prop].indexOf(p) === -1) {
|
||||
obj2[prop].push(p);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
obj2._defaults = obj1;
|
||||
return obj2;
|
||||
},
|
||||
merge: function(obj1, obj2) {
|
||||
for (var prop in obj2) {
|
||||
if (obj2.hasOwnProperty(prop)) {
|
||||
obj1[prop] = obj2[prop];
|
||||
}
|
||||
}
|
||||
return obj1;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
var tree = require("../tree"),
|
||||
Visitor = require("./visitor"),
|
||||
logger = require("../logger");
|
||||
logger = require("../logger"),
|
||||
utils = require("../utils");
|
||||
|
||||
/*jshint loopfunc:true */
|
||||
/* jshint loopfunc:true */
|
||||
|
||||
var ExtendFinderVisitor = function() {
|
||||
this._visitor = new Visitor(this);
|
||||
@@ -16,7 +17,7 @@ ExtendFinderVisitor.prototype = {
|
||||
root.allExtends = this.allExtendsStack[0];
|
||||
return root;
|
||||
},
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
visitDeclaration: function (declNode, visitArgs) {
|
||||
visitArgs.visitDeeper = false;
|
||||
},
|
||||
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
||||
@@ -46,7 +47,7 @@ ExtendFinderVisitor.prototype = {
|
||||
selector = selectorPath[selectorPath.length - 1],
|
||||
selExtendList = selector.extendList;
|
||||
|
||||
extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
|
||||
extendList = selExtendList ? utils.copyArray(selExtendList).concat(allSelectorsExtendList)
|
||||
: allSelectorsExtendList;
|
||||
|
||||
if (extendList) {
|
||||
@@ -79,11 +80,11 @@ ExtendFinderVisitor.prototype = {
|
||||
visitMediaOut: function (mediaNode) {
|
||||
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
||||
},
|
||||
visitDirective: function (directiveNode, visitArgs) {
|
||||
directiveNode.allExtends = [];
|
||||
this.allExtendsStack.push(directiveNode.allExtends);
|
||||
visitAtRule: function (atRuleNode, visitArgs) {
|
||||
atRuleNode.allExtends = [];
|
||||
this.allExtendsStack.push(atRuleNode.allExtends);
|
||||
},
|
||||
visitDirectiveOut: function (directiveNode) {
|
||||
visitAtRuleOut: function (atRuleNode) {
|
||||
this.allExtendsStack.length = this.allExtendsStack.length - 1;
|
||||
}
|
||||
};
|
||||
@@ -109,17 +110,17 @@ ProcessExtendsVisitor.prototype = {
|
||||
extendList.filter(function(extend) {
|
||||
return !extend.hasFoundMatches && extend.parent_ids.length == 1;
|
||||
}).forEach(function(extend) {
|
||||
var selector = "_unknown_";
|
||||
try {
|
||||
selector = extend.selector.toCSS({});
|
||||
}
|
||||
catch(_) {}
|
||||
var selector = "_unknown_";
|
||||
try {
|
||||
selector = extend.selector.toCSS({});
|
||||
}
|
||||
catch (_) {}
|
||||
|
||||
if (!indices[extend.index + ' ' + selector]) {
|
||||
indices[extend.index + ' ' + selector] = true;
|
||||
logger.warn("extend '" + selector + "' has no matches");
|
||||
}
|
||||
});
|
||||
if (!indices[extend.index + ' ' + selector]) {
|
||||
indices[extend.index + ' ' + selector] = true;
|
||||
logger.warn("extend '" + selector + "' has no matches");
|
||||
}
|
||||
});
|
||||
},
|
||||
doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
|
||||
//
|
||||
@@ -136,7 +137,7 @@ ProcessExtendsVisitor.prototype = {
|
||||
|
||||
iterationCount = iterationCount || 0;
|
||||
|
||||
//loop through comparing every extend with every target extend.
|
||||
// loop through comparing every extend with every target extend.
|
||||
// a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
|
||||
// e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
|
||||
// and the second is the target.
|
||||
@@ -166,7 +167,7 @@ ProcessExtendsVisitor.prototype = {
|
||||
newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector, extend.isVisible());
|
||||
|
||||
// but now we create a new extend from it
|
||||
newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0, targetExtend.currentFileInfo, info);
|
||||
newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0, targetExtend.fileInfo(), info);
|
||||
newExtend.selfSelectors = newSelector;
|
||||
|
||||
// add the extend onto the list of extends for that selector
|
||||
@@ -202,7 +203,7 @@ ProcessExtendsVisitor.prototype = {
|
||||
selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
|
||||
selectorTwo = extendsToAdd[0].selector.toCSS();
|
||||
}
|
||||
catch(e) {}
|
||||
catch (e) {}
|
||||
throw { message: "extend circular reference detected. One of the circular extends is currently:" +
|
||||
selectorOne + ":extend(" + selectorTwo + ")"};
|
||||
}
|
||||
@@ -214,7 +215,7 @@ ProcessExtendsVisitor.prototype = {
|
||||
return extendsToAdd;
|
||||
}
|
||||
},
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
visitDeclaration: function (ruleNode, visitArgs) {
|
||||
visitArgs.visitDeeper = false;
|
||||
},
|
||||
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
||||
@@ -367,7 +368,7 @@ ProcessExtendsVisitor.prototype = {
|
||||
},
|
||||
extendSelector:function (matches, selectorPath, replacementSelector, isVisible) {
|
||||
|
||||
//for a set of matches, replace each match with the replacement selector
|
||||
// for a set of matches, replace each match with the replacement selector
|
||||
|
||||
var currentSelectorPathIndex = 0,
|
||||
currentSelectorPathElementIndex = 0,
|
||||
@@ -384,8 +385,8 @@ ProcessExtendsVisitor.prototype = {
|
||||
firstElement = new tree.Element(
|
||||
match.initialCombinator,
|
||||
replacementSelector.elements[0].value,
|
||||
replacementSelector.elements[0].index,
|
||||
replacementSelector.elements[0].currentFileInfo
|
||||
replacementSelector.elements[0].getIndex(),
|
||||
replacementSelector.elements[0].fileInfo()
|
||||
);
|
||||
|
||||
if (match.pathIndex > currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
|
||||
@@ -446,12 +447,12 @@ ProcessExtendsVisitor.prototype = {
|
||||
var lastIndex = this.allExtendsStack.length - 1;
|
||||
this.allExtendsStack.length = lastIndex;
|
||||
},
|
||||
visitDirective: function (directiveNode, visitArgs) {
|
||||
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
|
||||
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
|
||||
visitAtRule: function (atRuleNode, visitArgs) {
|
||||
var newAllExtends = atRuleNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length - 1]);
|
||||
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, atRuleNode.allExtends));
|
||||
this.allExtendsStack.push(newAllExtends);
|
||||
},
|
||||
visitDirectiveOut: function (directiveNode) {
|
||||
visitAtRuleOut: function (atRuleNode) {
|
||||
var lastIndex = this.allExtendsStack.length - 1;
|
||||
this.allExtendsStack.length = lastIndex;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
var contexts = require("../contexts"),
|
||||
Visitor = require("./visitor"),
|
||||
ImportSequencer = require("./import-sequencer");
|
||||
ImportSequencer = require("./import-sequencer"),
|
||||
utils = require("../utils");
|
||||
|
||||
var ImportVisitor = function(importer, finish) {
|
||||
|
||||
@@ -21,7 +22,7 @@ ImportVisitor.prototype = {
|
||||
// process the contents
|
||||
this._visitor.visit(root);
|
||||
}
|
||||
catch(e) {
|
||||
catch (e) {
|
||||
this.error = e;
|
||||
}
|
||||
|
||||
@@ -39,7 +40,7 @@ ImportVisitor.prototype = {
|
||||
|
||||
if (!importNode.css || inlineCSS) {
|
||||
|
||||
var context = new contexts.Eval(this.context, this.context.frames.slice(0));
|
||||
var context = new contexts.Eval(this.context, utils.copyArray(this.context.frames));
|
||||
var importParent = context.frames[0];
|
||||
|
||||
this.importCount++;
|
||||
@@ -57,8 +58,8 @@ ImportVisitor.prototype = {
|
||||
|
||||
try {
|
||||
evaldImportNode = importNode.evalForImport(context);
|
||||
} catch(e) {
|
||||
if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
|
||||
} catch (e) {
|
||||
if (!e.filename) { e.index = importNode.getIndex(); e.filename = importNode.fileInfo().filename; }
|
||||
// attempt to eval properly and treat as css
|
||||
importNode.css = true;
|
||||
// if that fails, this error will be thrown
|
||||
@@ -84,7 +85,7 @@ ImportVisitor.prototype = {
|
||||
var onImported = this.onImported.bind(this, evaldImportNode, context),
|
||||
sequencedOnImported = this._sequencer.addImport(onImported);
|
||||
|
||||
this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo,
|
||||
this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.fileInfo(),
|
||||
evaldImportNode.options, sequencedOnImported);
|
||||
} else {
|
||||
this.importCount--;
|
||||
@@ -96,14 +97,14 @@ ImportVisitor.prototype = {
|
||||
onImported: function (importNode, context, e, root, importedAtRoot, fullPath) {
|
||||
if (e) {
|
||||
if (!e.filename) {
|
||||
e.index = importNode.index; e.filename = importNode.currentFileInfo.filename;
|
||||
e.index = importNode.getIndex(); e.filename = importNode.fileInfo().filename;
|
||||
}
|
||||
this.error = e;
|
||||
}
|
||||
|
||||
var importVisitor = this,
|
||||
inlineCSS = importNode.options.inline,
|
||||
isPlugin = importNode.options.plugin,
|
||||
isPlugin = importNode.options.isPlugin,
|
||||
isOptional = importNode.options.optional,
|
||||
duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector;
|
||||
|
||||
@@ -149,22 +150,22 @@ ImportVisitor.prototype = {
|
||||
importVisitor._sequencer.tryRun();
|
||||
}
|
||||
},
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
if (ruleNode.value.type === "DetachedRuleset") {
|
||||
this.context.frames.unshift(ruleNode);
|
||||
visitDeclaration: function (declNode, visitArgs) {
|
||||
if (declNode.value.type === "DetachedRuleset") {
|
||||
this.context.frames.unshift(declNode);
|
||||
} else {
|
||||
visitArgs.visitDeeper = false;
|
||||
}
|
||||
},
|
||||
visitRuleOut : function(ruleNode) {
|
||||
if (ruleNode.value.type === "DetachedRuleset") {
|
||||
visitDeclarationOut: function(declNode) {
|
||||
if (declNode.value.type === "DetachedRuleset") {
|
||||
this.context.frames.shift();
|
||||
}
|
||||
},
|
||||
visitDirective: function (directiveNode, visitArgs) {
|
||||
this.context.frames.unshift(directiveNode);
|
||||
visitAtRule: function (atRuleNode, visitArgs) {
|
||||
this.context.frames.unshift(atRuleNode);
|
||||
},
|
||||
visitDirectiveOut: function (directiveNode) {
|
||||
visitAtRuleOut: function (atRuleNode) {
|
||||
this.context.frames.shift();
|
||||
},
|
||||
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
||||
|
||||
@@ -9,7 +9,7 @@ JoinSelectorVisitor.prototype = {
|
||||
run: function (root) {
|
||||
return this._visitor.visit(root);
|
||||
},
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
visitDeclaration: function (declNode, visitArgs) {
|
||||
visitArgs.visitDeeper = false;
|
||||
},
|
||||
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
|
||||
@@ -22,7 +22,7 @@ JoinSelectorVisitor.prototype = {
|
||||
|
||||
this.contexts.push(paths);
|
||||
|
||||
if (! rulesetNode.root) {
|
||||
if (!rulesetNode.root) {
|
||||
selectors = rulesetNode.selectors;
|
||||
if (selectors) {
|
||||
selectors = selectors.filter(function(selector) { return selector.getIsOutput(); });
|
||||
@@ -40,10 +40,10 @@ JoinSelectorVisitor.prototype = {
|
||||
var context = this.contexts[this.contexts.length - 1];
|
||||
mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
|
||||
},
|
||||
visitDirective: function (directiveNode, visitArgs) {
|
||||
visitAtRule: function (atRuleNode, visitArgs) {
|
||||
var context = this.contexts[this.contexts.length - 1];
|
||||
if (directiveNode.rules && directiveNode.rules.length) {
|
||||
directiveNode.rules[0].root = (directiveNode.isRooted || context.length === 0 || null);
|
||||
if (atRuleNode.rules && atRuleNode.rules.length) {
|
||||
atRuleNode.rules[0].root = (atRuleNode.isRooted || context.length === 0 || null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,14 +9,14 @@ var CSSVisitorUtils = function(context) {
|
||||
CSSVisitorUtils.prototype = {
|
||||
containsSilentNonBlockedChild: function(bodyRules) {
|
||||
var rule;
|
||||
if (bodyRules == null) {
|
||||
if (!bodyRules) {
|
||||
return false;
|
||||
}
|
||||
for (var r = 0; r < bodyRules.length; r++) {
|
||||
rule = bodyRules[r];
|
||||
if (rule.isSilent && rule.isSilent(this._context) && !rule.blocksVisibility()) {
|
||||
//the directive contains something that was referenced (likely by extend)
|
||||
//therefore it needs to be shown in output too
|
||||
// the atrule contains something that was referenced (likely by extend)
|
||||
// therefore it needs to be shown in output too
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -24,28 +24,21 @@ CSSVisitorUtils.prototype = {
|
||||
},
|
||||
|
||||
keepOnlyVisibleChilds: function(owner) {
|
||||
if (owner == null || owner.rules == null) {
|
||||
return ;
|
||||
}
|
||||
|
||||
owner.rules = owner.rules.filter(function(thing) {
|
||||
if (owner && owner.rules) {
|
||||
owner.rules = owner.rules.filter(function(thing) {
|
||||
return thing.isVisible();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
isEmpty: function(owner) {
|
||||
if (owner == null || owner.rules == null) {
|
||||
return true;
|
||||
}
|
||||
return owner.rules.length === 0;
|
||||
return (owner && owner.rules)
|
||||
? (owner.rules.length === 0) : true;
|
||||
},
|
||||
|
||||
hasVisibleSelector: function(rulesetNode) {
|
||||
if (rulesetNode == null || rulesetNode.paths == null) {
|
||||
return false;
|
||||
}
|
||||
return rulesetNode.paths.length > 0;
|
||||
return (rulesetNode && rulesetNode.paths)
|
||||
? (rulesetNode.paths.length > 0) : false;
|
||||
},
|
||||
|
||||
resolveVisibility: function (node, originalRules) {
|
||||
@@ -100,11 +93,11 @@ ToCSSVisitor.prototype = {
|
||||
return this._visitor.visit(root);
|
||||
},
|
||||
|
||||
visitRule: function (ruleNode, visitArgs) {
|
||||
if (ruleNode.blocksVisibility() || ruleNode.variable) {
|
||||
visitDeclaration: function (declNode, visitArgs) {
|
||||
if (declNode.blocksVisibility() || declNode.variable) {
|
||||
return;
|
||||
}
|
||||
return ruleNode;
|
||||
return declNode;
|
||||
},
|
||||
|
||||
visitMixinDefinition: function (mixinNode, visitArgs) {
|
||||
@@ -138,56 +131,63 @@ ToCSSVisitor.prototype = {
|
||||
return importNode;
|
||||
},
|
||||
|
||||
visitDirective: function(directiveNode, visitArgs) {
|
||||
if (directiveNode.rules && directiveNode.rules.length) {
|
||||
return this.visitDirectiveWithBody(directiveNode, visitArgs);
|
||||
visitAtRule: function(atRuleNode, visitArgs) {
|
||||
if (atRuleNode.rules && atRuleNode.rules.length) {
|
||||
return this.visitAtRuleWithBody(atRuleNode, visitArgs);
|
||||
} else {
|
||||
return this.visitDirectiveWithoutBody(directiveNode, visitArgs);
|
||||
return this.visitAtRuleWithoutBody(atRuleNode, visitArgs);
|
||||
}
|
||||
},
|
||||
|
||||
visitDirectiveWithBody: function(directiveNode, visitArgs) {
|
||||
//if there is only one nested ruleset and that one has no path, then it is
|
||||
//just fake ruleset
|
||||
function hasFakeRuleset(directiveNode) {
|
||||
var bodyRules = directiveNode.rules;
|
||||
visitAnonymous: function(anonymousNode, visitArgs) {
|
||||
if (!anonymousNode.blocksVisibility()) {
|
||||
anonymousNode.accept(this._visitor);
|
||||
return anonymousNode;
|
||||
}
|
||||
},
|
||||
|
||||
visitAtRuleWithBody: function(atRuleNode, visitArgs) {
|
||||
// if there is only one nested ruleset and that one has no path, then it is
|
||||
// just fake ruleset
|
||||
function hasFakeRuleset(atRuleNode) {
|
||||
var bodyRules = atRuleNode.rules;
|
||||
return bodyRules.length === 1 && (!bodyRules[0].paths || bodyRules[0].paths.length === 0);
|
||||
}
|
||||
function getBodyRules(directiveNode) {
|
||||
var nodeRules = directiveNode.rules;
|
||||
if (hasFakeRuleset(directiveNode)) {
|
||||
function getBodyRules(atRuleNode) {
|
||||
var nodeRules = atRuleNode.rules;
|
||||
if (hasFakeRuleset(atRuleNode)) {
|
||||
return nodeRules[0].rules;
|
||||
}
|
||||
|
||||
return nodeRules;
|
||||
}
|
||||
//it is still true that it is only one ruleset in array
|
||||
//this is last such moment
|
||||
//process childs
|
||||
var originalRules = getBodyRules(directiveNode);
|
||||
directiveNode.accept(this._visitor);
|
||||
// it is still true that it is only one ruleset in array
|
||||
// this is last such moment
|
||||
// process childs
|
||||
var originalRules = getBodyRules(atRuleNode);
|
||||
atRuleNode.accept(this._visitor);
|
||||
visitArgs.visitDeeper = false;
|
||||
|
||||
if (!this.utils.isEmpty(directiveNode)) {
|
||||
this._mergeRules(directiveNode.rules[0].rules);
|
||||
if (!this.utils.isEmpty(atRuleNode)) {
|
||||
this._mergeRules(atRuleNode.rules[0].rules);
|
||||
}
|
||||
|
||||
return this.utils.resolveVisibility(directiveNode, originalRules);
|
||||
return this.utils.resolveVisibility(atRuleNode, originalRules);
|
||||
},
|
||||
|
||||
visitDirectiveWithoutBody: function(directiveNode, visitArgs) {
|
||||
if (directiveNode.blocksVisibility()) {
|
||||
visitAtRuleWithoutBody: function(atRuleNode, visitArgs) {
|
||||
if (atRuleNode.blocksVisibility()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (directiveNode.name === "@charset") {
|
||||
if (atRuleNode.name === "@charset") {
|
||||
// Only output the debug info together with subsequent @charset definitions
|
||||
// a comment (or @media statement) before the actual @charset directive would
|
||||
// a comment (or @media statement) before the actual @charset atrule would
|
||||
// be considered illegal css as it has to be on the first line
|
||||
if (this.charset) {
|
||||
if (directiveNode.debugInfo) {
|
||||
var comment = new tree.Comment("/* " + directiveNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
|
||||
comment.debugInfo = directiveNode.debugInfo;
|
||||
if (atRuleNode.debugInfo) {
|
||||
var comment = new tree.Comment("/* " + atRuleNode.toCSS(this._context).replace(/\n/g, "") + " */\n");
|
||||
comment.debugInfo = atRuleNode.debugInfo;
|
||||
return this._visitor.visit(comment);
|
||||
}
|
||||
return;
|
||||
@@ -195,7 +195,7 @@ ToCSSVisitor.prototype = {
|
||||
this.charset = true;
|
||||
}
|
||||
|
||||
return directiveNode;
|
||||
return atRuleNode;
|
||||
},
|
||||
|
||||
checkValidNodes: function(rules, isRoot) {
|
||||
@@ -205,29 +205,29 @@ ToCSSVisitor.prototype = {
|
||||
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
var ruleNode = rules[i];
|
||||
if (isRoot && ruleNode instanceof tree.Rule && !ruleNode.variable) {
|
||||
if (isRoot && ruleNode instanceof tree.Declaration && !ruleNode.variable) {
|
||||
throw { message: "Properties must be inside selector blocks. They cannot be in the root",
|
||||
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
|
||||
index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
|
||||
}
|
||||
if (ruleNode instanceof tree.Call) {
|
||||
throw { message: "Function '" + ruleNode.name + "' is undefined",
|
||||
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
|
||||
index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
|
||||
}
|
||||
if (ruleNode.type && !ruleNode.allowRoot) {
|
||||
throw { message: ruleNode.type + " node returned by a function is not valid here",
|
||||
index: ruleNode.index, filename: ruleNode.currentFileInfo && ruleNode.currentFileInfo.filename};
|
||||
index: ruleNode.getIndex(), filename: ruleNode.fileInfo() && ruleNode.fileInfo().filename};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
visitRuleset: function (rulesetNode, visitArgs) {
|
||||
//at this point rulesets are nested into each other
|
||||
// at this point rulesets are nested into each other
|
||||
var rule, rulesets = [];
|
||||
|
||||
this.checkValidNodes(rulesetNode.rules, rulesetNode.firstRoot);
|
||||
|
||||
if (! rulesetNode.root) {
|
||||
//remove invisible paths
|
||||
if (!rulesetNode.root) {
|
||||
// remove invisible paths
|
||||
this._compileRulesetPaths(rulesetNode);
|
||||
|
||||
// remove rulesets from this ruleset body and compile them separately
|
||||
@@ -253,7 +253,7 @@ ToCSSVisitor.prototype = {
|
||||
}
|
||||
visitArgs.visitDeeper = false;
|
||||
|
||||
} else { //if (! rulesetNode.root) {
|
||||
} else { // if (! rulesetNode.root) {
|
||||
rulesetNode.accept(this._visitor);
|
||||
visitArgs.visitDeeper = false;
|
||||
}
|
||||
@@ -263,7 +263,7 @@ ToCSSVisitor.prototype = {
|
||||
this._removeDuplicateRules(rulesetNode.rules);
|
||||
}
|
||||
|
||||
//now decide whether we keep the ruleset
|
||||
// now decide whether we keep the ruleset
|
||||
if (this.utils.isVisibleRuleset(rulesetNode)) {
|
||||
rulesetNode.ensureVisibility();
|
||||
rulesets.splice(0, 0, rulesetNode);
|
||||
@@ -302,12 +302,12 @@ ToCSSVisitor.prototype = {
|
||||
|
||||
for (i = rules.length - 1; i >= 0 ; i--) {
|
||||
rule = rules[i];
|
||||
if (rule instanceof tree.Rule) {
|
||||
if (rule instanceof tree.Declaration) {
|
||||
if (!ruleCache[rule.name]) {
|
||||
ruleCache[rule.name] = rule;
|
||||
} else {
|
||||
ruleList = ruleCache[rule.name];
|
||||
if (ruleList instanceof tree.Rule) {
|
||||
if (ruleList instanceof tree.Declaration) {
|
||||
ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._context)];
|
||||
}
|
||||
var ruleCSS = rule.toCSS(this._context);
|
||||
@@ -321,72 +321,39 @@ ToCSSVisitor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_mergeRules: function (rules) {
|
||||
if (!rules) { return; }
|
||||
|
||||
var groups = {},
|
||||
parts,
|
||||
rule,
|
||||
key;
|
||||
_mergeRules: function(rules) {
|
||||
if (!rules) {
|
||||
return;
|
||||
}
|
||||
|
||||
var groups = {},
|
||||
groupsArr = [];
|
||||
|
||||
for (var i = 0; i < rules.length; i++) {
|
||||
rule = rules[i];
|
||||
|
||||
if ((rule instanceof tree.Rule) && rule.merge) {
|
||||
key = [rule.name,
|
||||
rule.important ? "!" : ""].join(",");
|
||||
|
||||
if (!groups[key]) {
|
||||
groups[key] = [];
|
||||
} else {
|
||||
rules.splice(i--, 1);
|
||||
}
|
||||
|
||||
var rule = rules[i];
|
||||
if (rule.merge) {
|
||||
var key = rule.name;
|
||||
groups[key] ? rules.splice(i--, 1) :
|
||||
groupsArr.push(groups[key] = []);
|
||||
groups[key].push(rule);
|
||||
}
|
||||
}
|
||||
|
||||
Object.keys(groups).map(function (k) {
|
||||
|
||||
function toExpression(values) {
|
||||
return new (tree.Expression)(values.map(function (p) {
|
||||
return p.value;
|
||||
}));
|
||||
}
|
||||
|
||||
function toValue(values) {
|
||||
return new (tree.Value)(values.map(function (p) {
|
||||
return p;
|
||||
}));
|
||||
}
|
||||
|
||||
parts = groups[k];
|
||||
|
||||
if (parts.length > 1) {
|
||||
rule = parts[0];
|
||||
var spacedGroups = [];
|
||||
var lastSpacedGroup = [];
|
||||
parts.map(function (p) {
|
||||
if (p.merge === "+") {
|
||||
if (lastSpacedGroup.length > 0) {
|
||||
spacedGroups.push(toExpression(lastSpacedGroup));
|
||||
}
|
||||
lastSpacedGroup = [];
|
||||
groupsArr.forEach(function(group) {
|
||||
if (group.length > 0) {
|
||||
var result = group[0],
|
||||
space = [],
|
||||
comma = [new tree.Expression(space)];
|
||||
group.forEach(function(rule) {
|
||||
if ((rule.merge === '+') && (space.length > 0)) {
|
||||
comma.push(new tree.Expression(space = []));
|
||||
}
|
||||
lastSpacedGroup.push(p);
|
||||
space.push(rule.value);
|
||||
result.important = result.important || rule.important;
|
||||
});
|
||||
spacedGroups.push(toExpression(lastSpacedGroup));
|
||||
rule.value = toValue(spacedGroups);
|
||||
result.value = new tree.Value(comma);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
visitAnonymous: function(anonymousNode, visitArgs) {
|
||||
if (anonymousNode.blocksVisibility()) {
|
||||
return ;
|
||||
}
|
||||
anonymousNode.accept(this._visitor);
|
||||
return anonymousNode;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -10,21 +10,21 @@ function _noop(node) {
|
||||
function indexNodeTypes(parent, ticker) {
|
||||
// add .typeIndex to tree node types for lookup table
|
||||
var key, child;
|
||||
for (key in parent) {
|
||||
if (parent.hasOwnProperty(key)) {
|
||||
child = parent[key];
|
||||
switch (typeof child) {
|
||||
case "function":
|
||||
// ignore bound functions directly on tree which do not have a prototype
|
||||
// or aren't nodes
|
||||
if (child.prototype && child.prototype.type) {
|
||||
child.prototype.typeIndex = ticker++;
|
||||
}
|
||||
break;
|
||||
case "object":
|
||||
ticker = indexNodeTypes(child, ticker);
|
||||
break;
|
||||
}
|
||||
for (key in parent) {
|
||||
/* eslint guard-for-in: 0 */
|
||||
child = parent[key];
|
||||
switch (typeof child) {
|
||||
case "function":
|
||||
// ignore bound functions directly on tree which do not have a prototype
|
||||
// or aren't nodes
|
||||
if (child.prototype && child.prototype.type) {
|
||||
child.prototype.typeIndex = ticker++;
|
||||
}
|
||||
break;
|
||||
case "object":
|
||||
ticker = indexNodeTypes(child, ticker);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
return ticker;
|
||||
|
||||
5865
package-lock.json
generated
Normal file
5865
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
22
package.json
22
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "less",
|
||||
"version": "2.7.3",
|
||||
"version": "3.0.0",
|
||||
"description": "Leaner CSS",
|
||||
"homepage": "http://lesscss.org",
|
||||
"author": {
|
||||
@@ -34,7 +34,7 @@
|
||||
},
|
||||
"browser": "./dist/less.js",
|
||||
"engines": {
|
||||
"node": ">=0.12"
|
||||
"node": ">=4"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "grunt test"
|
||||
@@ -43,27 +43,32 @@
|
||||
"errno": "^0.1.1",
|
||||
"graceful-fs": "^4.1.2",
|
||||
"image-size": "~0.5.0",
|
||||
"mime": "^1.2.11",
|
||||
"mime": "^1.4.1",
|
||||
"mkdirp": "^0.5.0",
|
||||
"promise": "^7.1.1",
|
||||
"source-map": "^0.5.3",
|
||||
"request": "2.81.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"diff": "^2.2.2",
|
||||
"diff": "^3.2.0",
|
||||
"git-rev": "^0.2.1",
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-browserify": "^5.0.0",
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
"grunt-contrib-concat": "^1.0.1",
|
||||
"grunt-contrib-connect": "^1.0.2",
|
||||
"grunt-contrib-jasmine": "^1.0.3",
|
||||
"grunt-contrib-jshint": "^1.0.0",
|
||||
"grunt-contrib-uglify": "^1.0.1",
|
||||
"grunt-jscs": "^2.8.0",
|
||||
"grunt-saucelabs": "^8.6.2",
|
||||
"grunt-eslint": "^19.0.0",
|
||||
"grunt-saucelabs": "^9.0.0",
|
||||
"grunt-shell": "^1.3.0",
|
||||
"import-module": "file:test/import-module",
|
||||
"jit-grunt": "^0.10.0",
|
||||
"less-plugin-clean-css": "^1.5.1",
|
||||
"performance-now": "^0.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.7",
|
||||
"phin": "^2.2.3",
|
||||
"promise": "^7.1.1",
|
||||
"time-grunt": "^1.3.0"
|
||||
},
|
||||
"keywords": [
|
||||
@@ -92,5 +97,6 @@
|
||||
"css less"
|
||||
],
|
||||
"rawcurrent": "https://raw.github.com/less/less.js/v",
|
||||
"sourcearchive": "https://github.com/less/less.js/archive/v"
|
||||
"sourcearchive": "https://github.com/less/less.js/archive/v",
|
||||
"dependencies": {}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,42 @@
|
||||
/* Add js reporter for sauce */
|
||||
|
||||
jasmine.getEnv().addReporter(new jasmine.JSReporter2());
|
||||
jasmine.getEnv().defaultTimeoutInterval = 3000;
|
||||
|
||||
// From https://github.com/axemclion/grunt-saucelabs/issues/109#issuecomment-166767282
|
||||
// (function () {
|
||||
// var oldJSReport = window.jasmine.getJSReport;
|
||||
// window.jasmine.getJSReport = function () {
|
||||
// var results = oldJSReport();
|
||||
// if (results) {
|
||||
// return {
|
||||
// durationSec: results.durationSec,
|
||||
// suites: removePassingTests(results.suites),
|
||||
// passed: results.passed
|
||||
// };
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
// };
|
||||
|
||||
// function removePassingTests (suites) {
|
||||
// return suites.filter(specFailed)
|
||||
// .map(mapSuite);
|
||||
// }
|
||||
|
||||
// function mapSuite (suite) {
|
||||
// var result = {};
|
||||
// for (var s in suite) {
|
||||
// result[s] = suite[s];
|
||||
// }
|
||||
// result.specs = suite.specs.filter(specFailed);
|
||||
// result.suites = removePassingTests(suite.suites);
|
||||
// return result;
|
||||
// }
|
||||
|
||||
// function specFailed (item) {
|
||||
// return !item.passed;
|
||||
// }
|
||||
// })();
|
||||
/* record log messages for testing */
|
||||
|
||||
var logMessages = [];
|
||||
@@ -44,15 +79,15 @@ less.loggers = [
|
||||
}
|
||||
];
|
||||
|
||||
var testLessEqualsInDocument = function () {
|
||||
testLessEqualsInDocument = function () {
|
||||
testLessInDocument(testSheet);
|
||||
};
|
||||
|
||||
var testLessErrorsInDocument = function (isConsole) {
|
||||
testLessErrorsInDocument = function (isConsole) {
|
||||
testLessInDocument(isConsole ? testErrorSheetConsole : testErrorSheet);
|
||||
};
|
||||
|
||||
var testLessInDocument = function (testFunc) {
|
||||
testLessInDocument = function (testFunc) {
|
||||
var links = document.getElementsByTagName('link'),
|
||||
typePattern = /^text\/(x-)?less$/;
|
||||
|
||||
@@ -64,7 +99,7 @@ var testLessInDocument = function (testFunc) {
|
||||
}
|
||||
};
|
||||
|
||||
var ieFormat = function(text) {
|
||||
ieFormat = function(text) {
|
||||
var styleNode = document.createElement('style');
|
||||
styleNode.setAttribute('type', 'text/css');
|
||||
var headNode = document.getElementsByTagName('head')[0];
|
||||
@@ -83,7 +118,7 @@ var ieFormat = function(text) {
|
||||
return transformedText;
|
||||
};
|
||||
|
||||
var testSheet = function (sheet) {
|
||||
testSheet = function (sheet) {
|
||||
it(sheet.id + " should match the expected output", function (done) {
|
||||
var lessOutputId = sheet.id.replace("original-", ""),
|
||||
expectedOutputId = "expected-" + lessOutputId,
|
||||
@@ -112,7 +147,7 @@ var testSheet = function (sheet) {
|
||||
});
|
||||
};
|
||||
|
||||
//TODO: do it cleaner - the same way as in css
|
||||
// TODO: do it cleaner - the same way as in css
|
||||
|
||||
function extractId(href) {
|
||||
return href.replace(/^[a-z-]+:\/+?[^\/]+/i, '') // Remove protocol & domain
|
||||
@@ -122,7 +157,7 @@ function extractId(href) {
|
||||
.replace(/\./g, ':'); // Replace dots with colons(for valid id)
|
||||
}
|
||||
|
||||
var waitFor = function (waitFunc) {
|
||||
waitFor = function (waitFunc) {
|
||||
return new Promise(function (resolve) {
|
||||
var timeoutId = setInterval(function () {
|
||||
if (waitFunc()) {
|
||||
@@ -133,7 +168,7 @@ var waitFor = function (waitFunc) {
|
||||
});
|
||||
};
|
||||
|
||||
var testErrorSheet = function (sheet) {
|
||||
testErrorSheet = function (sheet) {
|
||||
it(sheet.id + " should match an error", function (done) {
|
||||
var lessHref = sheet.href,
|
||||
id = "less-error-message:" + extractId(lessHref),
|
||||
@@ -147,7 +182,7 @@ var testErrorSheet = function (sheet) {
|
||||
actualErrorElement = document.getElementById(id);
|
||||
return actualErrorElement !== null;
|
||||
}).then(function () {
|
||||
var innerText = (actualErrorElement.innerHTML
|
||||
var innerText = (actualErrorElement.innerHTML
|
||||
.replace(/<h3>|<\/?p>|<a href="[^"]*">|<\/a>|<ul>|<\/?pre( class="?[^">]*"?)?>|<\/li>|<\/?label>/ig, "")
|
||||
.replace(/<\/h3>/ig, " ")
|
||||
.replace(/<li>|<\/ul>|<br>/ig, "\n"))
|
||||
@@ -155,34 +190,36 @@ var testErrorSheet = function (sheet) {
|
||||
// for IE8
|
||||
.replace(/\r\n/g, "\n")
|
||||
.replace(/\. \nin/, ". in");
|
||||
actualErrorMsg = innerText
|
||||
actualErrorMsg = innerText
|
||||
.replace(/\n\d+/g, function (lineNo) {
|
||||
return lineNo + " ";
|
||||
})
|
||||
.replace(/\n\s*in /g, " in ")
|
||||
.replace(/\n{2,}/g, "\n")
|
||||
.replace(/\nStack Trace\n[\s\S]*/i, "")
|
||||
.replace(/\n$/, "");
|
||||
errorFile
|
||||
.replace(/\n$/, "")
|
||||
.trim();
|
||||
errorFile
|
||||
.then(function (errorTxt) {
|
||||
errorTxt = errorTxt
|
||||
.replace(/\{path\}/g, "")
|
||||
.replace(/\{pathrel\}/g, "")
|
||||
.replace(/\{pathhref\}/g, "http://localhost:8081/test/less/errors/")
|
||||
.replace(/\{404status\}/g, " (404)")
|
||||
.replace(/\{node\}.*\{\/node\}/g, "")
|
||||
.replace(/\n$/, "");
|
||||
.replace(/\{node\}[\s\S]*\{\/node\}/g, "")
|
||||
.replace(/\n$/, "")
|
||||
.trim();
|
||||
expect(actualErrorMsg).toEqual(errorTxt);
|
||||
if (errorTxt == actualErrorMsg) {
|
||||
actualErrorElement.style.display = "none";
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var testErrorSheetConsole = function (sheet) {
|
||||
testErrorSheetConsole = function (sheet) {
|
||||
it(sheet.id + " should match an error", function (done) {
|
||||
var lessHref = sheet.href,
|
||||
id = sheet.id.replace(/^original-less:/, "less-error-message:"),
|
||||
@@ -211,7 +248,7 @@ var testErrorSheetConsole = function (sheet) {
|
||||
});
|
||||
};
|
||||
|
||||
var loadFile = function (href) {
|
||||
loadFile = function (href) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', href, true);
|
||||
|
||||
3
test/browser/css/plugin/plugin.css
Normal file
3
test/browser/css/plugin/plugin.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.test {
|
||||
val: http://localhost:8081/tmp/browser/test-runner-browser.html;
|
||||
}
|
||||
@@ -42,7 +42,7 @@
|
||||
* @param startMs Start time in Milliseconds
|
||||
* @param finishMs Finish time in Milliseconds
|
||||
* @return Elapsed time in Seconds */
|
||||
function elapsedSec (startMs, finishMs) {
|
||||
function elapsedSec(startMs, finishMs) {
|
||||
return (finishMs - startMs) / 1000;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
* @param amount Amount to round
|
||||
* @param numOfDecDigits Number of Digits to round to. Default value is '2'.
|
||||
* @return Rounded amount */
|
||||
function round (amount, numOfDecDigits) {
|
||||
function round(amount, numOfDecDigits) {
|
||||
numOfDecDigits = numOfDecDigits || 2;
|
||||
return Math.round(amount * Math.pow(10, numOfDecDigits)) / Math.pow(10, numOfDecDigits);
|
||||
}
|
||||
@@ -61,7 +61,7 @@
|
||||
* Create a new array which contains only the failed items.
|
||||
* @param items Items which will be filtered
|
||||
* @returns {Array} of failed items */
|
||||
function failures (items) {
|
||||
function failures(items) {
|
||||
var fs = [], i, v;
|
||||
for (i = 0; i < items.length; i += 1) {
|
||||
v = items[i];
|
||||
@@ -76,7 +76,7 @@
|
||||
* Collect information about a Suite, recursively, and return a JSON result.
|
||||
* @param suite The Jasmine Suite to get data from
|
||||
*/
|
||||
function getSuiteData (suite) {
|
||||
function getSuiteData(suite) {
|
||||
var suiteData = {
|
||||
description : suite.description,
|
||||
durationSec : 0,
|
||||
|
||||
7
test/browser/less/plugin/plugin.js
Normal file
7
test/browser/less/plugin/plugin.js
Normal file
@@ -0,0 +1,7 @@
|
||||
registerPlugin({
|
||||
install: function(less, pluginManager, functions) {
|
||||
functions.add('func', function() {
|
||||
return less.anonymous(location.href);
|
||||
});
|
||||
}
|
||||
});
|
||||
4
test/browser/less/plugin/plugin.less
Normal file
4
test/browser/less/plugin/plugin.less
Normal file
@@ -0,0 +1,4 @@
|
||||
@plugin "plugin";
|
||||
.test {
|
||||
val: func();
|
||||
}
|
||||
@@ -1,4 +1,9 @@
|
||||
var less = {logLevel: 4, errorReporting: "console"};
|
||||
var less = {
|
||||
logLevel: 4,
|
||||
errorReporting: "console",
|
||||
javascriptEnabled: true,
|
||||
strictMath: false
|
||||
};
|
||||
|
||||
// There originally run inside describe method. However, since they have not
|
||||
// been inside it, they run at jasmine compile time (not runtime). It all
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
var less = {
|
||||
strictUnits: true,
|
||||
strictMath: true,
|
||||
logLevel: 4 };
|
||||
logLevel: 4,
|
||||
javascriptEnabled: true
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
var less = {logLevel: 4,
|
||||
var less = {
|
||||
logLevel: 4,
|
||||
errorReporting: "console",
|
||||
plugins: [AddFilePlugin]
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,4 +2,5 @@ var less = {
|
||||
logLevel: 4,
|
||||
errorReporting: "console",
|
||||
strictMath: false,
|
||||
strictUnits: false };
|
||||
strictUnits: false
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user