Load the ACO / Image file from your local drive and get a list of the Palette Colors as CSS HEX colors. HTML code will also be generated and a human name given to each color.
56 |
57 |
58 |
59 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 | Support or Contact
108 |
109 |
Having trouble with Pages? Contact adnan@websemantics.ca and we’ll help you sort it out.
110 |
111 |
112 |
113 |
114 |
122 |
123 |
124 |
125 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/lib/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/websemantics/Color-Palette-Toolkit/83f36b653aef3fe16058287d2f5f736bfe7c34aa/lib/.DS_Store
--------------------------------------------------------------------------------
/lib/color-thief.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Color Thief v2.0
3 | * by Lokesh Dhakar - http://www.lokeshdhakar.com
4 | *
5 | * License
6 | * -------
7 | * Creative Commons Attribution 2.5 License:
8 | * http://creativecommons.org/licenses/by/2.5/
9 | *
10 | * Thanks
11 | * ------
12 | * Nick Rabinowitz - For creating quantize.js.
13 | * John Schulz - For clean up and optimization. @JFSIII
14 | * Nathan Spady - For adding drag and drop support to the demo page.
15 | *
16 | */
17 |
18 |
19 | /*
20 | CanvasImage Class
21 | Class that wraps the html image element and canvas.
22 | It also simplifies some of the canvas context manipulation
23 | with a set of helper functions.
24 | */
25 | var CanvasImage = function (image) {
26 | this.canvas = document.createElement('canvas');
27 | this.context = this.canvas.getContext('2d');
28 |
29 | document.body.appendChild(this.canvas);
30 |
31 | this.width = this.canvas.width = image.width;
32 | this.height = this.canvas.height = image.height;
33 |
34 | this.context.drawImage(image, 0, 0, this.width, this.height);
35 | };
36 |
37 | CanvasImage.prototype.clear = function () {
38 | this.context.clearRect(0, 0, this.width, this.height);
39 | };
40 |
41 | CanvasImage.prototype.update = function (imageData) {
42 | this.context.putImageData(imageData, 0, 0);
43 | };
44 |
45 | CanvasImage.prototype.getPixelCount = function () {
46 | return this.width * this.height;
47 | };
48 |
49 | CanvasImage.prototype.getImageData = function () {
50 | return this.context.getImageData(0, 0, this.width, this.height);
51 | };
52 |
53 | CanvasImage.prototype.removeCanvas = function () {
54 | this.canvas.parentNode.removeChild(this.canvas);
55 | };
56 |
57 |
58 | var ColorThief = function () {};
59 |
60 | /*
61 | * getColor(sourceImage[, quality])
62 | * returns {r: num, g: num, b: num}
63 | *
64 | * Use the median cut algorithm provided by quantize.js to cluster similar
65 | * colors and return the base color from the largest cluster.
66 | *
67 | * Quality is an optional argument. It needs to be an integer. 0 is the highest quality settings.
68 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
69 | * faster a color will be returned but the greater the likelihood that it will not be the visually
70 | * most dominant color.
71 | *
72 | * */
73 | ColorThief.prototype.getColor = function(sourceImage, quality) {
74 | var palette = this.getPalette(sourceImage, 5, quality);
75 | var dominantColor = palette[0];
76 | return dominantColor;
77 | };
78 |
79 |
80 | /*
81 | * getPalette(sourceImage[, colorCount, quality])
82 | * returns array[ {r: num, g: num, b: num}, {r: num, g: num, b: num}, ...]
83 | *
84 | * Use the median cut algorithm provided by quantize.js to cluster similar colors.
85 | *
86 | * colorCount determines the size of the palette; the number of colors returned. If not set, it
87 | * defaults to 10.
88 | *
89 | * BUGGY: Function does not always return the requested amount of colors. It can be +/- 2.
90 | *
91 | * quality is an optional argument. It needs to be an integer. 0 is the highest quality settings.
92 | * 10 is the default. There is a trade-off between quality and speed. The bigger the number, the
93 | * faster the palette generation but the greater the likelihood that colors will be missed.
94 | *
95 | *
96 | */
97 | ColorThief.prototype.getPalette = function(sourceImage, colorCount, quality) {
98 |
99 | if (typeof colorCount === 'undefined') {
100 | colorCount = 10;
101 | }
102 | if (typeof quality === 'undefined') {
103 | quality = 10;
104 | }
105 |
106 | // Create custom CanvasImage object
107 | var image = new CanvasImage(sourceImage);
108 | var imageData = image.getImageData();
109 | var pixels = imageData.data;
110 | var pixelCount = image.getPixelCount();
111 |
112 | // Store the RGB values in an array format suitable for quantize function
113 | var pixelArray = [];
114 | for (var i = 0, offset, r, g, b, a; i < pixelCount; i = i + quality) {
115 | offset = i * 4;
116 | r = pixels[offset + 0];
117 | g = pixels[offset + 1];
118 | b = pixels[offset + 2];
119 | a = pixels[offset + 3];
120 | // If pixel is mostly opaque and not white
121 | if (a >= 125) {
122 | if (!(r > 250 && g > 250 && b > 250)) {
123 | pixelArray.push([r, g, b]);
124 | }
125 | }
126 | }
127 |
128 | // Send array to quantize function which clusters values
129 | // using median cut algorithm
130 | var cmap = MMCQ.quantize(pixelArray, colorCount);
131 | var palette = cmap.palette();
132 |
133 | // Clean up
134 | image.removeCanvas();
135 |
136 | return palette;
137 | };
138 |
139 |
140 |
141 |
142 | /*!
143 | * quantize.js Copyright 2008 Nick Rabinowitz.
144 | * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
145 | */
146 |
147 | // fill out a couple protovis dependencies
148 | /*!
149 | * Block below copied from Protovis: http://mbostock.github.com/protovis/
150 | * Copyright 2010 Stanford Visualization Group
151 | * Licensed under the BSD License: http://www.opensource.org/licenses/bsd-license.php
152 | */
153 | if (!pv) {
154 | var pv = {
155 | map: function(array, f) {
156 | var o = {};
157 | return f ? array.map(function(d, i) { o.index = i; return f.call(o, d); }) : array.slice();
158 | },
159 | naturalOrder: function(a, b) {
160 | return (a < b) ? -1 : ((a > b) ? 1 : 0);
161 | },
162 | sum: function(array, f) {
163 | var o = {};
164 | return array.reduce(f ? function(p, d, i) { o.index = i; return p + f.call(o, d); } : function(p, d) { return p + d; }, 0);
165 | },
166 | max: function(array, f) {
167 | return Math.max.apply(null, f ? pv.map(array, f) : array);
168 | }
169 | };
170 | }
171 |
172 |
173 |
174 | /**
175 | * Basic Javascript port of the MMCQ (modified median cut quantization)
176 | * algorithm from the Leptonica library (http://www.leptonica.com/).
177 | * Returns a color map you can use to map original pixels to the reduced
178 | * palette. Still a work in progress.
179 | *
180 | * @author Nick Rabinowitz
181 | * @example
182 |
183 | // array of pixels as [R,G,B] arrays
184 | var myPixels = [[190,197,190], [202,204,200], [207,214,210], [211,214,211], [205,207,207]
185 | // etc
186 | ];
187 | var maxColors = 4;
188 |
189 | var cmap = MMCQ.quantize(myPixels, maxColors);
190 | var newPalette = cmap.palette();
191 | var newPixels = myPixels.map(function(p) {
192 | return cmap.map(p);
193 | });
194 |
195 | */
196 | var MMCQ = (function() {
197 | // private constants
198 | var sigbits = 5,
199 | rshift = 8 - sigbits,
200 | maxIterations = 1000,
201 | fractByPopulations = 0.75;
202 |
203 | // get reduced-space color index for a pixel
204 | function getColorIndex(r, g, b) {
205 | return (r << (2 * sigbits)) + (g << sigbits) + b;
206 | }
207 |
208 | // Simple priority queue
209 | function PQueue(comparator) {
210 | var contents = [],
211 | sorted = false;
212 |
213 | function sort() {
214 | contents.sort(comparator);
215 | sorted = true;
216 | }
217 |
218 | return {
219 | push: function(o) {
220 | contents.push(o);
221 | sorted = false;
222 | },
223 | peek: function(index) {
224 | if (!sorted) sort();
225 | if (index===undefined) index = contents.length - 1;
226 | return contents[index];
227 | },
228 | pop: function() {
229 | if (!sorted) sort();
230 | return contents.pop();
231 | },
232 | size: function() {
233 | return contents.length;
234 | },
235 | map: function(f) {
236 | return contents.map(f);
237 | },
238 | debug: function() {
239 | if (!sorted) sort();
240 | return contents;
241 | }
242 | };
243 | }
244 |
245 | // 3d color space box
246 | function VBox(r1, r2, g1, g2, b1, b2, histo) {
247 | var vbox = this;
248 | vbox.r1 = r1;
249 | vbox.r2 = r2;
250 | vbox.g1 = g1;
251 | vbox.g2 = g2;
252 | vbox.b1 = b1;
253 | vbox.b2 = b2;
254 | vbox.histo = histo;
255 | }
256 | VBox.prototype = {
257 | volume: function(force) {
258 | var vbox = this;
259 | if (!vbox._volume || force) {
260 | vbox._volume = ((vbox.r2 - vbox.r1 + 1) * (vbox.g2 - vbox.g1 + 1) * (vbox.b2 - vbox.b1 + 1));
261 | }
262 | return vbox._volume;
263 | },
264 | count: function(force) {
265 | var vbox = this,
266 | histo = vbox.histo;
267 | if (!vbox._count_set || force) {
268 | var npix = 0,
269 | i, j, k;
270 | for (i = vbox.r1; i <= vbox.r2; i++) {
271 | for (j = vbox.g1; j <= vbox.g2; j++) {
272 | for (k = vbox.b1; k <= vbox.b2; k++) {
273 | index = getColorIndex(i,j,k);
274 | npix += (histo[index] || 0);
275 | }
276 | }
277 | }
278 | vbox._count = npix;
279 | vbox._count_set = true;
280 | }
281 | return vbox._count;
282 | },
283 | copy: function() {
284 | var vbox = this;
285 | return new VBox(vbox.r1, vbox.r2, vbox.g1, vbox.g2, vbox.b1, vbox.b2, vbox.histo);
286 | },
287 | avg: function(force) {
288 | var vbox = this,
289 | histo = vbox.histo;
290 | if (!vbox._avg || force) {
291 | var ntot = 0,
292 | mult = 1 << (8 - sigbits),
293 | rsum = 0,
294 | gsum = 0,
295 | bsum = 0,
296 | hval,
297 | i, j, k, histoindex;
298 | for (i = vbox.r1; i <= vbox.r2; i++) {
299 | for (j = vbox.g1; j <= vbox.g2; j++) {
300 | for (k = vbox.b1; k <= vbox.b2; k++) {
301 | histoindex = getColorIndex(i,j,k);
302 | hval = histo[histoindex] || 0;
303 | ntot += hval;
304 | rsum += (hval * (i + 0.5) * mult);
305 | gsum += (hval * (j + 0.5) * mult);
306 | bsum += (hval * (k + 0.5) * mult);
307 | }
308 | }
309 | }
310 | if (ntot) {
311 | vbox._avg = [~~(rsum/ntot), ~~(gsum/ntot), ~~(bsum/ntot)];
312 | } else {
313 | // console.log('empty box');
314 | vbox._avg = [
315 | ~~(mult * (vbox.r1 + vbox.r2 + 1) / 2),
316 | ~~(mult * (vbox.g1 + vbox.g2 + 1) / 2),
317 | ~~(mult * (vbox.b1 + vbox.b2 + 1) / 2)
318 | ];
319 | }
320 | }
321 | return vbox._avg;
322 | },
323 | contains: function(pixel) {
324 | var vbox = this,
325 | rval = pixel[0] >> rshift;
326 | gval = pixel[1] >> rshift;
327 | bval = pixel[2] >> rshift;
328 | return (rval >= vbox.r1 && rval <= vbox.r2 &&
329 | gval >= vbox.g1 && gval <= vbox.g2 &&
330 | bval >= vbox.b1 && bval <= vbox.b2);
331 | }
332 | };
333 |
334 | // Color map
335 | function CMap() {
336 | this.vboxes = new PQueue(function(a,b) {
337 | return pv.naturalOrder(
338 | a.vbox.count()*a.vbox.volume(),
339 | b.vbox.count()*b.vbox.volume()
340 | );
341 | });
342 | }
343 | CMap.prototype = {
344 | push: function(vbox) {
345 | this.vboxes.push({
346 | vbox: vbox,
347 | color: vbox.avg()
348 | });
349 | },
350 | palette: function() {
351 | return this.vboxes.map(function(vb) { return vb.color; });
352 | },
353 | size: function() {
354 | return this.vboxes.size();
355 | },
356 | map: function(color) {
357 | var vboxes = this.vboxes;
358 | for (var i=0; i 251
392 | var idx = vboxes.length-1,
393 | highest = vboxes[idx].color;
394 | if (highest[0] > 251 && highest[1] > 251 && highest[2] > 251)
395 | vboxes[idx].color = [255,255,255];
396 | }
397 | };
398 |
399 | // histo (1-d array, giving the number of pixels in
400 | // each quantized region of color space), or null on error
401 | function getHisto(pixels) {
402 | var histosize = 1 << (3 * sigbits),
403 | histo = new Array(histosize),
404 | index, rval, gval, bval;
405 | pixels.forEach(function(pixel) {
406 | rval = pixel[0] >> rshift;
407 | gval = pixel[1] >> rshift;
408 | bval = pixel[2] >> rshift;
409 | index = getColorIndex(rval, gval, bval);
410 | histo[index] = (histo[index] || 0) + 1;
411 | });
412 | return histo;
413 | }
414 |
415 | function vboxFromPixels(pixels, histo) {
416 | var rmin=1000000, rmax=0,
417 | gmin=1000000, gmax=0,
418 | bmin=1000000, bmax=0,
419 | rval, gval, bval;
420 | // find min/max
421 | pixels.forEach(function(pixel) {
422 | rval = pixel[0] >> rshift;
423 | gval = pixel[1] >> rshift;
424 | bval = pixel[2] >> rshift;
425 | if (rval < rmin) rmin = rval;
426 | else if (rval > rmax) rmax = rval;
427 | if (gval < gmin) gmin = gval;
428 | else if (gval > gmax) gmax = gval;
429 | if (bval < bmin) bmin = bval;
430 | else if (bval > bmax) bmax = bval;
431 | });
432 | return new VBox(rmin, rmax, gmin, gmax, bmin, bmax, histo);
433 | }
434 |
435 | function medianCutApply(histo, vbox) {
436 | if (!vbox.count()) return;
437 |
438 | var rw = vbox.r2 - vbox.r1 + 1,
439 | gw = vbox.g2 - vbox.g1 + 1,
440 | bw = vbox.b2 - vbox.b1 + 1,
441 | maxw = pv.max([rw, gw, bw]);
442 | // only one pixel, no split
443 | if (vbox.count() == 1) {
444 | return [vbox.copy()];
445 | }
446 | /* Find the partial sum arrays along the selected axis. */
447 | var total = 0,
448 | partialsum = [],
449 | lookaheadsum = [],
450 | i, j, k, sum, index;
451 | if (maxw == rw) {
452 | for (i = vbox.r1; i <= vbox.r2; i++) {
453 | sum = 0;
454 | for (j = vbox.g1; j <= vbox.g2; j++) {
455 | for (k = vbox.b1; k <= vbox.b2; k++) {
456 | index = getColorIndex(i,j,k);
457 | sum += (histo[index] || 0);
458 | }
459 | }
460 | total += sum;
461 | partialsum[i] = total;
462 | }
463 | }
464 | else if (maxw == gw) {
465 | for (i = vbox.g1; i <= vbox.g2; i++) {
466 | sum = 0;
467 | for (j = vbox.r1; j <= vbox.r2; j++) {
468 | for (k = vbox.b1; k <= vbox.b2; k++) {
469 | index = getColorIndex(j,i,k);
470 | sum += (histo[index] || 0);
471 | }
472 | }
473 | total += sum;
474 | partialsum[i] = total;
475 | }
476 | }
477 | else { /* maxw == bw */
478 | for (i = vbox.b1; i <= vbox.b2; i++) {
479 | sum = 0;
480 | for (j = vbox.r1; j <= vbox.r2; j++) {
481 | for (k = vbox.g1; k <= vbox.g2; k++) {
482 | index = getColorIndex(j,k,i);
483 | sum += (histo[index] || 0);
484 | }
485 | }
486 | total += sum;
487 | partialsum[i] = total;
488 | }
489 | }
490 | partialsum.forEach(function(d,i) {
491 | lookaheadsum[i] = total-d;
492 | });
493 | function doCut(color) {
494 | var dim1 = color + '1',
495 | dim2 = color + '2',
496 | left, right, vbox1, vbox2, d2, count2=0;
497 | for (i = vbox[dim1]; i <= vbox[dim2]; i++) {
498 | if (partialsum[i] > total / 2) {
499 | vbox1 = vbox.copy();
500 | vbox2 = vbox.copy();
501 | left = i - vbox[dim1];
502 | right = vbox[dim2] - i;
503 | if (left <= right)
504 | d2 = Math.min(vbox[dim2] - 1, ~~(i + right / 2));
505 | else d2 = Math.max(vbox[dim1], ~~(i - 1 - left / 2));
506 | // avoid 0-count boxes
507 | while (!partialsum[d2]) d2++;
508 | count2 = lookaheadsum[d2];
509 | while (!count2 && partialsum[d2-1]) count2 = lookaheadsum[--d2];
510 | // set dimensions
511 | vbox1[dim2] = d2;
512 | vbox2[dim1] = vbox1[dim2] + 1;
513 | // console.log('vbox counts:', vbox.count(), vbox1.count(), vbox2.count());
514 | return [vbox1, vbox2];
515 | }
516 | }
517 |
518 | }
519 | // determine the cut planes
520 | return maxw == rw ? doCut('r') :
521 | maxw == gw ? doCut('g') :
522 | doCut('b');
523 | }
524 |
525 | function quantize(pixels, maxcolors) {
526 | // short-circuit
527 | if (!pixels.length || maxcolors < 2 || maxcolors > 256) {
528 | // console.log('wrong number of maxcolors');
529 | return false;
530 | }
531 |
532 | // XXX: check color content and convert to grayscale if insufficient
533 |
534 | var histo = getHisto(pixels),
535 | histosize = 1 << (3 * sigbits);
536 |
537 | // check that we aren't below maxcolors already
538 | var nColors = 0;
539 | histo.forEach(function() { nColors++; });
540 | if (nColors <= maxcolors) {
541 | // XXX: generate the new colors from the histo and return
542 | }
543 |
544 | // get the beginning vbox from the colors
545 | var vbox = vboxFromPixels(pixels, histo),
546 | pq = new PQueue(function(a,b) { return pv.naturalOrder(a.count(), b.count()); });
547 | pq.push(vbox);
548 |
549 | // inner function to do the iteration
550 | function iter(lh, target) {
551 | var ncolors = 1,
552 | niters = 0,
553 | vbox;
554 | while (niters < maxIterations) {
555 | vbox = lh.pop();
556 | if (!vbox.count()) { /* just put it back */
557 | lh.push(vbox);
558 | niters++;
559 | continue;
560 | }
561 | // do the cut
562 | var vboxes = medianCutApply(histo, vbox),
563 | vbox1 = vboxes[0],
564 | vbox2 = vboxes[1];
565 |
566 | if (!vbox1) {
567 | // console.log("vbox1 not defined; shouldn't happen!");
568 | return;
569 | }
570 | lh.push(vbox1);
571 | if (vbox2) { /* vbox2 can be null */
572 | lh.push(vbox2);
573 | ncolors++;
574 | }
575 | if (ncolors >= target) return;
576 | if (niters++ > maxIterations) {
577 | // console.log("infinite loop; perhaps too few pixels!");
578 | return;
579 | }
580 | }
581 | }
582 |
583 | // first set of colors, sorted by population
584 | iter(pq, fractByPopulations * maxcolors);
585 |
586 | // Re-sort by the product of pixel occupancy times the size in color space.
587 | var pq2 = new PQueue(function(a,b) {
588 | return pv.naturalOrder(a.count()*a.volume(), b.count()*b.volume());
589 | });
590 | while (pq.size()) {
591 | pq2.push(pq.pop());
592 | }
593 |
594 | // next set - generate the median cuts using the (npix * vol) sorting.
595 | iter(pq2, maxcolors - pq2.size());
596 |
597 | // calculate the actual colors
598 | var cmap = new CMap();
599 | while (pq2.size()) {
600 | cmap.push(pq2.pop());
601 | }
602 |
603 | return cmap;
604 | }
605 |
606 | return {
607 | quantize: quantize
608 | };
609 | })();
--------------------------------------------------------------------------------
/lib/css.js:
--------------------------------------------------------------------------------
1 | // CodeMirror, copyright (c) by Marijn Haverbeke and others
2 | // Distributed under an MIT license: http://codemirror.net/LICENSE
3 |
4 | (function(mod) {
5 | if (typeof exports == "object" && typeof module == "object") // CommonJS
6 | mod(require("../../lib/codemirror"));
7 | else if (typeof define == "function" && define.amd) // AMD
8 | define(["../../lib/codemirror"], mod);
9 | else // Plain browser env
10 | mod(CodeMirror);
11 | })(function(CodeMirror) {
12 | "use strict";
13 |
14 | CodeMirror.defineMode("css", function(config, parserConfig) {
15 | if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
16 |
17 | var indentUnit = config.indentUnit,
18 | tokenHooks = parserConfig.tokenHooks,
19 | mediaTypes = parserConfig.mediaTypes || {},
20 | mediaFeatures = parserConfig.mediaFeatures || {},
21 | propertyKeywords = parserConfig.propertyKeywords || {},
22 | nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
23 | colorKeywords = parserConfig.colorKeywords || {},
24 | valueKeywords = parserConfig.valueKeywords || {},
25 | fontProperties = parserConfig.fontProperties || {},
26 | allowNested = parserConfig.allowNested;
27 |
28 | var type, override;
29 | function ret(style, tp) { type = tp; return style; }
30 |
31 | // Tokenizers
32 |
33 | function tokenBase(stream, state) {
34 | var ch = stream.next();
35 | if (tokenHooks[ch]) {
36 | var result = tokenHooks[ch](stream, state);
37 | if (result !== false) return result;
38 | }
39 | if (ch == "@") {
40 | stream.eatWhile(/[\w\\\-]/);
41 | return ret("def", stream.current());
42 | } else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
43 | return ret(null, "compare");
44 | } else if (ch == "\"" || ch == "'") {
45 | state.tokenize = tokenString(ch);
46 | return state.tokenize(stream, state);
47 | } else if (ch == "#") {
48 | stream.eatWhile(/[\w\\\-]/);
49 | return ret("atom", "hash");
50 | } else if (ch == "!") {
51 | stream.match(/^\s*\w*/);
52 | return ret("keyword", "important");
53 | } else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
54 | stream.eatWhile(/[\w.%]/);
55 | return ret("number", "unit");
56 | } else if (ch === "-") {
57 | if (/[\d.]/.test(stream.peek())) {
58 | stream.eatWhile(/[\w.%]/);
59 | return ret("number", "unit");
60 | } else if (stream.match(/^\w+-/)) {
61 | return ret("meta", "meta");
62 | }
63 | } else if (/[,+>*\/]/.test(ch)) {
64 | return ret(null, "select-op");
65 | } else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
66 | return ret("qualifier", "qualifier");
67 | } else if (/[:;{}\[\]\(\)]/.test(ch)) {
68 | return ret(null, ch);
69 | } else if (ch == "u" && stream.match("rl(")) {
70 | stream.backUp(1);
71 | state.tokenize = tokenParenthesized;
72 | return ret("property", "word");
73 | } else if (/[\w\\\-]/.test(ch)) {
74 | stream.eatWhile(/[\w\\\-]/);
75 | return ret("property", "word");
76 | } else {
77 | return ret(null, null);
78 | }
79 | }
80 |
81 | function tokenString(quote) {
82 | return function(stream, state) {
83 | var escaped = false, ch;
84 | while ((ch = stream.next()) != null) {
85 | if (ch == quote && !escaped) {
86 | if (quote == ")") stream.backUp(1);
87 | break;
88 | }
89 | escaped = !escaped && ch == "\\";
90 | }
91 | if (ch == quote || !escaped && quote != ")") state.tokenize = null;
92 | return ret("string", "string");
93 | };
94 | }
95 |
96 | function tokenParenthesized(stream, state) {
97 | stream.next(); // Must be '('
98 | if (!stream.match(/\s*[\"\')]/, false))
99 | state.tokenize = tokenString(")");
100 | else
101 | state.tokenize = null;
102 | return ret(null, "(");
103 | }
104 |
105 | // Context management
106 |
107 | function Context(type, indent, prev) {
108 | this.type = type;
109 | this.indent = indent;
110 | this.prev = prev;
111 | }
112 |
113 | function pushContext(state, stream, type) {
114 | state.context = new Context(type, stream.indentation() + indentUnit, state.context);
115 | return type;
116 | }
117 |
118 | function popContext(state) {
119 | state.context = state.context.prev;
120 | return state.context.type;
121 | }
122 |
123 | function pass(type, stream, state) {
124 | return states[state.context.type](type, stream, state);
125 | }
126 | function popAndPass(type, stream, state, n) {
127 | for (var i = n || 1; i > 0; i--)
128 | state.context = state.context.prev;
129 | return pass(type, stream, state);
130 | }
131 |
132 | // Parser
133 |
134 | function wordAsValue(stream) {
135 | var word = stream.current().toLowerCase();
136 | if (valueKeywords.hasOwnProperty(word))
137 | override = "atom";
138 | else if (colorKeywords.hasOwnProperty(word))
139 | override = "keyword";
140 | else
141 | override = "variable";
142 | }
143 |
144 | var states = {};
145 |
146 | states.top = function(type, stream, state) {
147 | if (type == "{") {
148 | return pushContext(state, stream, "block");
149 | } else if (type == "}" && state.context.prev) {
150 | return popContext(state);
151 | } else if (type == "@media") {
152 | return pushContext(state, stream, "media");
153 | } else if (type == "@font-face") {
154 | return "font_face_before";
155 | } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
156 | return "keyframes";
157 | } else if (type && type.charAt(0) == "@") {
158 | return pushContext(state, stream, "at");
159 | } else if (type == "hash") {
160 | override = "builtin";
161 | } else if (type == "word") {
162 | override = "tag";
163 | } else if (type == "variable-definition") {
164 | return "maybeprop";
165 | } else if (type == "interpolation") {
166 | return pushContext(state, stream, "interpolation");
167 | } else if (type == ":") {
168 | return "pseudo";
169 | } else if (allowNested && type == "(") {
170 | return pushContext(state, stream, "parens");
171 | }
172 | return state.context.type;
173 | };
174 |
175 | states.block = function(type, stream, state) {
176 | if (type == "word") {
177 | var word = stream.current().toLowerCase();
178 | if (propertyKeywords.hasOwnProperty(word)) {
179 | override = "property";
180 | return "maybeprop";
181 | } else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
182 | override = "string-2";
183 | return "maybeprop";
184 | } else if (allowNested) {
185 | override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
186 | return "block";
187 | } else {
188 | override += " error";
189 | return "maybeprop";
190 | }
191 | } else if (type == "meta") {
192 | return "block";
193 | } else if (!allowNested && (type == "hash" || type == "qualifier")) {
194 | override = "error";
195 | return "block";
196 | } else {
197 | return states.top(type, stream, state);
198 | }
199 | };
200 |
201 | states.maybeprop = function(type, stream, state) {
202 | if (type == ":") return pushContext(state, stream, "prop");
203 | return pass(type, stream, state);
204 | };
205 |
206 | states.prop = function(type, stream, state) {
207 | if (type == ";") return popContext(state);
208 | if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
209 | if (type == "}" || type == "{") return popAndPass(type, stream, state);
210 | if (type == "(") return pushContext(state, stream, "parens");
211 |
212 | if (type == "hash" && !/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) {
213 | override += " error";
214 | } else if (type == "word") {
215 | wordAsValue(stream);
216 | } else if (type == "interpolation") {
217 | return pushContext(state, stream, "interpolation");
218 | }
219 | return "prop";
220 | };
221 |
222 | states.propBlock = function(type, _stream, state) {
223 | if (type == "}") return popContext(state);
224 | if (type == "word") { override = "property"; return "maybeprop"; }
225 | return state.context.type;
226 | };
227 |
228 | states.parens = function(type, stream, state) {
229 | if (type == "{" || type == "}") return popAndPass(type, stream, state);
230 | if (type == ")") return popContext(state);
231 | if (type == "(") return pushContext(state, stream, "parens");
232 | if (type == "word") wordAsValue(stream);
233 | return "parens";
234 | };
235 |
236 | states.pseudo = function(type, stream, state) {
237 | if (type == "word") {
238 | override = "variable-3";
239 | return state.context.type;
240 | }
241 | return pass(type, stream, state);
242 | };
243 |
244 | states.media = function(type, stream, state) {
245 | if (type == "(") return pushContext(state, stream, "media_parens");
246 | if (type == "}") return popAndPass(type, stream, state);
247 | if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
248 |
249 | if (type == "word") {
250 | var word = stream.current().toLowerCase();
251 | if (word == "only" || word == "not" || word == "and")
252 | override = "keyword";
253 | else if (mediaTypes.hasOwnProperty(word))
254 | override = "attribute";
255 | else if (mediaFeatures.hasOwnProperty(word))
256 | override = "property";
257 | else
258 | override = "error";
259 | }
260 | return state.context.type;
261 | };
262 |
263 | states.media_parens = function(type, stream, state) {
264 | if (type == ")") return popContext(state);
265 | if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
266 | return states.media(type, stream, state);
267 | };
268 |
269 | states.font_face_before = function(type, stream, state) {
270 | if (type == "{")
271 | return pushContext(state, stream, "font_face");
272 | return pass(type, stream, state);
273 | };
274 |
275 | states.font_face = function(type, stream, state) {
276 | if (type == "}") return popContext(state);
277 | if (type == "word") {
278 | if (!fontProperties.hasOwnProperty(stream.current().toLowerCase()))
279 | override = "error";
280 | else
281 | override = "property";
282 | return "maybeprop";
283 | }
284 | return "font_face";
285 | };
286 |
287 | states.keyframes = function(type, stream, state) {
288 | if (type == "word") { override = "variable"; return "keyframes"; }
289 | if (type == "{") return pushContext(state, stream, "top");
290 | return pass(type, stream, state);
291 | };
292 |
293 | states.at = function(type, stream, state) {
294 | if (type == ";") return popContext(state);
295 | if (type == "{" || type == "}") return popAndPass(type, stream, state);
296 | if (type == "word") override = "tag";
297 | else if (type == "hash") override = "builtin";
298 | return "at";
299 | };
300 |
301 | states.interpolation = function(type, stream, state) {
302 | if (type == "}") return popContext(state);
303 | if (type == "{" || type == ";") return popAndPass(type, stream, state);
304 | if (type != "variable") override = "error";
305 | return "interpolation";
306 | };
307 |
308 | return {
309 | startState: function(base) {
310 | return {tokenize: null,
311 | state: "top",
312 | context: new Context("top", base || 0, null)};
313 | },
314 |
315 | token: function(stream, state) {
316 | if (!state.tokenize && stream.eatSpace()) return null;
317 | var style = (state.tokenize || tokenBase)(stream, state);
318 | if (style && typeof style == "object") {
319 | type = style[1];
320 | style = style[0];
321 | }
322 | override = style;
323 | state.state = states[state.state](type, stream, state);
324 | return override;
325 | },
326 |
327 | indent: function(state, textAfter) {
328 | var cx = state.context, ch = textAfter && textAfter.charAt(0);
329 | var indent = cx.indent;
330 | if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
331 | if (cx.prev &&
332 | (ch == "}" && (cx.type == "block" || cx.type == "top" || cx.type == "interpolation" || cx.type == "font_face") ||
333 | ch == ")" && (cx.type == "parens" || cx.type == "media_parens") ||
334 | ch == "{" && (cx.type == "at" || cx.type == "media"))) {
335 | indent = cx.indent - indentUnit;
336 | cx = cx.prev;
337 | }
338 | return indent;
339 | },
340 |
341 | electricChars: "}",
342 | blockCommentStart: "/*",
343 | blockCommentEnd: "*/",
344 | fold: "brace"
345 | };
346 | });
347 |
348 | function keySet(array) {
349 | var keys = {};
350 | for (var i = 0; i < array.length; ++i) {
351 | keys[array[i]] = true;
352 | }
353 | return keys;
354 | }
355 |
356 | var mediaTypes_ = [
357 | "all", "aural", "braille", "handheld", "print", "projection", "screen",
358 | "tty", "tv", "embossed"
359 | ], mediaTypes = keySet(mediaTypes_);
360 |
361 | var mediaFeatures_ = [
362 | "width", "min-width", "max-width", "height", "min-height", "max-height",
363 | "device-width", "min-device-width", "max-device-width", "device-height",
364 | "min-device-height", "max-device-height", "aspect-ratio",
365 | "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio",
366 | "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color",
367 | "max-color", "color-index", "min-color-index", "max-color-index",
368 | "monochrome", "min-monochrome", "max-monochrome", "resolution",
369 | "min-resolution", "max-resolution", "scan", "grid"
370 | ], mediaFeatures = keySet(mediaFeatures_);
371 |
372 | var propertyKeywords_ = [
373 | "align-content", "align-items", "align-self", "alignment-adjust",
374 | "alignment-baseline", "anchor-point", "animation", "animation-delay",
375 | "animation-direction", "animation-duration", "animation-fill-mode",
376 | "animation-iteration-count", "animation-name", "animation-play-state",
377 | "animation-timing-function", "appearance", "azimuth", "backface-visibility",
378 | "background", "background-attachment", "background-clip", "background-color",
379 | "background-image", "background-origin", "background-position",
380 | "background-repeat", "background-size", "baseline-shift", "binding",
381 | "bleed", "bookmark-label", "bookmark-level", "bookmark-state",
382 | "bookmark-target", "border", "border-bottom", "border-bottom-color",
383 | "border-bottom-left-radius", "border-bottom-right-radius",
384 | "border-bottom-style", "border-bottom-width", "border-collapse",
385 | "border-color", "border-image", "border-image-outset",
386 | "border-image-repeat", "border-image-slice", "border-image-source",
387 | "border-image-width", "border-left", "border-left-color",
388 | "border-left-style", "border-left-width", "border-radius", "border-right",
389 | "border-right-color", "border-right-style", "border-right-width",
390 | "border-spacing", "border-style", "border-top", "border-top-color",
391 | "border-top-left-radius", "border-top-right-radius", "border-top-style",
392 | "border-top-width", "border-width", "bottom", "box-decoration-break",
393 | "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
394 | "caption-side", "clear", "clip", "color", "color-profile", "column-count",
395 | "column-fill", "column-gap", "column-rule", "column-rule-color",
396 | "column-rule-style", "column-rule-width", "column-span", "column-width",
397 | "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
398 | "cue-after", "cue-before", "cursor", "direction", "display",
399 | "dominant-baseline", "drop-initial-after-adjust",
400 | "drop-initial-after-align", "drop-initial-before-adjust",
401 | "drop-initial-before-align", "drop-initial-size", "drop-initial-value",
402 | "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis",
403 | "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap",
404 | "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings",
405 | "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust",
406 | "font-stretch", "font-style", "font-synthesis", "font-variant",
407 | "font-variant-alternates", "font-variant-caps", "font-variant-east-asian",
408 | "font-variant-ligatures", "font-variant-numeric", "font-variant-position",
409 | "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow",
410 | "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end",
411 | "grid-column-start", "grid-row", "grid-row-end", "grid-row-start",
412 | "grid-template", "grid-template-areas", "grid-template-columns",
413 | "grid-template-rows", "hanging-punctuation", "height", "hyphens",
414 | "icon", "image-orientation", "image-rendering", "image-resolution",
415 | "inline-box-align", "justify-content", "left", "letter-spacing",
416 | "line-break", "line-height", "line-stacking", "line-stacking-ruby",
417 | "line-stacking-shift", "line-stacking-strategy", "list-style",
418 | "list-style-image", "list-style-position", "list-style-type", "margin",
419 | "margin-bottom", "margin-left", "margin-right", "margin-top",
420 | "marker-offset", "marks", "marquee-direction", "marquee-loop",
421 | "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
422 | "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
423 | "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
424 | "opacity", "order", "orphans", "outline",
425 | "outline-color", "outline-offset", "outline-style", "outline-width",
426 | "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y",
427 | "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
428 | "page", "page-break-after", "page-break-before", "page-break-inside",
429 | "page-policy", "pause", "pause-after", "pause-before", "perspective",
430 | "perspective-origin", "pitch", "pitch-range", "play-during", "position",
431 | "presentation-level", "punctuation-trim", "quotes", "region-break-after",
432 | "region-break-before", "region-break-inside", "region-fragment",
433 | "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
434 | "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang",
435 | "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin",
436 | "shape-outside", "size", "speak", "speak-as", "speak-header",
437 | "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set",
438 | "tab-size", "table-layout", "target", "target-name", "target-new",
439 | "target-position", "text-align", "text-align-last", "text-decoration",
440 | "text-decoration-color", "text-decoration-line", "text-decoration-skip",
441 | "text-decoration-style", "text-emphasis", "text-emphasis-color",
442 | "text-emphasis-position", "text-emphasis-style", "text-height",
443 | "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow",
444 | "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position",
445 | "text-wrap", "top", "transform", "transform-origin", "transform-style",
446 | "transition", "transition-delay", "transition-duration",
447 | "transition-property", "transition-timing-function", "unicode-bidi",
448 | "vertical-align", "visibility", "voice-balance", "voice-duration",
449 | "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
450 | "voice-volume", "volume", "white-space", "widows", "width", "word-break",
451 | "word-spacing", "word-wrap", "z-index",
452 | // SVG-specific
453 | "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
454 | "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events",
455 | "color-interpolation", "color-interpolation-filters",
456 | "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering",
457 | "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke",
458 | "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin",
459 | "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering",
460 | "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal",
461 | "glyph-orientation-vertical", "text-anchor", "writing-mode"
462 | ], propertyKeywords = keySet(propertyKeywords_);
463 |
464 | var nonStandardPropertyKeywords_ = [
465 | "scrollbar-arrow-color", "scrollbar-base-color", "scrollbar-dark-shadow-color",
466 | "scrollbar-face-color", "scrollbar-highlight-color", "scrollbar-shadow-color",
467 | "scrollbar-3d-light-color", "scrollbar-track-color", "shape-inside",
468 | "searchfield-cancel-button", "searchfield-decoration", "searchfield-results-button",
469 | "searchfield-results-decoration", "zoom"
470 | ], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
471 |
472 | var colorKeywords_ = [
473 | "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
474 | "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
475 | "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue",
476 | "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod",
477 | "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen",
478 | "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
479 | "darkslateblue", "darkslategray", "darkturquoise", "darkviolet",
480 | "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick",
481 | "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
482 | "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew",
483 | "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender",
484 | "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral",
485 | "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink",
486 | "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray",
487 | "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta",
488 | "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple",
489 | "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
490 | "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
491 | "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered",
492 | "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred",
493 | "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
494 | "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown",
495 | "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue",
496 | "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan",
497 | "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white",
498 | "whitesmoke", "yellow", "yellowgreen"
499 | ], colorKeywords = keySet(colorKeywords_);
500 |
501 | var valueKeywords_ = [
502 | "above", "absolute", "activeborder", "activecaption", "afar",
503 | "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate",
504 | "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
505 | "arabic-indic", "armenian", "asterisks", "auto", "avoid", "avoid-column", "avoid-page",
506 | "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
507 | "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
508 | "both", "bottom", "break", "break-all", "break-word", "button", "button-bevel",
509 | "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian",
510 | "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret",
511 | "cell", "center", "checkbox", "circle", "cjk-earthly-branch",
512 | "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
513 | "col-resize", "collapse", "column", "compact", "condensed", "contain", "content",
514 | "content-box", "context-menu", "continuous", "copy", "cover", "crop",
515 | "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal",
516 | "decimal-leading-zero", "default", "default-button", "destination-atop",
517 | "destination-in", "destination-out", "destination-over", "devanagari",
518 | "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted",
519 | "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out",
520 | "element", "ellipse", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede",
521 | "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er",
522 | "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er",
523 | "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et",
524 | "ethiopic-halehame-gez", "ethiopic-halehame-om-et",
525 | "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et",
526 | "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et",
527 | "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed",
528 | "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes",
529 | "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove",
530 | "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew",
531 | "help", "hidden", "hide", "higher", "highlight", "highlighttext",
532 | "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore",
533 | "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
534 | "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
535 | "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert",
536 | "italic", "justify", "kannada", "katakana", "katakana-iroha", "keep-all", "khmer",
537 | "landscape", "lao", "large", "larger", "left", "level", "lighter",
538 | "line-through", "linear", "lines", "list-item", "listbox", "listitem",
539 | "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian",
540 | "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian",
541 | "lower-roman", "lowercase", "ltr", "malayalam", "match",
542 | "media-controls-background", "media-current-time-display",
543 | "media-fullscreen-button", "media-mute-button", "media-play-button",
544 | "media-return-to-realtime-button", "media-rewind-button",
545 | "media-seek-back-button", "media-seek-forward-button", "media-slider",
546 | "media-sliderthumb", "media-time-remaining-display", "media-volume-slider",
547 | "media-volume-slider-container", "media-volume-sliderthumb", "medium",
548 | "menu", "menulist", "menulist-button", "menulist-text",
549 | "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic",
550 | "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize",
551 | "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
552 | "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
553 | "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
554 | "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
555 | "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
556 | "painted", "page", "paused", "persian", "plus-darker", "plus-lighter", "pointer",
557 | "polygon", "portrait", "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button",
558 | "radio", "read-only", "read-write", "read-write-plaintext-only", "rectangle", "region",
559 | "relative", "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba",
560 | "ridge", "right", "round", "row-resize", "rtl", "run-in", "running",
561 | "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield",
562 | "searchfield-cancel-button", "searchfield-decoration",
563 | "searchfield-results-button", "searchfield-results-decoration",
564 | "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
565 | "single", "skip-white-space", "slide", "slider-horizontal",
566 | "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
567 | "small", "small-caps", "small-caption", "smaller", "solid", "somali",
568 | "source-atop", "source-in", "source-out", "source-over", "space", "square",
569 | "square-button", "start", "static", "status-bar", "stretch", "stroke",
570 | "sub", "subpixel-antialiased", "super", "sw-resize", "table",
571 | "table-caption", "table-cell", "table-column", "table-column-group",
572 | "table-footer-group", "table-header-group", "table-row", "table-row-group",
573 | "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai",
574 | "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
575 | "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
576 | "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
577 | "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
578 | "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
579 | "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
580 | "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
581 | "visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
582 | "window", "windowframe", "windowtext", "x-large", "x-small", "xor",
583 | "xx-large", "xx-small"
584 | ], valueKeywords = keySet(valueKeywords_);
585 |
586 | var fontProperties_ = [
587 | "font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
588 | "font-stretch", "font-weight", "font-style"
589 | ], fontProperties = keySet(fontProperties_);
590 |
591 | var allWords = mediaTypes_.concat(mediaFeatures_).concat(propertyKeywords_)
592 | .concat(nonStandardPropertyKeywords_).concat(colorKeywords_).concat(valueKeywords_);
593 | CodeMirror.registerHelper("hintWords", "css", allWords);
594 |
595 | function tokenCComment(stream, state) {
596 | var maybeEnd = false, ch;
597 | while ((ch = stream.next()) != null) {
598 | if (maybeEnd && ch == "/") {
599 | state.tokenize = null;
600 | break;
601 | }
602 | maybeEnd = (ch == "*");
603 | }
604 | return ["comment", "comment"];
605 | }
606 |
607 | function tokenSGMLComment(stream, state) {
608 | if (stream.skipTo("-->")) {
609 | stream.match("-->");
610 | state.tokenize = null;
611 | } else {
612 | stream.skipToEnd();
613 | }
614 | return ["comment", "comment"];
615 | }
616 |
617 | CodeMirror.defineMIME("text/css", {
618 | mediaTypes: mediaTypes,
619 | mediaFeatures: mediaFeatures,
620 | propertyKeywords: propertyKeywords,
621 | nonStandardPropertyKeywords: nonStandardPropertyKeywords,
622 | colorKeywords: colorKeywords,
623 | valueKeywords: valueKeywords,
624 | fontProperties: fontProperties,
625 | tokenHooks: {
626 | "<": function(stream, state) {
627 | if (!stream.match("!--")) return false;
628 | state.tokenize = tokenSGMLComment;
629 | return tokenSGMLComment(stream, state);
630 | },
631 | "/": function(stream, state) {
632 | if (!stream.eat("*")) return false;
633 | state.tokenize = tokenCComment;
634 | return tokenCComment(stream, state);
635 | }
636 | },
637 | name: "css"
638 | });
639 |
640 | CodeMirror.defineMIME("text/x-scss", {
641 | mediaTypes: mediaTypes,
642 | mediaFeatures: mediaFeatures,
643 | propertyKeywords: propertyKeywords,
644 | nonStandardPropertyKeywords: nonStandardPropertyKeywords,
645 | colorKeywords: colorKeywords,
646 | valueKeywords: valueKeywords,
647 | fontProperties: fontProperties,
648 | allowNested: true,
649 | tokenHooks: {
650 | "/": function(stream, state) {
651 | if (stream.eat("/")) {
652 | stream.skipToEnd();
653 | return ["comment", "comment"];
654 | } else if (stream.eat("*")) {
655 | state.tokenize = tokenCComment;
656 | return tokenCComment(stream, state);
657 | } else {
658 | return ["operator", "operator"];
659 | }
660 | },
661 | ":": function(stream) {
662 | if (stream.match(/\s*\{/))
663 | return [null, "{"];
664 | return false;
665 | },
666 | "$": function(stream) {
667 | stream.match(/^[\w-]+/);
668 | if (stream.match(/^\s*:/, false))
669 | return ["variable-2", "variable-definition"];
670 | return ["variable-2", "variable"];
671 | },
672 | "#": function(stream) {
673 | if (!stream.eat("{")) return false;
674 | return [null, "interpolation"];
675 | }
676 | },
677 | name: "css",
678 | helperType: "scss"
679 | });
680 |
681 | CodeMirror.defineMIME("text/x-less", {
682 | mediaTypes: mediaTypes,
683 | mediaFeatures: mediaFeatures,
684 | propertyKeywords: propertyKeywords,
685 | nonStandardPropertyKeywords: nonStandardPropertyKeywords,
686 | colorKeywords: colorKeywords,
687 | valueKeywords: valueKeywords,
688 | fontProperties: fontProperties,
689 | allowNested: true,
690 | tokenHooks: {
691 | "/": function(stream, state) {
692 | if (stream.eat("/")) {
693 | stream.skipToEnd();
694 | return ["comment", "comment"];
695 | } else if (stream.eat("*")) {
696 | state.tokenize = tokenCComment;
697 | return tokenCComment(stream, state);
698 | } else {
699 | return ["operator", "operator"];
700 | }
701 | },
702 | "@": function(stream) {
703 | if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
704 | stream.eatWhile(/[\w\\\-]/);
705 | if (stream.match(/^\s*:/, false))
706 | return ["variable-2", "variable-definition"];
707 | return ["variable-2", "variable"];
708 | },
709 | "&": function() {
710 | return ["atom", "atom"];
711 | }
712 | },
713 | name: "css",
714 | helperType: "less"
715 | });
716 |
717 | });
--------------------------------------------------------------------------------
/lib/filereader.js:
--------------------------------------------------------------------------------
1 | /*!
2 | FileReader.js - v0.99
3 | A lightweight wrapper for common FileReader usage.
4 | Copyright 2014 Brian Grinstead - MIT License.
5 | See http://github.com/bgrins/filereader.js for documentation.
6 | */
7 |
8 | (function(window, document) {
9 |
10 | var FileReader = window.FileReader;
11 | var FileReaderSyncSupport = false;
12 | var workerScript = "self.addEventListener('message', function(e) { var data=e.data; try { var reader = new FileReaderSync; postMessage({ result: reader[data.readAs](data.file), extra: data.extra, file: data.file})} catch(e){ postMessage({ result:'error', extra:data.extra, file:data.file}); } }, false);";
13 | var syncDetectionScript = "onmessage = function(e) { postMessage(!!FileReaderSync); };";
14 | var fileReaderEvents = ['loadstart', 'progress', 'load', 'abort', 'error', 'loadend'];
15 | var sync = false;
16 | var FileReaderJS = window.FileReaderJS = {
17 | enabled: false,
18 | setupInput: setupInput,
19 | setupDrop: setupDrop,
20 | setupClipboard: setupClipboard,
21 | setSync: function (value) {
22 | sync = value;
23 |
24 | if (sync && !FileReaderSyncSupport) {
25 | checkFileReaderSyncSupport();
26 | }
27 | },
28 | getSync: function() {
29 | return sync && FileReaderSyncSupport;
30 | },
31 | output: [],
32 | opts: {
33 | dragClass: "drag",
34 | accept: false,
35 | readAsDefault: 'DataURL',
36 | readAsMap: {
37 | },
38 | on: {
39 | loadstart: noop,
40 | progress: noop,
41 | load: noop,
42 | abort: noop,
43 | error: noop,
44 | loadend: noop,
45 | skip: noop,
46 | groupstart: noop,
47 | groupend: noop,
48 | beforestart: noop
49 | }
50 | }
51 | };
52 |
53 | // Setup jQuery plugin (if available)
54 | if (typeof(jQuery) !== "undefined") {
55 | jQuery.fn.fileReaderJS = function(opts) {
56 | return this.each(function() {
57 | if (jQuery(this).is("input")) {
58 | setupInput(this, opts);
59 | }
60 | else {
61 | setupDrop(this, opts);
62 | }
63 | });
64 | };
65 |
66 | jQuery.fn.fileClipboard = function(opts) {
67 | return this.each(function() {
68 | setupClipboard(this, opts);
69 | });
70 | };
71 | }
72 |
73 | // Not all browsers support the FileReader interface. Return with the enabled bit = false.
74 | if (!FileReader) {
75 | return;
76 | }
77 |
78 |
79 | // makeWorker is a little wrapper for generating web workers from strings
80 | function makeWorker(script) {
81 | var URL = window.URL || window.webkitURL;
82 | var Blob = window.Blob;
83 | var Worker = window.Worker;
84 |
85 | if (!URL || !Blob || !Worker || !script) {
86 | return null;
87 | }
88 |
89 | var blob = new Blob([script]);
90 | var worker = new Worker(URL.createObjectURL(blob));
91 | return worker;
92 | }
93 |
94 | // setupClipboard: bind to clipboard events (intended for document.body)
95 | function setupClipboard(element, opts) {
96 |
97 | if (!FileReaderJS.enabled) {
98 | return;
99 | }
100 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
101 |
102 | element.addEventListener("paste", onpaste, false);
103 |
104 | function onpaste(e) {
105 | var files = [];
106 | var clipboardData = e.clipboardData || {};
107 | var items = clipboardData.items || [];
108 |
109 | for (var i = 0; i < items.length; i++) {
110 | var file = items[i].getAsFile();
111 |
112 | if (file) {
113 |
114 | // Create a fake file name for images from clipboard, since this data doesn't get sent
115 | var matches = new RegExp("/\(.*\)").exec(file.type);
116 | if (!file.name && matches) {
117 | var extension = matches[1];
118 | file.name = "clipboard" + i + "." + extension;
119 | }
120 |
121 | files.push(file);
122 | }
123 | }
124 |
125 | if (files.length) {
126 | processFileList(e, files, instanceOptions);
127 | e.preventDefault();
128 | e.stopPropagation();
129 | }
130 | }
131 | }
132 |
133 | // setupInput: bind the 'change' event to an input[type=file]
134 | function setupInput(input, opts) {
135 |
136 | if (!FileReaderJS.enabled) {
137 | return;
138 | }
139 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
140 |
141 | input.addEventListener("change", inputChange, false);
142 | input.addEventListener("drop", inputDrop, false);
143 |
144 | function inputChange(e) {
145 | processFileList(e, input.files, instanceOptions);
146 | }
147 |
148 | function inputDrop(e) {
149 | e.stopPropagation();
150 | e.preventDefault();
151 | processFileList(e, e.dataTransfer.files, instanceOptions);
152 | }
153 | }
154 |
155 | // setupDrop: bind the 'drop' event for a DOM element
156 | function setupDrop(dropbox, opts) {
157 |
158 | if (!FileReaderJS.enabled) {
159 | return;
160 | }
161 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
162 | var dragClass = instanceOptions.dragClass;
163 | var initializedOnBody = false;
164 |
165 | // Bind drag events to the dropbox to add the class while dragging, and accept the drop data transfer.
166 | dropbox.addEventListener("dragenter", onlyWithFiles(dragenter), false);
167 | dropbox.addEventListener("dragleave", onlyWithFiles(dragleave), false);
168 | dropbox.addEventListener("dragover", onlyWithFiles(dragover), false);
169 | dropbox.addEventListener("drop", onlyWithFiles(drop), false);
170 |
171 | // Bind to body to prevent the dropbox events from firing when it was initialized on the page.
172 | document.body.addEventListener("dragstart", bodydragstart, true);
173 | document.body.addEventListener("dragend", bodydragend, true);
174 | document.body.addEventListener("drop", bodydrop, false);
175 |
176 | function bodydragend(e) {
177 | initializedOnBody = false;
178 | }
179 |
180 | function bodydragstart(e) {
181 | initializedOnBody = true;
182 | }
183 |
184 | function bodydrop(e) {
185 | if (e.dataTransfer.files && e.dataTransfer.files.length ){
186 | e.stopPropagation();
187 | e.preventDefault();
188 | }
189 | }
190 |
191 | function onlyWithFiles(fn) {
192 | return function() {
193 | if (!initializedOnBody) {
194 | fn.apply(this, arguments);
195 | }
196 | };
197 | }
198 |
199 | function drop(e) {
200 | e.stopPropagation();
201 | e.preventDefault();
202 | if (dragClass) {
203 | removeClass(dropbox, dragClass);
204 | }
205 | processFileList(e, e.dataTransfer.files, instanceOptions);
206 | }
207 |
208 | function dragenter(e) {
209 | e.stopPropagation();
210 | e.preventDefault();
211 | if (dragClass) {
212 | addClass(dropbox, dragClass);
213 | }
214 | }
215 |
216 | function dragleave(e) {
217 | if (dragClass) {
218 | removeClass(dropbox, dragClass);
219 | }
220 | }
221 |
222 | function dragover(e) {
223 | e.stopPropagation();
224 | e.preventDefault();
225 | if (dragClass) {
226 | addClass(dropbox, dragClass);
227 | }
228 | }
229 | }
230 |
231 | // setupCustomFileProperties: modify the file object with extra properties
232 | function setupCustomFileProperties(files, groupID) {
233 | for (var i = 0; i < files.length; i++) {
234 | var file = files[i];
235 | file.extra = {
236 | nameNoExtension: file.name.substring(0, file.name.lastIndexOf('.')),
237 | extension: file.name.substring(file.name.lastIndexOf('.') + 1),
238 | fileID: i,
239 | uniqueID: getUniqueID(),
240 | groupID: groupID,
241 | prettySize: prettySize(file.size)
242 | };
243 | }
244 | }
245 |
246 | // getReadAsMethod: return method name for 'readAs*' - http://www.w3.org/TR/FileAPI/#reading-a-file
247 | function getReadAsMethod(type, readAsMap, readAsDefault) {
248 | for (var r in readAsMap) {
249 | if (type.match(new RegExp(r))) {
250 | return 'readAs' + readAsMap[r];
251 | }
252 | }
253 |
254 | return 'readAs' + readAsDefault;
255 | }
256 |
257 | // processFileList: read the files with FileReader, send off custom events.
258 | function processFileList(e, files, opts) {
259 |
260 | var filesLeft = files.length;
261 | var group = {
262 | groupID: getGroupID(),
263 | files: files,
264 | started: new Date()
265 | };
266 |
267 | function groupEnd() {
268 | group.ended = new Date();
269 | opts.on.groupend(group);
270 | }
271 |
272 | function groupFileDone() {
273 | if (--filesLeft === 0) {
274 | groupEnd();
275 | }
276 | }
277 |
278 | FileReaderJS.output.push(group);
279 | setupCustomFileProperties(files, group.groupID);
280 |
281 | opts.on.groupstart(group);
282 |
283 | // No files in group - end immediately
284 | if (!files.length) {
285 | groupEnd();
286 | return;
287 | }
288 |
289 | var supportsSync = sync && FileReaderSyncSupport;
290 | var syncWorker;
291 |
292 | // Only initialize the synchronous worker if the option is enabled - to prevent the overhead
293 | if (supportsSync) {
294 | syncWorker = makeWorker(workerScript);
295 | syncWorker.onmessage = function(e) {
296 | var file = e.data.file;
297 | var result = e.data.result;
298 |
299 | // Workers seem to lose the custom property on the file object.
300 | if (!file.extra) {
301 | file.extra = e.data.extra;
302 | }
303 |
304 | file.extra.ended = new Date();
305 |
306 | // Call error or load event depending on success of the read from the worker.
307 | opts.on[result === "error" ? "error" : "load"]({ target: { result: result } }, file);
308 | groupFileDone();
309 | };
310 | }
311 |
312 | Array.prototype.forEach.call(files, function(file) {
313 |
314 | file.extra.started = new Date();
315 |
316 | if (opts.accept && !file.type.match(new RegExp(opts.accept))) {
317 | opts.on.skip(file);
318 | groupFileDone();
319 | return;
320 | }
321 |
322 | if (opts.on.beforestart(file) === false) {
323 | opts.on.skip(file);
324 | groupFileDone();
325 | return;
326 | }
327 |
328 | var readAs = getReadAsMethod(file.type, opts.readAsMap, opts.readAsDefault);
329 |
330 | if (syncWorker) {
331 | syncWorker.postMessage({
332 | file: file,
333 | extra: file.extra,
334 | readAs: readAs
335 | });
336 | }
337 | else {
338 |
339 | var reader = new FileReader();
340 | reader.originalEvent = e;
341 |
342 | fileReaderEvents.forEach(function(eventName) {
343 | reader['on' + eventName] = function(e) {
344 | if (eventName == 'load' || eventName == 'error') {
345 | file.extra.ended = new Date();
346 | }
347 | opts.on[eventName](e, file);
348 | if (eventName == 'loadend') {
349 | groupFileDone();
350 | }
351 | };
352 | });
353 | reader[readAs](file);
354 | }
355 | });
356 | }
357 |
358 | // checkFileReaderSyncSupport: Create a temporary worker and see if FileReaderSync exists
359 | function checkFileReaderSyncSupport() {
360 | var worker = makeWorker(syncDetectionScript);
361 | if (worker) {
362 | worker.onmessage =function(e) {
363 | FileReaderSyncSupport = e.data;
364 | };
365 | worker.postMessage({});
366 | }
367 | }
368 |
369 | // noop: do nothing
370 | function noop() {
371 |
372 | }
373 |
374 | // extend: used to make deep copies of options object
375 | function extend(destination, source) {
376 | for (var property in source) {
377 | if (source[property] && source[property].constructor &&
378 | source[property].constructor === Object) {
379 | destination[property] = destination[property] || {};
380 | arguments.callee(destination[property], source[property]);
381 | }
382 | else {
383 | destination[property] = source[property];
384 | }
385 | }
386 | return destination;
387 | }
388 |
389 | // hasClass: does an element have the css class?
390 | function hasClass(el, name) {
391 | return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(el.className);
392 | }
393 |
394 | // addClass: add the css class for the element.
395 | function addClass(el, name) {
396 | if (!hasClass(el, name)) {
397 | el.className = el.className ? [el.className, name].join(' ') : name;
398 | }
399 | }
400 |
401 | // removeClass: remove the css class from the element.
402 | function removeClass(el, name) {
403 | if (hasClass(el, name)) {
404 | var c = el.className;
405 | el.className = c.replace(new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
406 | }
407 | }
408 |
409 | // prettySize: convert bytes to a more readable string.
410 | function prettySize(bytes) {
411 | var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB'];
412 | var e = Math.floor(Math.log(bytes)/Math.log(1024));
413 | return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
414 | }
415 |
416 | // getGroupID: generate a unique int ID for groups.
417 | var getGroupID = (function(id) {
418 | return function() {
419 | return id++;
420 | };
421 | })(0);
422 |
423 | // getUniqueID: generate a unique int ID for files
424 | var getUniqueID = (function(id) {
425 | return function() {
426 | return id++;
427 | };
428 | })(0);
429 |
430 | // The interface is supported, bind the FileReaderJS callbacks
431 | FileReaderJS.enabled = true;
432 |
433 | })(this, document);
--------------------------------------------------------------------------------
/lib/inflection.js:
--------------------------------------------------------------------------------
1 | var InflectionJS = (function() {
2 |
3 | /*
4 | This is a list of nouns that use the same form for both singular and plural.
5 | This list should remain entirely in lower case to correctly match Strings.
6 | */
7 | var uncountable_words = [
8 | 'equipment', 'information', 'rice', 'money', 'species', 'series',
9 | 'fish', 'sheep', 'moose', 'deer', 'news'
10 | ];
11 |
12 | /*
13 | These rules translate from the singular form of a noun to its plural form.
14 | */
15 | var plural_rules = [
16 | [new RegExp('(m)an$', 'gi'), '$1en'],
17 | [new RegExp('(pe)rson$', 'gi'), '$1ople'],
18 | [new RegExp('(child)$', 'gi'), '$1ren'],
19 | [new RegExp('^(ox)$', 'gi'), '$1en'],
20 | [new RegExp('(ax|test)is$', 'gi'), '$1es'],
21 | [new RegExp('(octop|vir)us$', 'gi'), '$1i'],
22 | [new RegExp('(alias|status)$', 'gi'), '$1es'],
23 | [new RegExp('(bu)s$', 'gi'), '$1ses'],
24 | [new RegExp('(buffal|tomat|potat)o$', 'gi'), '$1oes'],
25 | [new RegExp('([ti])um$', 'gi'), '$1a'],
26 | [new RegExp('sis$', 'gi'), 'ses'],
27 | [new RegExp('(?:([^f])fe|([lr])f)$', 'gi'), '$1$2ves'],
28 | [new RegExp('(hive)$', 'gi'), '$1s'],
29 | [new RegExp('([^aeiouy]|qu)y$', 'gi'), '$1ies'],
30 | [new RegExp('(x|ch|ss|sh)$', 'gi'), '$1es'],
31 | [new RegExp('(matr|vert|ind)ix|ex$', 'gi'), '$1ices'],
32 | [new RegExp('([m|l])ouse$', 'gi'), '$1ice'],
33 | [new RegExp('(quiz)$', 'gi'), '$1zes'],
34 | [new RegExp('s$', 'gi'), 's'],
35 | [new RegExp('$', 'gi'), 's']
36 | ];
37 |
38 | /*
39 | These rules translate from the plural form of a noun to its singular form.
40 | */
41 | var singular_rules = [
42 | [new RegExp('(m)en$', 'gi'), '$1an'],
43 | [new RegExp('(pe)ople$', 'gi'), '$1rson'],
44 | [new RegExp('(child)ren$', 'gi'), '$1'],
45 | [new RegExp('([ti])a$', 'gi'), '$1um'],
46 | [new RegExp('((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$', 'gi'), '$1$2sis'],
47 | [new RegExp('(hive)s$', 'gi'), '$1'],
48 | [new RegExp('(tive)s$', 'gi'), '$1'],
49 | [new RegExp('(curve)s$', 'gi'), '$1'],
50 | [new RegExp('([lr])ves$', 'gi'), '$1f'],
51 | [new RegExp('([^fo])ves$', 'gi'), '$1fe'],
52 | [new RegExp('([^aeiouy]|qu)ies$', 'gi'), '$1y'],
53 | [new RegExp('(s)eries$', 'gi'), '$1eries'],
54 | [new RegExp('(m)ovies$', 'gi'), '$1ovie'],
55 | [new RegExp('(x|ch|ss|sh)es$', 'gi'), '$1'],
56 | [new RegExp('([m|l])ice$', 'gi'), '$1ouse'],
57 | [new RegExp('(bus)es$', 'gi'), '$1'],
58 | [new RegExp('(o)es$', 'gi'), '$1'],
59 | [new RegExp('(shoe)s$', 'gi'), '$1'],
60 | [new RegExp('(cris|ax|test)es$', 'gi'), '$1is'],
61 | [new RegExp('(octop|vir)i$', 'gi'), '$1us'],
62 | [new RegExp('(alias|status)es$', 'gi'), '$1'],
63 | [new RegExp('^(ox)en', 'gi'), '$1'],
64 | [new RegExp('(vert|ind)ices$', 'gi'), '$1ex'],
65 | [new RegExp('(matr)ices$', 'gi'), '$1ix'],
66 | [new RegExp('(quiz)zes$', 'gi'), '$1'],
67 | [new RegExp('s$', 'gi'), '']
68 | ];
69 |
70 | /*
71 | This is a list of words that should not be capitalized for title case
72 | */
73 | var non_titlecased_words = [
74 | 'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
75 | 'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
76 | 'with', 'for'
77 | ];
78 |
79 | /*
80 | These are regular expressions used for converting between String formats
81 | */
82 | var id_suffix = new RegExp('(_ids|_id)$', 'g');
83 | var underbar = new RegExp('_', 'g');
84 | var space_or_underbar = new RegExp('[\ _]', 'g');
85 | var uppercase = new RegExp('([A-Z])', 'g');
86 | var underbar_prefix = new RegExp('^_');
87 |
88 | /*
89 | This is a helper method that applies rules based replacement to a String
90 | Signature:
91 | apply_rules(str, rules, skip, override) == String
92 | Arguments:
93 | str - String - String to modify and return based on the passed rules
94 | rules - Array: [RegExp, String] - Regexp to match paired with String to use for replacement
95 | skip - Array: [String] - Strings to skip if they match
96 | override - String (optional) - String to return as though this method succeeded (used to conform to APIs)
97 | Returns:
98 | String - passed String modified by passed rules
99 | Examples:
100 | apply_rules("cows", singular_rules) === 'cow'
101 | */
102 | var apply_rules = function(str, rules, skip, override) {
103 | if (override) {
104 | str = override;
105 | } else {
106 | var ignore = (skip.indexOf(str.toLowerCase()) > -1);
107 | if (!ignore) {
108 | for (var x = 0; x < rules.length; x++) {
109 | if (str.match(rules[x][0])) {
110 | str = str.replace(rules[x][0], rules[x][1]);
111 | break;
112 | }
113 | }
114 | }
115 | }
116 | return str;
117 | };
118 |
119 |
120 | /*
121 | This lets us detect if an Array contains a given element in IE < 9
122 | Signature:
123 | Array.indexOf(item, fromIndex, compareFunc) == Integer
124 | Arguments:
125 | item - Object - object to locate in the Array
126 | fromIndex - Integer (optional) - starts checking from this position in the Array
127 | compareFunc - Function (optional) - function used to compare Array item vs passed item
128 | Returns:
129 | Integer - index position in the Array of the passed item
130 | Examples:
131 | ['hi','there'].indexOf("guys") === -1
132 | ['hi','there'].indexOf("hi") === 0
133 | */
134 | if (!Array.prototype.indexOf) {
135 | Array.prototype.indexOf = function(item, fromIndex, compareFunc) {
136 | if (!fromIndex) {
137 | fromIndex = -1;
138 | }
139 | var index = -1;
140 | for (var i = fromIndex; i < this.length; i++) {
141 | if (this[i] === item || compareFunc && compareFunc(this[i], item)) {
142 | index = i;
143 | break;
144 | }
145 | }
146 | return index;
147 | };
148 | }
149 |
150 | /*
151 | This function adds plurilization support to every String object
152 | Signature:
153 | String.pluralize(plural) == String
154 | Arguments:
155 | plural - String (optional) - overrides normal output with said String
156 | Returns:
157 | String - singular English language nouns are returned in plural form
158 | Examples:
159 | "person".pluralize() == "people"
160 | "octopus".pluralize() == "octopi"
161 | "Hat".pluralize() == "Hats"
162 | "person".pluralize("guys") == "guys"
163 | */
164 | var pluralize = function(string, plural) {
165 | return apply_rules(string, plural_rules, uncountable_words, plural);
166 | };
167 |
168 | /*
169 | This function adds singularization support to every String object
170 | Signature:
171 | String.singularize(singular) == String
172 | Arguments:
173 | singular - String (optional) - overrides normal output with said String
174 | Returns:
175 | String - plural English language nouns are returned in singular form
176 | Examples:
177 | "people".singularize() == "person"
178 | "octopi".singularize() == "octopus"
179 | "Hats".singularize() == "Hat"
180 | "guys".singularize("person") == "person"
181 | */
182 | var singularize = function(string, singular) {
183 | return apply_rules(string, singular_rules, uncountable_words, singular);
184 | };
185 |
186 | /*
187 | This function adds camelization support to every String object
188 | Signature:
189 | String.camelize(lowFirstLetter) == String
190 | Arguments:
191 | lowFirstLetter - boolean (optional) - default is to capitalize the first
192 | letter of the results... passing true will lowercase it
193 | Returns:
194 | String - lower case underscored words will be returned in camel case
195 | additionally '/' is translated to '::'
196 | Examples:
197 | "message_properties".camelize() == "MessageProperties"
198 | "message_properties".camelize(true) == "messageProperties"
199 | */
200 | var camelize = function(string, lowFirstLetter) {
201 | string = string.toLowerCase();
202 | var str_path = string.split('/');
203 | for (var i = 0; i < str_path.length; i++) {
204 | var str_arr = str_path[i].split('_');
205 | var initX = ((lowFirstLetter && i + 1 === str_path.length) ? (1) : (0));
206 | for (var x = initX; x < str_arr.length; x++) {
207 | str_arr[x] = str_arr[x].charAt(0).toUpperCase() + str_arr[x].substring(1);
208 | }
209 | str_path[i] = str_arr.join('');
210 | }
211 | string = str_path.join('::');
212 | return string;
213 | };
214 |
215 | /*
216 | This function adds underscore support to every String object
217 | Signature:
218 | String.underscore() == String
219 | Arguments:
220 | N/A
221 | Returns:
222 | String - camel cased words are returned as lower cased and underscored
223 | additionally '::' is translated to '/'
224 | Examples:
225 | "MessageProperties".camelize() == "message_properties"
226 | "messageProperties".underscore() == "message_properties"
227 | */
228 | var underscore = function(string) {
229 | var str_path = string.split('::');
230 | for (var i = 0; i < str_path.length; i++) {
231 | str_path[i] = str_path[i].replace(uppercase, '_$1');
232 | str_path[i] = str_path[i].replace(underbar_prefix, '');
233 | }
234 | string = str_path.join('/').toLowerCase();
235 | return string;
236 | };
237 |
238 | /*
239 | This function adds humanize support to every String object
240 | Signature:
241 | String.humanize(lowFirstLetter) == String
242 | Arguments:
243 | lowFirstLetter - boolean (optional) - default is to capitalize the first
244 | letter of the results... passing true will lowercase it
245 | Returns:
246 | String - lower case underscored words will be returned in humanized form
247 | Examples:
248 | "message_properties".humanize() == "Message properties"
249 | "message_properties".humanize(true) == "message properties"
250 | */
251 | var humanize = function(string, lowFirstLetter) {
252 | string = string.toLowerCase();
253 | string = string.replace(id_suffix, '');
254 | string = string.replace(underbar, ' ');
255 | if (!lowFirstLetter) {
256 | string = capitalize(string);
257 | }
258 | return string;
259 | };
260 |
261 | /*
262 | This function adds capitalization support to every String object
263 | Signature:
264 | String.capitalize() == String
265 | Arguments:
266 | N/A
267 | Returns:
268 | String - all characters will be lower case and the first will be upper
269 | Examples:
270 | "message_properties".capitalize() == "Message_properties"
271 | "message properties".capitalize() == "Message properties"
272 | */
273 | var capitalize = function(string) {
274 | string = string.toLowerCase();
275 | string = string.substring(0, 1).toUpperCase() + string.substring(1);
276 | return string;
277 | };
278 |
279 | /*
280 | This function adds dasherization support to every String object
281 | Signature:
282 | String.dasherize() == String
283 | Arguments:
284 | N/A
285 | Returns:
286 | String - replaces all spaces or underbars with dashes
287 | Examples:
288 | "message_properties".capitalize() == "message-properties"
289 | "Message Properties".capitalize() == "Message-Properties"
290 | */
291 | var dasherize = function(string) {
292 | string = string.replace(space_or_underbar, '-');
293 | return string;
294 | };
295 |
296 | /*
297 | This function adds titleize support to every String object
298 | Signature:
299 | String.titleize() == String
300 | Arguments:
301 | N/A
302 | Returns:
303 | String - capitalizes words as you would for a book title
304 | Examples:
305 | "message_properties".titleize() == "Message Properties"
306 | "message properties to keep".titleize() == "Message Properties to Keep"
307 | */
308 | var titleize = function(string) {
309 | string = string.toLowerCase();
310 | string = string.replace(underbar, ' ');
311 | var str_arr = string.split(' ');
312 | for (var x = 0; x < str_arr.length; x++) {
313 | var d = str_arr[x].split('-');
314 | for (var i = 0; i < d.length; i++) {
315 | if (non_titlecased_words.indexOf(d[i].toLowerCase()) < 0) {
316 | d[i] = capitalize(d[i]);
317 | }
318 | }
319 | str_arr[x] = d.join('-');
320 | }
321 | string = str_arr.join(' ');
322 | string = string.substring(0, 1).toUpperCase() + string.substring(1);
323 | return string;
324 | };
325 |
326 | /*
327 | This function adds demodulize support to every String object
328 | Signature:
329 | String.demodulize() == String
330 | Arguments:
331 | N/A
332 | Returns:
333 | String - removes module names leaving only class names (Ruby style)
334 | Examples:
335 | "Message::Bus::Properties".demodulize() == "Properties"
336 | */
337 | var demodulize = function(string) {
338 | var str_arr = string.split('::');
339 | string = str_arr[str_arr.length - 1];
340 | return string;
341 | };
342 |
343 | /*
344 | This function adds tableize support to every String object
345 | Signature:
346 | String.tableize() == String
347 | Arguments:
348 | N/A
349 | Returns:
350 | String - renders camel cased words into their underscored plural form
351 | Examples:
352 | "MessageBusProperty".tableize() == "message_bus_properties"
353 | */
354 | var tableize = function(string) {
355 | string = underscore(string);
356 | string = pluralize(string);
357 | return string;
358 | };
359 |
360 | /*
361 | This function adds classification support to every String object
362 | Signature:
363 | String.classify() == String
364 | Arguments:
365 | N/A
366 | Returns:
367 | String - underscored plural nouns become the camel cased singular form
368 | Examples:
369 | "message_bus_properties".classify() == "MessageBusProperty"
370 | */
371 | var classify = function(string) {
372 | string = camelize(string);
373 | string = singularize(string);
374 | return string;
375 | };
376 |
377 | /*
378 | This function adds foreign key support to every String object
379 | Signature:
380 | String.foreign_key(dropIdUbar) == String
381 | Arguments:
382 | dropIdUbar - boolean (optional) - default is to seperate id with an
383 | underbar at the end of the class name, you can pass true to skip it
384 | Returns:
385 | String - camel cased singular class names become underscored with id
386 | Examples:
387 | "MessageBusProperty".foreign_key() == "message_bus_property_id"
388 | "MessageBusProperty".foreign_key(true) == "message_bus_propertyid"
389 | */
390 | var foreign_key = function(string, dropIdUbar) {
391 | string = demodulize(string);
392 | string = underscore(string) + ((dropIdUbar) ? ('') : ('_')) + 'id';
393 | return string;
394 | };
395 |
396 | /*
397 | This function adds ordinalize support to every String object
398 | Signature:
399 | String.ordinalize() == String
400 | Arguments:
401 | N/A
402 | Returns:
403 | String - renders all found numbers their sequence like "22nd"
404 | Examples:
405 | "the 1 pitch".ordinalize() == "the 1st pitch"
406 | */
407 | var ordinalize = function(string) {
408 | var str_arr = string.split(' ');
409 | for (var x = 0; x < str_arr.length; x++) {
410 | var i = parseInt(str_arr[x]);
411 | if (!isNaN(i)) {
412 | var ltd = str_arr[x].substring(str_arr[x].length - 2);
413 | var ld = str_arr[x].substring(str_arr[x].length - 1);
414 | var suf = "th";
415 | if (ltd != "11" && ltd != "12" && ltd != "13") {
416 | if (ld === "1") {
417 | suf = "st";
418 | }
419 | else if (ld === "2") {
420 | suf = "nd";
421 | }
422 | else if (ld === "3") {
423 | suf = "rd";
424 | }
425 | }
426 | str_arr[x] += suf;
427 | }
428 | }
429 | string = str_arr.join(' ');
430 | return string;
431 | };
432 |
433 | return {
434 | pluralize: pluralize,
435 | singularize: singularize,
436 | camelize: camelize,
437 | underscore: underscore,
438 | humanize: humanize,
439 | capitalize: capitalize,
440 | dasherize: dasherize,
441 | titleize: titleize,
442 | demodulize: demodulize,
443 | tableize: tableize,
444 | classify: classify,
445 | foreign_key: foreign_key,
446 | ordinalize: ordinalize
447 | };
448 |
449 | })();
--------------------------------------------------------------------------------
/lib/jsbeautify/beautify-css.js:
--------------------------------------------------------------------------------
1 | /*jshint curly:true, eqeqeq:true, laxbreak:true, noempty:false */
2 | /*
3 |
4 | The MIT License (MIT)
5 |
6 | Copyright (c) 2007-2013 Einar Lielmanis and contributors.
7 |
8 | Permission is hereby granted, free of charge, to any person
9 | obtaining a copy of this software and associated documentation files
10 | (the "Software"), to deal in the Software without restriction,
11 | including without limitation the rights to use, copy, modify, merge,
12 | publish, distribute, sublicense, and/or sell copies of the Software,
13 | and to permit persons to whom the Software is furnished to do so,
14 | subject to the following conditions:
15 |
16 | The above copyright notice and this permission notice shall be
17 | included in all copies or substantial portions of the Software.
18 |
19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
23 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
24 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 | SOFTWARE.
27 |
28 |
29 | CSS Beautifier
30 | ---------------
31 |
32 | Written by Harutyun Amirjanyan, (amirjanyan@gmail.com)
33 |
34 | Based on code initially developed by: Einar Lielmanis,
35 | http://jsbeautifier.org/
36 |
37 | Usage:
38 | css_beautify(source_text);
39 | css_beautify(source_text, options);
40 |
41 | The options are (default in brackets):
42 | indent_size (4) — indentation size,
43 | indent_char (space) — character to indent with,
44 | selector_separator_newline (true) - separate selectors with newline or
45 | not (e.g. "a,\nbr" or "a, br")
46 | end_with_newline (false) - end with a newline
47 |
48 | e.g
49 |
50 | css_beautify(css_source_text, {
51 | 'indent_size': 1,
52 | 'indent_char': '\t',
53 | 'selector_separator': ' ',
54 | 'end_with_newline': false,
55 | });
56 | */
57 |
58 | // http://www.w3.org/TR/CSS21/syndata.html#tokenization
59 | // http://www.w3.org/TR/css3-syntax/
60 |
61 | (function() {
62 | function css_beautify(source_text, options) {
63 | options = options || {};
64 | var indentSize = options.indent_size || 4;
65 | var indentCharacter = options.indent_char || ' ';
66 | var selectorSeparatorNewline = (options.selector_separator_newline === undefined) ? true : options.selector_separator_newline;
67 | var end_with_newline = (options.end_with_newline === undefined) ? false : options.end_with_newline;
68 |
69 | // compatibility
70 | if (typeof indentSize === "string") {
71 | indentSize = parseInt(indentSize, 10);
72 | }
73 |
74 |
75 | // tokenizer
76 | var whiteRe = /^\s+$/;
77 | var wordRe = /[\w$\-_]/;
78 |
79 | var pos = -1,
80 | ch;
81 |
82 | function next() {
83 | ch = source_text.charAt(++pos);
84 | return ch || '';
85 | }
86 |
87 | function peek(skipWhitespace) {
88 | var prev_pos = pos;
89 | if (skipWhitespace) {
90 | eatWhitespace();
91 | }
92 | result = source_text.charAt(pos + 1) || '';
93 | pos = prev_pos - 1;
94 | next();
95 | return result;
96 | }
97 |
98 | function eatString(endChars) {
99 | var start = pos;
100 | while (next()) {
101 | if (ch === "\\") {
102 | next();
103 | } else if (endChars.indexOf(ch) !== -1) {
104 | break;
105 | } else if (ch === "\n") {
106 | break;
107 | }
108 | }
109 | return source_text.substring(start, pos + 1);
110 | }
111 |
112 | function peekString(endChar) {
113 | var prev_pos = pos;
114 | var str = eatString(endChar);
115 | pos = prev_pos - 1;
116 | next();
117 | return str;
118 | }
119 |
120 | function eatWhitespace() {
121 | var result = '';
122 | while (whiteRe.test(peek())) {
123 | next()
124 | result += ch;
125 | }
126 | return result;
127 | }
128 |
129 | function skipWhitespace() {
130 | var result = '';
131 | if (ch && whiteRe.test(ch)) {
132 | result = ch;
133 | }
134 | while (whiteRe.test(next())) {
135 | result += ch
136 | }
137 | return result;
138 | }
139 |
140 | function eatComment(singleLine) {
141 | var start = pos;
142 | var singleLine = peek() === "/";
143 | next();
144 | while (next()) {
145 | if (!singleLine && ch === "*" && peek() === "/") {
146 | next();
147 | break;
148 | } else if (singleLine && ch === "\n") {
149 | return source_text.substring(start, pos);
150 | }
151 | }
152 |
153 | return source_text.substring(start, pos) + ch;
154 | }
155 |
156 |
157 | function lookBack(str) {
158 | return source_text.substring(pos - str.length, pos).toLowerCase() ===
159 | str;
160 | }
161 |
162 | // Nested pseudo-class if we are insideRule
163 | // and the next special character found opens
164 | // a new block
165 | function foundNestedPseudoClass() {
166 | for (var i = pos + 1; i < source_text.length; i++){
167 | var ch = source_text.charAt(i);
168 | if (ch === "{"){
169 | return true;
170 | } else if (ch === ";" || ch === "}" || ch === ")") {
171 | return false;
172 | }
173 | }
174 | return false;
175 | }
176 |
177 | // printer
178 | var basebaseIndentString = source_text.match(/^[\t ]*/)[0];
179 | var singleIndent = new Array(indentSize + 1).join(indentCharacter);
180 | var indentLevel = 0;
181 | var nestedLevel = 0;
182 |
183 | function indent() {
184 | indentLevel++;
185 | basebaseIndentString += singleIndent;
186 | }
187 |
188 | function outdent() {
189 | indentLevel--;
190 | basebaseIndentString = basebaseIndentString.slice(0, -indentSize);
191 | }
192 |
193 | var print = {};
194 | print["{"] = function(ch) {
195 | print.singleSpace();
196 | output.push(ch);
197 | print.newLine();
198 | };
199 | print["}"] = function(ch) {
200 | print.newLine();
201 | output.push(ch);
202 | print.newLine();
203 | };
204 |
205 | print._lastCharWhitespace = function() {
206 | return whiteRe.test(output[output.length - 1]);
207 | };
208 |
209 | print.newLine = function(keepWhitespace) {
210 | if (!keepWhitespace) {
211 | print.trim();
212 | }
213 |
214 | if (output.length) {
215 | output.push('\n');
216 | }
217 | if (basebaseIndentString) {
218 | output.push(basebaseIndentString);
219 | }
220 | };
221 | print.singleSpace = function() {
222 | if (output.length && !print._lastCharWhitespace()) {
223 | output.push(' ');
224 | }
225 | };
226 |
227 | print.trim = function() {
228 | while (print._lastCharWhitespace()) {
229 | output.pop();
230 | }
231 | };
232 |
233 |
234 | var output = [];
235 | if (basebaseIndentString) {
236 | output.push(basebaseIndentString);
237 | }
238 | /*_____________________--------------------_____________________*/
239 |
240 | var insideRule = false;
241 | var enteringConditionalGroup = false;
242 | var top_ch = '';
243 | var last_top_ch = '';
244 |
245 | while (true) {
246 | var whitespace = skipWhitespace();
247 | var isAfterSpace = whitespace !== '';
248 | var isAfterNewline = whitespace.indexOf('\n') !== -1;
249 | var last_top_ch = top_ch;
250 | var top_ch = ch;
251 |
252 | if (!ch) {
253 | break;
254 | } else if (ch === '/' && peek() === '*') { /* css comment */
255 | var header = lookBack("");
256 | print.newLine();
257 | output.push(eatComment());
258 | print.newLine();
259 | if (header) {
260 | print.newLine(true);
261 | }
262 | } else if (ch === '/' && peek() === '/') { // single line comment
263 | if (!isAfterNewline && last_top_ch !== '{') {
264 | print.trim();
265 | }
266 | print.singleSpace();
267 | output.push(eatComment());
268 | print.newLine();
269 | } else if (ch === '@') {
270 | // pass along the space we found as a separate item
271 | if (isAfterSpace) {
272 | print.singleSpace();
273 | }
274 | output.push(ch);
275 |
276 | // strip trailing space, if present, for hash property checks
277 | var variableOrRule = peekString(": ,;{}()[]/='\"").replace(/\s$/, '');
278 |
279 | // might be a nesting at-rule
280 | if (variableOrRule in css_beautify.NESTED_AT_RULE) {
281 | nestedLevel += 1;
282 | if (variableOrRule in css_beautify.CONDITIONAL_GROUP_RULE) {
283 | enteringConditionalGroup = true;
284 | }
285 | } else if (': '.indexOf(variableOrRule[variableOrRule.length -1]) >= 0) {
286 | //we have a variable, add it and insert one space before continuing
287 | next();
288 | variableOrRule = eatString(": ").replace(/\s$/, '');
289 | output.push(variableOrRule);
290 | print.singleSpace();
291 | }
292 | } else if (ch === '{') {
293 | if (peek(true) === '}') {
294 | eatWhitespace();
295 | next();
296 | print.singleSpace();
297 | output.push("{}");
298 | } else {
299 | indent();
300 | print["{"](ch);
301 | // when entering conditional groups, only rulesets are allowed
302 | if (enteringConditionalGroup) {
303 | enteringConditionalGroup = false;
304 | insideRule = (indentLevel > nestedLevel);
305 | } else {
306 | // otherwise, declarations are also allowed
307 | insideRule = (indentLevel >= nestedLevel);
308 | }
309 | }
310 | } else if (ch === '}') {
311 | outdent();
312 | print["}"](ch);
313 | insideRule = false;
314 | if (nestedLevel) {
315 | nestedLevel--;
316 | }
317 | } else if (ch === ":") {
318 | eatWhitespace();
319 | if ((insideRule || enteringConditionalGroup) &&
320 | !(lookBack("&") || foundNestedPseudoClass())) {
321 | // 'property: value' delimiter
322 | // which could be in a conditional group query
323 | output.push(':');
324 | print.singleSpace();
325 | } else {
326 | // sass/less parent reference don't use a space
327 | // sass nested pseudo-class don't use a space
328 | if (peek() === ":") {
329 | // pseudo-element
330 | next();
331 | output.push("::");
332 | } else {
333 | // pseudo-class
334 | output.push(':');
335 | }
336 | }
337 | } else if (ch === '"' || ch === '\'') {
338 | if (isAfterSpace) {
339 | print.singleSpace();
340 | }
341 | output.push(eatString(ch));
342 | } else if (ch === ';') {
343 | output.push(ch);
344 | print.newLine();
345 | } else if (ch === '(') { // may be a url
346 | if (lookBack("url")) {
347 | output.push(ch);
348 | eatWhitespace();
349 | if (next()) {
350 | if (ch !== ')' && ch !== '"' && ch !== '\'') {
351 | output.push(eatString(')'));
352 | } else {
353 | pos--;
354 | }
355 | }
356 | } else {
357 | if (isAfterSpace) {
358 | print.singleSpace();
359 | }
360 | output.push(ch);
361 | eatWhitespace();
362 | }
363 | } else if (ch === ')') {
364 | output.push(ch);
365 | } else if (ch === ',') {
366 | output.push(ch);
367 | eatWhitespace();
368 | if (!insideRule && selectorSeparatorNewline) {
369 | print.newLine();
370 | } else {
371 | print.singleSpace();
372 | }
373 | } else if (ch === ']') {
374 | output.push(ch);
375 | } else if (ch === '[') {
376 | if (isAfterSpace) {
377 | print.singleSpace();
378 | }
379 | output.push(ch);
380 | } else if (ch === '=') { // no whitespace before or after
381 | eatWhitespace();
382 | output.push(ch);
383 | } else {
384 | if (isAfterSpace) {
385 | print.singleSpace();
386 | }
387 |
388 | output.push(ch);
389 | }
390 | }
391 |
392 |
393 | var sweetCode = output.join('').replace(/[\r\n\t ]+$/, '');
394 |
395 | // establish end_with_newline
396 | if (end_with_newline) {
397 | sweetCode += "\n";
398 | }
399 |
400 | return sweetCode;
401 | }
402 |
403 | // https://developer.mozilla.org/en-US/docs/Web/CSS/At-rule
404 | css_beautify.NESTED_AT_RULE = {
405 | "@page": true,
406 | "@font-face": true,
407 | "@keyframes": true,
408 | // also in CONDITIONAL_GROUP_RULE below
409 | "@media": true,
410 | "@supports": true,
411 | "@document": true
412 | };
413 | css_beautify.CONDITIONAL_GROUP_RULE = {
414 | "@media": true,
415 | "@supports": true,
416 | "@document": true
417 | };
418 |
419 | /*global define */
420 | if (typeof define === "function" && define.amd) {
421 | // Add support for AMD ( https://github.com/amdjs/amdjs-api/wiki/AMD#defineamd-property- )
422 | define([], function() {
423 | return {
424 | css_beautify: css_beautify
425 | };
426 | });
427 | } else if (typeof exports !== "undefined") {
428 | // Add support for CommonJS. Just put this file somewhere on your require.paths
429 | // and you will be able to `var html_beautify = require("beautify").html_beautify`.
430 | exports.css_beautify = css_beautify;
431 | } else if (typeof window !== "undefined") {
432 | // If we're running a web page and don't have either of the above, add our one global
433 | window.css_beautify = css_beautify;
434 | } else if (typeof global !== "undefined") {
435 | // If we don't even have window, try global.
436 | global.css_beautify = css_beautify;
437 | }
438 |
439 | }());
--------------------------------------------------------------------------------
/lib/template.js:
--------------------------------------------------------------------------------
1 |
2 | // Source: http://krasimirtsonev.com/blog/article/Javascript-template-engine-in-just-20-line
3 |
4 | var TemplateEngine = function(html, options) {
5 | var re = /<%([^%>]+)?%>/g, reExp = /(^( )?(if|for|else|switch|case|break|{|}))(.*)?/g, code = 'var r=[];\n', cursor = 0;
6 | var add = function(line, js) {
7 | js? (code += line.match(reExp) ? line + '\n' : 'r.push(' + line + ');\n') :
8 | (code += line != '' ? 'r.push("' + line.replace(/"/g, '\\"') + '");\n' : '');
9 | return add;
10 | }
11 | while(match = re.exec(html)) {
12 | add(html.slice(cursor, match.index))(match[1], true);
13 | cursor = match.index + match[0].length;
14 | }
15 | add(html.substr(cursor, html.length - cursor));
16 | code += 'return r.join("");';
17 | return new Function(code.replace(/[\r\t\n]/g, '')).apply(options);
18 | }
--------------------------------------------------------------------------------
/lib/tinycolor.js:
--------------------------------------------------------------------------------
1 | // TinyColor v1.1.1
2 | // https://github.com/bgrins/TinyColor
3 | // Brian Grinstead, MIT License
4 |
5 | (function() {
6 |
7 | var trimLeft = /^[\s,#]+/,
8 | trimRight = /\s+$/,
9 | tinyCounter = 0,
10 | math = Math,
11 | mathRound = math.round,
12 | mathMin = math.min,
13 | mathMax = math.max,
14 | mathRandom = math.random;
15 |
16 | var tinycolor = function tinycolor (color, opts) {
17 |
18 | color = (color) ? color : '';
19 | opts = opts || { };
20 |
21 | // If input is already a tinycolor, return itself
22 | if (color instanceof tinycolor) {
23 | return color;
24 | }
25 | // If we are called as a function, call using new instead
26 | if (!(this instanceof tinycolor)) {
27 | return new tinycolor(color, opts);
28 | }
29 |
30 | var rgb = inputToRGB(color);
31 | this._originalInput = color,
32 | this._r = rgb.r,
33 | this._g = rgb.g,
34 | this._b = rgb.b,
35 | this._a = rgb.a,
36 | this._roundA = mathRound(100*this._a) / 100,
37 | this._format = opts.format || rgb.format;
38 | this._gradientType = opts.gradientType;
39 |
40 | // Don't let the range of [0,255] come back in [0,1].
41 | // Potentially lose a little bit of precision here, but will fix issues where
42 | // .5 gets interpreted as half of the total, instead of half of 1
43 | // If it was supposed to be 128, this was already taken care of by `inputToRgb`
44 | if (this._r < 1) { this._r = mathRound(this._r); }
45 | if (this._g < 1) { this._g = mathRound(this._g); }
46 | if (this._b < 1) { this._b = mathRound(this._b); }
47 |
48 | this._ok = rgb.ok;
49 | this._tc_id = tinyCounter++;
50 | };
51 |
52 | tinycolor.prototype = {
53 | isDark: function() {
54 | return this.getBrightness() < 128;
55 | },
56 | isLight: function() {
57 | return !this.isDark();
58 | },
59 | isValid: function() {
60 | return this._ok;
61 | },
62 | getOriginalInput: function() {
63 | return this._originalInput;
64 | },
65 | getFormat: function() {
66 | return this._format;
67 | },
68 | getAlpha: function() {
69 | return this._a;
70 | },
71 | getBrightness: function() {
72 | var rgb = this.toRgb();
73 | return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000;
74 | },
75 | setAlpha: function(value) {
76 | this._a = boundAlpha(value);
77 | this._roundA = mathRound(100*this._a) / 100;
78 | return this;
79 | },
80 | toHsv: function() {
81 | var hsv = rgbToHsv(this._r, this._g, this._b);
82 | return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a };
83 | },
84 | toHsvString: function() {
85 | var hsv = rgbToHsv(this._r, this._g, this._b);
86 | var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
87 | return (this._a == 1) ?
88 | "hsv(" + h + ", " + s + "%, " + v + "%)" :
89 | "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")";
90 | },
91 | toHsl: function() {
92 | var hsl = rgbToHsl(this._r, this._g, this._b);
93 | return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a };
94 | },
95 | toHslString: function() {
96 | var hsl = rgbToHsl(this._r, this._g, this._b);
97 | var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
98 | return (this._a == 1) ?
99 | "hsl(" + h + ", " + s + "%, " + l + "%)" :
100 | "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")";
101 | },
102 | toHex: function(allow3Char) {
103 | return rgbToHex(this._r, this._g, this._b, allow3Char);
104 | },
105 | toHexString: function(allow3Char) {
106 | return '#' + this.toHex(allow3Char);
107 | },
108 | toHex8: function() {
109 | return rgbaToHex(this._r, this._g, this._b, this._a);
110 | },
111 | toHex8String: function() {
112 | return '#' + this.toHex8();
113 | },
114 | toRgb: function() {
115 | return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a };
116 | },
117 | toRgbString: function() {
118 | return (this._a == 1) ?
119 | "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" :
120 | "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")";
121 | },
122 | toPercentageRgb: function() {
123 | return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a };
124 | },
125 | toPercentageRgbString: function() {
126 | return (this._a == 1) ?
127 | "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" :
128 | "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")";
129 | },
130 | toName: function() {
131 | if (this._a === 0) {
132 | return "transparent";
133 | }
134 |
135 | if (this._a < 1) {
136 | return false;
137 | }
138 |
139 | return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false;
140 | },
141 | toFilter: function(secondColor) {
142 | var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a);
143 | var secondHex8String = hex8String;
144 | var gradientType = this._gradientType ? "GradientType = 1, " : "";
145 |
146 | if (secondColor) {
147 | var s = tinycolor(secondColor);
148 | secondHex8String = s.toHex8String();
149 | }
150 |
151 | return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")";
152 | },
153 | toString: function(format) {
154 | var formatSet = !!format;
155 | format = format || this._format;
156 |
157 | var formattedString = false;
158 | var hasAlpha = this._a < 1 && this._a >= 0;
159 | var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
160 |
161 | if (needsAlphaFormat) {
162 | // Special case for "transparent", all other non-alpha formats
163 | // will return rgba when there is transparency.
164 | if (format === "name" && this._a === 0) {
165 | return this.toName();
166 | }
167 | return this.toRgbString();
168 | }
169 | if (format === "rgb") {
170 | formattedString = this.toRgbString();
171 | }
172 | if (format === "prgb") {
173 | formattedString = this.toPercentageRgbString();
174 | }
175 | if (format === "hex" || format === "hex6") {
176 | formattedString = this.toHexString();
177 | }
178 | if (format === "hex3") {
179 | formattedString = this.toHexString(true);
180 | }
181 | if (format === "hex8") {
182 | formattedString = this.toHex8String();
183 | }
184 | if (format === "name") {
185 | formattedString = this.toName();
186 | }
187 | if (format === "hsl") {
188 | formattedString = this.toHslString();
189 | }
190 | if (format === "hsv") {
191 | formattedString = this.toHsvString();
192 | }
193 |
194 | return formattedString || this.toHexString();
195 | },
196 |
197 | _applyModification: function(fn, args) {
198 | var color = fn.apply(null, [this].concat([].slice.call(args)));
199 | this._r = color._r;
200 | this._g = color._g;
201 | this._b = color._b;
202 | this.setAlpha(color._a);
203 | return this;
204 | },
205 | lighten: function() {
206 | return this._applyModification(lighten, arguments);
207 | },
208 | brighten: function() {
209 | return this._applyModification(brighten, arguments);
210 | },
211 | darken: function() {
212 | return this._applyModification(darken, arguments);
213 | },
214 | desaturate: function() {
215 | return this._applyModification(desaturate, arguments);
216 | },
217 | saturate: function() {
218 | return this._applyModification(saturate, arguments);
219 | },
220 | greyscale: function() {
221 | return this._applyModification(greyscale, arguments);
222 | },
223 | spin: function() {
224 | return this._applyModification(spin, arguments);
225 | },
226 |
227 | _applyCombination: function(fn, args) {
228 | return fn.apply(null, [this].concat([].slice.call(args)));
229 | },
230 | analogous: function() {
231 | return this._applyCombination(analogous, arguments);
232 | },
233 | complement: function() {
234 | return this._applyCombination(complement, arguments);
235 | },
236 | monochromatic: function() {
237 | return this._applyCombination(monochromatic, arguments);
238 | },
239 | splitcomplement: function() {
240 | return this._applyCombination(splitcomplement, arguments);
241 | },
242 | triad: function() {
243 | return this._applyCombination(triad, arguments);
244 | },
245 | tetrad: function() {
246 | return this._applyCombination(tetrad, arguments);
247 | }
248 | };
249 |
250 | // If input is an object, force 1 into "1.0" to handle ratios properly
251 | // String input requires "1.0" as input, so 1 will be treated as 1
252 | tinycolor.fromRatio = function(color, opts) {
253 | if (typeof color == "object") {
254 | var newColor = {};
255 | for (var i in color) {
256 | if (color.hasOwnProperty(i)) {
257 | if (i === "a") {
258 | newColor[i] = color[i];
259 | }
260 | else {
261 | newColor[i] = convertToPercentage(color[i]);
262 | }
263 | }
264 | }
265 | color = newColor;
266 | }
267 |
268 | return tinycolor(color, opts);
269 | };
270 |
271 | // Given a string or object, convert that input to RGB
272 | // Possible string inputs:
273 | //
274 | // "red"
275 | // "#f00" or "f00"
276 | // "#ff0000" or "ff0000"
277 | // "#ff000000" or "ff000000"
278 | // "rgb 255 0 0" or "rgb (255, 0, 0)"
279 | // "rgb 1.0 0 0" or "rgb (1, 0, 0)"
280 | // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
281 | // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
282 | // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
283 | // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
284 | // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
285 | //
286 | function inputToRGB(color) {
287 |
288 | var rgb = { r: 0, g: 0, b: 0 };
289 | var a = 1;
290 | var ok = false;
291 | var format = false;
292 |
293 | if (typeof color == "string") {
294 | color = stringInputToObject(color);
295 | }
296 |
297 | if (typeof color == "object") {
298 | if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
299 | rgb = rgbToRgb(color.r, color.g, color.b);
300 | ok = true;
301 | format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
302 | }
303 | else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
304 | color.s = convertToPercentage(color.s);
305 | color.v = convertToPercentage(color.v);
306 | rgb = hsvToRgb(color.h, color.s, color.v);
307 | ok = true;
308 | format = "hsv";
309 | }
310 | else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
311 | color.s = convertToPercentage(color.s);
312 | color.l = convertToPercentage(color.l);
313 | rgb = hslToRgb(color.h, color.s, color.l);
314 | ok = true;
315 | format = "hsl";
316 | }
317 |
318 | if (color.hasOwnProperty("a")) {
319 | a = color.a;
320 | }
321 | }
322 |
323 | a = boundAlpha(a);
324 |
325 | return {
326 | ok: ok,
327 | format: color.format || format,
328 | r: mathMin(255, mathMax(rgb.r, 0)),
329 | g: mathMin(255, mathMax(rgb.g, 0)),
330 | b: mathMin(255, mathMax(rgb.b, 0)),
331 | a: a
332 | };
333 | }
334 |
335 |
336 | // Conversion Functions
337 | // --------------------
338 |
339 | // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
340 | //
341 |
342 | // `rgbToRgb`
343 | // Handle bounds / percentage checking to conform to CSS color spec
344 | //
345 | // *Assumes:* r, g, b in [0, 255] or [0, 1]
346 | // *Returns:* { r, g, b } in [0, 255]
347 | function rgbToRgb(r, g, b){
348 | return {
349 | r: bound01(r, 255) * 255,
350 | g: bound01(g, 255) * 255,
351 | b: bound01(b, 255) * 255
352 | };
353 | }
354 |
355 | // `rgbToHsl`
356 | // Converts an RGB color value to HSL.
357 | // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
358 | // *Returns:* { h, s, l } in [0,1]
359 | function rgbToHsl(r, g, b) {
360 |
361 | r = bound01(r, 255);
362 | g = bound01(g, 255);
363 | b = bound01(b, 255);
364 |
365 | var max = mathMax(r, g, b), min = mathMin(r, g, b);
366 | var h, s, l = (max + min) / 2;
367 |
368 | if(max == min) {
369 | h = s = 0; // achromatic
370 | }
371 | else {
372 | var d = max - min;
373 | s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
374 | switch(max) {
375 | case r: h = (g - b) / d + (g < b ? 6 : 0); break;
376 | case g: h = (b - r) / d + 2; break;
377 | case b: h = (r - g) / d + 4; break;
378 | }
379 |
380 | h /= 6;
381 | }
382 |
383 | return { h: h, s: s, l: l };
384 | }
385 |
386 | // `hslToRgb`
387 | // Converts an HSL color value to RGB.
388 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
389 | // *Returns:* { r, g, b } in the set [0, 255]
390 | function hslToRgb(h, s, l) {
391 | var r, g, b;
392 |
393 | h = bound01(h, 360);
394 | s = bound01(s, 100);
395 | l = bound01(l, 100);
396 |
397 | function hue2rgb(p, q, t) {
398 | if(t < 0) t += 1;
399 | if(t > 1) t -= 1;
400 | if(t < 1/6) return p + (q - p) * 6 * t;
401 | if(t < 1/2) return q;
402 | if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
403 | return p;
404 | }
405 |
406 | if(s === 0) {
407 | r = g = b = l; // achromatic
408 | }
409 | else {
410 | var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
411 | var p = 2 * l - q;
412 | r = hue2rgb(p, q, h + 1/3);
413 | g = hue2rgb(p, q, h);
414 | b = hue2rgb(p, q, h - 1/3);
415 | }
416 |
417 | return { r: r * 255, g: g * 255, b: b * 255 };
418 | }
419 |
420 | // `rgbToHsv`
421 | // Converts an RGB color value to HSV
422 | // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
423 | // *Returns:* { h, s, v } in [0,1]
424 | function rgbToHsv(r, g, b) {
425 |
426 | r = bound01(r, 255);
427 | g = bound01(g, 255);
428 | b = bound01(b, 255);
429 |
430 | var max = mathMax(r, g, b), min = mathMin(r, g, b);
431 | var h, s, v = max;
432 |
433 | var d = max - min;
434 | s = max === 0 ? 0 : d / max;
435 |
436 | if(max == min) {
437 | h = 0; // achromatic
438 | }
439 | else {
440 | switch(max) {
441 | case r: h = (g - b) / d + (g < b ? 6 : 0); break;
442 | case g: h = (b - r) / d + 2; break;
443 | case b: h = (r - g) / d + 4; break;
444 | }
445 | h /= 6;
446 | }
447 | return { h: h, s: s, v: v };
448 | }
449 |
450 | // `hsvToRgb`
451 | // Converts an HSV color value to RGB.
452 | // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
453 | // *Returns:* { r, g, b } in the set [0, 255]
454 | function hsvToRgb(h, s, v) {
455 |
456 | h = bound01(h, 360) * 6;
457 | s = bound01(s, 100);
458 | v = bound01(v, 100);
459 |
460 | var i = math.floor(h),
461 | f = h - i,
462 | p = v * (1 - s),
463 | q = v * (1 - f * s),
464 | t = v * (1 - (1 - f) * s),
465 | mod = i % 6,
466 | r = [v, q, p, p, t, v][mod],
467 | g = [t, v, v, q, p, p][mod],
468 | b = [p, p, t, v, v, q][mod];
469 |
470 | return { r: r * 255, g: g * 255, b: b * 255 };
471 | }
472 |
473 | // `rgbToHex`
474 | // Converts an RGB color to hex
475 | // Assumes r, g, and b are contained in the set [0, 255]
476 | // Returns a 3 or 6 character hex
477 | function rgbToHex(r, g, b, allow3Char) {
478 |
479 | var hex = [
480 | pad2(mathRound(r).toString(16)),
481 | pad2(mathRound(g).toString(16)),
482 | pad2(mathRound(b).toString(16))
483 | ];
484 |
485 | // Return a 3 character hex if possible
486 | if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
487 | return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
488 | }
489 |
490 | return hex.join("");
491 | }
492 | // `rgbaToHex`
493 | // Converts an RGBA color plus alpha transparency to hex
494 | // Assumes r, g, b and a are contained in the set [0, 255]
495 | // Returns an 8 character hex
496 | function rgbaToHex(r, g, b, a) {
497 |
498 | var hex = [
499 | pad2(convertDecimalToHex(a)),
500 | pad2(mathRound(r).toString(16)),
501 | pad2(mathRound(g).toString(16)),
502 | pad2(mathRound(b).toString(16))
503 | ];
504 |
505 | return hex.join("");
506 | }
507 |
508 | // `equals`
509 | // Can be called with any tinycolor input
510 | tinycolor.equals = function (color1, color2) {
511 | if (!color1 || !color2) { return false; }
512 | return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
513 | };
514 | tinycolor.random = function() {
515 | return tinycolor.fromRatio({
516 | r: mathRandom(),
517 | g: mathRandom(),
518 | b: mathRandom()
519 | });
520 | };
521 |
522 |
523 | // Modification Functions
524 | // ----------------------
525 | // Thanks to less.js for some of the basics here
526 | //
527 |
528 | function desaturate(color, amount) {
529 | amount = (amount === 0) ? 0 : (amount || 10);
530 | var hsl = tinycolor(color).toHsl();
531 | hsl.s -= amount / 100;
532 | hsl.s = clamp01(hsl.s);
533 | return tinycolor(hsl);
534 | }
535 |
536 | function saturate(color, amount) {
537 | amount = (amount === 0) ? 0 : (amount || 10);
538 | var hsl = tinycolor(color).toHsl();
539 | hsl.s += amount / 100;
540 | hsl.s = clamp01(hsl.s);
541 | return tinycolor(hsl);
542 | }
543 |
544 | function greyscale(color) {
545 | return tinycolor(color).desaturate(100);
546 | }
547 |
548 | function lighten (color, amount) {
549 | amount = (amount === 0) ? 0 : (amount || 10);
550 | var hsl = tinycolor(color).toHsl();
551 | hsl.l += amount / 100;
552 | hsl.l = clamp01(hsl.l);
553 | return tinycolor(hsl);
554 | }
555 |
556 | function brighten(color, amount) {
557 | amount = (amount === 0) ? 0 : (amount || 10);
558 | var rgb = tinycolor(color).toRgb();
559 | rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100))));
560 | rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100))));
561 | rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100))));
562 | return tinycolor(rgb);
563 | }
564 |
565 | function darken (color, amount) {
566 | amount = (amount === 0) ? 0 : (amount || 10);
567 | var hsl = tinycolor(color).toHsl();
568 | hsl.l -= amount / 100;
569 | hsl.l = clamp01(hsl.l);
570 | return tinycolor(hsl);
571 | }
572 |
573 | // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue.
574 | // Values outside of this range will be wrapped into this range.
575 | function spin(color, amount) {
576 | var hsl = tinycolor(color).toHsl();
577 | var hue = (mathRound(hsl.h) + amount) % 360;
578 | hsl.h = hue < 0 ? 360 + hue : hue;
579 | return tinycolor(hsl);
580 | }
581 |
582 | // Combination Functions
583 | // ---------------------
584 | // Thanks to jQuery xColor for some of the ideas behind these
585 | //
586 |
587 | function complement(color) {
588 | var hsl = tinycolor(color).toHsl();
589 | hsl.h = (hsl.h + 180) % 360;
590 | return tinycolor(hsl);
591 | }
592 |
593 | function triad(color) {
594 | var hsl = tinycolor(color).toHsl();
595 | var h = hsl.h;
596 | return [
597 | tinycolor(color),
598 | tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
599 | tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
600 | ];
601 | }
602 |
603 | function tetrad(color) {
604 | var hsl = tinycolor(color).toHsl();
605 | var h = hsl.h;
606 | return [
607 | tinycolor(color),
608 | tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
609 | tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
610 | tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
611 | ];
612 | }
613 |
614 | function splitcomplement(color) {
615 | var hsl = tinycolor(color).toHsl();
616 | var h = hsl.h;
617 | return [
618 | tinycolor(color),
619 | tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
620 | tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
621 | ];
622 | }
623 |
624 | function analogous(color, results, slices) {
625 | results = results || 6;
626 | slices = slices || 30;
627 |
628 | var hsl = tinycolor(color).toHsl();
629 | var part = 360 / slices;
630 | var ret = [tinycolor(color)];
631 |
632 | for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
633 | hsl.h = (hsl.h + part) % 360;
634 | ret.push(tinycolor(hsl));
635 | }
636 | return ret;
637 | }
638 |
639 | function monochromatic(color, results) {
640 | results = results || 6;
641 | var hsv = tinycolor(color).toHsv();
642 | var h = hsv.h, s = hsv.s, v = hsv.v;
643 | var ret = [];
644 | var modification = 1 / results;
645 |
646 | while (results--) {
647 | ret.push(tinycolor({ h: h, s: s, v: v}));
648 | v = (v + modification) % 1;
649 | }
650 |
651 | return ret;
652 | }
653 |
654 | // Utility Functions
655 | // ---------------------
656 |
657 | tinycolor.mix = function(color1, color2, amount) {
658 | amount = (amount === 0) ? 0 : (amount || 50);
659 |
660 | var rgb1 = tinycolor(color1).toRgb();
661 | var rgb2 = tinycolor(color2).toRgb();
662 |
663 | var p = amount / 100;
664 | var w = p * 2 - 1;
665 | var a = rgb2.a - rgb1.a;
666 |
667 | var w1;
668 |
669 | if (w * a == -1) {
670 | w1 = w;
671 | } else {
672 | w1 = (w + a) / (1 + w * a);
673 | }
674 |
675 | w1 = (w1 + 1) / 2;
676 |
677 | var w2 = 1 - w1;
678 |
679 | var rgba = {
680 | r: rgb2.r * w1 + rgb1.r * w2,
681 | g: rgb2.g * w1 + rgb1.g * w2,
682 | b: rgb2.b * w1 + rgb1.b * w2,
683 | a: rgb2.a * p + rgb1.a * (1 - p)
684 | };
685 |
686 | return tinycolor(rgba);
687 | };
688 |
689 |
690 | // Readability Functions
691 | // ---------------------
692 | //
693 |
694 | // `readability`
695 | // Analyze the 2 colors and returns an object with the following properties:
696 | // `brightness`: difference in brightness between the two colors
697 | // `color`: difference in color/hue between the two colors
698 | tinycolor.readability = function(color1, color2) {
699 | var c1 = tinycolor(color1);
700 | var c2 = tinycolor(color2);
701 | var rgb1 = c1.toRgb();
702 | var rgb2 = c2.toRgb();
703 | var brightnessA = c1.getBrightness();
704 | var brightnessB = c2.getBrightness();
705 | var colorDiff = (
706 | Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) +
707 | Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) +
708 | Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b)
709 | );
710 |
711 | return {
712 | brightness: Math.abs(brightnessA - brightnessB),
713 | color: colorDiff
714 | };
715 | };
716 |
717 | // `readable`
718 | // http://www.w3.org/TR/AERT#color-contrast
719 | // Ensure that foreground and background color combinations provide sufficient contrast.
720 | // *Example*
721 | // tinycolor.isReadable("#000", "#111") => false
722 | tinycolor.isReadable = function(color1, color2) {
723 | var readability = tinycolor.readability(color1, color2);
724 | return readability.brightness > 125 && readability.color > 500;
725 | };
726 |
727 | // `mostReadable`
728 | // Given a base color and a list of possible foreground or background
729 | // colors for that base, returns the most readable color.
730 | // *Example*
731 | // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
732 | tinycolor.mostReadable = function(baseColor, colorList) {
733 | var bestColor = null;
734 | var bestScore = 0;
735 | var bestIsReadable = false;
736 | for (var i=0; i < colorList.length; i++) {
737 |
738 | // We normalize both around the "acceptable" breaking point,
739 | // but rank brightness constrast higher than hue.
740 |
741 | var readability = tinycolor.readability(baseColor, colorList[i]);
742 | var readable = readability.brightness > 125 && readability.color > 500;
743 | var score = 3 * (readability.brightness / 125) + (readability.color / 500);
744 |
745 | if ((readable && ! bestIsReadable) ||
746 | (readable && bestIsReadable && score > bestScore) ||
747 | ((! readable) && (! bestIsReadable) && score > bestScore)) {
748 | bestIsReadable = readable;
749 | bestScore = score;
750 | bestColor = tinycolor(colorList[i]);
751 | }
752 | }
753 | return bestColor;
754 | };
755 |
756 |
757 | // Big List of Colors
758 | // ------------------
759 | //
760 | var names = tinycolor.names = {
761 | aliceblue: "f0f8ff",
762 | antiquewhite: "faebd7",
763 | aqua: "0ff",
764 | aquamarine: "7fffd4",
765 | azure: "f0ffff",
766 | beige: "f5f5dc",
767 | bisque: "ffe4c4",
768 | black: "000",
769 | blanchedalmond: "ffebcd",
770 | blue: "00f",
771 | blueviolet: "8a2be2",
772 | brown: "a52a2a",
773 | burlywood: "deb887",
774 | burntsienna: "ea7e5d",
775 | cadetblue: "5f9ea0",
776 | chartreuse: "7fff00",
777 | chocolate: "d2691e",
778 | coral: "ff7f50",
779 | cornflowerblue: "6495ed",
780 | cornsilk: "fff8dc",
781 | crimson: "dc143c",
782 | cyan: "0ff",
783 | darkblue: "00008b",
784 | darkcyan: "008b8b",
785 | darkgoldenrod: "b8860b",
786 | darkgray: "a9a9a9",
787 | darkgreen: "006400",
788 | darkgrey: "a9a9a9",
789 | darkkhaki: "bdb76b",
790 | darkmagenta: "8b008b",
791 | darkolivegreen: "556b2f",
792 | darkorange: "ff8c00",
793 | darkorchid: "9932cc",
794 | darkred: "8b0000",
795 | darksalmon: "e9967a",
796 | darkseagreen: "8fbc8f",
797 | darkslateblue: "483d8b",
798 | darkslategray: "2f4f4f",
799 | darkslategrey: "2f4f4f",
800 | darkturquoise: "00ced1",
801 | darkviolet: "9400d3",
802 | deeppink: "ff1493",
803 | deepskyblue: "00bfff",
804 | dimgray: "696969",
805 | dimgrey: "696969",
806 | dodgerblue: "1e90ff",
807 | firebrick: "b22222",
808 | floralwhite: "fffaf0",
809 | forestgreen: "228b22",
810 | fuchsia: "f0f",
811 | gainsboro: "dcdcdc",
812 | ghostwhite: "f8f8ff",
813 | gold: "ffd700",
814 | goldenrod: "daa520",
815 | gray: "808080",
816 | green: "008000",
817 | greenyellow: "adff2f",
818 | grey: "808080",
819 | honeydew: "f0fff0",
820 | hotpink: "ff69b4",
821 | indianred: "cd5c5c",
822 | indigo: "4b0082",
823 | ivory: "fffff0",
824 | khaki: "f0e68c",
825 | lavender: "e6e6fa",
826 | lavenderblush: "fff0f5",
827 | lawngreen: "7cfc00",
828 | lemonchiffon: "fffacd",
829 | lightblue: "add8e6",
830 | lightcoral: "f08080",
831 | lightcyan: "e0ffff",
832 | lightgoldenrodyellow: "fafad2",
833 | lightgray: "d3d3d3",
834 | lightgreen: "90ee90",
835 | lightgrey: "d3d3d3",
836 | lightpink: "ffb6c1",
837 | lightsalmon: "ffa07a",
838 | lightseagreen: "20b2aa",
839 | lightskyblue: "87cefa",
840 | lightslategray: "789",
841 | lightslategrey: "789",
842 | lightsteelblue: "b0c4de",
843 | lightyellow: "ffffe0",
844 | lime: "0f0",
845 | limegreen: "32cd32",
846 | linen: "faf0e6",
847 | magenta: "f0f",
848 | maroon: "800000",
849 | mediumaquamarine: "66cdaa",
850 | mediumblue: "0000cd",
851 | mediumorchid: "ba55d3",
852 | mediumpurple: "9370db",
853 | mediumseagreen: "3cb371",
854 | mediumslateblue: "7b68ee",
855 | mediumspringgreen: "00fa9a",
856 | mediumturquoise: "48d1cc",
857 | mediumvioletred: "c71585",
858 | midnightblue: "191970",
859 | mintcream: "f5fffa",
860 | mistyrose: "ffe4e1",
861 | moccasin: "ffe4b5",
862 | navajowhite: "ffdead",
863 | navy: "000080",
864 | oldlace: "fdf5e6",
865 | olive: "808000",
866 | olivedrab: "6b8e23",
867 | orange: "ffa500",
868 | orangered: "ff4500",
869 | orchid: "da70d6",
870 | palegoldenrod: "eee8aa",
871 | palegreen: "98fb98",
872 | paleturquoise: "afeeee",
873 | palevioletred: "db7093",
874 | papayawhip: "ffefd5",
875 | peachpuff: "ffdab9",
876 | peru: "cd853f",
877 | pink: "ffc0cb",
878 | plum: "dda0dd",
879 | powderblue: "b0e0e6",
880 | purple: "800080",
881 | rebeccapurple: "663399",
882 | red: "f00",
883 | rosybrown: "bc8f8f",
884 | royalblue: "4169e1",
885 | saddlebrown: "8b4513",
886 | salmon: "fa8072",
887 | sandybrown: "f4a460",
888 | seagreen: "2e8b57",
889 | seashell: "fff5ee",
890 | sienna: "a0522d",
891 | silver: "c0c0c0",
892 | skyblue: "87ceeb",
893 | slateblue: "6a5acd",
894 | slategray: "708090",
895 | slategrey: "708090",
896 | snow: "fffafa",
897 | springgreen: "00ff7f",
898 | steelblue: "4682b4",
899 | tan: "d2b48c",
900 | teal: "008080",
901 | thistle: "d8bfd8",
902 | tomato: "ff6347",
903 | turquoise: "40e0d0",
904 | violet: "ee82ee",
905 | wheat: "f5deb3",
906 | white: "fff",
907 | whitesmoke: "f5f5f5",
908 | yellow: "ff0",
909 | yellowgreen: "9acd32"
910 | };
911 |
912 | // Make it easy to access colors via `hexNames[hex]`
913 | var hexNames = tinycolor.hexNames = flip(names);
914 |
915 |
916 | // Utilities
917 | // ---------
918 |
919 | // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
920 | function flip(o) {
921 | var flipped = { };
922 | for (var i in o) {
923 | if (o.hasOwnProperty(i)) {
924 | flipped[o[i]] = i;
925 | }
926 | }
927 | return flipped;
928 | }
929 |
930 | // Return a valid alpha value [0,1] with all invalid values being set to 1
931 | function boundAlpha(a) {
932 | a = parseFloat(a);
933 |
934 | if (isNaN(a) || a < 0 || a > 1) {
935 | a = 1;
936 | }
937 |
938 | return a;
939 | }
940 |
941 | // Take input from [0, n] and return it as [0, 1]
942 | function bound01(n, max) {
943 | if (isOnePointZero(n)) { n = "100%"; }
944 |
945 | var processPercent = isPercentage(n);
946 | n = mathMin(max, mathMax(0, parseFloat(n)));
947 |
948 | // Automatically convert percentage into number
949 | if (processPercent) {
950 | n = parseInt(n * max, 10) / 100;
951 | }
952 |
953 | // Handle floating point rounding errors
954 | if ((math.abs(n - max) < 0.000001)) {
955 | return 1;
956 | }
957 |
958 | // Convert into [0, 1] range if it isn't already
959 | return (n % max) / parseFloat(max);
960 | }
961 |
962 | // Force a number between 0 and 1
963 | function clamp01(val) {
964 | return mathMin(1, mathMax(0, val));
965 | }
966 |
967 | // Parse a base-16 hex value into a base-10 integer
968 | function parseIntFromHex(val) {
969 | return parseInt(val, 16);
970 | }
971 |
972 | // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
973 | //
974 | function isOnePointZero(n) {
975 | return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
976 | }
977 |
978 | // Check to see if string passed in is a percentage
979 | function isPercentage(n) {
980 | return typeof n === "string" && n.indexOf('%') != -1;
981 | }
982 |
983 | // Force a hex value to have 2 characters
984 | function pad2(c) {
985 | return c.length == 1 ? '0' + c : '' + c;
986 | }
987 |
988 | // Replace a decimal with it's percentage value
989 | function convertToPercentage(n) {
990 | if (n <= 1) {
991 | n = (n * 100) + "%";
992 | }
993 |
994 | return n;
995 | }
996 |
997 | // Converts a decimal to a hex value
998 | function convertDecimalToHex(d) {
999 | return Math.round(parseFloat(d) * 255).toString(16);
1000 | }
1001 | // Converts a hex value to a decimal
1002 | function convertHexToDecimal(h) {
1003 | return (parseIntFromHex(h) / 255);
1004 | }
1005 |
1006 | var matchers = (function() {
1007 |
1008 | //
1009 | var CSS_INTEGER = "[-\\+]?\\d+%?";
1010 |
1011 | //
1012 | var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
1013 |
1014 | // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
1015 | var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
1016 |
1017 | // Actual matching.
1018 | // Parentheses and commas are optional, but not required.
1019 | // Whitespace can take the place of commas or opening paren
1020 | var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
1021 | var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
1022 |
1023 | return {
1024 | rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
1025 | rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
1026 | hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
1027 | hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
1028 | hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
1029 | hsva: new RegExp("hsva" + PERMISSIVE_MATCH4),
1030 | hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
1031 | hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
1032 | hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
1033 | };
1034 | })();
1035 |
1036 | // `stringInputToObject`
1037 | // Permissive string parsing. Take in a number of formats, and output an object
1038 | // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
1039 | function stringInputToObject(color) {
1040 |
1041 | color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
1042 | var named = false;
1043 | if (names[color]) {
1044 | color = names[color];
1045 | named = true;
1046 | }
1047 | else if (color == 'transparent') {
1048 | return { r: 0, g: 0, b: 0, a: 0, format: "name" };
1049 | }
1050 |
1051 | // Try to match string input using regular expressions.
1052 | // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
1053 | // Just return an object and let the conversion functions handle that.
1054 | // This way the result will be the same whether the tinycolor is initialized with string or object.
1055 | var match;
1056 | if ((match = matchers.rgb.exec(color))) {
1057 | return { r: match[1], g: match[2], b: match[3] };
1058 | }
1059 | if ((match = matchers.rgba.exec(color))) {
1060 | return { r: match[1], g: match[2], b: match[3], a: match[4] };
1061 | }
1062 | if ((match = matchers.hsl.exec(color))) {
1063 | return { h: match[1], s: match[2], l: match[3] };
1064 | }
1065 | if ((match = matchers.hsla.exec(color))) {
1066 | return { h: match[1], s: match[2], l: match[3], a: match[4] };
1067 | }
1068 | if ((match = matchers.hsv.exec(color))) {
1069 | return { h: match[1], s: match[2], v: match[3] };
1070 | }
1071 | if ((match = matchers.hsva.exec(color))) {
1072 | return { h: match[1], s: match[2], v: match[3], a: match[4] };
1073 | }
1074 | if ((match = matchers.hex8.exec(color))) {
1075 | return {
1076 | a: convertHexToDecimal(match[1]),
1077 | r: parseIntFromHex(match[2]),
1078 | g: parseIntFromHex(match[3]),
1079 | b: parseIntFromHex(match[4]),
1080 | format: named ? "name" : "hex8"
1081 | };
1082 | }
1083 | if ((match = matchers.hex6.exec(color))) {
1084 | return {
1085 | r: parseIntFromHex(match[1]),
1086 | g: parseIntFromHex(match[2]),
1087 | b: parseIntFromHex(match[3]),
1088 | format: named ? "name" : "hex"
1089 | };
1090 | }
1091 | if ((match = matchers.hex3.exec(color))) {
1092 | return {
1093 | r: parseIntFromHex(match[1] + '' + match[1]),
1094 | g: parseIntFromHex(match[2] + '' + match[2]),
1095 | b: parseIntFromHex(match[3] + '' + match[3]),
1096 | format: named ? "name" : "hex"
1097 | };
1098 | }
1099 |
1100 | return false;
1101 | }
1102 |
1103 | // Node: Export function
1104 | if (typeof module !== "undefined" && module.exports) {
1105 | module.exports = tinycolor;
1106 | }
1107 | // AMD/requirejs: Define the module
1108 | else if (typeof define === 'function' && define.amd) {
1109 | define(function () {return tinycolor;});
1110 | }
1111 | // Browser: Expose to window
1112 | else {
1113 | window.tinycolor = tinycolor;
1114 | }
1115 |
1116 | })();
--------------------------------------------------------------------------------
/src/aco2html.js:
--------------------------------------------------------------------------------
1 | /* aco2html.js - .aco photoshop palette file to HTML converter
2 | * http://websemantics.ca
3 | *
4 | * aco2html.c - .aco photoshop palette file to HTML converter
5 | *
6 | * Copyright(C) 2006 Salvatore Sanfilippo
7 | * All Rights Reserved.
8 | *
9 | * This software is released under the GPL license version 2
10 | * Read the LICENSE file in this software distribution for
11 | * more information.
12 | */
13 |
14 | var Aco = (function() {
15 |
16 | var pointer = 0;
17 |
18 | /* Read from a file */
19 | var fread = function(fp)
20 | {
21 | if(pointer > fp.length)
22 | return 0;
23 |
24 | return [
25 | fp.charCodeAt(pointer++),
26 | fp.charCodeAt(pointer++)
27 | ];
28 | }
29 |
30 | /* Read a 16bit word in big endian from 'fp' and return it
31 | * converted in the host byte order as usigned var.
32 | * On end of file -1 is returned. */
33 | var readword = function(fp)
34 | {
35 | var buf;
36 | var w;
37 |
38 | buf = fread(fp);
39 | if (buf == 0)
40 | return -1;
41 |
42 | w = buf[1] | buf[0] << 8;
43 | return w;
44 | }
45 |
46 | /* Version of readword() that exists with an error message
47 | * if an EOF occurs */
48 | var mustreadword = function(fp) {
49 | var w;
50 |
51 | w = readword(fp);
52 | if (w == -1) {
53 | console.log(stderr, "Unexpected end of file!\n");
54 | return -1;
55 | }
56 | return w;
57 | }
58 |
59 | // http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
60 | function componentToHex(c) {
61 | c = Math.floor(c);
62 | var hex = c.toString(16);
63 | return hex.length == 1 ? "0" + hex : hex;
64 | }
65 |
66 | function rgbToHex(r,g,b) {
67 | return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
68 | }
69 |
70 | /* Convert the color read from 'fp' according to the .aco file version.
71 | * On success zero is returned and the pass-by-reference parameters
72 | * populated, otherwise non-zero is returned.
73 | *
74 | * The color name is stored in the 'name' buffer that can't
75 | * hold more than 'buflen' bytes including the nul-term. */
76 | var convertcolor = function(fp, ver, buflen)
77 | {
78 | var cspace = mustreadword(fp);
79 | var namelen;
80 |
81 | if (cspace != 0) {
82 | var j;
83 | for (j = 0; j < 4; j++) mustreadword(fp);
84 | if (ver == 2) {
85 | mustreadword(fp);
86 | namelen = mustreadword(fp);
87 | for (j = 0; j < namelen; j++)
88 | mustreadword(fp);
89 | }
90 | console.log("Non RGB color (colorspace "+cspace+") skipped\n");
91 | return null;
92 | }
93 |
94 | /* data in common between version 1 and 2 record */
95 | var rgb = {
96 | r:mustreadword(fp)/256,
97 | g:mustreadword(fp)/256,
98 | b:mustreadword(fp)/256,
99 | name: null
100 | }
101 |
102 | // Add hex code
103 | rgb.hex = rgbToHex(rgb.r, rgb.g, rgb.b);
104 |
105 | mustreadword(fp); /* just skip this word, (Z not used for RGB) */
106 | if (ver == 1) return rgb;
107 |
108 | rgb.name = [];
109 |
110 | /* version 2 specific data (name) */
111 |
112 | mustreadword(fp); /* just skip this word, don't know what it's used for */
113 | /* Color name, only for version 1 */
114 | namelen = mustreadword(fp);
115 | namelen--;
116 | while(namelen > 0) {
117 | var c = mustreadword(fp);
118 |
119 | if (c > 0xff) /* To handle utf-16 here is an overkill ... */
120 | c = ' ';
121 | if (buflen > 1) {
122 | rgb.name[rgb.name.length] = c;
123 | buflen--;
124 | }
125 | namelen--;
126 | }
127 | rgb.name='\0';
128 | mustreadword(fp); /* Skip the nul term */
129 | return rgb;
130 | }
131 |
132 |
133 | /* Read an ACO file from 'infp' FILE and return
134 | * the structure describing the palette.
135 | *
136 | * On initial end of file NULL is returned.
137 | * That's not a real library to read this format but just
138 | * an hack in order to write this convertion utility, so
139 | * on error we just exit(1) brutally after priting an error. */
140 | var readaco = function(infp)
141 | {
142 | pointer = 0;
143 |
144 | var ver;
145 | var colors;
146 | var j;
147 | var aco;
148 |
149 | /* Read file version */
150 | ver = readword(infp);
151 |
152 | if (ver == -1) return NULL;
153 | console.log("reading ACO straem version:");
154 | if (ver == 1) {
155 | console.log(" 1 (photoshop < 7.0)\n");
156 | } else if (ver == 2) {
157 | console.log(" 2 (photoshop >= 7.0)\n");
158 | } else {
159 | console.log("Unknown ACO file version %d. Exiting...\n", ver);
160 | return false;
161 | }
162 |
163 | /* Read number of colors in this file */
164 | colors = readword(infp);
165 | console.log("%d colors in this file\n", colors);
166 |
167 | /* Allocate memory */
168 | aco = {
169 | len: colors,
170 | ver: ver,
171 | color: []
172 |
173 | };
174 |
175 | /* Convert every color inside */
176 | for(var j=0; j < colors; j++) {
177 |
178 | var rgb = convertcolor(infp, ver, 256);
179 |
180 | if (rgb == null) continue;
181 |
182 | aco.color.push(rgb);
183 |
184 | }
185 | return aco;
186 | }
187 |
188 | /* Read an ACO file from 'infp' FILE and return
189 | * the hex colors palette. */
190 |
191 | var colors = function(infp)
192 | {
193 |
194 | colors = [];
195 |
196 | var aco_struct = readaco(infp);
197 |
198 | for (var i = 0; i < aco_struct.color.length; i++)
199 | colors.push(aco_struct.color[i].hex);
200 |
201 | return colors;
202 | }
203 |
204 | return {
205 | readaco: readaco,
206 | rgbToHex:rgbToHex,
207 | colors: colors
208 | };
209 |
210 | })();
211 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /* index.js -
2 | *
3 | * Copyright(C) 2015 Web Semantics, Inc/
4 | * All Rights Reserved.
5 | *
6 | * This software is released under the GPL license version 2
7 | * Read the LICENSE file in this software distribution for
8 | * more information.
9 | */
10 |
11 | var Local = (function() {
12 |
13 | var colors = [
14 | '#fafacc',
15 | '#dfedd6',
16 | '#b5d6c9',
17 | '#d5d6b4',
18 | '#96a69a',
19 | '#839c95',
20 | '#e95d44',
21 | '#6f5f5e'
22 | ];
23 |
24 | // 1- No shades, 2- One shade light, 3- One shade dark, 4- Two shades
25 | var shades = 1;
26 |
27 | // The lighter / darker degree of shades
28 | var shadeFactor = 10;
29 |
30 | var embed = '';
31 |
32 | // Template for each color shades
33 | var htmlTemplate = ['\
34 | \
35 |