less 2.3.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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'));