less 2.0.4 → 2.1.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 (112) hide show
  1. data/.gitmodules +0 -3
  2. data/.travis.yml +10 -0
  3. data/README.md +1 -1
  4. data/Rakefile +2 -0
  5. data/bin/lessc +21 -2
  6. data/less.gemspec +5 -3
  7. data/lib/less/defaults.rb +13 -0
  8. data/lib/less/js/CHANGELOG +41 -0
  9. data/lib/less/js/Makefile +21 -2
  10. data/lib/less/js/benchmark/benchmark.less +11 -11
  11. data/lib/less/js/benchmark/less-benchmark.js +3 -5
  12. data/lib/less/js/bin/lessc +52 -18
  13. data/lib/less/js/build/amd.js +6 -0
  14. data/lib/less/js/build/require-rhino.js +7 -0
  15. data/lib/less/js/dist/{less-1.0.44.js → less-1.1.4.js} +159 -45
  16. data/lib/less/js/dist/less-1.1.4.min.js +16 -0
  17. data/lib/less/js/dist/less-1.1.5.js +2805 -0
  18. data/lib/less/js/dist/less-1.1.5.min.js +9 -0
  19. data/lib/less/js/dist/less-1.1.6.js +3004 -0
  20. data/lib/less/js/dist/less-1.1.6.min.js +9 -0
  21. data/lib/less/js/dist/less-1.2.0.js +3293 -0
  22. data/lib/less/js/dist/less-1.2.0.min.js +9 -0
  23. data/lib/less/js/dist/less-1.2.1.js +3318 -0
  24. data/lib/less/js/dist/less-1.2.1.min.js +9 -0
  25. data/lib/less/js/dist/less-1.2.2.js +3337 -0
  26. data/lib/less/js/dist/less-1.2.2.min.js +9 -0
  27. data/lib/less/js/dist/less-1.3.0.js +3478 -0
  28. data/lib/less/js/dist/less-1.3.0.min.js +9 -0
  29. data/lib/less/js/dist/less-rhino-1.1.3.js +2460 -0
  30. data/lib/less/js/dist/less-rhino-1.1.5.js +2481 -0
  31. data/lib/less/js/lib/less/browser.js +38 -27
  32. data/lib/less/js/lib/less/colors.js +151 -0
  33. data/lib/less/js/lib/less/cssmin.js +355 -0
  34. data/lib/less/js/lib/less/functions.js +61 -7
  35. data/lib/less/js/lib/less/index.js +48 -37
  36. data/lib/less/js/lib/less/parser.js +327 -120
  37. data/lib/less/js/lib/less/rhino.js +62 -0
  38. data/lib/less/js/lib/less/tree/alpha.js +1 -1
  39. data/lib/less/js/lib/less/tree/anonymous.js +1 -1
  40. data/lib/less/js/lib/less/tree/assignment.js +17 -0
  41. data/lib/less/js/lib/less/tree/call.js +7 -4
  42. data/lib/less/js/lib/less/tree/color.js +9 -6
  43. data/lib/less/js/lib/less/tree/comment.js +1 -1
  44. data/lib/less/js/lib/less/tree/condition.js +42 -0
  45. data/lib/less/js/lib/less/tree/dimension.js +16 -1
  46. data/lib/less/js/lib/less/tree/directive.js +4 -2
  47. data/lib/less/js/lib/less/tree/element.js +20 -5
  48. data/lib/less/js/lib/less/tree/expression.js +2 -2
  49. data/lib/less/js/lib/less/tree/import.js +16 -14
  50. data/lib/less/js/lib/less/tree/javascript.js +1 -1
  51. data/lib/less/js/lib/less/tree/keyword.js +12 -2
  52. data/lib/less/js/lib/less/tree/media.js +114 -0
  53. data/lib/less/js/lib/less/tree/mixin.js +49 -20
  54. data/lib/less/js/lib/less/tree/operation.js +1 -1
  55. data/lib/less/js/lib/less/tree/paren.js +16 -0
  56. data/lib/less/js/lib/less/tree/quoted.js +2 -2
  57. data/lib/less/js/lib/less/tree/rule.js +8 -4
  58. data/lib/less/js/lib/less/tree/ruleset.js +49 -12
  59. data/lib/less/js/lib/less/tree/selector.js +18 -4
  60. data/lib/less/js/lib/less/tree/url.js +2 -2
  61. data/lib/less/js/lib/less/tree/value.js +1 -1
  62. data/lib/less/js/lib/less/tree/variable.js +5 -3
  63. data/lib/less/js/lib/less/tree.js +6 -2
  64. data/lib/less/js/package.json +2 -2
  65. data/lib/less/js/test/css/colors.css +16 -0
  66. data/lib/less/js/test/css/comments.css +9 -5
  67. data/lib/less/js/test/css/css-3.css +18 -2
  68. data/lib/less/js/test/css/css-escapes.css +1 -1
  69. data/lib/less/js/test/css/css.css +11 -4
  70. data/lib/less/js/test/css/functions.css +13 -0
  71. data/lib/less/js/test/css/ie-filters.css +5 -0
  72. data/lib/less/js/test/css/import.css +12 -3
  73. data/lib/less/js/test/css/media.css +60 -2
  74. data/lib/less/js/test/css/mixins-args.css +17 -2
  75. data/lib/less/js/test/css/mixins-guards.css +58 -0
  76. data/lib/less/js/test/css/mixins-important.css +17 -0
  77. data/lib/less/js/test/css/mixins-pattern.css +4 -6
  78. data/lib/less/js/test/css/mixins.css +21 -0
  79. data/lib/less/js/test/css/operations.css +3 -0
  80. data/lib/less/js/test/css/parens.css +1 -1
  81. data/lib/less/js/test/css/rulesets.css +6 -2
  82. data/lib/less/js/test/css/scope.css +6 -6
  83. data/lib/less/js/test/css/selectors.css +38 -1
  84. data/lib/less/js/test/css/strings.css +6 -4
  85. data/lib/less/js/test/css/variables.css +3 -0
  86. data/lib/less/js/test/css/whitespace.css +5 -3
  87. data/lib/less/js/test/less/colors.less +19 -0
  88. data/lib/less/js/test/less/comments.less +8 -6
  89. data/lib/less/js/test/less/css-3.less +10 -0
  90. data/lib/less/js/test/less/css.less +4 -0
  91. data/lib/less/js/test/less/functions.less +15 -1
  92. data/lib/less/js/test/less/ie-filters.less +8 -0
  93. data/lib/less/js/test/less/import/import-test-e.less +2 -0
  94. data/lib/less/js/test/less/import.less +4 -1
  95. data/lib/less/js/test/less/media.less +55 -1
  96. data/lib/less/js/test/less/mixins-args.less +20 -1
  97. data/lib/less/js/test/less/mixins-guards.less +94 -0
  98. data/lib/less/js/test/less/mixins-important.less +18 -0
  99. data/lib/less/js/test/less/mixins-pattern.less +3 -0
  100. data/lib/less/js/test/less/mixins.less +20 -0
  101. data/lib/less/js/test/less/operations.less +4 -0
  102. data/lib/less/js/test/less/selectors.less +31 -0
  103. data/lib/less/js/test/less/strings.less +2 -0
  104. data/lib/less/js/test/less/variables.less +4 -0
  105. data/lib/less/js/test/less-test.js +3 -5
  106. data/lib/less/loader.rb +24 -25
  107. data/lib/less/parser.rb +32 -6
  108. data/lib/less/version.rb +1 -1
  109. data/lib/less.rb +7 -8
  110. data/spec/less/faulty/faulty.less +3 -0
  111. data/spec/less/parser_spec.rb +28 -2
  112. metadata +321 -149
@@ -39,7 +39,7 @@ if (less.env === 'development') {
39
39
  }
40
40
  less.watchTimer = setInterval(function () {
41
41
  if (less.watchMode) {
42
- loadStyleSheets(function (root, sheet, env) {
42
+ loadStyleSheets(function (e, root, _, sheet, env) {
43
43
  if (root) {
44
44
  createCSS(root.toCSS(), sheet, env.lastModified);
45
45
  }
@@ -78,7 +78,7 @@ less.refresh = function (reload) {
78
78
  var startTime, endTime;
79
79
  startTime = endTime = new(Date);
80
80
 
81
- loadStyleSheets(function (root, sheet, env) {
81
+ loadStyleSheets(function (e, root, _, sheet, env) {
82
82
  if (env.local) {
83
83
  log("loading " + sheet.href + " from cache.");
84
84
  } else {
@@ -101,8 +101,14 @@ function loadStyles() {
101
101
  for (var i = 0; i < styles.length; i++) {
102
102
  if (styles[i].type.match(typePattern)) {
103
103
  new(less.Parser)().parse(styles[i].innerHTML || '', function (e, tree) {
104
- styles[i].type = 'text/css';
105
- styles[i].innerHTML = tree.toCSS();
104
+ var css = tree.toCSS();
105
+ var style = styles[i];
106
+ style.type = 'text/css';
107
+ if (style.styleSheet) {
108
+ style.styleSheet.cssText = css;
109
+ } else {
110
+ style.innerHTML = css;
111
+ }
106
112
  });
107
113
  }
108
114
  }
@@ -129,6 +135,7 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
129
135
  href = url.slice(0, url.lastIndexOf('/') + 1) + href;
130
136
  }
131
137
  }
138
+ var filename = href.match(/([^\/]+)$/)[1];
132
139
 
133
140
  xhr(sheet.href, sheet.type, function (data, lastModified) {
134
141
  if (!reload && styles && lastModified &&
@@ -136,18 +143,19 @@ function loadStyleSheet(sheet, callback, reload, remaining) {
136
143
  new(Date)(styles.timestamp).valueOf())) {
137
144
  // Use local copy
138
145
  createCSS(styles.css, sheet);
139
- callback(null, sheet, { local: true, remaining: remaining });
146
+ callback(null, null, data, sheet, { local: true, remaining: remaining });
140
147
  } else {
141
148
  // Use remote copy (re-parse)
142
149
  try {
143
150
  new(less.Parser)({
144
151
  optimization: less.optimization,
145
152
  paths: [href.replace(/[\w\.-]+$/, '')],
146
- mime: sheet.type
153
+ mime: sheet.type,
154
+ filename: filename
147
155
  }).parse(data, function (e, root) {
148
156
  if (e) { return error(e, href) }
149
157
  try {
150
- callback(root, sheet, { local: false, lastModified: lastModified, remaining: remaining });
158
+ callback(e, root, data, sheet, { local: false, lastModified: lastModified, remaining: remaining });
151
159
  removeNode(document.getElementById('less-error-message:' + extractId(href)));
152
160
  } catch (e) {
153
161
  error(e, href);
@@ -227,7 +235,7 @@ function xhr(url, type, callback, errback) {
227
235
  xhr.send(null);
228
236
 
229
237
  if (isFileProtocol) {
230
- if (xhr.status === 0) {
238
+ if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
231
239
  callback(xhr.responseText);
232
240
  } else {
233
241
  errback(xhr.status, url);
@@ -275,29 +283,32 @@ function log(str) {
275
283
 
276
284
  function error(e, href) {
277
285
  var id = 'less-error-message:' + extractId(href);
278
-
279
- var template = ['<ul>',
280
- '<li><label>[-1]</label><pre class="ctx">{0}</pre></li>',
281
- '<li><label>[0]</label><pre>{current}</pre></li>',
282
- '<li><label>[1]</label><pre class="ctx">{2}</pre></li>',
283
- '</ul>'].join('\n');
284
-
285
- var elem = document.createElement('div'), timer, content;
286
+ var template = '<li><label>{line}</label><pre class="{class}">{content}</pre></li>';
287
+ var elem = document.createElement('div'), timer, content, error = [];
288
+ var filename = e.filename || href;
286
289
 
287
290
  elem.id = id;
288
291
  elem.className = "less-error-message";
289
292
 
290
293
  content = '<h3>' + (e.message || 'There is an error in your .less file') +
291
- '</h3>' + '<p><a href="' + href + '">' + href + "</a> ";
294
+ '</h3>' + '<p>in <a href="' + filename + '">' + filename + "</a> ";
292
295
 
293
- if (e.extract) {
296
+ var errorline = function (e, i, classname) {
297
+ if (e.extract[i]) {
298
+ error.push(template.replace(/\{line\}/, parseInt(e.line) + (i - 1))
299
+ .replace(/\{class\}/, classname)
300
+ .replace(/\{content\}/, e.extract[i]));
301
+ }
302
+ };
303
+
304
+ if (e.stack) {
305
+ content += '<br/>' + e.stack.split('\n').slice(1).join('<br/>');
306
+ } else if (e.extract) {
307
+ errorline(e, 0, '');
308
+ errorline(e, 1, 'line');
309
+ errorline(e, 2, '');
294
310
  content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':</p>' +
295
- template.replace(/\[(-?\d)\]/g, function (_, i) {
296
- return (parseInt(e.line) + parseInt(i)) || '';
297
- }).replace(/\{(\d)\}/g, function (_, i) {
298
- return e.extract[parseInt(i)] || '';
299
- }).replace(/\{current\}/, e.extract[1].slice(0, e.column) + '<span class="error">' +
300
- e.extract[1].slice(e.column) + '</span>');
311
+ '<ul>' + error.join('') + '</ul>';
301
312
  }
302
313
  elem.innerHTML = content;
303
314
 
@@ -316,13 +327,13 @@ function error(e, href) {
316
327
  'color: #cc7777;',
317
328
  '}',
318
329
  '.less-error-message pre {',
319
- 'color: #ee4444;',
330
+ 'color: #dd6666;',
320
331
  'padding: 4px 0;',
321
332
  'margin: 0;',
322
333
  'display: inline-block;',
323
334
  '}',
324
- '.less-error-message pre.ctx {',
325
- 'color: #dd4444;',
335
+ '.less-error-message pre.line {',
336
+ 'color: #ff0000;',
326
337
  '}',
327
338
  '.less-error-message h3 {',
328
339
  'font-size: 20px;',
@@ -0,0 +1,151 @@
1
+ (function (tree) {
2
+ tree.colors = {
3
+ 'aliceblue':'#f0f8ff',
4
+ 'antiquewhite':'#faebd7',
5
+ 'aqua':'#00ffff',
6
+ 'aquamarine':'#7fffd4',
7
+ 'azure':'#f0ffff',
8
+ 'beige':'#f5f5dc',
9
+ 'bisque':'#ffe4c4',
10
+ 'black':'#000000',
11
+ 'blanchedalmond':'#ffebcd',
12
+ 'blue':'#0000ff',
13
+ 'blueviolet':'#8a2be2',
14
+ 'brown':'#a52a2a',
15
+ 'burlywood':'#deb887',
16
+ 'cadetblue':'#5f9ea0',
17
+ 'chartreuse':'#7fff00',
18
+ 'chocolate':'#d2691e',
19
+ 'coral':'#ff7f50',
20
+ 'cornflowerblue':'#6495ed',
21
+ 'cornsilk':'#fff8dc',
22
+ 'crimson':'#dc143c',
23
+ 'cyan':'#00ffff',
24
+ 'darkblue':'#00008b',
25
+ 'darkcyan':'#008b8b',
26
+ 'darkgoldenrod':'#b8860b',
27
+ 'darkgray':'#a9a9a9',
28
+ 'darkgrey':'#a9a9a9',
29
+ 'darkgreen':'#006400',
30
+ 'darkkhaki':'#bdb76b',
31
+ 'darkmagenta':'#8b008b',
32
+ 'darkolivegreen':'#556b2f',
33
+ 'darkorange':'#ff8c00',
34
+ 'darkorchid':'#9932cc',
35
+ 'darkred':'#8b0000',
36
+ 'darksalmon':'#e9967a',
37
+ 'darkseagreen':'#8fbc8f',
38
+ 'darkslateblue':'#483d8b',
39
+ 'darkslategray':'#2f4f4f',
40
+ 'darkslategrey':'#2f4f4f',
41
+ 'darkturquoise':'#00ced1',
42
+ 'darkviolet':'#9400d3',
43
+ 'deeppink':'#ff1493',
44
+ 'deepskyblue':'#00bfff',
45
+ 'dimgray':'#696969',
46
+ 'dimgrey':'#696969',
47
+ 'dodgerblue':'#1e90ff',
48
+ 'firebrick':'#b22222',
49
+ 'floralwhite':'#fffaf0',
50
+ 'forestgreen':'#228b22',
51
+ 'fuchsia':'#ff00ff',
52
+ 'gainsboro':'#dcdcdc',
53
+ 'ghostwhite':'#f8f8ff',
54
+ 'gold':'#ffd700',
55
+ 'goldenrod':'#daa520',
56
+ 'gray':'#808080',
57
+ 'grey':'#808080',
58
+ 'green':'#008000',
59
+ 'greenyellow':'#adff2f',
60
+ 'honeydew':'#f0fff0',
61
+ 'hotpink':'#ff69b4',
62
+ 'indianred':'#cd5c5c',
63
+ 'indigo':'#4b0082',
64
+ 'ivory':'#fffff0',
65
+ 'khaki':'#f0e68c',
66
+ 'lavender':'#e6e6fa',
67
+ 'lavenderblush':'#fff0f5',
68
+ 'lawngreen':'#7cfc00',
69
+ 'lemonchiffon':'#fffacd',
70
+ 'lightblue':'#add8e6',
71
+ 'lightcoral':'#f08080',
72
+ 'lightcyan':'#e0ffff',
73
+ 'lightgoldenrodyellow':'#fafad2',
74
+ 'lightgray':'#d3d3d3',
75
+ 'lightgrey':'#d3d3d3',
76
+ 'lightgreen':'#90ee90',
77
+ 'lightpink':'#ffb6c1',
78
+ 'lightsalmon':'#ffa07a',
79
+ 'lightseagreen':'#20b2aa',
80
+ 'lightskyblue':'#87cefa',
81
+ 'lightslategray':'#778899',
82
+ 'lightslategrey':'#778899',
83
+ 'lightsteelblue':'#b0c4de',
84
+ 'lightyellow':'#ffffe0',
85
+ 'lime':'#00ff00',
86
+ 'limegreen':'#32cd32',
87
+ 'linen':'#faf0e6',
88
+ 'magenta':'#ff00ff',
89
+ 'maroon':'#800000',
90
+ 'mediumaquamarine':'#66cdaa',
91
+ 'mediumblue':'#0000cd',
92
+ 'mediumorchid':'#ba55d3',
93
+ 'mediumpurple':'#9370d8',
94
+ 'mediumseagreen':'#3cb371',
95
+ 'mediumslateblue':'#7b68ee',
96
+ 'mediumspringgreen':'#00fa9a',
97
+ 'mediumturquoise':'#48d1cc',
98
+ 'mediumvioletred':'#c71585',
99
+ 'midnightblue':'#191970',
100
+ 'mintcream':'#f5fffa',
101
+ 'mistyrose':'#ffe4e1',
102
+ 'moccasin':'#ffe4b5',
103
+ 'navajowhite':'#ffdead',
104
+ 'navy':'#000080',
105
+ 'oldlace':'#fdf5e6',
106
+ 'olive':'#808000',
107
+ 'olivedrab':'#6b8e23',
108
+ 'orange':'#ffa500',
109
+ 'orangered':'#ff4500',
110
+ 'orchid':'#da70d6',
111
+ 'palegoldenrod':'#eee8aa',
112
+ 'palegreen':'#98fb98',
113
+ 'paleturquoise':'#afeeee',
114
+ 'palevioletred':'#d87093',
115
+ 'papayawhip':'#ffefd5',
116
+ 'peachpuff':'#ffdab9',
117
+ 'peru':'#cd853f',
118
+ 'pink':'#ffc0cb',
119
+ 'plum':'#dda0dd',
120
+ 'powderblue':'#b0e0e6',
121
+ 'purple':'#800080',
122
+ 'red':'#ff0000',
123
+ 'rosybrown':'#bc8f8f',
124
+ 'royalblue':'#4169e1',
125
+ 'saddlebrown':'#8b4513',
126
+ 'salmon':'#fa8072',
127
+ 'sandybrown':'#f4a460',
128
+ 'seagreen':'#2e8b57',
129
+ 'seashell':'#fff5ee',
130
+ 'sienna':'#a0522d',
131
+ 'silver':'#c0c0c0',
132
+ 'skyblue':'#87ceeb',
133
+ 'slateblue':'#6a5acd',
134
+ 'slategray':'#708090',
135
+ 'slategrey':'#708090',
136
+ 'snow':'#fffafa',
137
+ 'springgreen':'#00ff7f',
138
+ 'steelblue':'#4682b4',
139
+ 'tan':'#d2b48c',
140
+ 'teal':'#008080',
141
+ 'thistle':'#d8bfd8',
142
+ 'tomato':'#ff6347',
143
+ 'turquoise':'#40e0d0',
144
+ 'violet':'#ee82ee',
145
+ 'wheat':'#f5deb3',
146
+ 'white':'#ffffff',
147
+ 'whitesmoke':'#f5f5f5',
148
+ 'yellow':'#ffff00',
149
+ 'yellowgreen':'#9acd32'
150
+ };
151
+ })(require('./tree'));
@@ -0,0 +1,355 @@
1
+ /**
2
+ * cssmin.js
3
+ * Author: Stoyan Stefanov - http://phpied.com/
4
+ * This is a JavaScript port of the CSS minification tool
5
+ * distributed with YUICompressor, itself a port
6
+ * of the cssmin utility by Isaac Schlueter - http://foohack.com/
7
+ * Permission is hereby granted to use the JavaScript version under the same
8
+ * conditions as the YUICompressor (original YUICompressor note below).
9
+ */
10
+
11
+ /*
12
+ * YUI Compressor
13
+ * http://developer.yahoo.com/yui/compressor/
14
+ * Author: Julien Lecomte - http://www.julienlecomte.net/
15
+ * Copyright (c) 2011 Yahoo! Inc. All rights reserved.
16
+ * The copyrights embodied in the content of this file are licensed
17
+ * by Yahoo! Inc. under the BSD (revised) open source license.
18
+ */
19
+ var YAHOO = YAHOO || {};
20
+ YAHOO.compressor = YAHOO.compressor || {};
21
+
22
+ /**
23
+ * Utility method to replace all data urls with tokens before we start
24
+ * compressing, to avoid performance issues running some of the subsequent
25
+ * regexes against large strings chunks.
26
+ *
27
+ * @private
28
+ * @method _extractDataUrls
29
+ * @param {String} css The input css
30
+ * @param {Array} The global array of tokens to preserve
31
+ * @returns String The processed css
32
+ */
33
+ YAHOO.compressor._extractDataUrls = function (css, preservedTokens) {
34
+
35
+ // Leave data urls alone to increase parse performance.
36
+ var maxIndex = css.length - 1,
37
+ appendIndex = 0,
38
+ startIndex,
39
+ endIndex,
40
+ terminator,
41
+ foundTerminator,
42
+ sb = [],
43
+ m,
44
+ preserver,
45
+ token,
46
+ pattern = /url\(\s*(["']?)data\:/g;
47
+
48
+ // Since we need to account for non-base64 data urls, we need to handle
49
+ // ' and ) being part of the data string. Hence switching to indexOf,
50
+ // to determine whether or not we have matching string terminators and
51
+ // handling sb appends directly, instead of using matcher.append* methods.
52
+
53
+ while ((m = pattern.exec(css)) !== null) {
54
+
55
+ startIndex = m.index + 4; // "url(".length()
56
+ terminator = m[1]; // ', " or empty (not quoted)
57
+
58
+ if (terminator.length === 0) {
59
+ terminator = ")";
60
+ }
61
+
62
+ foundTerminator = false;
63
+
64
+ endIndex = pattern.lastIndex - 1;
65
+
66
+ while(foundTerminator === false && endIndex+1 <= maxIndex) {
67
+ endIndex = css.indexOf(terminator, endIndex + 1);
68
+
69
+ // endIndex == 0 doesn't really apply here
70
+ if ((endIndex > 0) && (css.charAt(endIndex - 1) !== '\\')) {
71
+ foundTerminator = true;
72
+ if (")" != terminator) {
73
+ endIndex = css.indexOf(")", endIndex);
74
+ }
75
+ }
76
+ }
77
+
78
+ // Enough searching, start moving stuff over to the buffer
79
+ sb.push(css.substring(appendIndex, m.index));
80
+
81
+ if (foundTerminator) {
82
+ token = css.substring(startIndex, endIndex);
83
+ token = token.replace(/\s+/g, "");
84
+ preservedTokens.push(token);
85
+
86
+ preserver = "url(___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___)";
87
+ sb.push(preserver);
88
+
89
+ appendIndex = endIndex + 1;
90
+ } else {
91
+ // No end terminator found, re-add the whole match. Should we throw/warn here?
92
+ sb.push(css.substring(m.index, pattern.lastIndex));
93
+ appendIndex = pattern.lastIndex;
94
+ }
95
+ }
96
+
97
+ sb.push(css.substring(appendIndex));
98
+
99
+ return sb.join("");
100
+ };
101
+
102
+ /**
103
+ * Utility method to compress hex color values of the form #AABBCC to #ABC.
104
+ *
105
+ * DOES NOT compress CSS ID selectors which match the above pattern (which would break things).
106
+ * e.g. #AddressForm { ... }
107
+ *
108
+ * DOES NOT compress IE filters, which have hex color values (which would break things).
109
+ * e.g. filter: chroma(color="#FFFFFF");
110
+ *
111
+ * DOES NOT compress invalid hex values.
112
+ * e.g. background-color: #aabbccdd
113
+ *
114
+ * @private
115
+ * @method _compressHexColors
116
+ * @param {String} css The input css
117
+ * @returns String The processed css
118
+ */
119
+ YAHOO.compressor._compressHexColors = function(css) {
120
+
121
+ // Look for hex colors inside { ... } (to avoid IDs) and which don't have a =, or a " in front of them (to avoid filters)
122
+ var pattern = /(\=\s*?["']?)?#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])(\}|[^0-9a-f{][^{]*?\})/gi,
123
+ m,
124
+ index = 0,
125
+ isFilter,
126
+ sb = [];
127
+
128
+ while ((m = pattern.exec(css)) !== null) {
129
+
130
+ sb.push(css.substring(index, m.index));
131
+
132
+ isFilter = m[1];
133
+
134
+ if (isFilter) {
135
+ // Restore, maintain case, otherwise filter will break
136
+ sb.push(m[1] + "#" + (m[2] + m[3] + m[4] + m[5] + m[6] + m[7]));
137
+ } else {
138
+ if (m[2].toLowerCase() == m[3].toLowerCase() &&
139
+ m[4].toLowerCase() == m[5].toLowerCase() &&
140
+ m[6].toLowerCase() == m[7].toLowerCase()) {
141
+
142
+ // Compress.
143
+ sb.push("#" + (m[3] + m[5] + m[7]).toLowerCase());
144
+ } else {
145
+ // Non compressible color, restore but lower case.
146
+ sb.push("#" + (m[2] + m[3] + m[4] + m[5] + m[6] + m[7]).toLowerCase());
147
+ }
148
+ }
149
+
150
+ index = pattern.lastIndex = pattern.lastIndex - m[8].length;
151
+ }
152
+
153
+ sb.push(css.substring(index));
154
+
155
+ return sb.join("");
156
+ };
157
+
158
+ YAHOO.compressor.cssmin = function (css, linebreakpos) {
159
+
160
+ var startIndex = 0,
161
+ endIndex = 0,
162
+ i = 0, max = 0,
163
+ preservedTokens = [],
164
+ comments = [],
165
+ token = '',
166
+ totallen = css.length,
167
+ placeholder = '';
168
+
169
+ css = this._extractDataUrls(css, preservedTokens);
170
+
171
+ // collect all comment blocks...
172
+ while ((startIndex = css.indexOf("/*", startIndex)) >= 0) {
173
+ endIndex = css.indexOf("*/", startIndex + 2);
174
+ if (endIndex < 0) {
175
+ endIndex = totallen;
176
+ }
177
+ token = css.slice(startIndex + 2, endIndex);
178
+ comments.push(token);
179
+ css = css.slice(0, startIndex + 2) + "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + (comments.length - 1) + "___" + css.slice(endIndex);
180
+ startIndex += 2;
181
+ }
182
+
183
+ // preserve strings so their content doesn't get accidentally minified
184
+ css = css.replace(/("([^\\"]|\\.|\\)*")|('([^\\']|\\.|\\)*')/g, function (match) {
185
+ var i, max, quote = match.substring(0, 1);
186
+
187
+ match = match.slice(1, -1);
188
+
189
+ // maybe the string contains a comment-like substring?
190
+ // one, maybe more? put'em back then
191
+ if (match.indexOf("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_") >= 0) {
192
+ for (i = 0, max = comments.length; i < max; i = i + 1) {
193
+ match = match.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", comments[i]);
194
+ }
195
+ }
196
+
197
+ // minify alpha opacity in filter strings
198
+ match = match.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity=");
199
+
200
+ preservedTokens.push(match);
201
+ return quote + "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___" + quote;
202
+ });
203
+
204
+ // strings are safe, now wrestle the comments
205
+ for (i = 0, max = comments.length; i < max; i = i + 1) {
206
+
207
+ token = comments[i];
208
+ placeholder = "___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___";
209
+
210
+ // ! in the first position of the comment means preserve
211
+ // so push to the preserved tokens keeping the !
212
+ if (token.charAt(0) === "!") {
213
+ preservedTokens.push(token);
214
+ css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___");
215
+ continue;
216
+ }
217
+
218
+ // \ in the last position looks like hack for Mac/IE5
219
+ // shorten that to /*\*/ and the next one to /**/
220
+ if (token.charAt(token.length - 1) === "\\") {
221
+ preservedTokens.push("\\");
222
+ css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___");
223
+ i = i + 1; // attn: advancing the loop
224
+ preservedTokens.push("");
225
+ css = css.replace("___YUICSSMIN_PRESERVE_CANDIDATE_COMMENT_" + i + "___", "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___");
226
+ continue;
227
+ }
228
+
229
+ // keep empty comments after child selectors (IE7 hack)
230
+ // e.g. html >/**/ body
231
+ if (token.length === 0) {
232
+ startIndex = css.indexOf(placeholder);
233
+ if (startIndex > 2) {
234
+ if (css.charAt(startIndex - 3) === '>') {
235
+ preservedTokens.push("");
236
+ css = css.replace(placeholder, "___YUICSSMIN_PRESERVED_TOKEN_" + (preservedTokens.length - 1) + "___");
237
+ }
238
+ }
239
+ }
240
+
241
+ // in all other cases kill the comment
242
+ css = css.replace("/*" + placeholder + "*/", "");
243
+ }
244
+
245
+
246
+ // Normalize all whitespace strings to single spaces. Easier to work with that way.
247
+ css = css.replace(/\s+/g, " ");
248
+
249
+ // Remove the spaces before the things that should not have spaces before them.
250
+ // But, be careful not to turn "p :link {...}" into "p:link{...}"
251
+ // Swap out any pseudo-class colons with the token, and then swap back.
252
+ css = css.replace(/(^|\})(([^\{:])+:)+([^\{]*\{)/g, function (m) {
253
+ return m.replace(":", "___YUICSSMIN_PSEUDOCLASSCOLON___");
254
+ });
255
+ css = css.replace(/\s+([!{};:>+\(\)\],])/g, '$1');
256
+ css = css.replace(/___YUICSSMIN_PSEUDOCLASSCOLON___/g, ":");
257
+
258
+ // retain space for special IE6 cases
259
+ css = css.replace(/:first-(line|letter)(\{|,)/g, ":first-$1 $2");
260
+
261
+ // no space after the end of a preserved comment
262
+ css = css.replace(/\*\/ /g, '*/');
263
+
264
+
265
+ // If there is a @charset, then only allow one, and push to the top of the file.
266
+ css = css.replace(/^(.*)(@charset "[^"]*";)/gi, '$2$1');
267
+ css = css.replace(/^(\s*@charset [^;]+;\s*)+/gi, '$1');
268
+
269
+ // Put the space back in some cases, to support stuff like
270
+ // @media screen and (-webkit-min-device-pixel-ratio:0){
271
+ css = css.replace(/\band\(/gi, "and (");
272
+
273
+
274
+ // Remove the spaces after the things that should not have spaces after them.
275
+ css = css.replace(/([!{}:;>+\(\[,])\s+/g, '$1');
276
+
277
+ // remove unnecessary semicolons
278
+ css = css.replace(/;+\}/g, "}");
279
+
280
+ // Replace 0(px,em,%) with 0.
281
+ css = css.replace(/([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)/gi, "$1$2");
282
+
283
+ // Replace 0 0 0 0; with 0.
284
+ css = css.replace(/:0 0 0 0(;|\})/g, ":0$1");
285
+ css = css.replace(/:0 0 0(;|\})/g, ":0$1");
286
+ css = css.replace(/:0 0(;|\})/g, ":0$1");
287
+
288
+ // Replace background-position:0; with background-position:0 0;
289
+ // same for transform-origin
290
+ css = css.replace(/(background-position|transform-origin|webkit-transform-origin|moz-transform-origin|o-transform-origin|ms-transform-origin):0(;|\})/gi, function(all, prop, tail) {
291
+ return prop.toLowerCase() + ":0 0" + tail;
292
+ });
293
+
294
+ // Replace 0.6 to .6, but only when preceded by : or a white-space
295
+ css = css.replace(/(:|\s)0+\.(\d+)/g, "$1.$2");
296
+
297
+ // Shorten colors from rgb(51,102,153) to #336699
298
+ // This makes it more likely that it'll get further compressed in the next step.
299
+ css = css.replace(/rgb\s*\(\s*([0-9,\s]+)\s*\)/gi, function () {
300
+ var i, rgbcolors = arguments[1].split(',');
301
+ for (i = 0; i < rgbcolors.length; i = i + 1) {
302
+ rgbcolors[i] = parseInt(rgbcolors[i], 10).toString(16);
303
+ if (rgbcolors[i].length === 1) {
304
+ rgbcolors[i] = '0' + rgbcolors[i];
305
+ }
306
+ }
307
+ return '#' + rgbcolors.join('');
308
+ });
309
+
310
+ // Shorten colors from #AABBCC to #ABC.
311
+ css = this._compressHexColors(css);
312
+
313
+ // border: none -> border:0
314
+ css = css.replace(/(border|border-top|border-right|border-bottom|border-right|outline|background):none(;|\})/gi, function(all, prop, tail) {
315
+ return prop.toLowerCase() + ":0" + tail;
316
+ });
317
+
318
+ // shorter opacity IE filter
319
+ css = css.replace(/progid:DXImageTransform\.Microsoft\.Alpha\(Opacity=/gi, "alpha(opacity=");
320
+
321
+ // Remove empty rules.
322
+ css = css.replace(/[^\};\{\/]+\{\}/g, "");
323
+
324
+ if (linebreakpos >= 0) {
325
+ // Some source control tools don't like it when files containing lines longer
326
+ // than, say 8000 characters, are checked in. The linebreak option is used in
327
+ // that case to split long lines after a specific column.
328
+ startIndex = 0;
329
+ i = 0;
330
+ while (i < css.length) {
331
+ i = i + 1;
332
+ if (css[i - 1] === '}' && i - startIndex > linebreakpos) {
333
+ css = css.slice(0, i) + '\n' + css.slice(i);
334
+ startIndex = i;
335
+ }
336
+ }
337
+ }
338
+
339
+ // Replace multiple semi-colons in a row by a single one
340
+ // See SF bug #1980989
341
+ css = css.replace(/;;+/g, ";");
342
+
343
+ // restore preserved comments and strings
344
+ for (i = 0, max = preservedTokens.length; i < max; i = i + 1) {
345
+ css = css.replace("___YUICSSMIN_PRESERVED_TOKEN_" + i + "___", preservedTokens[i]);
346
+ }
347
+
348
+ // Trim the final string (for any leading or trailing white spaces)
349
+ css = css.replace(/^\s+|\s+$/g, "");
350
+
351
+ return css;
352
+
353
+ };
354
+
355
+ exports.compressor = YAHOO.compressor;