less 2.3.3 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Changelog.md +5 -0
  3. data/less.gemspec +1 -1
  4. data/lib/less/js/.gitattributes +9 -0
  5. data/lib/less/js/.gitignore +1 -0
  6. data/lib/less/js/.npmignore +1 -1
  7. data/lib/less/js/CHANGELOG.md +68 -0
  8. data/lib/less/js/CONTRIBUTING.md +33 -34
  9. data/lib/less/js/Makefile +24 -9
  10. data/lib/less/js/README.md +2 -2
  11. data/lib/less/js/bin/lessc +102 -25
  12. data/lib/less/js/build/amd.js +1 -1
  13. data/lib/less/js/build/header.js +9 -7
  14. data/lib/less/js/dist/less-1.3.3.js +2 -2
  15. data/lib/less/js/dist/less-1.3.3.min.js +2 -2
  16. data/lib/less/js/dist/less-1.4.0-beta.js +5830 -0
  17. data/lib/less/js/dist/less-1.4.0-beta.min.js +11 -0
  18. data/lib/less/js/dist/less-1.4.0.js +5830 -0
  19. data/lib/less/js/dist/less-1.4.0.min.js +11 -0
  20. data/lib/less/js/dist/less-1.4.1.js +5837 -0
  21. data/lib/less/js/dist/less-1.4.1.min.js +11 -0
  22. data/lib/less/js/dist/less-1.4.2.js +5837 -0
  23. data/lib/less/js/dist/less-1.4.2.min.js +11 -0
  24. data/lib/less/js/dist/less-rhino-1.4.0.js +4273 -0
  25. data/lib/less/js/lib/less/browser.js +131 -101
  26. data/lib/less/js/lib/less/env.js +105 -0
  27. data/lib/less/js/lib/less/extend-visitor.js +391 -0
  28. data/lib/less/js/lib/less/functions.js +174 -19
  29. data/lib/less/js/lib/less/import-visitor.js +107 -0
  30. data/lib/less/js/lib/less/index.js +70 -63
  31. data/lib/less/js/lib/less/join-selector-visitor.js +37 -0
  32. data/lib/less/js/lib/less/lessc_helper.js +13 -4
  33. data/lib/less/js/lib/less/parser.js +353 -264
  34. data/lib/less/js/lib/less/rhino.js +5 -2
  35. data/lib/less/js/lib/less/tree.js +1 -1
  36. data/lib/less/js/lib/less/tree/alpha.js +7 -3
  37. data/lib/less/js/lib/less/tree/anonymous.js +1 -0
  38. data/lib/less/js/lib/less/tree/assignment.js +4 -0
  39. data/lib/less/js/lib/less/tree/call.js +14 -8
  40. data/lib/less/js/lib/less/tree/color.js +50 -5
  41. data/lib/less/js/lib/less/tree/comment.js +1 -0
  42. data/lib/less/js/lib/less/tree/condition.js +35 -28
  43. data/lib/less/js/lib/less/tree/dimension.js +270 -16
  44. data/lib/less/js/lib/less/tree/directive.js +7 -2
  45. data/lib/less/js/lib/less/tree/element.js +57 -21
  46. data/lib/less/js/lib/less/tree/expression.js +29 -4
  47. data/lib/less/js/lib/less/tree/extend.js +43 -0
  48. data/lib/less/js/lib/less/tree/import.js +49 -28
  49. data/lib/less/js/lib/less/tree/javascript.js +1 -0
  50. data/lib/less/js/lib/less/tree/keyword.js +3 -2
  51. data/lib/less/js/lib/less/tree/media.js +20 -4
  52. data/lib/less/js/lib/less/tree/mixin.js +38 -18
  53. data/lib/less/js/lib/less/tree/negative.js +22 -0
  54. data/lib/less/js/lib/less/tree/operation.js +32 -17
  55. data/lib/less/js/lib/less/tree/paren.js +5 -1
  56. data/lib/less/js/lib/less/tree/quoted.js +5 -3
  57. data/lib/less/js/lib/less/tree/rule.js +44 -31
  58. data/lib/less/js/lib/less/tree/ruleset.js +50 -23
  59. data/lib/less/js/lib/less/tree/selector.js +49 -39
  60. data/lib/less/js/lib/less/tree/unicode-descriptor.js +1 -0
  61. data/lib/less/js/lib/less/tree/url.js +9 -5
  62. data/lib/less/js/lib/less/tree/value.js +4 -1
  63. data/lib/less/js/lib/less/tree/variable.js +4 -3
  64. data/lib/less/js/lib/less/visitor.js +54 -0
  65. data/lib/less/js/package.json +69 -19
  66. data/lib/less/js/test/browser-test-prepare.js +23 -6
  67. data/lib/less/js/test/browser/common.js +55 -3
  68. data/lib/less/js/test/browser/css/urls.css +13 -0
  69. data/lib/less/js/test/browser/less/relative-urls/urls.less +1 -1
  70. data/lib/less/js/test/browser/less/urls.less +16 -0
  71. data/lib/less/js/test/browser/phantom-runner.js +7 -5
  72. data/lib/less/js/test/browser/runner-browser.js +5 -1
  73. data/lib/less/js/test/browser/runner-errors.js +5 -0
  74. data/lib/less/js/test/browser/runner-legacy.js +6 -0
  75. data/lib/less/js/test/browser/runner-production.js +7 -0
  76. data/lib/less/js/test/browser/template.htm +6 -6
  77. data/lib/less/js/test/css/comments.css +1 -0
  78. data/lib/less/js/test/css/compression/compression.css +2 -0
  79. data/lib/less/js/test/css/css-3.css +4 -0
  80. data/lib/less/js/test/css/css.css +9 -3
  81. data/lib/less/js/test/css/extend-chaining.css +72 -0
  82. data/lib/less/js/test/css/extend-clearfix.css +19 -0
  83. data/lib/less/js/test/css/extend-exact.css +37 -0
  84. data/lib/less/js/test/css/extend-media.css +24 -0
  85. data/lib/less/js/test/css/extend-nest.css +57 -0
  86. data/lib/less/js/test/css/extend-selector.css +72 -0
  87. data/lib/less/js/test/css/extend.css +76 -0
  88. data/lib/less/js/test/css/functions.css +28 -0
  89. data/lib/less/js/test/css/import-interpolation.css +6 -0
  90. data/lib/less/js/test/css/import.css +18 -1
  91. data/lib/less/js/test/css/legacy/legacy.css +7 -0
  92. data/lib/less/js/test/css/media.css +9 -1
  93. data/lib/less/js/test/css/mixins-args.css +18 -0
  94. data/lib/less/js/test/css/mixins-guards.css +5 -0
  95. data/lib/less/js/test/css/parens.css +18 -5
  96. data/lib/less/js/test/css/selectors.css +14 -6
  97. data/lib/less/js/test/css/urls.css +17 -0
  98. data/lib/less/js/test/css/variables.css +22 -3
  99. data/lib/less/js/test/data/data-uri-fail.png +0 -0
  100. data/lib/less/js/test/data/image.jpg +0 -0
  101. data/lib/less/js/test/data/page.html +1 -0
  102. data/lib/less/js/test/less-test.js +41 -9
  103. data/lib/less/js/test/less/colors.less +4 -4
  104. data/lib/less/js/test/less/comments.less +2 -2
  105. data/lib/less/js/test/less/compression/compression.less +16 -0
  106. data/lib/less/js/test/less/css-3.less +5 -1
  107. data/lib/less/js/test/less/css.less +9 -3
  108. data/lib/less/js/test/less/errors/add-mixed-units.less +3 -0
  109. data/lib/less/js/test/less/errors/add-mixed-units.txt +2 -0
  110. data/lib/less/js/test/less/errors/add-mixed-units2.less +3 -0
  111. data/lib/less/js/test/less/errors/add-mixed-units2.txt +2 -0
  112. data/lib/less/js/test/less/errors/bad-variable-declaration1.txt +1 -1
  113. data/lib/less/js/test/less/errors/color-operation-error.less +3 -0
  114. data/lib/less/js/test/less/errors/color-operation-error.txt +2 -0
  115. data/lib/less/js/test/less/errors/comment-in-selector.txt +1 -1
  116. data/lib/less/js/test/less/errors/divide-mixed-units.less +3 -0
  117. data/lib/less/js/test/less/errors/divide-mixed-units.txt +4 -0
  118. data/lib/less/js/test/less/errors/extend-no-selector.less +3 -0
  119. data/lib/less/js/test/less/errors/extend-no-selector.txt +3 -0
  120. data/lib/less/js/test/less/errors/extend-not-at-end.less +3 -0
  121. data/lib/less/js/test/less/errors/extend-not-at-end.txt +3 -0
  122. data/lib/less/js/test/less/errors/import-missing.less +5 -0
  123. data/lib/less/js/test/less/errors/import-missing.txt +3 -3
  124. data/lib/less/js/test/less/errors/import-no-semi.txt +1 -1
  125. data/lib/less/js/test/less/errors/import-subfolder1.txt +1 -1
  126. data/lib/less/js/test/less/errors/import-subfolder2.txt +1 -1
  127. data/lib/less/js/test/less/errors/javascript-error.txt +1 -1
  128. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-1.txt +1 -1
  129. data/lib/less/js/test/less/errors/mixed-mixin-definition-args-2.txt +1 -1
  130. data/lib/less/js/test/less/errors/mixin-not-defined.txt +1 -1
  131. data/lib/less/js/test/less/errors/mixin-not-matched.txt +1 -1
  132. data/lib/less/js/test/less/errors/mixin-not-matched2.txt +1 -1
  133. data/lib/less/js/test/less/errors/multiply-mixed-units.less +7 -0
  134. data/lib/less/js/test/less/errors/multiply-mixed-units.txt +4 -0
  135. data/lib/less/js/test/less/errors/parens-error-1.less +3 -0
  136. data/lib/less/js/test/less/errors/parens-error-1.txt +4 -0
  137. data/lib/less/js/test/less/errors/parens-error-2.less +3 -0
  138. data/lib/less/js/test/less/errors/parens-error-2.txt +4 -0
  139. data/lib/less/js/test/less/errors/parens-error-3.less +3 -0
  140. data/lib/less/js/test/less/errors/parens-error-3.txt +4 -0
  141. data/lib/less/js/test/less/errors/parse-error-curly-bracket.txt +1 -1
  142. data/lib/less/js/test/less/errors/parse-error-missing-bracket.txt +2 -1
  143. data/lib/less/js/test/less/errors/parse-error-with-import.txt +1 -1
  144. data/lib/less/js/test/less/errors/property-ie5-hack.txt +1 -1
  145. data/lib/less/js/test/less/errors/property-in-root.less +4 -0
  146. data/lib/less/js/test/less/errors/property-in-root.txt +4 -0
  147. data/lib/less/js/test/less/errors/property-in-root2.less +1 -0
  148. data/lib/less/js/test/less/errors/property-in-root2.txt +4 -0
  149. data/lib/less/js/test/less/errors/property-in-root3.less +4 -0
  150. data/lib/less/js/test/less/errors/property-in-root3.txt +3 -0
  151. data/lib/less/js/test/less/errors/recursive-variable.txt +1 -1
  152. data/lib/less/js/test/less/extend-chaining.less +79 -0
  153. data/lib/less/js/test/less/extend-clearfix.less +19 -0
  154. data/lib/less/js/test/less/extend-exact.less +46 -0
  155. data/lib/less/js/test/less/extend-media.less +24 -0
  156. data/lib/less/js/test/less/extend-nest.less +65 -0
  157. data/lib/less/js/test/less/extend-selector.less +84 -0
  158. data/lib/less/js/test/less/extend.less +81 -0
  159. data/lib/less/js/test/less/functions.less +37 -6
  160. data/lib/less/js/test/less/import-interpolation.less +8 -0
  161. data/lib/less/js/test/less/import-once.less +4 -4
  162. data/lib/less/js/test/less/import.less +11 -2
  163. data/lib/less/js/test/less/import/deeper/import-once-test-a.less +1 -1
  164. data/lib/less/js/test/less/import/import-interpolation.less +1 -0
  165. data/lib/less/js/test/less/import/import-interpolation2.less +5 -0
  166. data/lib/less/js/test/less/javascript.less +1 -1
  167. data/lib/less/js/test/less/legacy/legacy.less +7 -0
  168. data/lib/less/js/test/less/media.less +14 -3
  169. data/lib/less/js/test/less/mixins-args.less +43 -5
  170. data/lib/less/js/test/less/mixins-guards.less +13 -0
  171. data/lib/less/js/test/less/mixins-named-args.less +5 -5
  172. data/lib/less/js/test/less/mixins-nested.less +2 -2
  173. data/lib/less/js/test/less/mixins-pattern.less +1 -1
  174. data/lib/less/js/test/less/mixins.less +1 -1
  175. data/lib/less/js/test/less/operations.less +27 -27
  176. data/lib/less/js/test/less/parens.less +20 -5
  177. data/lib/less/js/test/less/selectors.less +14 -7
  178. data/lib/less/js/test/less/urls.less +24 -0
  179. data/lib/less/js/test/less/variables.less +42 -12
  180. data/lib/less/loader.rb +33 -0
  181. data/lib/less/version.rb +1 -1
  182. data/spec/less/parser_spec.rb +5 -5
  183. metadata +76 -6
  184. data/lib/less/js/build/ecma-5.js +0 -120
  185. data/lib/less/js/lib/less/tree/ratio.js +0 -13
@@ -38,35 +38,37 @@ if (dumpLineNumbers) {
38
38
  //
39
39
  // Watch mode
40
40
  //
41
- less.watch = function () {
42
- if (!less.watchMode ){
43
- less.env = 'development';
44
- initRunningMode();
45
- }
46
- return this.watchMode = true
41
+ less.watch = function () {
42
+ if (!less.watchMode ){
43
+ less.env = 'development';
44
+ initRunningMode();
45
+ }
46
+ return this.watchMode = true
47
47
  };
48
48
 
49
49
  less.unwatch = function () {clearInterval(less.watchTimer); return this.watchMode = false; };
50
50
 
51
51
  function initRunningMode(){
52
- if (less.env === 'development') {
53
- less.optimization = 0;
54
- less.watchTimer = setInterval(function () {
55
- if (less.watchMode) {
56
- loadStyleSheets(function (e, root, _, sheet, env) {
57
- if (root) {
58
- createCSS(root.toCSS(), sheet, env.lastModified);
59
- }
60
- });
61
- }
62
- }, less.poll);
63
- } else {
64
- less.optimization = 3;
65
- }
52
+ if (less.env === 'development') {
53
+ less.optimization = 0;
54
+ less.watchTimer = setInterval(function () {
55
+ if (less.watchMode) {
56
+ loadStyleSheets(function (e, root, _, sheet, env) {
57
+ if (e) {
58
+ error(e, sheet.href);
59
+ } else if (root) {
60
+ createCSS(root.toCSS(less), sheet, env.lastModified);
61
+ }
62
+ });
63
+ }
64
+ }, less.poll);
65
+ } else {
66
+ less.optimization = 3;
67
+ }
66
68
  }
67
69
 
68
70
  if (/!watch/.test(location.hash)) {
69
- less.watch();
71
+ less.watch();
70
72
  }
71
73
 
72
74
  var cache = null;
@@ -98,13 +100,17 @@ for (var i = 0; i < links.length; i++) {
98
100
  //
99
101
  var session_cache = '';
100
102
  less.modifyVars = function(record) {
101
- var str = session_cache;
102
- for (name in record) {
103
+ var str = session_cache;
104
+ for (var name in record) {
103
105
  str += ((name.slice(0,1) === '@')? '' : '@') + name +': '+
104
106
  ((record[name].slice(-1) === ';')? record[name] : record[name] +';');
105
107
  }
106
- new(less.Parser)().parse(str, function (e, root) {
107
- createCSS(root.toCSS(), less.sheets[less.sheets.length - 1]);
108
+ new(less.Parser)(new less.tree.parseEnv(less)).parse(str, function (e, root) {
109
+ if (e) {
110
+ error(e, "session_cache");
111
+ } else {
112
+ createCSS(root.toCSS(less), less.sheets[less.sheets.length - 1]);
113
+ }
108
114
  });
109
115
  };
110
116
 
@@ -113,11 +119,14 @@ less.refresh = function (reload) {
113
119
  startTime = endTime = new(Date);
114
120
 
115
121
  loadStyleSheets(function (e, root, _, sheet, env) {
122
+ if (e) {
123
+ return error(e, sheet.href);
124
+ }
116
125
  if (env.local) {
117
126
  log("loading " + sheet.href + " from cache.");
118
127
  } else {
119
128
  log("parsed " + sheet.href + " successfully.");
120
- createCSS(root.toCSS(), sheet, env.lastModified);
129
+ createCSS(root.toCSS(less), sheet, env.lastModified);
121
130
  }
122
131
  log("css for " + sheet.href + " generated in " + (new(Date) - endTime) + 'ms');
123
132
  (env.remaining === 0) && log("css generated in " + (new(Date) - startTime) + 'ms');
@@ -134,11 +143,14 @@ function loadStyles() {
134
143
  var styles = document.getElementsByTagName('style');
135
144
  for (var i = 0; i < styles.length; i++) {
136
145
  if (styles[i].type.match(typePattern)) {
137
- new(less.Parser)({
138
- filename: document.location.href.replace(/#.*$/, ''),
139
- dumpLineNumbers: less.dumpLineNumbers
140
- }).parse(styles[i].innerHTML || '', function (e, tree) {
141
- var css = tree.toCSS();
146
+ var env = new less.tree.parseEnv(less);
147
+ env.filename = document.location.href.replace(/#.*$/, '');
148
+
149
+ new(less.Parser)(env).parse(styles[i].innerHTML || '', function (e, cssAST) {
150
+ if (e) {
151
+ return error(e, "inline");
152
+ }
153
+ var css = cssAST.toCSS(less);
142
154
  var style = styles[i];
143
155
  style.type = 'text/css';
144
156
  if (style.styleSheet) {
@@ -188,7 +200,7 @@ function extractUrlParts(url, baseUrl) {
188
200
  // urlParts[4] = filename
189
201
  // urlParts[5] = parameters
190
202
 
191
- var urlPartsRegex = /^((?:[a-z-]+:)?\/\/(?:[^\/\?#]+\/)|([\/\\]))?((?:[^\/\\\?#]+[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/,
203
+ var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
192
204
  urlParts = url.match(urlPartsRegex),
193
205
  returner = {}, directories = [], i, baseUrlParts;
194
206
 
@@ -202,14 +214,22 @@ function extractUrlParts(url, baseUrl) {
202
214
  if (!baseUrlParts) {
203
215
  throw new Error("Could not parse page url - '"+baseUrl+"'");
204
216
  }
205
- urlParts[1] = baseUrlParts[1];
217
+ urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
206
218
  if (!urlParts[2]) {
207
219
  urlParts[3] = baseUrlParts[3] + urlParts[3];
208
220
  }
209
221
  }
210
222
 
211
223
  if (urlParts[3]) {
212
- directories = urlParts[3].replace("\\", "/").split("/");
224
+ directories = urlParts[3].replace(/\\/g, "/").split("/");
225
+
226
+ // extract out . before .. so .. doesn't absorb a non-directory
227
+ for(i = 0; i < directories.length; i++) {
228
+ if (directories[i] === ".") {
229
+ directories.splice(i, 1);
230
+ i -= 1;
231
+ }
232
+ }
213
233
 
214
234
  for(i = 0; i < directories.length; i++) {
215
235
  if (directories[i] === ".." && i > 0) {
@@ -228,36 +248,41 @@ function extractUrlParts(url, baseUrl) {
228
248
  }
229
249
 
230
250
  function loadStyleSheet(sheet, callback, reload, remaining) {
251
+
231
252
  // sheet may be set to the stylesheet for the initial load or a collection of properties including
232
253
  // some env variables for imports
233
- var contents = sheet.contents || {};
234
- var files = sheet.files || {};
235
254
  var hrefParts = extractUrlParts(sheet.href, window.location.href);
236
255
  var href = hrefParts.url;
237
256
  var css = cache && cache.getItem(href);
238
257
  var timestamp = cache && cache.getItem(href + ':timestamp');
239
258
  var styles = { css: css, timestamp: timestamp };
240
- var rootpath;
259
+ var env;
260
+ var newFileInfo = {
261
+ relativeUrls: less.relativeUrls,
262
+ currentDirectory: hrefParts.path,
263
+ filename: href
264
+ };
241
265
 
242
- if (less.relativeUrls) {
243
- if (less.rootpath) {
244
- if (sheet.entryPath) {
245
- rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, sheet.entryPath)).path;
246
- } else {
247
- rootpath = less.rootpath;
248
- }
249
- } else {
250
- rootpath = hrefParts.path;
251
- }
252
- } else {
266
+ if (sheet instanceof less.tree.parseEnv) {
267
+ env = new less.tree.parseEnv(sheet);
268
+ newFileInfo.entryPath = env.currentFileInfo.entryPath;
269
+ newFileInfo.rootpath = env.currentFileInfo.rootpath;
270
+ newFileInfo.rootFilename = env.currentFileInfo.rootFilename;
271
+ } else {
272
+ env = new less.tree.parseEnv(less);
273
+ env.mime = sheet.type;
274
+ newFileInfo.entryPath = hrefParts.path;
275
+ newFileInfo.rootpath = less.rootpath || hrefParts.path;
276
+ newFileInfo.rootFilename = href;
277
+ }
278
+
279
+ if (env.relativeUrls) {
280
+ //todo - this relies on option being set on less object rather than being passed in as an option
281
+ // - need an originalRootpath
253
282
  if (less.rootpath) {
254
- rootpath = less.rootpath;
283
+ newFileInfo.rootpath = extractUrlParts(less.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path;
255
284
  } else {
256
- if (sheet.entryPath) {
257
- rootpath = sheet.entryPath;
258
- } else {
259
- rootpath = hrefParts.path;
260
- }
285
+ newFileInfo.rootpath = hrefParts.path;
261
286
  }
262
287
  }
263
288
 
@@ -274,38 +299,35 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
274
299
  } else {
275
300
  // Use remote copy (re-parse)
276
301
  try {
277
- contents[href] = data; // Updating top importing parser content cache
278
- new(less.Parser)({
279
- optimization: less.optimization,
280
- paths: [hrefParts.path],
281
- entryPath: sheet.entryPath || hrefParts.path,
282
- mime: sheet.type,
283
- filename: href,
284
- rootpath: rootpath,
285
- relativeUrls: sheet.relativeUrls,
286
- contents: contents, // Passing top importing parser content cache ref down.
287
- files: files,
288
- dumpLineNumbers: less.dumpLineNumbers
289
- }).parse(data, function (e, root) {
290
- if (e) { return error(e, href) }
302
+ env.contents[href] = data; // Updating content cache
303
+ env.paths = [hrefParts.path];
304
+ env.currentFileInfo = newFileInfo;
305
+
306
+ new(less.Parser)(env).parse(data, function (e, root) {
307
+ if (e) { return callback(e, null, null, sheet); }
291
308
  try {
292
309
  callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining }, href);
293
- removeNode(document.getElementById('less-error-message:' + extractId(href)));
310
+ //TODO - there must be a better way? A generic less-to-css function that can both call error
311
+ //and removeNode where appropriate
312
+ //should also add tests
313
+ if (env.currentFileInfo.rootFilename === href) {
314
+ removeNode(document.getElementById('less-error-message:' + extractId(href)));
315
+ }
294
316
  } catch (e) {
295
- error(e, href);
317
+ callback(e, null, null, sheet);
296
318
  }
297
319
  });
298
320
  } catch (e) {
299
- error(e, href);
321
+ callback(e, null, null, sheet);
300
322
  }
301
323
  }
302
324
  }, function (status, url) {
303
- throw new(Error)("Couldn't load " + url + " (" + status + ")");
325
+ callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, null, sheet);
304
326
  });
305
327
  }
306
328
 
307
329
  function extractId(href) {
308
- return href.replace(/^[a-z]+:\/\/?[^\/]+/, '' ) // Remove protocol & domain
330
+ return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain
309
331
  .replace(/^\//, '' ) // Remove root /
310
332
  .replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
311
333
  .replace(/[^\.\w-]+/g, '-') // Replace illegal characters
@@ -313,23 +335,23 @@ function extractId(href) {
313
335
  }
314
336
 
315
337
  function createCSS(styles, sheet, lastModified) {
316
- var css;
317
-
318
338
  // Strip the query-string
319
339
  var href = sheet.href || '';
320
340
 
321
341
  // If there is no title set, use the filename, minus the extension
322
342
  var id = 'less:' + (sheet.title || extractId(href));
323
343
 
324
- // If the stylesheet doesn't exist, create a new node
325
- if ((css = document.getElementById(id)) === null) {
326
- css = document.createElement('style');
327
- css.type = 'text/css';
328
- if( sheet.media ){ css.media = sheet.media; }
329
- css.id = id;
330
- var nextEl = sheet && sheet.nextSibling || null;
331
- (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl);
344
+ // If this has already been inserted into the DOM, we may need to replace it
345
+ var oldCss = document.getElementById(id);
346
+ var keepOldCss = false;
347
+
348
+ // Create a new stylesheet node for insertion or (if necessary) replacement
349
+ var css = document.createElement('style');
350
+ css.setAttribute('type', 'text/css');
351
+ if (sheet.media) {
352
+ css.setAttribute('media', sheet.media);
332
353
  }
354
+ css.id = id;
333
355
 
334
356
  if (css.styleSheet) { // IE
335
357
  try {
@@ -338,15 +360,23 @@ function createCSS(styles, sheet, lastModified) {
338
360
  throw new(Error)("Couldn't reassign styleSheet.cssText.");
339
361
  }
340
362
  } else {
341
- (function (node) {
342
- if (css.childNodes.length > 0) {
343
- if (css.firstChild.nodeValue !== node.nodeValue) {
344
- css.replaceChild(node, css.firstChild);
345
- }
346
- } else {
347
- css.appendChild(node);
348
- }
349
- })(document.createTextNode(styles));
363
+ css.appendChild(document.createTextNode(styles));
364
+
365
+ // If new contents match contents of oldCss, don't replace oldCss
366
+ keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 &&
367
+ oldCss.firstChild.nodeValue === css.firstChild.nodeValue);
368
+ }
369
+
370
+ var head = document.getElementsByTagName('head')[0];
371
+
372
+ // If there is no oldCss, just append; otherwise, only append if we need
373
+ // to replace oldCss with an updated stylesheet
374
+ if (oldCss == null || keepOldCss === false) {
375
+ var nextEl = sheet && sheet.nextSibling || null;
376
+ (nextEl || document.getElementsByTagName('head')[0]).parentNode.insertBefore(css, nextEl);
377
+ }
378
+ if (oldCss && keepOldCss === false) {
379
+ head.removeChild(oldCss);
350
380
  }
351
381
 
352
382
  // Don't update the local store if the file wasn't modified
@@ -420,35 +450,35 @@ function log(str) {
420
450
  if (less.env == 'development' && typeof(console) !== "undefined") { console.log('less: ' + str) }
421
451
  }
422
452
 
423
- function error(e, href) {
424
- var id = 'less-error-message:' + extractId(href);
453
+ function error(e, rootHref) {
454
+ var id = 'less-error-message:' + extractId(rootHref || "");
425
455
  var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
426
456
  var elem = document.createElement('div'), timer, content, error = [];
427
- var filename = e.filename || href;
457
+ var filename = e.filename || rootHref;
428
458
  var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
429
459
 
430
460
  elem.id = id;
431
461
  elem.className = "less-error-message";
432
462
 
433
- content = '<h3>' + (e.message || 'There is an error in your .less file') +
463
+ content = '<h3>' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
434
464
  '</h3>' + '<p>in <a href="' + filename + '">' + filenameNoPath + "</a> ";
435
465
 
436
466
  var errorline = function (e, i, classname) {
437
- if (e.extract[i]) {
438
- error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1))
467
+ if (e.extract[i] != undefined) {
468
+ error.push(template.replace(/\{line\}/, (parseInt(e.line) || 0) + (i - 1))
439
469
  .replace(/\{class\}/, classname)
440
470
  .replace(/\{content\}/, e.extract[i]));
441
471
  }
442
472
  };
443
473
 
444
- if (e.stack) {
445
- content += '<br/>' + e.stack.split('\n').slice(1).join('<br/>');
446
- } else if (e.extract) {
474
+ if (e.extract) {
447
475
  errorline(e, 0, '');
448
476
  errorline(e, 1, 'line');
449
477
  errorline(e, 2, '');
450
478
  content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
451
479
  '<ul>' + error.join('') + '</ul>';
480
+ } else if (e.stack) {
481
+ content += '<br/>' + e.stack.split('\n').slice(1).join('<br/>');
452
482
  }
453
483
  elem.innerHTML = content;
454
484
 
@@ -0,0 +1,105 @@
1
+ (function (tree) {
2
+
3
+ var parseCopyProperties = [
4
+ 'paths', // option - unmodified - paths to search for imports on
5
+ 'optimization', // option - optimization level (for the chunker)
6
+ 'files', // list of files that have been imported, used for import-once
7
+ 'contents', // browser-only, contents of all the files
8
+ 'relativeUrls', // option - whether to adjust URL's to be relative
9
+ 'strictImports', // option -
10
+ 'dumpLineNumbers', // option - whether to dump line numbers
11
+ 'compress', // option - whether to compress
12
+ 'processImports', // option - whether to process imports. if false then imports will not be imported
13
+ 'syncImport', // option - whether to import synchronously
14
+ 'mime', // browser only - mime type for sheet import
15
+ 'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
16
+ ];
17
+
18
+ //currentFileInfo = {
19
+ // 'relativeUrls' - option - whether to adjust URL's to be relative
20
+ // 'filename' - full resolved filename of current file
21
+ // 'rootpath' - path to append to normal URLs for this node
22
+ // 'currentDirectory' - path to the current file, absolute
23
+ // 'rootFilename' - filename of the base file
24
+ // 'entryPath' = absolute path to the entry file
25
+
26
+ tree.parseEnv = function(options) {
27
+ copyFromOriginal(options, this, parseCopyProperties);
28
+
29
+ if (!this.contents) { this.contents = {}; }
30
+ if (!this.files) { this.files = {}; }
31
+
32
+ if (!this.currentFileInfo) {
33
+ var filename = (options && options.filename) || "input";
34
+ var entryPath = filename.replace(/[^\/\\]*$/, "");
35
+ if (options) {
36
+ options.filename = null;
37
+ }
38
+ this.currentFileInfo = {
39
+ filename: filename,
40
+ relativeUrls: this.relativeUrls,
41
+ rootpath: (options && options.rootpath) || "",
42
+ currentDirectory: entryPath,
43
+ entryPath: entryPath,
44
+ rootFilename: filename
45
+ };
46
+ }
47
+ };
48
+
49
+ tree.parseEnv.prototype.toSheet = function (path) {
50
+ var env = new tree.parseEnv(this);
51
+ env.href = path;
52
+ //env.title = path;
53
+ env.type = this.mime;
54
+ return env;
55
+ };
56
+
57
+ var evalCopyProperties = [
58
+ 'silent', // whether to swallow errors and warnings
59
+ 'verbose', // whether to log more activity
60
+ 'compress', // whether to compress
61
+ 'yuicompress', // whether to compress with the outside tool yui compressor
62
+ 'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
63
+ 'strictMath', // whether math has to be within parenthesis
64
+ 'strictUnits' // whether units need to evaluate correctly
65
+ ];
66
+
67
+ tree.evalEnv = function(options, frames) {
68
+ copyFromOriginal(options, this, evalCopyProperties);
69
+
70
+ this.frames = frames || [];
71
+ };
72
+
73
+ tree.evalEnv.prototype.inParenthesis = function () {
74
+ if (!this.parensStack) {
75
+ this.parensStack = [];
76
+ }
77
+ this.parensStack.push(true);
78
+ };
79
+
80
+ tree.evalEnv.prototype.outOfParenthesis = function () {
81
+ this.parensStack.pop();
82
+ };
83
+
84
+ tree.evalEnv.prototype.isMathOn = function () {
85
+ return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
86
+ };
87
+
88
+ tree.evalEnv.prototype.isPathRelative = function (path) {
89
+ return !/^(?:[a-z-]+:|\/)/.test(path);
90
+ };
91
+
92
+ //todo - do the same for the toCSS env
93
+ //tree.toCSSEnv = function (options) {
94
+ //};
95
+
96
+ var copyFromOriginal = function(original, destination, propertiesToCopy) {
97
+ if (!original) { return; }
98
+
99
+ for(var i = 0; i < propertiesToCopy.length; i++) {
100
+ if (original.hasOwnProperty(propertiesToCopy[i])) {
101
+ destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
102
+ }
103
+ }
104
+ }
105
+ })(require('./tree'));