1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
---|
2 | /* MIT license */
|
---|
3 | var convert = require("color-convert"),
|
---|
4 | string = require("color-string");
|
---|
5 |
|
---|
6 | var Color = function(obj) {
|
---|
7 | if (obj instanceof Color) return obj;
|
---|
8 | if (! (this instanceof Color)) return new Color(obj);
|
---|
9 |
|
---|
10 | this.values = {
|
---|
11 | rgb: [0, 0, 0],
|
---|
12 | hsl: [0, 0, 0],
|
---|
13 | hsv: [0, 0, 0],
|
---|
14 | hwb: [0, 0, 0],
|
---|
15 | cmyk: [0, 0, 0, 0],
|
---|
16 | alpha: 1
|
---|
17 | }
|
---|
18 |
|
---|
19 | // parse Color() argument
|
---|
20 | if (typeof obj == "string") {
|
---|
21 | var vals = string.getRgba(obj);
|
---|
22 | if (vals) {
|
---|
23 | this.setValues("rgb", vals);
|
---|
24 | }
|
---|
25 | else if(vals = string.getHsla(obj)) {
|
---|
26 | this.setValues("hsl", vals);
|
---|
27 | }
|
---|
28 | else if(vals = string.getHwb(obj)) {
|
---|
29 | this.setValues("hwb", vals);
|
---|
30 | }
|
---|
31 | else {
|
---|
32 | throw new Error("Unable to parse color from string \"" + obj + "\"");
|
---|
33 | }
|
---|
34 | }
|
---|
35 | else if (typeof obj == "object") {
|
---|
36 | var vals = obj;
|
---|
37 | if(vals["r"] !== undefined || vals["red"] !== undefined) {
|
---|
38 | this.setValues("rgb", vals)
|
---|
39 | }
|
---|
40 | else if(vals["l"] !== undefined || vals["lightness"] !== undefined) {
|
---|
41 | this.setValues("hsl", vals)
|
---|
42 | }
|
---|
43 | else if(vals["v"] !== undefined || vals["value"] !== undefined) {
|
---|
44 | this.setValues("hsv", vals)
|
---|
45 | }
|
---|
46 | else if(vals["w"] !== undefined || vals["whiteness"] !== undefined) {
|
---|
47 | this.setValues("hwb", vals)
|
---|
48 | }
|
---|
49 | else if(vals["c"] !== undefined || vals["cyan"] !== undefined) {
|
---|
50 | this.setValues("cmyk", vals)
|
---|
51 | }
|
---|
52 | else {
|
---|
53 | throw new Error("Unable to parse color from object " + JSON.stringify(obj));
|
---|
54 | }
|
---|
55 | }
|
---|
56 | }
|
---|
57 |
|
---|
58 | Color.prototype = {
|
---|
59 | rgb: function (vals) {
|
---|
60 | return this.setSpace("rgb", arguments);
|
---|
61 | },
|
---|
62 | hsl: function(vals) {
|
---|
63 | return this.setSpace("hsl", arguments);
|
---|
64 | },
|
---|
65 | hsv: function(vals) {
|
---|
66 | return this.setSpace("hsv", arguments);
|
---|
67 | },
|
---|
68 | hwb: function(vals) {
|
---|
69 | return this.setSpace("hwb", arguments);
|
---|
70 | },
|
---|
71 | cmyk: function(vals) {
|
---|
72 | return this.setSpace("cmyk", arguments);
|
---|
73 | },
|
---|
74 |
|
---|
75 | rgbArray: function() {
|
---|
76 | return this.values.rgb;
|
---|
77 | },
|
---|
78 | hslArray: function() {
|
---|
79 | return this.values.hsl;
|
---|
80 | },
|
---|
81 | hsvArray: function() {
|
---|
82 | return this.values.hsv;
|
---|
83 | },
|
---|
84 | hwbArray: function() {
|
---|
85 | if (this.values.alpha !== 1) {
|
---|
86 | return this.values.hwb.concat([this.values.alpha])
|
---|
87 | }
|
---|
88 | return this.values.hwb;
|
---|
89 | },
|
---|
90 | cmykArray: function() {
|
---|
91 | return this.values.cmyk;
|
---|
92 | },
|
---|
93 | rgbaArray: function() {
|
---|
94 | var rgb = this.values.rgb;
|
---|
95 | return rgb.concat([this.values.alpha]);
|
---|
96 | },
|
---|
97 | hslaArray: function() {
|
---|
98 | var hsl = this.values.hsl;
|
---|
99 | return hsl.concat([this.values.alpha]);
|
---|
100 | },
|
---|
101 | alpha: function(val) {
|
---|
102 | if (val === undefined) {
|
---|
103 | return this.values.alpha;
|
---|
104 | }
|
---|
105 | this.setValues("alpha", val);
|
---|
106 | return this;
|
---|
107 | },
|
---|
108 |
|
---|
109 | red: function(val) {
|
---|
110 | return this.setChannel("rgb", 0, val);
|
---|
111 | },
|
---|
112 | green: function(val) {
|
---|
113 | return this.setChannel("rgb", 1, val);
|
---|
114 | },
|
---|
115 | blue: function(val) {
|
---|
116 | return this.setChannel("rgb", 2, val);
|
---|
117 | },
|
---|
118 | hue: function(val) {
|
---|
119 | return this.setChannel("hsl", 0, val);
|
---|
120 | },
|
---|
121 | saturation: function(val) {
|
---|
122 | return this.setChannel("hsl", 1, val);
|
---|
123 | },
|
---|
124 | lightness: function(val) {
|
---|
125 | return this.setChannel("hsl", 2, val);
|
---|
126 | },
|
---|
127 | saturationv: function(val) {
|
---|
128 | return this.setChannel("hsv", 1, val);
|
---|
129 | },
|
---|
130 | whiteness: function(val) {
|
---|
131 | return this.setChannel("hwb", 1, val);
|
---|
132 | },
|
---|
133 | blackness: function(val) {
|
---|
134 | return this.setChannel("hwb", 2, val);
|
---|
135 | },
|
---|
136 | value: function(val) {
|
---|
137 | return this.setChannel("hsv", 2, val);
|
---|
138 | },
|
---|
139 | cyan: function(val) {
|
---|
140 | return this.setChannel("cmyk", 0, val);
|
---|
141 | },
|
---|
142 | magenta: function(val) {
|
---|
143 | return this.setChannel("cmyk", 1, val);
|
---|
144 | },
|
---|
145 | yellow: function(val) {
|
---|
146 | return this.setChannel("cmyk", 2, val);
|
---|
147 | },
|
---|
148 | black: function(val) {
|
---|
149 | return this.setChannel("cmyk", 3, val);
|
---|
150 | },
|
---|
151 |
|
---|
152 | hexString: function() {
|
---|
153 | return string.hexString(this.values.rgb);
|
---|
154 | },
|
---|
155 | rgbString: function() {
|
---|
156 | return string.rgbString(this.values.rgb, this.values.alpha);
|
---|
157 | },
|
---|
158 | rgbaString: function() {
|
---|
159 | return string.rgbaString(this.values.rgb, this.values.alpha);
|
---|
160 | },
|
---|
161 | percentString: function() {
|
---|
162 | return string.percentString(this.values.rgb, this.values.alpha);
|
---|
163 | },
|
---|
164 | hslString: function() {
|
---|
165 | return string.hslString(this.values.hsl, this.values.alpha);
|
---|
166 | },
|
---|
167 | hslaString: function() {
|
---|
168 | return string.hslaString(this.values.hsl, this.values.alpha);
|
---|
169 | },
|
---|
170 | hwbString: function() {
|
---|
171 | return string.hwbString(this.values.hwb, this.values.alpha);
|
---|
172 | },
|
---|
173 | keyword: function() {
|
---|
174 | return string.keyword(this.values.rgb, this.values.alpha);
|
---|
175 | },
|
---|
176 |
|
---|
177 | rgbNumber: function() {
|
---|
178 | return (this.values.rgb[0] << 16) | (this.values.rgb[1] << 8) | this.values.rgb[2];
|
---|
179 | },
|
---|
180 |
|
---|
181 | luminosity: function() {
|
---|
182 | // http://www.w3.org/TR/WCAG20/#relativeluminancedef
|
---|
183 | var rgb = this.values.rgb;
|
---|
184 | var lum = [];
|
---|
185 | for (var i = 0; i < rgb.length; i++) {
|
---|
186 | var chan = rgb[i] / 255;
|
---|
187 | lum[i] = (chan <= 0.03928) ? chan / 12.92
|
---|
188 | : Math.pow(((chan + 0.055) / 1.055), 2.4)
|
---|
189 | }
|
---|
190 | return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
|
---|
191 | },
|
---|
192 |
|
---|
193 | contrast: function(color2) {
|
---|
194 | // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
|
---|
195 | var lum1 = this.luminosity();
|
---|
196 | var lum2 = color2.luminosity();
|
---|
197 | if (lum1 > lum2) {
|
---|
198 | return (lum1 + 0.05) / (lum2 + 0.05)
|
---|
199 | };
|
---|
200 | return (lum2 + 0.05) / (lum1 + 0.05);
|
---|
201 | },
|
---|
202 |
|
---|
203 | level: function(color2) {
|
---|
204 | var contrastRatio = this.contrast(color2);
|
---|
205 | return (contrastRatio >= 7.1)
|
---|
206 | ? 'AAA'
|
---|
207 | : (contrastRatio >= 4.5)
|
---|
208 | ? 'AA'
|
---|
209 | : '';
|
---|
210 | },
|
---|
211 |
|
---|
212 | dark: function() {
|
---|
213 | // YIQ equation from http://24ways.org/2010/calculating-color-contrast
|
---|
214 | var rgb = this.values.rgb,
|
---|
215 | yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
|
---|
216 | return yiq < 128;
|
---|
217 | },
|
---|
218 |
|
---|
219 | light: function() {
|
---|
220 | return !this.dark();
|
---|
221 | },
|
---|
222 |
|
---|
223 | negate: function() {
|
---|
224 | var rgb = []
|
---|
225 | for (var i = 0; i < 3; i++) {
|
---|
226 | rgb[i] = 255 - this.values.rgb[i];
|
---|
227 | }
|
---|
228 | this.setValues("rgb", rgb);
|
---|
229 | return this;
|
---|
230 | },
|
---|
231 |
|
---|
232 | lighten: function(ratio) {
|
---|
233 | this.values.hsl[2] += this.values.hsl[2] * ratio;
|
---|
234 | this.setValues("hsl", this.values.hsl);
|
---|
235 | return this;
|
---|
236 | },
|
---|
237 |
|
---|
238 | darken: function(ratio) {
|
---|
239 | this.values.hsl[2] -= this.values.hsl[2] * ratio;
|
---|
240 | this.setValues("hsl", this.values.hsl);
|
---|
241 | return this;
|
---|
242 | },
|
---|
243 |
|
---|
244 | saturate: function(ratio) {
|
---|
245 | this.values.hsl[1] += this.values.hsl[1] * ratio;
|
---|
246 | this.setValues("hsl", this.values.hsl);
|
---|
247 | return this;
|
---|
248 | },
|
---|
249 |
|
---|
250 | desaturate: function(ratio) {
|
---|
251 | this.values.hsl[1] -= this.values.hsl[1] * ratio;
|
---|
252 | this.setValues("hsl", this.values.hsl);
|
---|
253 | return this;
|
---|
254 | },
|
---|
255 |
|
---|
256 | whiten: function(ratio) {
|
---|
257 | this.values.hwb[1] += this.values.hwb[1] * ratio;
|
---|
258 | this.setValues("hwb", this.values.hwb);
|
---|
259 | return this;
|
---|
260 | },
|
---|
261 |
|
---|
262 | blacken: function(ratio) {
|
---|
263 | this.values.hwb[2] += this.values.hwb[2] * ratio;
|
---|
264 | this.setValues("hwb", this.values.hwb);
|
---|
265 | return this;
|
---|
266 | },
|
---|
267 |
|
---|
268 | greyscale: function() {
|
---|
269 | var rgb = this.values.rgb;
|
---|
270 | // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
|
---|
271 | var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
|
---|
272 | this.setValues("rgb", [val, val, val]);
|
---|
273 | return this;
|
---|
274 | },
|
---|
275 |
|
---|
276 | clearer: function(ratio) {
|
---|
277 | this.setValues("alpha", this.values.alpha - (this.values.alpha * ratio));
|
---|
278 | return this;
|
---|
279 | },
|
---|
280 |
|
---|
281 | opaquer: function(ratio) {
|
---|
282 | this.setValues("alpha", this.values.alpha + (this.values.alpha * ratio));
|
---|
283 | return this;
|
---|
284 | },
|
---|
285 |
|
---|
286 | rotate: function(degrees) {
|
---|
287 | var hue = this.values.hsl[0];
|
---|
288 | hue = (hue + degrees) % 360;
|
---|
289 | hue = hue < 0 ? 360 + hue : hue;
|
---|
290 | this.values.hsl[0] = hue;
|
---|
291 | this.setValues("hsl", this.values.hsl);
|
---|
292 | return this;
|
---|
293 | },
|
---|
294 |
|
---|
295 | mix: function(color2, weight) {
|
---|
296 | weight = 1 - (weight == null ? 0.5 : weight);
|
---|
297 |
|
---|
298 | // algorithm from Sass's mix(). Ratio of first color in mix is
|
---|
299 | // determined by the alphas of both colors and the weight
|
---|
300 | var t1 = weight * 2 - 1,
|
---|
301 | d = this.alpha() - color2.alpha();
|
---|
302 |
|
---|
303 | var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2;
|
---|
304 | var weight2 = 1 - weight1;
|
---|
305 |
|
---|
306 | var rgb = this.rgbArray();
|
---|
307 | var rgb2 = color2.rgbArray();
|
---|
308 |
|
---|
309 | for (var i = 0; i < rgb.length; i++) {
|
---|
310 | rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2;
|
---|
311 | }
|
---|
312 | this.setValues("rgb", rgb);
|
---|
313 |
|
---|
314 | var alpha = this.alpha() * weight + color2.alpha() * (1 - weight);
|
---|
315 | this.setValues("alpha", alpha);
|
---|
316 |
|
---|
317 | return this;
|
---|
318 | },
|
---|
319 |
|
---|
320 | toJSON: function() {
|
---|
321 | return this.rgb();
|
---|
322 | },
|
---|
323 |
|
---|
324 | clone: function() {
|
---|
325 | return new Color(this.rgb());
|
---|
326 | }
|
---|
327 | }
|
---|
328 |
|
---|
329 |
|
---|
330 | Color.prototype.getValues = function(space) {
|
---|
331 | var vals = {};
|
---|
332 | for (var i = 0; i < space.length; i++) {
|
---|
333 | vals[space.charAt(i)] = this.values[space][i];
|
---|
334 | }
|
---|
335 | if (this.values.alpha != 1) {
|
---|
336 | vals["a"] = this.values.alpha;
|
---|
337 | }
|
---|
338 | // {r: 255, g: 255, b: 255, a: 0.4}
|
---|
339 | return vals;
|
---|
340 | }
|
---|
341 |
|
---|
342 | Color.prototype.setValues = function(space, vals) {
|
---|
343 | var spaces = {
|
---|
344 | "rgb": ["red", "green", "blue"],
|
---|
345 | "hsl": ["hue", "saturation", "lightness"],
|
---|
346 | "hsv": ["hue", "saturation", "value"],
|
---|
347 | "hwb": ["hue", "whiteness", "blackness"],
|
---|
348 | "cmyk": ["cyan", "magenta", "yellow", "black"]
|
---|
349 | };
|
---|
350 |
|
---|
351 | var maxes = {
|
---|
352 | "rgb": [255, 255, 255],
|
---|
353 | "hsl": [360, 100, 100],
|
---|
354 | "hsv": [360, 100, 100],
|
---|
355 | "hwb": [360, 100, 100],
|
---|
356 | "cmyk": [100, 100, 100, 100]
|
---|
357 | };
|
---|
358 |
|
---|
359 | var alpha = 1;
|
---|
360 | if (space == "alpha") {
|
---|
361 | alpha = vals;
|
---|
362 | }
|
---|
363 | else if (vals.length) {
|
---|
364 | // [10, 10, 10]
|
---|
365 | this.values[space] = vals.slice(0, space.length);
|
---|
366 | alpha = vals[space.length];
|
---|
367 | }
|
---|
368 | else if (vals[space.charAt(0)] !== undefined) {
|
---|
369 | // {r: 10, g: 10, b: 10}
|
---|
370 | for (var i = 0; i < space.length; i++) {
|
---|
371 | this.values[space][i] = vals[space.charAt(i)];
|
---|
372 | }
|
---|
373 | alpha = vals.a;
|
---|
374 | }
|
---|
375 | else if (vals[spaces[space][0]] !== undefined) {
|
---|
376 | // {red: 10, green: 10, blue: 10}
|
---|
377 | var chans = spaces[space];
|
---|
378 | for (var i = 0; i < space.length; i++) {
|
---|
379 | this.values[space][i] = vals[chans[i]];
|
---|
380 | }
|
---|
381 | alpha = vals.alpha;
|
---|
382 | }
|
---|
383 | this.values.alpha = Math.max(0, Math.min(1, (alpha !== undefined ? alpha : this.values.alpha) ));
|
---|
384 | if (space == "alpha") {
|
---|
385 | return;
|
---|
386 | }
|
---|
387 |
|
---|
388 | // cap values of the space prior converting all values
|
---|
389 | for (var i = 0; i < space.length; i++) {
|
---|
390 | var capped = Math.max(0, Math.min(maxes[space][i], this.values[space][i]));
|
---|
391 | this.values[space][i] = Math.round(capped);
|
---|
392 | }
|
---|
393 |
|
---|
394 | // convert to all the other color spaces
|
---|
395 | for (var sname in spaces) {
|
---|
396 | if (sname != space) {
|
---|
397 | this.values[sname] = convert[space][sname](this.values[space])
|
---|
398 | }
|
---|
399 |
|
---|
400 | // cap values
|
---|
401 | for (var i = 0; i < sname.length; i++) {
|
---|
402 | var capped = Math.max(0, Math.min(maxes[sname][i], this.values[sname][i]));
|
---|
403 | this.values[sname][i] = Math.round(capped);
|
---|
404 | }
|
---|
405 | }
|
---|
406 | return true;
|
---|
407 | }
|
---|
408 |
|
---|
409 | Color.prototype.setSpace = function(space, args) {
|
---|
410 | var vals = args[0];
|
---|
411 | if (vals === undefined) {
|
---|
412 | // color.rgb()
|
---|
413 | return this.getValues(space);
|
---|
414 | }
|
---|
415 | // color.rgb(10, 10, 10)
|
---|
416 | if (typeof vals == "number") {
|
---|
417 | vals = Array.prototype.slice.call(args);
|
---|
418 | }
|
---|
419 | this.setValues(space, vals);
|
---|
420 | return this;
|
---|
421 | }
|
---|
422 |
|
---|
423 | Color.prototype.setChannel = function(space, index, val) {
|
---|
424 | if (val === undefined) {
|
---|
425 | // color.red()
|
---|
426 | return this.values[space][index];
|
---|
427 | }
|
---|
428 | // color.red(100)
|
---|
429 | this.values[space][index] = val;
|
---|
430 | this.setValues(space, this.values[space]);
|
---|
431 | return this;
|
---|
432 | }
|
---|
433 |
|
---|
434 | module.exports = Color;
|
---|
435 |
|
---|
436 | },{"color-convert":3,"color-string":4}],2:[function(require,module,exports){
|
---|
437 | /* MIT license */
|
---|
438 |
|
---|
439 | module.exports = {
|
---|
440 | rgb2hsl: rgb2hsl,
|
---|
441 | rgb2hsv: rgb2hsv,
|
---|
442 | rgb2hwb: rgb2hwb,
|
---|
443 | rgb2cmyk: rgb2cmyk,
|
---|
444 | rgb2keyword: rgb2keyword,
|
---|
445 | rgb2xyz: rgb2xyz,
|
---|
446 | rgb2lab: rgb2lab,
|
---|
447 | rgb2lch: rgb2lch,
|
---|
448 |
|
---|
449 | hsl2rgb: hsl2rgb,
|
---|
450 | hsl2hsv: hsl2hsv,
|
---|
451 | hsl2hwb: hsl2hwb,
|
---|
452 | hsl2cmyk: hsl2cmyk,
|
---|
453 | hsl2keyword: hsl2keyword,
|
---|
454 |
|
---|
455 | hsv2rgb: hsv2rgb,
|
---|
456 | hsv2hsl: hsv2hsl,
|
---|
457 | hsv2hwb: hsv2hwb,
|
---|
458 | hsv2cmyk: hsv2cmyk,
|
---|
459 | hsv2keyword: hsv2keyword,
|
---|
460 |
|
---|
461 | hwb2rgb: hwb2rgb,
|
---|
462 | hwb2hsl: hwb2hsl,
|
---|
463 | hwb2hsv: hwb2hsv,
|
---|
464 | hwb2cmyk: hwb2cmyk,
|
---|
465 | hwb2keyword: hwb2keyword,
|
---|
466 |
|
---|
467 | cmyk2rgb: cmyk2rgb,
|
---|
468 | cmyk2hsl: cmyk2hsl,
|
---|
469 | cmyk2hsv: cmyk2hsv,
|
---|
470 | cmyk2hwb: cmyk2hwb,
|
---|
471 | cmyk2keyword: cmyk2keyword,
|
---|
472 |
|
---|
473 | keyword2rgb: keyword2rgb,
|
---|
474 | keyword2hsl: keyword2hsl,
|
---|
475 | keyword2hsv: keyword2hsv,
|
---|
476 | keyword2hwb: keyword2hwb,
|
---|
477 | keyword2cmyk: keyword2cmyk,
|
---|
478 | keyword2lab: keyword2lab,
|
---|
479 | keyword2xyz: keyword2xyz,
|
---|
480 |
|
---|
481 | xyz2rgb: xyz2rgb,
|
---|
482 | xyz2lab: xyz2lab,
|
---|
483 | xyz2lch: xyz2lch,
|
---|
484 |
|
---|
485 | lab2xyz: lab2xyz,
|
---|
486 | lab2rgb: lab2rgb,
|
---|
487 | lab2lch: lab2lch,
|
---|
488 |
|
---|
489 | lch2lab: lch2lab,
|
---|
490 | lch2xyz: lch2xyz,
|
---|
491 | lch2rgb: lch2rgb
|
---|
492 | }
|
---|
493 |
|
---|
494 |
|
---|
495 | function rgb2hsl(rgb) {
|
---|
496 | var r = rgb[0]/255,
|
---|
497 | g = rgb[1]/255,
|
---|
498 | b = rgb[2]/255,
|
---|
499 | min = Math.min(r, g, b),
|
---|
500 | max = Math.max(r, g, b),
|
---|
501 | delta = max - min,
|
---|
502 | h, s, l;
|
---|
503 |
|
---|
504 | if (max == min)
|
---|
505 | h = 0;
|
---|
506 | else if (r == max)
|
---|
507 | h = (g - b) / delta;
|
---|
508 | else if (g == max)
|
---|
509 | h = 2 + (b - r) / delta;
|
---|
510 | else if (b == max)
|
---|
511 | h = 4 + (r - g)/ delta;
|
---|
512 |
|
---|
513 | h = Math.min(h * 60, 360);
|
---|
514 |
|
---|
515 | if (h < 0)
|
---|
516 | h += 360;
|
---|
517 |
|
---|
518 | l = (min + max) / 2;
|
---|
519 |
|
---|
520 | if (max == min)
|
---|
521 | s = 0;
|
---|
522 | else if (l <= 0.5)
|
---|
523 | s = delta / (max + min);
|
---|
524 | else
|
---|
525 | s = delta / (2 - max - min);
|
---|
526 |
|
---|
527 | return [h, s * 100, l * 100];
|
---|
528 | }
|
---|
529 |
|
---|
530 | function rgb2hsv(rgb) {
|
---|
531 | var r = rgb[0],
|
---|
532 | g = rgb[1],
|
---|
533 | b = rgb[2],
|
---|
534 | min = Math.min(r, g, b),
|
---|
535 | max = Math.max(r, g, b),
|
---|
536 | delta = max - min,
|
---|
537 | h, s, v;
|
---|
538 |
|
---|
539 | if (max == 0)
|
---|
540 | s = 0;
|
---|
541 | else
|
---|
542 | s = (delta/max * 1000)/10;
|
---|
543 |
|
---|
544 | if (max == min)
|
---|
545 | h = 0;
|
---|
546 | else if (r == max)
|
---|
547 | h = (g - b) / delta;
|
---|
548 | else if (g == max)
|
---|
549 | h = 2 + (b - r) / delta;
|
---|
550 | else if (b == max)
|
---|
551 | h = 4 + (r - g) / delta;
|
---|
552 |
|
---|
553 | h = Math.min(h * 60, 360);
|
---|
554 |
|
---|
555 | if (h < 0)
|
---|
556 | h += 360;
|
---|
557 |
|
---|
558 | v = ((max / 255) * 1000) / 10;
|
---|
559 |
|
---|
560 | return [h, s, v];
|
---|
561 | }
|
---|
562 |
|
---|
563 | function rgb2hwb(rgb) {
|
---|
564 | var r = rgb[0],
|
---|
565 | g = rgb[1],
|
---|
566 | b = rgb[2],
|
---|
567 | h = rgb2hsl(rgb)[0],
|
---|
568 | w = 1/255 * Math.min(r, Math.min(g, b)),
|
---|
569 | b = 1 - 1/255 * Math.max(r, Math.max(g, b));
|
---|
570 |
|
---|
571 | return [h, w * 100, b * 100];
|
---|
572 | }
|
---|
573 |
|
---|
574 | function rgb2cmyk(rgb) {
|
---|
575 | var r = rgb[0] / 255,
|
---|
576 | g = rgb[1] / 255,
|
---|
577 | b = rgb[2] / 255,
|
---|
578 | c, m, y, k;
|
---|
579 |
|
---|
580 | k = Math.min(1 - r, 1 - g, 1 - b);
|
---|
581 | c = (1 - r - k) / (1 - k) || 0;
|
---|
582 | m = (1 - g - k) / (1 - k) || 0;
|
---|
583 | y = (1 - b - k) / (1 - k) || 0;
|
---|
584 | return [c * 100, m * 100, y * 100, k * 100];
|
---|
585 | }
|
---|
586 |
|
---|
587 | function rgb2keyword(rgb) {
|
---|
588 | return reverseKeywords[JSON.stringify(rgb)];
|
---|
589 | }
|
---|
590 |
|
---|
591 | function rgb2xyz(rgb) {
|
---|
592 | var r = rgb[0] / 255,
|
---|
593 | g = rgb[1] / 255,
|
---|
594 | b = rgb[2] / 255;
|
---|
595 |
|
---|
596 | // assume sRGB
|
---|
597 | r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
|
---|
598 | g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
|
---|
599 | b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
|
---|
600 |
|
---|
601 | var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
|
---|
602 | var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
|
---|
603 | var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
|
---|
604 |
|
---|
605 | return [x * 100, y *100, z * 100];
|
---|
606 | }
|
---|
607 |
|
---|
608 | function rgb2lab(rgb) {
|
---|
609 | var xyz = rgb2xyz(rgb),
|
---|
610 | x = xyz[0],
|
---|
611 | y = xyz[1],
|
---|
612 | z = xyz[2],
|
---|
613 | l, a, b;
|
---|
614 |
|
---|
615 | x /= 95.047;
|
---|
616 | y /= 100;
|
---|
617 | z /= 108.883;
|
---|
618 |
|
---|
619 | x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
|
---|
620 | y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
|
---|
621 | z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
|
---|
622 |
|
---|
623 | l = (116 * y) - 16;
|
---|
624 | a = 500 * (x - y);
|
---|
625 | b = 200 * (y - z);
|
---|
626 |
|
---|
627 | return [l, a, b];
|
---|
628 | }
|
---|
629 |
|
---|
630 | function rgb2lch(args) {
|
---|
631 | return lab2lch(rgb2lab(args));
|
---|
632 | }
|
---|
633 |
|
---|
634 | function hsl2rgb(hsl) {
|
---|
635 | var h = hsl[0] / 360,
|
---|
636 | s = hsl[1] / 100,
|
---|
637 | l = hsl[2] / 100,
|
---|
638 | t1, t2, t3, rgb, val;
|
---|
639 |
|
---|
640 | if (s == 0) {
|
---|
641 | val = l * 255;
|
---|
642 | return [val, val, val];
|
---|
643 | }
|
---|
644 |
|
---|
645 | if (l < 0.5)
|
---|
646 | t2 = l * (1 + s);
|
---|
647 | else
|
---|
648 | t2 = l + s - l * s;
|
---|
649 | t1 = 2 * l - t2;
|
---|
650 |
|
---|
651 | rgb = [0, 0, 0];
|
---|
652 | for (var i = 0; i < 3; i++) {
|
---|
653 | t3 = h + 1 / 3 * - (i - 1);
|
---|
654 | t3 < 0 && t3++;
|
---|
655 | t3 > 1 && t3--;
|
---|
656 |
|
---|
657 | if (6 * t3 < 1)
|
---|
658 | val = t1 + (t2 - t1) * 6 * t3;
|
---|
659 | else if (2 * t3 < 1)
|
---|
660 | val = t2;
|
---|
661 | else if (3 * t3 < 2)
|
---|
662 | val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
|
---|
663 | else
|
---|
664 | val = t1;
|
---|
665 |
|
---|
666 | rgb[i] = val * 255;
|
---|
667 | }
|
---|
668 |
|
---|
669 | return rgb;
|
---|
670 | }
|
---|
671 |
|
---|
672 | function hsl2hsv(hsl) {
|
---|
673 | var h = hsl[0],
|
---|
674 | s = hsl[1] / 100,
|
---|
675 | l = hsl[2] / 100,
|
---|
676 | sv, v;
|
---|
677 | l *= 2;
|
---|
678 | s *= (l <= 1) ? l : 2 - l;
|
---|
679 | v = (l + s) / 2;
|
---|
680 | sv = (2 * s) / (l + s);
|
---|
681 | return [h, sv * 100, v * 100];
|
---|
682 | }
|
---|
683 |
|
---|
684 | function hsl2hwb(args) {
|
---|
685 | return rgb2hwb(hsl2rgb(args));
|
---|
686 | }
|
---|
687 |
|
---|
688 | function hsl2cmyk(args) {
|
---|
689 | return rgb2cmyk(hsl2rgb(args));
|
---|
690 | }
|
---|
691 |
|
---|
692 | function hsl2keyword(args) {
|
---|
693 | return rgb2keyword(hsl2rgb(args));
|
---|
694 | }
|
---|
695 |
|
---|
696 |
|
---|
697 | function hsv2rgb(hsv) {
|
---|
698 | var h = hsv[0] / 60,
|
---|
699 | s = hsv[1] / 100,
|
---|
700 | v = hsv[2] / 100,
|
---|
701 | hi = Math.floor(h) % 6;
|
---|
702 |
|
---|
703 | var f = h - Math.floor(h),
|
---|
704 | p = 255 * v * (1 - s),
|
---|
705 | q = 255 * v * (1 - (s * f)),
|
---|
706 | t = 255 * v * (1 - (s * (1 - f))),
|
---|
707 | v = 255 * v;
|
---|
708 |
|
---|
709 | switch(hi) {
|
---|
710 | case 0:
|
---|
711 | return [v, t, p];
|
---|
712 | case 1:
|
---|
713 | return [q, v, p];
|
---|
714 | case 2:
|
---|
715 | return [p, v, t];
|
---|
716 | case 3:
|
---|
717 | return [p, q, v];
|
---|
718 | case 4:
|
---|
719 | return [t, p, v];
|
---|
720 | case 5:
|
---|
721 | return [v, p, q];
|
---|
722 | }
|
---|
723 | }
|
---|
724 |
|
---|
725 | function hsv2hsl(hsv) {
|
---|
726 | var h = hsv[0],
|
---|
727 | s = hsv[1] / 100,
|
---|
728 | v = hsv[2] / 100,
|
---|
729 | sl, l;
|
---|
730 |
|
---|
731 | l = (2 - s) * v;
|
---|
732 | sl = s * v;
|
---|
733 | sl /= (l <= 1) ? l : 2 - l;
|
---|
734 | sl = sl || 0;
|
---|
735 | l /= 2;
|
---|
736 | return [h, sl * 100, l * 100];
|
---|
737 | }
|
---|
738 |
|
---|
739 | function hsv2hwb(args) {
|
---|
740 | return rgb2hwb(hsv2rgb(args))
|
---|
741 | }
|
---|
742 |
|
---|
743 | function hsv2cmyk(args) {
|
---|
744 | return rgb2cmyk(hsv2rgb(args));
|
---|
745 | }
|
---|
746 |
|
---|
747 | function hsv2keyword(args) {
|
---|
748 | return rgb2keyword(hsv2rgb(args));
|
---|
749 | }
|
---|
750 |
|
---|
751 | // http://dev.w3.org/csswg/css-color/#hwb-to-rgb
|
---|
752 | function hwb2rgb(hwb) {
|
---|
753 | var h = hwb[0] / 360,
|
---|
754 | wh = hwb[1] / 100,
|
---|
755 | bl = hwb[2] / 100,
|
---|
756 | ratio = wh + bl,
|
---|
757 | i, v, f, n;
|
---|
758 |
|
---|
759 | // wh + bl cant be > 1
|
---|
760 | if (ratio > 1) {
|
---|
761 | wh /= ratio;
|
---|
762 | bl /= ratio;
|
---|
763 | }
|
---|
764 |
|
---|
765 | i = Math.floor(6 * h);
|
---|
766 | v = 1 - bl;
|
---|
767 | f = 6 * h - i;
|
---|
768 | if ((i & 0x01) != 0) {
|
---|
769 | f = 1 - f;
|
---|
770 | }
|
---|
771 | n = wh + f * (v - wh); // linear interpolation
|
---|
772 |
|
---|
773 | switch (i) {
|
---|
774 | default:
|
---|
775 | case 6:
|
---|
776 | case 0: r = v; g = n; b = wh; break;
|
---|
777 | case 1: r = n; g = v; b = wh; break;
|
---|
778 | case 2: r = wh; g = v; b = n; break;
|
---|
779 | case 3: r = wh; g = n; b = v; break;
|
---|
780 | case 4: r = n; g = wh; b = v; break;
|
---|
781 | case 5: r = v; g = wh; b = n; break;
|
---|
782 | }
|
---|
783 |
|
---|
784 | return [r * 255, g * 255, b * 255];
|
---|
785 | }
|
---|
786 |
|
---|
787 | function hwb2hsl(args) {
|
---|
788 | return rgb2hsl(hwb2rgb(args));
|
---|
789 | }
|
---|
790 |
|
---|
791 | function hwb2hsv(args) {
|
---|
792 | return rgb2hsv(hwb2rgb(args));
|
---|
793 | }
|
---|
794 |
|
---|
795 | function hwb2cmyk(args) {
|
---|
796 | return rgb2cmyk(hwb2rgb(args));
|
---|
797 | }
|
---|
798 |
|
---|
799 | function hwb2keyword(args) {
|
---|
800 | return rgb2keyword(hwb2rgb(args));
|
---|
801 | }
|
---|
802 |
|
---|
803 | function cmyk2rgb(cmyk) {
|
---|
804 | var c = cmyk[0] / 100,
|
---|
805 | m = cmyk[1] / 100,
|
---|
806 | y = cmyk[2] / 100,
|
---|
807 | k = cmyk[3] / 100,
|
---|
808 | r, g, b;
|
---|
809 |
|
---|
810 | r = 1 - Math.min(1, c * (1 - k) + k);
|
---|
811 | g = 1 - Math.min(1, m * (1 - k) + k);
|
---|
812 | b = 1 - Math.min(1, y * (1 - k) + k);
|
---|
813 | return [r * 255, g * 255, b * 255];
|
---|
814 | }
|
---|
815 |
|
---|
816 | function cmyk2hsl(args) {
|
---|
817 | return rgb2hsl(cmyk2rgb(args));
|
---|
818 | }
|
---|
819 |
|
---|
820 | function cmyk2hsv(args) {
|
---|
821 | return rgb2hsv(cmyk2rgb(args));
|
---|
822 | }
|
---|
823 |
|
---|
824 | function cmyk2hwb(args) {
|
---|
825 | return rgb2hwb(cmyk2rgb(args));
|
---|
826 | }
|
---|
827 |
|
---|
828 | function cmyk2keyword(args) {
|
---|
829 | return rgb2keyword(cmyk2rgb(args));
|
---|
830 | }
|
---|
831 |
|
---|
832 |
|
---|
833 | function xyz2rgb(xyz) {
|
---|
834 | var x = xyz[0] / 100,
|
---|
835 | y = xyz[1] / 100,
|
---|
836 | z = xyz[2] / 100,
|
---|
837 | r, g, b;
|
---|
838 |
|
---|
839 | r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
|
---|
840 | g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
|
---|
841 | b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
|
---|
842 |
|
---|
843 | // assume sRGB
|
---|
844 | r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
|
---|
845 | : r = (r * 12.92);
|
---|
846 |
|
---|
847 | g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
|
---|
848 | : g = (g * 12.92);
|
---|
849 |
|
---|
850 | b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
|
---|
851 | : b = (b * 12.92);
|
---|
852 |
|
---|
853 | r = Math.min(Math.max(0, r), 1);
|
---|
854 | g = Math.min(Math.max(0, g), 1);
|
---|
855 | b = Math.min(Math.max(0, b), 1);
|
---|
856 |
|
---|
857 | return [r * 255, g * 255, b * 255];
|
---|
858 | }
|
---|
859 |
|
---|
860 | function xyz2lab(xyz) {
|
---|
861 | var x = xyz[0],
|
---|
862 | y = xyz[1],
|
---|
863 | z = xyz[2],
|
---|
864 | l, a, b;
|
---|
865 |
|
---|
866 | x /= 95.047;
|
---|
867 | y /= 100;
|
---|
868 | z /= 108.883;
|
---|
869 |
|
---|
870 | x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
|
---|
871 | y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
|
---|
872 | z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
|
---|
873 |
|
---|
874 | l = (116 * y) - 16;
|
---|
875 | a = 500 * (x - y);
|
---|
876 | b = 200 * (y - z);
|
---|
877 |
|
---|
878 | return [l, a, b];
|
---|
879 | }
|
---|
880 |
|
---|
881 | function xyz2lch(args) {
|
---|
882 | return lab2lch(xyz2lab(args));
|
---|
883 | }
|
---|
884 |
|
---|
885 | function lab2xyz(lab) {
|
---|
886 | var l = lab[0],
|
---|
887 | a = lab[1],
|
---|
888 | b = lab[2],
|
---|
889 | x, y, z, y2;
|
---|
890 |
|
---|
891 | if (l <= 8) {
|
---|
892 | y = (l * 100) / 903.3;
|
---|
893 | y2 = (7.787 * (y / 100)) + (16 / 116);
|
---|
894 | } else {
|
---|
895 | y = 100 * Math.pow((l + 16) / 116, 3);
|
---|
896 | y2 = Math.pow(y / 100, 1/3);
|
---|
897 | }
|
---|
898 |
|
---|
899 | x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
|
---|
900 |
|
---|
901 | z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
|
---|
902 |
|
---|
903 | return [x, y, z];
|
---|
904 | }
|
---|
905 |
|
---|
906 | function lab2lch(lab) {
|
---|
907 | var l = lab[0],
|
---|
908 | a = lab[1],
|
---|
909 | b = lab[2],
|
---|
910 | hr, h, c;
|
---|
911 |
|
---|
912 | hr = Math.atan2(b, a);
|
---|
913 | h = hr * 360 / 2 / Math.PI;
|
---|
914 | if (h < 0) {
|
---|
915 | h += 360;
|
---|
916 | }
|
---|
917 | c = Math.sqrt(a * a + b * b);
|
---|
918 | return [l, c, h];
|
---|
919 | }
|
---|
920 |
|
---|
921 | function lab2rgb(args) {
|
---|
922 | return xyz2rgb(lab2xyz(args));
|
---|
923 | }
|
---|
924 |
|
---|
925 | function lch2lab(lch) {
|
---|
926 | var l = lch[0],
|
---|
927 | c = lch[1],
|
---|
928 | h = lch[2],
|
---|
929 | a, b, hr;
|
---|
930 |
|
---|
931 | hr = h / 360 * 2 * Math.PI;
|
---|
932 | a = c * Math.cos(hr);
|
---|
933 | b = c * Math.sin(hr);
|
---|
934 | return [l, a, b];
|
---|
935 | }
|
---|
936 |
|
---|
937 | function lch2xyz(args) {
|
---|
938 | return lab2xyz(lch2lab(args));
|
---|
939 | }
|
---|
940 |
|
---|
941 | function lch2rgb(args) {
|
---|
942 | return lab2rgb(lch2lab(args));
|
---|
943 | }
|
---|
944 |
|
---|
945 | function keyword2rgb(keyword) {
|
---|
946 | return cssKeywords[keyword];
|
---|
947 | }
|
---|
948 |
|
---|
949 | function keyword2hsl(args) {
|
---|
950 | return rgb2hsl(keyword2rgb(args));
|
---|
951 | }
|
---|
952 |
|
---|
953 | function keyword2hsv(args) {
|
---|
954 | return rgb2hsv(keyword2rgb(args));
|
---|
955 | }
|
---|
956 |
|
---|
957 | function keyword2hwb(args) {
|
---|
958 | return rgb2hwb(keyword2rgb(args));
|
---|
959 | }
|
---|
960 |
|
---|
961 | function keyword2cmyk(args) {
|
---|
962 | return rgb2cmyk(keyword2rgb(args));
|
---|
963 | }
|
---|
964 |
|
---|
965 | function keyword2lab(args) {
|
---|
966 | return rgb2lab(keyword2rgb(args));
|
---|
967 | }
|
---|
968 |
|
---|
969 | function keyword2xyz(args) {
|
---|
970 | return rgb2xyz(keyword2rgb(args));
|
---|
971 | }
|
---|
972 |
|
---|
973 | var cssKeywords = {
|
---|
974 | aliceblue: [240,248,255],
|
---|
975 | antiquewhite: [250,235,215],
|
---|
976 | aqua: [0,255,255],
|
---|
977 | aquamarine: [127,255,212],
|
---|
978 | azure: [240,255,255],
|
---|
979 | beige: [245,245,220],
|
---|
980 | bisque: [255,228,196],
|
---|
981 | black: [0,0,0],
|
---|
982 | blanchedalmond: [255,235,205],
|
---|
983 | blue: [0,0,255],
|
---|
984 | blueviolet: [138,43,226],
|
---|
985 | brown: [165,42,42],
|
---|
986 | burlywood: [222,184,135],
|
---|
987 | cadetblue: [95,158,160],
|
---|
988 | chartreuse: [127,255,0],
|
---|
989 | chocolate: [210,105,30],
|
---|
990 | coral: [255,127,80],
|
---|
991 | cornflowerblue: [100,149,237],
|
---|
992 | cornsilk: [255,248,220],
|
---|
993 | crimson: [220,20,60],
|
---|
994 | cyan: [0,255,255],
|
---|
995 | darkblue: [0,0,139],
|
---|
996 | darkcyan: [0,139,139],
|
---|
997 | darkgoldenrod: [184,134,11],
|
---|
998 | darkgray: [169,169,169],
|
---|
999 | darkgreen: [0,100,0],
|
---|
1000 | darkgrey: [169,169,169],
|
---|
1001 | darkkhaki: [189,183,107],
|
---|
1002 | darkmagenta: [139,0,139],
|
---|
1003 | darkolivegreen: [85,107,47],
|
---|
1004 | darkorange: [255,140,0],
|
---|
1005 | darkorchid: [153,50,204],
|
---|
1006 | darkred: [139,0,0],
|
---|
1007 | darksalmon: [233,150,122],
|
---|
1008 | darkseagreen: [143,188,143],
|
---|
1009 | darkslateblue: [72,61,139],
|
---|
1010 | darkslategray: [47,79,79],
|
---|
1011 | darkslategrey: [47,79,79],
|
---|
1012 | darkturquoise: [0,206,209],
|
---|
1013 | darkviolet: [148,0,211],
|
---|
1014 | deeppink: [255,20,147],
|
---|
1015 | deepskyblue: [0,191,255],
|
---|
1016 | dimgray: [105,105,105],
|
---|
1017 | dimgrey: [105,105,105],
|
---|
1018 | dodgerblue: [30,144,255],
|
---|
1019 | firebrick: [178,34,34],
|
---|
1020 | floralwhite: [255,250,240],
|
---|
1021 | forestgreen: [34,139,34],
|
---|
1022 | fuchsia: [255,0,255],
|
---|
1023 | gainsboro: [220,220,220],
|
---|
1024 | ghostwhite: [248,248,255],
|
---|
1025 | gold: [255,215,0],
|
---|
1026 | goldenrod: [218,165,32],
|
---|
1027 | gray: [128,128,128],
|
---|
1028 | green: [0,128,0],
|
---|
1029 | greenyellow: [173,255,47],
|
---|
1030 | grey: [128,128,128],
|
---|
1031 | honeydew: [240,255,240],
|
---|
1032 | hotpink: [255,105,180],
|
---|
1033 | indianred: [205,92,92],
|
---|
1034 | indigo: [75,0,130],
|
---|
1035 | ivory: [255,255,240],
|
---|
1036 | khaki: [240,230,140],
|
---|
1037 | lavender: [230,230,250],
|
---|
1038 | lavenderblush: [255,240,245],
|
---|
1039 | lawngreen: [124,252,0],
|
---|
1040 | lemonchiffon: [255,250,205],
|
---|
1041 | lightblue: [173,216,230],
|
---|
1042 | lightcoral: [240,128,128],
|
---|
1043 | lightcyan: [224,255,255],
|
---|
1044 | lightgoldenrodyellow: [250,250,210],
|
---|
1045 | lightgray: [211,211,211],
|
---|
1046 | lightgreen: [144,238,144],
|
---|
1047 | lightgrey: [211,211,211],
|
---|
1048 | lightpink: [255,182,193],
|
---|
1049 | lightsalmon: [255,160,122],
|
---|
1050 | lightseagreen: [32,178,170],
|
---|
1051 | lightskyblue: [135,206,250],
|
---|
1052 | lightslategray: [119,136,153],
|
---|
1053 | lightslategrey: [119,136,153],
|
---|
1054 | lightsteelblue: [176,196,222],
|
---|
1055 | lightyellow: [255,255,224],
|
---|
1056 | lime: [0,255,0],
|
---|
1057 | limegreen: [50,205,50],
|
---|
1058 | linen: [250,240,230],
|
---|
1059 | magenta: [255,0,255],
|
---|
1060 | maroon: [128,0,0],
|
---|
1061 | mediumaquamarine: [102,205,170],
|
---|
1062 | mediumblue: [0,0,205],
|
---|
1063 | mediumorchid: [186,85,211],
|
---|
1064 | mediumpurple: [147,112,219],
|
---|
1065 | mediumseagreen: [60,179,113],
|
---|
1066 | mediumslateblue: [123,104,238],
|
---|
1067 | mediumspringgreen: [0,250,154],
|
---|
1068 | mediumturquoise: [72,209,204],
|
---|
1069 | mediumvioletred: [199,21,133],
|
---|
1070 | midnightblue: [25,25,112],
|
---|
1071 | mintcream: [245,255,250],
|
---|
1072 | mistyrose: [255,228,225],
|
---|
1073 | moccasin: [255,228,181],
|
---|
1074 | navajowhite: [255,222,173],
|
---|
1075 | navy: [0,0,128],
|
---|
1076 | oldlace: [253,245,230],
|
---|
1077 | olive: [128,128,0],
|
---|
1078 | olivedrab: [107,142,35],
|
---|
1079 | orange: [255,165,0],
|
---|
1080 | orangered: [255,69,0],
|
---|
1081 | orchid: [218,112,214],
|
---|
1082 | palegoldenrod: [238,232,170],
|
---|
1083 | palegreen: [152,251,152],
|
---|
1084 | paleturquoise: [175,238,238],
|
---|
1085 | palevioletred: [219,112,147],
|
---|
1086 | papayawhip: [255,239,213],
|
---|
1087 | peachpuff: [255,218,185],
|
---|
1088 | peru: [205,133,63],
|
---|
1089 | pink: [255,192,203],
|
---|
1090 | plum: [221,160,221],
|
---|
1091 | powderblue: [176,224,230],
|
---|
1092 | purple: [128,0,128],
|
---|
1093 | rebeccapurple: [102, 51, 153],
|
---|
1094 | red: [255,0,0],
|
---|
1095 | rosybrown: [188,143,143],
|
---|
1096 | royalblue: [65,105,225],
|
---|
1097 | saddlebrown: [139,69,19],
|
---|
1098 | salmon: [250,128,114],
|
---|
1099 | sandybrown: [244,164,96],
|
---|
1100 | seagreen: [46,139,87],
|
---|
1101 | seashell: [255,245,238],
|
---|
1102 | sienna: [160,82,45],
|
---|
1103 | silver: [192,192,192],
|
---|
1104 | skyblue: [135,206,235],
|
---|
1105 | slateblue: [106,90,205],
|
---|
1106 | slategray: [112,128,144],
|
---|
1107 | slategrey: [112,128,144],
|
---|
1108 | snow: [255,250,250],
|
---|
1109 | springgreen: [0,255,127],
|
---|
1110 | steelblue: [70,130,180],
|
---|
1111 | tan: [210,180,140],
|
---|
1112 | teal: [0,128,128],
|
---|
1113 | thistle: [216,191,216],
|
---|
1114 | tomato: [255,99,71],
|
---|
1115 | turquoise: [64,224,208],
|
---|
1116 | violet: [238,130,238],
|
---|
1117 | wheat: [245,222,179],
|
---|
1118 | white: [255,255,255],
|
---|
1119 | whitesmoke: [245,245,245],
|
---|
1120 | yellow: [255,255,0],
|
---|
1121 | yellowgreen: [154,205,50]
|
---|
1122 | };
|
---|
1123 |
|
---|
1124 | var reverseKeywords = {};
|
---|
1125 | for (var key in cssKeywords) {
|
---|
1126 | reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
|
---|
1127 | }
|
---|
1128 |
|
---|
1129 | },{}],3:[function(require,module,exports){
|
---|
1130 | var conversions = require("./conversions");
|
---|
1131 |
|
---|
1132 | var convert = function() {
|
---|
1133 | return new Converter();
|
---|
1134 | }
|
---|
1135 |
|
---|
1136 | for (var func in conversions) {
|
---|
1137 | // export Raw versions
|
---|
1138 | convert[func + "Raw"] = (function(func) {
|
---|
1139 | // accept array or plain args
|
---|
1140 | return function(arg) {
|
---|
1141 | if (typeof arg == "number")
|
---|
1142 | arg = Array.prototype.slice.call(arguments);
|
---|
1143 | return conversions[func](arg);
|
---|
1144 | }
|
---|
1145 | })(func);
|
---|
1146 |
|
---|
1147 | var pair = /(\w+)2(\w+)/.exec(func),
|
---|
1148 | from = pair[1],
|
---|
1149 | to = pair[2];
|
---|
1150 |
|
---|
1151 | // export rgb2hsl and ["rgb"]["hsl"]
|
---|
1152 | convert[from] = convert[from] || {};
|
---|
1153 |
|
---|
1154 | convert[from][to] = convert[func] = (function(func) {
|
---|
1155 | return function(arg) {
|
---|
1156 | if (typeof arg == "number")
|
---|
1157 | arg = Array.prototype.slice.call(arguments);
|
---|
1158 |
|
---|
1159 | var val = conversions[func](arg);
|
---|
1160 | if (typeof val == "string" || val === undefined)
|
---|
1161 | return val; // keyword
|
---|
1162 |
|
---|
1163 | for (var i = 0; i < val.length; i++)
|
---|
1164 | val[i] = Math.round(val[i]);
|
---|
1165 | return val;
|
---|
1166 | }
|
---|
1167 | })(func);
|
---|
1168 | }
|
---|
1169 |
|
---|
1170 |
|
---|
1171 | /* Converter does lazy conversion and caching */
|
---|
1172 | var Converter = function() {
|
---|
1173 | this.convs = {};
|
---|
1174 | };
|
---|
1175 |
|
---|
1176 | /* Either get the values for a space or
|
---|
1177 | set the values for a space, depending on args */
|
---|
1178 | Converter.prototype.routeSpace = function(space, args) {
|
---|
1179 | var values = args[0];
|
---|
1180 | if (values === undefined) {
|
---|
1181 | // color.rgb()
|
---|
1182 | return this.getValues(space);
|
---|
1183 | }
|
---|
1184 | // color.rgb(10, 10, 10)
|
---|
1185 | if (typeof values == "number") {
|
---|
1186 | values = Array.prototype.slice.call(args);
|
---|
1187 | }
|
---|
1188 |
|
---|
1189 | return this.setValues(space, values);
|
---|
1190 | };
|
---|
1191 |
|
---|
1192 | /* Set the values for a space, invalidating cache */
|
---|
1193 | Converter.prototype.setValues = function(space, values) {
|
---|
1194 | this.space = space;
|
---|
1195 | this.convs = {};
|
---|
1196 | this.convs[space] = values;
|
---|
1197 | return this;
|
---|
1198 | };
|
---|
1199 |
|
---|
1200 | /* Get the values for a space. If there's already
|
---|
1201 | a conversion for the space, fetch it, otherwise
|
---|
1202 | compute it */
|
---|
1203 | Converter.prototype.getValues = function(space) {
|
---|
1204 | var vals = this.convs[space];
|
---|
1205 | if (!vals) {
|
---|
1206 | var fspace = this.space,
|
---|
1207 | from = this.convs[fspace];
|
---|
1208 | vals = convert[fspace][space](from);
|
---|
1209 |
|
---|
1210 | this.convs[space] = vals;
|
---|
1211 | }
|
---|
1212 | return vals;
|
---|
1213 | };
|
---|
1214 |
|
---|
1215 | ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
|
---|
1216 | Converter.prototype[space] = function(vals) {
|
---|
1217 | return this.routeSpace(space, arguments);
|
---|
1218 | }
|
---|
1219 | });
|
---|
1220 |
|
---|
1221 | module.exports = convert;
|
---|
1222 | },{"./conversions":2}],4:[function(require,module,exports){
|
---|
1223 | /* MIT license */
|
---|
1224 | var colorNames = require('color-name');
|
---|
1225 |
|
---|
1226 | module.exports = {
|
---|
1227 | getRgba: getRgba,
|
---|
1228 | getHsla: getHsla,
|
---|
1229 | getRgb: getRgb,
|
---|
1230 | getHsl: getHsl,
|
---|
1231 | getHwb: getHwb,
|
---|
1232 | getAlpha: getAlpha,
|
---|
1233 |
|
---|
1234 | hexString: hexString,
|
---|
1235 | rgbString: rgbString,
|
---|
1236 | rgbaString: rgbaString,
|
---|
1237 | percentString: percentString,
|
---|
1238 | percentaString: percentaString,
|
---|
1239 | hslString: hslString,
|
---|
1240 | hslaString: hslaString,
|
---|
1241 | hwbString: hwbString,
|
---|
1242 | keyword: keyword
|
---|
1243 | }
|
---|
1244 |
|
---|
1245 | function getRgba(string) {
|
---|
1246 | if (!string) {
|
---|
1247 | return;
|
---|
1248 | }
|
---|
1249 | var abbr = /^#([a-fA-F0-9]{3})$/,
|
---|
1250 | hex = /^#([a-fA-F0-9]{6})$/,
|
---|
1251 | rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
|
---|
1252 | per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/,
|
---|
1253 | keyword = /(\D+)/;
|
---|
1254 |
|
---|
1255 | var rgb = [0, 0, 0],
|
---|
1256 | a = 1,
|
---|
1257 | match = string.match(abbr);
|
---|
1258 | if (match) {
|
---|
1259 | match = match[1];
|
---|
1260 | for (var i = 0; i < rgb.length; i++) {
|
---|
1261 | rgb[i] = parseInt(match[i] + match[i], 16);
|
---|
1262 | }
|
---|
1263 | }
|
---|
1264 | else if (match = string.match(hex)) {
|
---|
1265 | match = match[1];
|
---|
1266 | for (var i = 0; i < rgb.length; i++) {
|
---|
1267 | rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16);
|
---|
1268 | }
|
---|
1269 | }
|
---|
1270 | else if (match = string.match(rgba)) {
|
---|
1271 | for (var i = 0; i < rgb.length; i++) {
|
---|
1272 | rgb[i] = parseInt(match[i + 1]);
|
---|
1273 | }
|
---|
1274 | a = parseFloat(match[4]);
|
---|
1275 | }
|
---|
1276 | else if (match = string.match(per)) {
|
---|
1277 | for (var i = 0; i < rgb.length; i++) {
|
---|
1278 | rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
|
---|
1279 | }
|
---|
1280 | a = parseFloat(match[4]);
|
---|
1281 | }
|
---|
1282 | else if (match = string.match(keyword)) {
|
---|
1283 | if (match[1] == "transparent") {
|
---|
1284 | return [0, 0, 0, 0];
|
---|
1285 | }
|
---|
1286 | rgb = colorNames[match[1]];
|
---|
1287 | if (!rgb) {
|
---|
1288 | return;
|
---|
1289 | }
|
---|
1290 | }
|
---|
1291 |
|
---|
1292 | for (var i = 0; i < rgb.length; i++) {
|
---|
1293 | rgb[i] = scale(rgb[i], 0, 255);
|
---|
1294 | }
|
---|
1295 | if (!a && a != 0) {
|
---|
1296 | a = 1;
|
---|
1297 | }
|
---|
1298 | else {
|
---|
1299 | a = scale(a, 0, 1);
|
---|
1300 | }
|
---|
1301 | rgb[3] = a;
|
---|
1302 | return rgb;
|
---|
1303 | }
|
---|
1304 |
|
---|
1305 | function getHsla(string) {
|
---|
1306 | if (!string) {
|
---|
1307 | return;
|
---|
1308 | }
|
---|
1309 | var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
---|
1310 | var match = string.match(hsl);
|
---|
1311 | if (match) {
|
---|
1312 | var alpha = parseFloat(match[4]);
|
---|
1313 | var h = scale(parseInt(match[1]), 0, 360),
|
---|
1314 | s = scale(parseFloat(match[2]), 0, 100),
|
---|
1315 | l = scale(parseFloat(match[3]), 0, 100),
|
---|
1316 | a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
---|
1317 | return [h, s, l, a];
|
---|
1318 | }
|
---|
1319 | }
|
---|
1320 |
|
---|
1321 | function getHwb(string) {
|
---|
1322 | if (!string) {
|
---|
1323 | return;
|
---|
1324 | }
|
---|
1325 | var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/;
|
---|
1326 | var match = string.match(hwb);
|
---|
1327 | if (match) {
|
---|
1328 | var alpha = parseFloat(match[4]);
|
---|
1329 | var h = scale(parseInt(match[1]), 0, 360),
|
---|
1330 | w = scale(parseFloat(match[2]), 0, 100),
|
---|
1331 | b = scale(parseFloat(match[3]), 0, 100),
|
---|
1332 | a = scale(isNaN(alpha) ? 1 : alpha, 0, 1);
|
---|
1333 | return [h, w, b, a];
|
---|
1334 | }
|
---|
1335 | }
|
---|
1336 |
|
---|
1337 | function getRgb(string) {
|
---|
1338 | var rgba = getRgba(string);
|
---|
1339 | return rgba && rgba.slice(0, 3);
|
---|
1340 | }
|
---|
1341 |
|
---|
1342 | function getHsl(string) {
|
---|
1343 | var hsla = getHsla(string);
|
---|
1344 | return hsla && hsla.slice(0, 3);
|
---|
1345 | }
|
---|
1346 |
|
---|
1347 | function getAlpha(string) {
|
---|
1348 | var vals = getRgba(string);
|
---|
1349 | if (vals) {
|
---|
1350 | return vals[3];
|
---|
1351 | }
|
---|
1352 | else if (vals = getHsla(string)) {
|
---|
1353 | return vals[3];
|
---|
1354 | }
|
---|
1355 | else if (vals = getHwb(string)) {
|
---|
1356 | return vals[3];
|
---|
1357 | }
|
---|
1358 | }
|
---|
1359 |
|
---|
1360 | // generators
|
---|
1361 | function hexString(rgb) {
|
---|
1362 | return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1])
|
---|
1363 | + hexDouble(rgb[2]);
|
---|
1364 | }
|
---|
1365 |
|
---|
1366 | function rgbString(rgba, alpha) {
|
---|
1367 | if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
---|
1368 | return rgbaString(rgba, alpha);
|
---|
1369 | }
|
---|
1370 | return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")";
|
---|
1371 | }
|
---|
1372 |
|
---|
1373 | function rgbaString(rgba, alpha) {
|
---|
1374 | if (alpha === undefined) {
|
---|
1375 | alpha = (rgba[3] !== undefined ? rgba[3] : 1);
|
---|
1376 | }
|
---|
1377 | return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2]
|
---|
1378 | + ", " + alpha + ")";
|
---|
1379 | }
|
---|
1380 |
|
---|
1381 | function percentString(rgba, alpha) {
|
---|
1382 | if (alpha < 1 || (rgba[3] && rgba[3] < 1)) {
|
---|
1383 | return percentaString(rgba, alpha);
|
---|
1384 | }
|
---|
1385 | var r = Math.round(rgba[0]/255 * 100),
|
---|
1386 | g = Math.round(rgba[1]/255 * 100),
|
---|
1387 | b = Math.round(rgba[2]/255 * 100);
|
---|
1388 |
|
---|
1389 | return "rgb(" + r + "%, " + g + "%, " + b + "%)";
|
---|
1390 | }
|
---|
1391 |
|
---|
1392 | function percentaString(rgba, alpha) {
|
---|
1393 | var r = Math.round(rgba[0]/255 * 100),
|
---|
1394 | g = Math.round(rgba[1]/255 * 100),
|
---|
1395 | b = Math.round(rgba[2]/255 * 100);
|
---|
1396 | return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")";
|
---|
1397 | }
|
---|
1398 |
|
---|
1399 | function hslString(hsla, alpha) {
|
---|
1400 | if (alpha < 1 || (hsla[3] && hsla[3] < 1)) {
|
---|
1401 | return hslaString(hsla, alpha);
|
---|
1402 | }
|
---|
1403 | return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)";
|
---|
1404 | }
|
---|
1405 |
|
---|
1406 | function hslaString(hsla, alpha) {
|
---|
1407 | if (alpha === undefined) {
|
---|
1408 | alpha = (hsla[3] !== undefined ? hsla[3] : 1);
|
---|
1409 | }
|
---|
1410 | return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, "
|
---|
1411 | + alpha + ")";
|
---|
1412 | }
|
---|
1413 |
|
---|
1414 | // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
|
---|
1415 | // (hwb have alpha optional & 1 is default value)
|
---|
1416 | function hwbString(hwb, alpha) {
|
---|
1417 | if (alpha === undefined) {
|
---|
1418 | alpha = (hwb[3] !== undefined ? hwb[3] : 1);
|
---|
1419 | }
|
---|
1420 | return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%"
|
---|
1421 | + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")";
|
---|
1422 | }
|
---|
1423 |
|
---|
1424 | function keyword(rgb) {
|
---|
1425 | return reverseNames[rgb.slice(0, 3)];
|
---|
1426 | }
|
---|
1427 |
|
---|
1428 | // helpers
|
---|
1429 | function scale(num, min, max) {
|
---|
1430 | return Math.min(Math.max(min, num), max);
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 | function hexDouble(num) {
|
---|
1434 | var str = num.toString(16).toUpperCase();
|
---|
1435 | return (str.length < 2) ? "0" + str : str;
|
---|
1436 | }
|
---|
1437 |
|
---|
1438 |
|
---|
1439 | //create a list of reverse color names
|
---|
1440 | var reverseNames = {};
|
---|
1441 | for (var name in colorNames) {
|
---|
1442 | reverseNames[colorNames[name]] = name;
|
---|
1443 | }
|
---|
1444 |
|
---|
1445 | },{"color-name":5}],5:[function(require,module,exports){
|
---|
1446 | module.exports={
|
---|
1447 | "aliceblue": [240, 248, 255],
|
---|
1448 | "antiquewhite": [250, 235, 215],
|
---|
1449 | "aqua": [0, 255, 255],
|
---|
1450 | "aquamarine": [127, 255, 212],
|
---|
1451 | "azure": [240, 255, 255],
|
---|
1452 | "beige": [245, 245, 220],
|
---|
1453 | "bisque": [255, 228, 196],
|
---|
1454 | "black": [0, 0, 0],
|
---|
1455 | "blanchedalmond": [255, 235, 205],
|
---|
1456 | "blue": [0, 0, 255],
|
---|
1457 | "blueviolet": [138, 43, 226],
|
---|
1458 | "brown": [165, 42, 42],
|
---|
1459 | "burlywood": [222, 184, 135],
|
---|
1460 | "cadetblue": [95, 158, 160],
|
---|
1461 | "chartreuse": [127, 255, 0],
|
---|
1462 | "chocolate": [210, 105, 30],
|
---|
1463 | "coral": [255, 127, 80],
|
---|
1464 | "cornflowerblue": [100, 149, 237],
|
---|
1465 | "cornsilk": [255, 248, 220],
|
---|
1466 | "crimson": [220, 20, 60],
|
---|
1467 | "cyan": [0, 255, 255],
|
---|
1468 | "darkblue": [0, 0, 139],
|
---|
1469 | "darkcyan": [0, 139, 139],
|
---|
1470 | "darkgoldenrod": [184, 134, 11],
|
---|
1471 | "darkgray": [169, 169, 169],
|
---|
1472 | "darkgreen": [0, 100, 0],
|
---|
1473 | "darkgrey": [169, 169, 169],
|
---|
1474 | "darkkhaki": [189, 183, 107],
|
---|
1475 | "darkmagenta": [139, 0, 139],
|
---|
1476 | "darkolivegreen": [85, 107, 47],
|
---|
1477 | "darkorange": [255, 140, 0],
|
---|
1478 | "darkorchid": [153, 50, 204],
|
---|
1479 | "darkred": [139, 0, 0],
|
---|
1480 | "darksalmon": [233, 150, 122],
|
---|
1481 | "darkseagreen": [143, 188, 143],
|
---|
1482 | "darkslateblue": [72, 61, 139],
|
---|
1483 | "darkslategray": [47, 79, 79],
|
---|
1484 | "darkslategrey": [47, 79, 79],
|
---|
1485 | "darkturquoise": [0, 206, 209],
|
---|
1486 | "darkviolet": [148, 0, 211],
|
---|
1487 | "deeppink": [255, 20, 147],
|
---|
1488 | "deepskyblue": [0, 191, 255],
|
---|
1489 | "dimgray": [105, 105, 105],
|
---|
1490 | "dimgrey": [105, 105, 105],
|
---|
1491 | "dodgerblue": [30, 144, 255],
|
---|
1492 | "firebrick": [178, 34, 34],
|
---|
1493 | "floralwhite": [255, 250, 240],
|
---|
1494 | "forestgreen": [34, 139, 34],
|
---|
1495 | "fuchsia": [255, 0, 255],
|
---|
1496 | "gainsboro": [220, 220, 220],
|
---|
1497 | "ghostwhite": [248, 248, 255],
|
---|
1498 | "gold": [255, 215, 0],
|
---|
1499 | "goldenrod": [218, 165, 32],
|
---|
1500 | "gray": [128, 128, 128],
|
---|
1501 | "green": [0, 128, 0],
|
---|
1502 | "greenyellow": [173, 255, 47],
|
---|
1503 | "grey": [128, 128, 128],
|
---|
1504 | "honeydew": [240, 255, 240],
|
---|
1505 | "hotpink": [255, 105, 180],
|
---|
1506 | "indianred": [205, 92, 92],
|
---|
1507 | "indigo": [75, 0, 130],
|
---|
1508 | "ivory": [255, 255, 240],
|
---|
1509 | "khaki": [240, 230, 140],
|
---|
1510 | "lavender": [230, 230, 250],
|
---|
1511 | "lavenderblush": [255, 240, 245],
|
---|
1512 | "lawngreen": [124, 252, 0],
|
---|
1513 | "lemonchiffon": [255, 250, 205],
|
---|
1514 | "lightblue": [173, 216, 230],
|
---|
1515 | "lightcoral": [240, 128, 128],
|
---|
1516 | "lightcyan": [224, 255, 255],
|
---|
1517 | "lightgoldenrodyellow": [250, 250, 210],
|
---|
1518 | "lightgray": [211, 211, 211],
|
---|
1519 | "lightgreen": [144, 238, 144],
|
---|
1520 | "lightgrey": [211, 211, 211],
|
---|
1521 | "lightpink": [255, 182, 193],
|
---|
1522 | "lightsalmon": [255, 160, 122],
|
---|
1523 | "lightseagreen": [32, 178, 170],
|
---|
1524 | "lightskyblue": [135, 206, 250],
|
---|
1525 | "lightslategray": [119, 136, 153],
|
---|
1526 | "lightslategrey": [119, 136, 153],
|
---|
1527 | "lightsteelblue": [176, 196, 222],
|
---|
1528 | "lightyellow": [255, 255, 224],
|
---|
1529 | "lime": [0, 255, 0],
|
---|
1530 | "limegreen": [50, 205, 50],
|
---|
1531 | "linen": [250, 240, 230],
|
---|
1532 | "magenta": [255, 0, 255],
|
---|
1533 | "maroon": [128, 0, 0],
|
---|
1534 | "mediumaquamarine": [102, 205, 170],
|
---|
1535 | "mediumblue": [0, 0, 205],
|
---|
1536 | "mediumorchid": [186, 85, 211],
|
---|
1537 | "mediumpurple": [147, 112, 219],
|
---|
1538 | "mediumseagreen": [60, 179, 113],
|
---|
1539 | "mediumslateblue": [123, 104, 238],
|
---|
1540 | "mediumspringgreen": [0, 250, 154],
|
---|
1541 | "mediumturquoise": [72, 209, 204],
|
---|
1542 | "mediumvioletred": [199, 21, 133],
|
---|
1543 | "midnightblue": [25, 25, 112],
|
---|
1544 | "mintcream": [245, 255, 250],
|
---|
1545 | "mistyrose": [255, 228, 225],
|
---|
1546 | "moccasin": [255, 228, 181],
|
---|
1547 | "navajowhite": [255, 222, 173],
|
---|
1548 | "navy": [0, 0, 128],
|
---|
1549 | "oldlace": [253, 245, 230],
|
---|
1550 | "olive": [128, 128, 0],
|
---|
1551 | "olivedrab": [107, 142, 35],
|
---|
1552 | "orange": [255, 165, 0],
|
---|
1553 | "orangered": [255, 69, 0],
|
---|
1554 | "orchid": [218, 112, 214],
|
---|
1555 | "palegoldenrod": [238, 232, 170],
|
---|
1556 | "palegreen": [152, 251, 152],
|
---|
1557 | "paleturquoise": [175, 238, 238],
|
---|
1558 | "palevioletred": [219, 112, 147],
|
---|
1559 | "papayawhip": [255, 239, 213],
|
---|
1560 | "peachpuff": [255, 218, 185],
|
---|
1561 | "peru": [205, 133, 63],
|
---|
1562 | "pink": [255, 192, 203],
|
---|
1563 | "plum": [221, 160, 221],
|
---|
1564 | "powderblue": [176, 224, 230],
|
---|
1565 | "purple": [128, 0, 128],
|
---|
1566 | "rebeccapurple": [102, 51, 153],
|
---|
1567 | "red": [255, 0, 0],
|
---|
1568 | "rosybrown": [188, 143, 143],
|
---|
1569 | "royalblue": [65, 105, 225],
|
---|
1570 | "saddlebrown": [139, 69, 19],
|
---|
1571 | "salmon": [250, 128, 114],
|
---|
1572 | "sandybrown": [244, 164, 96],
|
---|
1573 | "seagreen": [46, 139, 87],
|
---|
1574 | "seashell": [255, 245, 238],
|
---|
1575 | "sienna": [160, 82, 45],
|
---|
1576 | "silver": [192, 192, 192],
|
---|
1577 | "skyblue": [135, 206, 235],
|
---|
1578 | "slateblue": [106, 90, 205],
|
---|
1579 | "slategray": [112, 128, 144],
|
---|
1580 | "slategrey": [112, 128, 144],
|
---|
1581 | "snow": [255, 250, 250],
|
---|
1582 | "springgreen": [0, 255, 127],
|
---|
1583 | "steelblue": [70, 130, 180],
|
---|
1584 | "tan": [210, 180, 140],
|
---|
1585 | "teal": [0, 128, 128],
|
---|
1586 | "thistle": [216, 191, 216],
|
---|
1587 | "tomato": [255, 99, 71],
|
---|
1588 | "turquoise": [64, 224, 208],
|
---|
1589 | "violet": [238, 130, 238],
|
---|
1590 | "wheat": [245, 222, 179],
|
---|
1591 | "white": [255, 255, 255],
|
---|
1592 | "whitesmoke": [245, 245, 245],
|
---|
1593 | "yellow": [255, 255, 0],
|
---|
1594 | "yellowgreen": [154, 205, 50]
|
---|
1595 | }
|
---|
1596 | },{}],6:[function(require,module,exports){
|
---|
1597 | module.exports = require('./lib/geocrunch');
|
---|
1598 | },{"./lib/geocrunch":11}],7:[function(require,module,exports){
|
---|
1599 | // distance.js - Distance mixins for Paths
|
---|
1600 |
|
---|
1601 | var _ = require('underscore');
|
---|
1602 |
|
---|
1603 | var R = require('./constants').EARTHRADIUS;
|
---|
1604 | var units = require('./units');
|
---|
1605 | var flipCoords = require('./flipcoords');
|
---|
1606 |
|
---|
1607 | // Area conversions (from sqmeters)
|
---|
1608 | var convertFuncs = {
|
---|
1609 | sqmeters: function (a) {
|
---|
1610 | return a;
|
---|
1611 | },
|
---|
1612 | sqmiles: function (a) {
|
---|
1613 | return units.sqMeters.toSqMiles(a);
|
---|
1614 | },
|
---|
1615 | acres: function (a) {
|
---|
1616 | return units.sqMeters.toAcres(a);
|
---|
1617 | }
|
---|
1618 | };
|
---|
1619 |
|
---|
1620 | // Calculates area in square meters
|
---|
1621 | // Method taken from OpenLayers API, https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270
|
---|
1622 | var calcArea = function (coordArray) {
|
---|
1623 | var area = 0, i, l, c1, c2;
|
---|
1624 | for (i = 0, l = coordArray.length; i < l; i += 1) {
|
---|
1625 | c1 = coordArray[i];
|
---|
1626 | c2 = coordArray[(i + 1) % coordArray.length]; // Access next item in array until last item is i, then accesses first (0)
|
---|
1627 | area = area + units.degrees.toRadians(c2[0] - c1[0]) * (2 + Math.sin(units.degrees.toRadians(c1[1])) + Math.sin(units.degrees.toRadians(c2[1])));
|
---|
1628 | }
|
---|
1629 | return Math.abs(area * R * R / 2);
|
---|
1630 | };
|
---|
1631 |
|
---|
1632 | var calcCenter = function (coordArray) {
|
---|
1633 | var offset = coordArray[0], twiceArea = 0, x = 0, y = 0, i, l, c1, c2, f;
|
---|
1634 | if (coordArray.length === 1) {
|
---|
1635 | return coordArray[0];
|
---|
1636 | }
|
---|
1637 | for (i = 0, l = coordArray.length; i < l; i += 1) {
|
---|
1638 | c1 = coordArray[i];
|
---|
1639 | c2 = coordArray[(i + 1) % coordArray.length]; // Access next item in array until last item is i, then accesses first (0)
|
---|
1640 | f = (c1[1] - offset[1]) * (c2[0] - offset[0]) - (c2[1] - offset[1]) * (c1[0] - offset[0]);
|
---|
1641 | twiceArea = twiceArea + f;
|
---|
1642 | x = x + ((c1[0] + c2[0] - 2 * offset[0]) * f);
|
---|
1643 | y = y + ((c1[1] + c2[1] - 2 * offset[1]) * f);
|
---|
1644 | }
|
---|
1645 | f = twiceArea * 3;
|
---|
1646 | return [x / f + offset[0], y / f + offset[1]];
|
---|
1647 | };
|
---|
1648 |
|
---|
1649 | module.exports = {
|
---|
1650 | _internalAreaCalc: function () {
|
---|
1651 | // If not set, set this._calcedArea to total area in UNITS
|
---|
1652 | // Checks for cache to prevent additional unnecessary calcs
|
---|
1653 | if (!this._calcedArea) {
|
---|
1654 | if (this._coords.length < 3) {
|
---|
1655 | this._calcedArea = 0;
|
---|
1656 | } else {
|
---|
1657 | this._calcedArea = calcArea(this._coords);
|
---|
1658 | }
|
---|
1659 | }
|
---|
1660 | },
|
---|
1661 | _internalCenterCalc: function () {
|
---|
1662 | if (!this._calcedCenter && this._coords.length) {
|
---|
1663 | this._calcedCenter = calcCenter(this._coords);
|
---|
1664 | }
|
---|
1665 | },
|
---|
1666 | area: function (options) {
|
---|
1667 | var opts = _.extend({
|
---|
1668 | units: 'sqmeters'
|
---|
1669 | }, options);
|
---|
1670 | this._internalAreaCalc();
|
---|
1671 | if (_.isFunction(convertFuncs[opts.units])) {
|
---|
1672 | return convertFuncs[opts.units](this._calcedArea);
|
---|
1673 | }
|
---|
1674 | // TODO. Handle non-matching units
|
---|
1675 | },
|
---|
1676 | center: function () {
|
---|
1677 | this._internalCenterCalc();
|
---|
1678 | return this._options.imBackwards === true ? flipCoords(this._calcedCenter) : this._calcedCenter;
|
---|
1679 | }
|
---|
1680 | };
|
---|
1681 | },{"./constants":8,"./flipcoords":10,"./units":13,"underscore":14}],8:[function(require,module,exports){
|
---|
1682 | // utils/constants.js
|
---|
1683 |
|
---|
1684 | module.exports = {
|
---|
1685 | EARTHRADIUS: 6371000 // R in meters
|
---|
1686 | };
|
---|
1687 | },{}],9:[function(require,module,exports){
|
---|
1688 | // distance.js - Distance mixins for Paths
|
---|
1689 |
|
---|
1690 | var _ = require('underscore');
|
---|
1691 |
|
---|
1692 | var R = require('./constants').EARTHRADIUS;
|
---|
1693 | var units = require('./units');
|
---|
1694 |
|
---|
1695 | // Distance conversions (from meters)
|
---|
1696 | var convertFuncs = {
|
---|
1697 | meters: function (d) {
|
---|
1698 | return d;
|
---|
1699 | },
|
---|
1700 | kilometers: function (d) {
|
---|
1701 | return units.meters.toKilometers(d);
|
---|
1702 | },
|
---|
1703 | feet: function (d) {
|
---|
1704 | return units.meters.toFeet(d);
|
---|
1705 | },
|
---|
1706 | miles: function (d) {
|
---|
1707 | return units.meters.toMiles(d);
|
---|
1708 | }
|
---|
1709 | };
|
---|
1710 |
|
---|
1711 | // Distance in meters
|
---|
1712 | // Always positive regardless of direction
|
---|
1713 | // Calculation based on Haversine Formula http://en.wikipedia.org/wiki/Haversine_formula
|
---|
1714 | // Another method is @ http://www.movable-type.co.uk/scripts/latlong-vincenty.html but seems way overcomplicated
|
---|
1715 | var calcDistance = function (coord1, coord2) {
|
---|
1716 | var deltaLng = units.degrees.toRadians(coord1[0] - coord2[0]),
|
---|
1717 | deltaLat = units.degrees.toRadians(coord1[1] - coord2[1]),
|
---|
1718 | lat1 = units.degrees.toRadians(coord1[1]),
|
---|
1719 | lat2 = units.degrees.toRadians(coord2[1]),
|
---|
1720 | hvsLng = Math.sin(deltaLng / 2),
|
---|
1721 | hvsLat = Math.sin(deltaLat / 2);
|
---|
1722 |
|
---|
1723 | var a = hvsLat * hvsLat + hvsLng * hvsLng * Math.cos(lat1) * Math.cos(lat2);
|
---|
1724 | return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
---|
1725 | };
|
---|
1726 |
|
---|
1727 | module.exports = {
|
---|
1728 | _internalDistanceCalc: function () {
|
---|
1729 | // If not set, set this._calcedDistance to total distance in meters
|
---|
1730 | // Checks for cache to prevent additional unnecessary calcs
|
---|
1731 | var distance = 0, i, l;
|
---|
1732 | if (!this._calcedDistance) {
|
---|
1733 | for (i = 0, l = this._coords.length; i < l; i += 1) {
|
---|
1734 | if (i > 0) {
|
---|
1735 | distance = distance + calcDistance(this._coords[i - 1], this._coords[i]);
|
---|
1736 | }
|
---|
1737 | }
|
---|
1738 | this._calcedDistance = distance;
|
---|
1739 | }
|
---|
1740 | },
|
---|
1741 | distance: function (options) {
|
---|
1742 | var opts = _.extend({
|
---|
1743 | units: 'meters'
|
---|
1744 | }, options);
|
---|
1745 | this._internalDistanceCalc();
|
---|
1746 | if (_.isFunction(convertFuncs[opts.units])) {
|
---|
1747 | return convertFuncs[opts.units](this._calcedDistance);
|
---|
1748 | }
|
---|
1749 | // TODO. Handle non-matching units
|
---|
1750 | }
|
---|
1751 | };
|
---|
1752 | },{"./constants":8,"./units":13,"underscore":14}],10:[function(require,module,exports){
|
---|
1753 | // utils/flipcoords.js - Util functions for working with backwards coordinates [lat, lng]
|
---|
1754 |
|
---|
1755 | var _ = require('underscore');
|
---|
1756 |
|
---|
1757 | module.exports = function (backwardsCoordArray) {
|
---|
1758 | return _.map(backwardsCoordArray, function (backwardsCoord) {
|
---|
1759 | return [backwardsCoord[1], backwardsCoord[0]];
|
---|
1760 | });
|
---|
1761 | };
|
---|
1762 | },{"underscore":14}],11:[function(require,module,exports){
|
---|
1763 | // geocrunch.js
|
---|
1764 |
|
---|
1765 | var _ = require('underscore');
|
---|
1766 |
|
---|
1767 | var Path = require('./path');
|
---|
1768 | var distanceMixins = require('./distance'),
|
---|
1769 | areaMixins = require('./area');
|
---|
1770 |
|
---|
1771 | _.extend(Path.prototype, distanceMixins, areaMixins);
|
---|
1772 |
|
---|
1773 | exports.path = function (coords, options) {
|
---|
1774 | return new Path(coords, options);
|
---|
1775 | };
|
---|
1776 | },{"./area":7,"./distance":9,"./path":12,"underscore":14}],12:[function(require,module,exports){
|
---|
1777 | // path.js - Object for working with a linear path of coordinates
|
---|
1778 |
|
---|
1779 | var flipCoords = require('./flipcoords');
|
---|
1780 |
|
---|
1781 | var Path = function (coords, options) {
|
---|
1782 | this._options = options || {};
|
---|
1783 |
|
---|
1784 | // Set this._coords... Think about flipping at time of calcs for less iterations/better perf. May risk code clarity and mixin ease.
|
---|
1785 | coords = coords || [];
|
---|
1786 | this._coords = this._options.imBackwards === true ? flipCoords(coords) : coords;
|
---|
1787 | };
|
---|
1788 |
|
---|
1789 | module.exports = Path;
|
---|
1790 |
|
---|
1791 | },{"./flipcoords":10}],13:[function(require,module,exports){
|
---|
1792 | // units.js - Standard unit conversions
|
---|
1793 |
|
---|
1794 | exports.meters = {
|
---|
1795 | toFeet: function (m) {
|
---|
1796 | return m * 3.28084;
|
---|
1797 | },
|
---|
1798 | toKilometers: function (m) {
|
---|
1799 | return m * 0.001;
|
---|
1800 | },
|
---|
1801 | toMiles: function (m) {
|
---|
1802 | return m * 0.000621371;
|
---|
1803 | }
|
---|
1804 | };
|
---|
1805 |
|
---|
1806 | exports.sqMeters = {
|
---|
1807 | toSqMiles: function (m) {
|
---|
1808 | return m * 0.000000386102;
|
---|
1809 | },
|
---|
1810 | toAcres: function (m) {
|
---|
1811 | return m * 0.000247105;
|
---|
1812 | }
|
---|
1813 | };
|
---|
1814 |
|
---|
1815 | exports.degrees = {
|
---|
1816 | toRadians: function (d) {
|
---|
1817 | return d * Math.PI / 180;
|
---|
1818 | }
|
---|
1819 | };
|
---|
1820 | },{}],14:[function(require,module,exports){
|
---|
1821 | // Underscore.js 1.5.2
|
---|
1822 | // http://underscorejs.org
|
---|
1823 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
---|
1824 | // Underscore may be freely distributed under the MIT license.
|
---|
1825 |
|
---|
1826 | (function() {
|
---|
1827 |
|
---|
1828 | // Baseline setup
|
---|
1829 | // --------------
|
---|
1830 |
|
---|
1831 | // Establish the root object, `window` in the browser, or `exports` on the server.
|
---|
1832 | var root = this;
|
---|
1833 |
|
---|
1834 | // Save the previous value of the `_` variable.
|
---|
1835 | var previousUnderscore = root._;
|
---|
1836 |
|
---|
1837 | // Establish the object that gets returned to break out of a loop iteration.
|
---|
1838 | var breaker = {};
|
---|
1839 |
|
---|
1840 | // Save bytes in the minified (but not gzipped) version:
|
---|
1841 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
---|
1842 |
|
---|
1843 | // Create quick reference variables for speed access to core prototypes.
|
---|
1844 | var
|
---|
1845 | push = ArrayProto.push,
|
---|
1846 | slice = ArrayProto.slice,
|
---|
1847 | concat = ArrayProto.concat,
|
---|
1848 | toString = ObjProto.toString,
|
---|
1849 | hasOwnProperty = ObjProto.hasOwnProperty;
|
---|
1850 |
|
---|
1851 | // All **ECMAScript 5** native function implementations that we hope to use
|
---|
1852 | // are declared here.
|
---|
1853 | var
|
---|
1854 | nativeForEach = ArrayProto.forEach,
|
---|
1855 | nativeMap = ArrayProto.map,
|
---|
1856 | nativeReduce = ArrayProto.reduce,
|
---|
1857 | nativeReduceRight = ArrayProto.reduceRight,
|
---|
1858 | nativeFilter = ArrayProto.filter,
|
---|
1859 | nativeEvery = ArrayProto.every,
|
---|
1860 | nativeSome = ArrayProto.some,
|
---|
1861 | nativeIndexOf = ArrayProto.indexOf,
|
---|
1862 | nativeLastIndexOf = ArrayProto.lastIndexOf,
|
---|
1863 | nativeIsArray = Array.isArray,
|
---|
1864 | nativeKeys = Object.keys,
|
---|
1865 | nativeBind = FuncProto.bind;
|
---|
1866 |
|
---|
1867 | // Create a safe reference to the Underscore object for use below.
|
---|
1868 | var _ = function(obj) {
|
---|
1869 | if (obj instanceof _) return obj;
|
---|
1870 | if (!(this instanceof _)) return new _(obj);
|
---|
1871 | this._wrapped = obj;
|
---|
1872 | };
|
---|
1873 |
|
---|
1874 | // Export the Underscore object for **Node.js**, with
|
---|
1875 | // backwards-compatibility for the old `require()` API. If we're in
|
---|
1876 | // the browser, add `_` as a global object via a string identifier,
|
---|
1877 | // for Closure Compiler "advanced" mode.
|
---|
1878 | if (typeof exports !== 'undefined') {
|
---|
1879 | if (typeof module !== 'undefined' && module.exports) {
|
---|
1880 | exports = module.exports = _;
|
---|
1881 | }
|
---|
1882 | exports._ = _;
|
---|
1883 | } else {
|
---|
1884 | root._ = _;
|
---|
1885 | }
|
---|
1886 |
|
---|
1887 | // Current version.
|
---|
1888 | _.VERSION = '1.5.2';
|
---|
1889 |
|
---|
1890 | // Collection Functions
|
---|
1891 | // --------------------
|
---|
1892 |
|
---|
1893 | // The cornerstone, an `each` implementation, aka `forEach`.
|
---|
1894 | // Handles objects with the built-in `forEach`, arrays, and raw objects.
|
---|
1895 | // Delegates to **ECMAScript 5**'s native `forEach` if available.
|
---|
1896 | var each = _.each = _.forEach = function(obj, iterator, context) {
|
---|
1897 | if (obj == null) return;
|
---|
1898 | if (nativeForEach && obj.forEach === nativeForEach) {
|
---|
1899 | obj.forEach(iterator, context);
|
---|
1900 | } else if (obj.length === +obj.length) {
|
---|
1901 | for (var i = 0, length = obj.length; i < length; i++) {
|
---|
1902 | if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
---|
1903 | }
|
---|
1904 | } else {
|
---|
1905 | var keys = _.keys(obj);
|
---|
1906 | for (var i = 0, length = keys.length; i < length; i++) {
|
---|
1907 | if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
|
---|
1908 | }
|
---|
1909 | }
|
---|
1910 | };
|
---|
1911 |
|
---|
1912 | // Return the results of applying the iterator to each element.
|
---|
1913 | // Delegates to **ECMAScript 5**'s native `map` if available.
|
---|
1914 | _.map = _.collect = function(obj, iterator, context) {
|
---|
1915 | var results = [];
|
---|
1916 | if (obj == null) return results;
|
---|
1917 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
|
---|
1918 | each(obj, function(value, index, list) {
|
---|
1919 | results.push(iterator.call(context, value, index, list));
|
---|
1920 | });
|
---|
1921 | return results;
|
---|
1922 | };
|
---|
1923 |
|
---|
1924 | var reduceError = 'Reduce of empty array with no initial value';
|
---|
1925 |
|
---|
1926 | // **Reduce** builds up a single result from a list of values, aka `inject`,
|
---|
1927 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
---|
1928 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
---|
1929 | var initial = arguments.length > 2;
|
---|
1930 | if (obj == null) obj = [];
|
---|
1931 | if (nativeReduce && obj.reduce === nativeReduce) {
|
---|
1932 | if (context) iterator = _.bind(iterator, context);
|
---|
1933 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
|
---|
1934 | }
|
---|
1935 | each(obj, function(value, index, list) {
|
---|
1936 | if (!initial) {
|
---|
1937 | memo = value;
|
---|
1938 | initial = true;
|
---|
1939 | } else {
|
---|
1940 | memo = iterator.call(context, memo, value, index, list);
|
---|
1941 | }
|
---|
1942 | });
|
---|
1943 | if (!initial) throw new TypeError(reduceError);
|
---|
1944 | return memo;
|
---|
1945 | };
|
---|
1946 |
|
---|
1947 | // The right-associative version of reduce, also known as `foldr`.
|
---|
1948 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
|
---|
1949 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
|
---|
1950 | var initial = arguments.length > 2;
|
---|
1951 | if (obj == null) obj = [];
|
---|
1952 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
|
---|
1953 | if (context) iterator = _.bind(iterator, context);
|
---|
1954 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
---|
1955 | }
|
---|
1956 | var length = obj.length;
|
---|
1957 | if (length !== +length) {
|
---|
1958 | var keys = _.keys(obj);
|
---|
1959 | length = keys.length;
|
---|
1960 | }
|
---|
1961 | each(obj, function(value, index, list) {
|
---|
1962 | index = keys ? keys[--length] : --length;
|
---|
1963 | if (!initial) {
|
---|
1964 | memo = obj[index];
|
---|
1965 | initial = true;
|
---|
1966 | } else {
|
---|
1967 | memo = iterator.call(context, memo, obj[index], index, list);
|
---|
1968 | }
|
---|
1969 | });
|
---|
1970 | if (!initial) throw new TypeError(reduceError);
|
---|
1971 | return memo;
|
---|
1972 | };
|
---|
1973 |
|
---|
1974 | // Return the first value which passes a truth test. Aliased as `detect`.
|
---|
1975 | _.find = _.detect = function(obj, iterator, context) {
|
---|
1976 | var result;
|
---|
1977 | any(obj, function(value, index, list) {
|
---|
1978 | if (iterator.call(context, value, index, list)) {
|
---|
1979 | result = value;
|
---|
1980 | return true;
|
---|
1981 | }
|
---|
1982 | });
|
---|
1983 | return result;
|
---|
1984 | };
|
---|
1985 |
|
---|
1986 | // Return all the elements that pass a truth test.
|
---|
1987 | // Delegates to **ECMAScript 5**'s native `filter` if available.
|
---|
1988 | // Aliased as `select`.
|
---|
1989 | _.filter = _.select = function(obj, iterator, context) {
|
---|
1990 | var results = [];
|
---|
1991 | if (obj == null) return results;
|
---|
1992 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
|
---|
1993 | each(obj, function(value, index, list) {
|
---|
1994 | if (iterator.call(context, value, index, list)) results.push(value);
|
---|
1995 | });
|
---|
1996 | return results;
|
---|
1997 | };
|
---|
1998 |
|
---|
1999 | // Return all the elements for which a truth test fails.
|
---|
2000 | _.reject = function(obj, iterator, context) {
|
---|
2001 | return _.filter(obj, function(value, index, list) {
|
---|
2002 | return !iterator.call(context, value, index, list);
|
---|
2003 | }, context);
|
---|
2004 | };
|
---|
2005 |
|
---|
2006 | // Determine whether all of the elements match a truth test.
|
---|
2007 | // Delegates to **ECMAScript 5**'s native `every` if available.
|
---|
2008 | // Aliased as `all`.
|
---|
2009 | _.every = _.all = function(obj, iterator, context) {
|
---|
2010 | iterator || (iterator = _.identity);
|
---|
2011 | var result = true;
|
---|
2012 | if (obj == null) return result;
|
---|
2013 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
---|
2014 | each(obj, function(value, index, list) {
|
---|
2015 | if (!(result = result && iterator.call(context, value, index, list))) return breaker;
|
---|
2016 | });
|
---|
2017 | return !!result;
|
---|
2018 | };
|
---|
2019 |
|
---|
2020 | // Determine if at least one element in the object matches a truth test.
|
---|
2021 | // Delegates to **ECMAScript 5**'s native `some` if available.
|
---|
2022 | // Aliased as `any`.
|
---|
2023 | var any = _.some = _.any = function(obj, iterator, context) {
|
---|
2024 | iterator || (iterator = _.identity);
|
---|
2025 | var result = false;
|
---|
2026 | if (obj == null) return result;
|
---|
2027 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
|
---|
2028 | each(obj, function(value, index, list) {
|
---|
2029 | if (result || (result = iterator.call(context, value, index, list))) return breaker;
|
---|
2030 | });
|
---|
2031 | return !!result;
|
---|
2032 | };
|
---|
2033 |
|
---|
2034 | // Determine if the array or object contains a given value (using `===`).
|
---|
2035 | // Aliased as `include`.
|
---|
2036 | _.contains = _.include = function(obj, target) {
|
---|
2037 | if (obj == null) return false;
|
---|
2038 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
---|
2039 | return any(obj, function(value) {
|
---|
2040 | return value === target;
|
---|
2041 | });
|
---|
2042 | };
|
---|
2043 |
|
---|
2044 | // Invoke a method (with arguments) on every item in a collection.
|
---|
2045 | _.invoke = function(obj, method) {
|
---|
2046 | var args = slice.call(arguments, 2);
|
---|
2047 | var isFunc = _.isFunction(method);
|
---|
2048 | return _.map(obj, function(value) {
|
---|
2049 | return (isFunc ? method : value[method]).apply(value, args);
|
---|
2050 | });
|
---|
2051 | };
|
---|
2052 |
|
---|
2053 | // Convenience version of a common use case of `map`: fetching a property.
|
---|
2054 | _.pluck = function(obj, key) {
|
---|
2055 | return _.map(obj, function(value){ return value[key]; });
|
---|
2056 | };
|
---|
2057 |
|
---|
2058 | // Convenience version of a common use case of `filter`: selecting only objects
|
---|
2059 | // containing specific `key:value` pairs.
|
---|
2060 | _.where = function(obj, attrs, first) {
|
---|
2061 | if (_.isEmpty(attrs)) return first ? void 0 : [];
|
---|
2062 | return _[first ? 'find' : 'filter'](obj, function(value) {
|
---|
2063 | for (var key in attrs) {
|
---|
2064 | if (attrs[key] !== value[key]) return false;
|
---|
2065 | }
|
---|
2066 | return true;
|
---|
2067 | });
|
---|
2068 | };
|
---|
2069 |
|
---|
2070 | // Convenience version of a common use case of `find`: getting the first object
|
---|
2071 | // containing specific `key:value` pairs.
|
---|
2072 | _.findWhere = function(obj, attrs) {
|
---|
2073 | return _.where(obj, attrs, true);
|
---|
2074 | };
|
---|
2075 |
|
---|
2076 | // Return the maximum element or (element-based computation).
|
---|
2077 | // Can't optimize arrays of integers longer than 65,535 elements.
|
---|
2078 | // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
|
---|
2079 | _.max = function(obj, iterator, context) {
|
---|
2080 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
---|
2081 | return Math.max.apply(Math, obj);
|
---|
2082 | }
|
---|
2083 | if (!iterator && _.isEmpty(obj)) return -Infinity;
|
---|
2084 | var result = {computed : -Infinity, value: -Infinity};
|
---|
2085 | each(obj, function(value, index, list) {
|
---|
2086 | var computed = iterator ? iterator.call(context, value, index, list) : value;
|
---|
2087 | computed > result.computed && (result = {value : value, computed : computed});
|
---|
2088 | });
|
---|
2089 | return result.value;
|
---|
2090 | };
|
---|
2091 |
|
---|
2092 | // Return the minimum element (or element-based computation).
|
---|
2093 | _.min = function(obj, iterator, context) {
|
---|
2094 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
---|
2095 | return Math.min.apply(Math, obj);
|
---|
2096 | }
|
---|
2097 | if (!iterator && _.isEmpty(obj)) return Infinity;
|
---|
2098 | var result = {computed : Infinity, value: Infinity};
|
---|
2099 | each(obj, function(value, index, list) {
|
---|
2100 | var computed = iterator ? iterator.call(context, value, index, list) : value;
|
---|
2101 | computed < result.computed && (result = {value : value, computed : computed});
|
---|
2102 | });
|
---|
2103 | return result.value;
|
---|
2104 | };
|
---|
2105 |
|
---|
2106 | // Shuffle an array, using the modern version of the
|
---|
2107 | // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
---|
2108 | _.shuffle = function(obj) {
|
---|
2109 | var rand;
|
---|
2110 | var index = 0;
|
---|
2111 | var shuffled = [];
|
---|
2112 | each(obj, function(value) {
|
---|
2113 | rand = _.random(index++);
|
---|
2114 | shuffled[index - 1] = shuffled[rand];
|
---|
2115 | shuffled[rand] = value;
|
---|
2116 | });
|
---|
2117 | return shuffled;
|
---|
2118 | };
|
---|
2119 |
|
---|
2120 | // Sample **n** random values from an array.
|
---|
2121 | // If **n** is not specified, returns a single random element from the array.
|
---|
2122 | // The internal `guard` argument allows it to work with `map`.
|
---|
2123 | _.sample = function(obj, n, guard) {
|
---|
2124 | if (arguments.length < 2 || guard) {
|
---|
2125 | return obj[_.random(obj.length - 1)];
|
---|
2126 | }
|
---|
2127 | return _.shuffle(obj).slice(0, Math.max(0, n));
|
---|
2128 | };
|
---|
2129 |
|
---|
2130 | // An internal function to generate lookup iterators.
|
---|
2131 | var lookupIterator = function(value) {
|
---|
2132 | return _.isFunction(value) ? value : function(obj){ return obj[value]; };
|
---|
2133 | };
|
---|
2134 |
|
---|
2135 | // Sort the object's values by a criterion produced by an iterator.
|
---|
2136 | _.sortBy = function(obj, value, context) {
|
---|
2137 | var iterator = lookupIterator(value);
|
---|
2138 | return _.pluck(_.map(obj, function(value, index, list) {
|
---|
2139 | return {
|
---|
2140 | value: value,
|
---|
2141 | index: index,
|
---|
2142 | criteria: iterator.call(context, value, index, list)
|
---|
2143 | };
|
---|
2144 | }).sort(function(left, right) {
|
---|
2145 | var a = left.criteria;
|
---|
2146 | var b = right.criteria;
|
---|
2147 | if (a !== b) {
|
---|
2148 | if (a > b || a === void 0) return 1;
|
---|
2149 | if (a < b || b === void 0) return -1;
|
---|
2150 | }
|
---|
2151 | return left.index - right.index;
|
---|
2152 | }), 'value');
|
---|
2153 | };
|
---|
2154 |
|
---|
2155 | // An internal function used for aggregate "group by" operations.
|
---|
2156 | var group = function(behavior) {
|
---|
2157 | return function(obj, value, context) {
|
---|
2158 | var result = {};
|
---|
2159 | var iterator = value == null ? _.identity : lookupIterator(value);
|
---|
2160 | each(obj, function(value, index) {
|
---|
2161 | var key = iterator.call(context, value, index, obj);
|
---|
2162 | behavior(result, key, value);
|
---|
2163 | });
|
---|
2164 | return result;
|
---|
2165 | };
|
---|
2166 | };
|
---|
2167 |
|
---|
2168 | // Groups the object's values by a criterion. Pass either a string attribute
|
---|
2169 | // to group by, or a function that returns the criterion.
|
---|
2170 | _.groupBy = group(function(result, key, value) {
|
---|
2171 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
|
---|
2172 | });
|
---|
2173 |
|
---|
2174 | // Indexes the object's values by a criterion, similar to `groupBy`, but for
|
---|
2175 | // when you know that your index values will be unique.
|
---|
2176 | _.indexBy = group(function(result, key, value) {
|
---|
2177 | result[key] = value;
|
---|
2178 | });
|
---|
2179 |
|
---|
2180 | // Counts instances of an object that group by a certain criterion. Pass
|
---|
2181 | // either a string attribute to count by, or a function that returns the
|
---|
2182 | // criterion.
|
---|
2183 | _.countBy = group(function(result, key) {
|
---|
2184 | _.has(result, key) ? result[key]++ : result[key] = 1;
|
---|
2185 | });
|
---|
2186 |
|
---|
2187 | // Use a comparator function to figure out the smallest index at which
|
---|
2188 | // an object should be inserted so as to maintain order. Uses binary search.
|
---|
2189 | _.sortedIndex = function(array, obj, iterator, context) {
|
---|
2190 | iterator = iterator == null ? _.identity : lookupIterator(iterator);
|
---|
2191 | var value = iterator.call(context, obj);
|
---|
2192 | var low = 0, high = array.length;
|
---|
2193 | while (low < high) {
|
---|
2194 | var mid = (low + high) >>> 1;
|
---|
2195 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
|
---|
2196 | }
|
---|
2197 | return low;
|
---|
2198 | };
|
---|
2199 |
|
---|
2200 | // Safely create a real, live array from anything iterable.
|
---|
2201 | _.toArray = function(obj) {
|
---|
2202 | if (!obj) return [];
|
---|
2203 | if (_.isArray(obj)) return slice.call(obj);
|
---|
2204 | if (obj.length === +obj.length) return _.map(obj, _.identity);
|
---|
2205 | return _.values(obj);
|
---|
2206 | };
|
---|
2207 |
|
---|
2208 | // Return the number of elements in an object.
|
---|
2209 | _.size = function(obj) {
|
---|
2210 | if (obj == null) return 0;
|
---|
2211 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
|
---|
2212 | };
|
---|
2213 |
|
---|
2214 | // Array Functions
|
---|
2215 | // ---------------
|
---|
2216 |
|
---|
2217 | // Get the first element of an array. Passing **n** will return the first N
|
---|
2218 | // values in the array. Aliased as `head` and `take`. The **guard** check
|
---|
2219 | // allows it to work with `_.map`.
|
---|
2220 | _.first = _.head = _.take = function(array, n, guard) {
|
---|
2221 | if (array == null) return void 0;
|
---|
2222 | return (n == null) || guard ? array[0] : slice.call(array, 0, n);
|
---|
2223 | };
|
---|
2224 |
|
---|
2225 | // Returns everything but the last entry of the array. Especially useful on
|
---|
2226 | // the arguments object. Passing **n** will return all the values in
|
---|
2227 | // the array, excluding the last N. The **guard** check allows it to work with
|
---|
2228 | // `_.map`.
|
---|
2229 | _.initial = function(array, n, guard) {
|
---|
2230 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
|
---|
2231 | };
|
---|
2232 |
|
---|
2233 | // Get the last element of an array. Passing **n** will return the last N
|
---|
2234 | // values in the array. The **guard** check allows it to work with `_.map`.
|
---|
2235 | _.last = function(array, n, guard) {
|
---|
2236 | if (array == null) return void 0;
|
---|
2237 | if ((n == null) || guard) {
|
---|
2238 | return array[array.length - 1];
|
---|
2239 | } else {
|
---|
2240 | return slice.call(array, Math.max(array.length - n, 0));
|
---|
2241 | }
|
---|
2242 | };
|
---|
2243 |
|
---|
2244 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
---|
2245 | // Especially useful on the arguments object. Passing an **n** will return
|
---|
2246 | // the rest N values in the array. The **guard**
|
---|
2247 | // check allows it to work with `_.map`.
|
---|
2248 | _.rest = _.tail = _.drop = function(array, n, guard) {
|
---|
2249 | return slice.call(array, (n == null) || guard ? 1 : n);
|
---|
2250 | };
|
---|
2251 |
|
---|
2252 | // Trim out all falsy values from an array.
|
---|
2253 | _.compact = function(array) {
|
---|
2254 | return _.filter(array, _.identity);
|
---|
2255 | };
|
---|
2256 |
|
---|
2257 | // Internal implementation of a recursive `flatten` function.
|
---|
2258 | var flatten = function(input, shallow, output) {
|
---|
2259 | if (shallow && _.every(input, _.isArray)) {
|
---|
2260 | return concat.apply(output, input);
|
---|
2261 | }
|
---|
2262 | each(input, function(value) {
|
---|
2263 | if (_.isArray(value) || _.isArguments(value)) {
|
---|
2264 | shallow ? push.apply(output, value) : flatten(value, shallow, output);
|
---|
2265 | } else {
|
---|
2266 | output.push(value);
|
---|
2267 | }
|
---|
2268 | });
|
---|
2269 | return output;
|
---|
2270 | };
|
---|
2271 |
|
---|
2272 | // Flatten out an array, either recursively (by default), or just one level.
|
---|
2273 | _.flatten = function(array, shallow) {
|
---|
2274 | return flatten(array, shallow, []);
|
---|
2275 | };
|
---|
2276 |
|
---|
2277 | // Return a version of the array that does not contain the specified value(s).
|
---|
2278 | _.without = function(array) {
|
---|
2279 | return _.difference(array, slice.call(arguments, 1));
|
---|
2280 | };
|
---|
2281 |
|
---|
2282 | // Produce a duplicate-free version of the array. If the array has already
|
---|
2283 | // been sorted, you have the option of using a faster algorithm.
|
---|
2284 | // Aliased as `unique`.
|
---|
2285 | _.uniq = _.unique = function(array, isSorted, iterator, context) {
|
---|
2286 | if (_.isFunction(isSorted)) {
|
---|
2287 | context = iterator;
|
---|
2288 | iterator = isSorted;
|
---|
2289 | isSorted = false;
|
---|
2290 | }
|
---|
2291 | var initial = iterator ? _.map(array, iterator, context) : array;
|
---|
2292 | var results = [];
|
---|
2293 | var seen = [];
|
---|
2294 | each(initial, function(value, index) {
|
---|
2295 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
|
---|
2296 | seen.push(value);
|
---|
2297 | results.push(array[index]);
|
---|
2298 | }
|
---|
2299 | });
|
---|
2300 | return results;
|
---|
2301 | };
|
---|
2302 |
|
---|
2303 | // Produce an array that contains the union: each distinct element from all of
|
---|
2304 | // the passed-in arrays.
|
---|
2305 | _.union = function() {
|
---|
2306 | return _.uniq(_.flatten(arguments, true));
|
---|
2307 | };
|
---|
2308 |
|
---|
2309 | // Produce an array that contains every item shared between all the
|
---|
2310 | // passed-in arrays.
|
---|
2311 | _.intersection = function(array) {
|
---|
2312 | var rest = slice.call(arguments, 1);
|
---|
2313 | return _.filter(_.uniq(array), function(item) {
|
---|
2314 | return _.every(rest, function(other) {
|
---|
2315 | return _.indexOf(other, item) >= 0;
|
---|
2316 | });
|
---|
2317 | });
|
---|
2318 | };
|
---|
2319 |
|
---|
2320 | // Take the difference between one array and a number of other arrays.
|
---|
2321 | // Only the elements present in just the first array will remain.
|
---|
2322 | _.difference = function(array) {
|
---|
2323 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
|
---|
2324 | return _.filter(array, function(value){ return !_.contains(rest, value); });
|
---|
2325 | };
|
---|
2326 |
|
---|
2327 | // Zip together multiple lists into a single array -- elements that share
|
---|
2328 | // an index go together.
|
---|
2329 | _.zip = function() {
|
---|
2330 | var length = _.max(_.pluck(arguments, "length").concat(0));
|
---|
2331 | var results = new Array(length);
|
---|
2332 | for (var i = 0; i < length; i++) {
|
---|
2333 | results[i] = _.pluck(arguments, '' + i);
|
---|
2334 | }
|
---|
2335 | return results;
|
---|
2336 | };
|
---|
2337 |
|
---|
2338 | // Converts lists into objects. Pass either a single array of `[key, value]`
|
---|
2339 | // pairs, or two parallel arrays of the same length -- one of keys, and one of
|
---|
2340 | // the corresponding values.
|
---|
2341 | _.object = function(list, values) {
|
---|
2342 | if (list == null) return {};
|
---|
2343 | var result = {};
|
---|
2344 | for (var i = 0, length = list.length; i < length; i++) {
|
---|
2345 | if (values) {
|
---|
2346 | result[list[i]] = values[i];
|
---|
2347 | } else {
|
---|
2348 | result[list[i][0]] = list[i][1];
|
---|
2349 | }
|
---|
2350 | }
|
---|
2351 | return result;
|
---|
2352 | };
|
---|
2353 |
|
---|
2354 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
---|
2355 | // we need this function. Return the position of the first occurrence of an
|
---|
2356 | // item in an array, or -1 if the item is not included in the array.
|
---|
2357 | // Delegates to **ECMAScript 5**'s native `indexOf` if available.
|
---|
2358 | // If the array is large and already in sort order, pass `true`
|
---|
2359 | // for **isSorted** to use binary search.
|
---|
2360 | _.indexOf = function(array, item, isSorted) {
|
---|
2361 | if (array == null) return -1;
|
---|
2362 | var i = 0, length = array.length;
|
---|
2363 | if (isSorted) {
|
---|
2364 | if (typeof isSorted == 'number') {
|
---|
2365 | i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
|
---|
2366 | } else {
|
---|
2367 | i = _.sortedIndex(array, item);
|
---|
2368 | return array[i] === item ? i : -1;
|
---|
2369 | }
|
---|
2370 | }
|
---|
2371 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
|
---|
2372 | for (; i < length; i++) if (array[i] === item) return i;
|
---|
2373 | return -1;
|
---|
2374 | };
|
---|
2375 |
|
---|
2376 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
---|
2377 | _.lastIndexOf = function(array, item, from) {
|
---|
2378 | if (array == null) return -1;
|
---|
2379 | var hasIndex = from != null;
|
---|
2380 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
|
---|
2381 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
|
---|
2382 | }
|
---|
2383 | var i = (hasIndex ? from : array.length);
|
---|
2384 | while (i--) if (array[i] === item) return i;
|
---|
2385 | return -1;
|
---|
2386 | };
|
---|
2387 |
|
---|
2388 | // Generate an integer Array containing an arithmetic progression. A port of
|
---|
2389 | // the native Python `range()` function. See
|
---|
2390 | // [the Python documentation](http://docs.python.org/library/functions.html#range).
|
---|
2391 | _.range = function(start, stop, step) {
|
---|
2392 | if (arguments.length <= 1) {
|
---|
2393 | stop = start || 0;
|
---|
2394 | start = 0;
|
---|
2395 | }
|
---|
2396 | step = arguments[2] || 1;
|
---|
2397 |
|
---|
2398 | var length = Math.max(Math.ceil((stop - start) / step), 0);
|
---|
2399 | var idx = 0;
|
---|
2400 | var range = new Array(length);
|
---|
2401 |
|
---|
2402 | while(idx < length) {
|
---|
2403 | range[idx++] = start;
|
---|
2404 | start += step;
|
---|
2405 | }
|
---|
2406 |
|
---|
2407 | return range;
|
---|
2408 | };
|
---|
2409 |
|
---|
2410 | // Function (ahem) Functions
|
---|
2411 | // ------------------
|
---|
2412 |
|
---|
2413 | // Reusable constructor function for prototype setting.
|
---|
2414 | var ctor = function(){};
|
---|
2415 |
|
---|
2416 | // Create a function bound to a given object (assigning `this`, and arguments,
|
---|
2417 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
---|
2418 | // available.
|
---|
2419 | _.bind = function(func, context) {
|
---|
2420 | var args, bound;
|
---|
2421 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
---|
2422 | if (!_.isFunction(func)) throw new TypeError;
|
---|
2423 | args = slice.call(arguments, 2);
|
---|
2424 | return bound = function() {
|
---|
2425 | if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
---|
2426 | ctor.prototype = func.prototype;
|
---|
2427 | var self = new ctor;
|
---|
2428 | ctor.prototype = null;
|
---|
2429 | var result = func.apply(self, args.concat(slice.call(arguments)));
|
---|
2430 | if (Object(result) === result) return result;
|
---|
2431 | return self;
|
---|
2432 | };
|
---|
2433 | };
|
---|
2434 |
|
---|
2435 | // Partially apply a function by creating a version that has had some of its
|
---|
2436 | // arguments pre-filled, without changing its dynamic `this` context.
|
---|
2437 | _.partial = function(func) {
|
---|
2438 | var args = slice.call(arguments, 1);
|
---|
2439 | return function() {
|
---|
2440 | return func.apply(this, args.concat(slice.call(arguments)));
|
---|
2441 | };
|
---|
2442 | };
|
---|
2443 |
|
---|
2444 | // Bind all of an object's methods to that object. Useful for ensuring that
|
---|
2445 | // all callbacks defined on an object belong to it.
|
---|
2446 | _.bindAll = function(obj) {
|
---|
2447 | var funcs = slice.call(arguments, 1);
|
---|
2448 | if (funcs.length === 0) throw new Error("bindAll must be passed function names");
|
---|
2449 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
---|
2450 | return obj;
|
---|
2451 | };
|
---|
2452 |
|
---|
2453 | // Memoize an expensive function by storing its results.
|
---|
2454 | _.memoize = function(func, hasher) {
|
---|
2455 | var memo = {};
|
---|
2456 | hasher || (hasher = _.identity);
|
---|
2457 | return function() {
|
---|
2458 | var key = hasher.apply(this, arguments);
|
---|
2459 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
|
---|
2460 | };
|
---|
2461 | };
|
---|
2462 |
|
---|
2463 | // Delays a function for the given number of milliseconds, and then calls
|
---|
2464 | // it with the arguments supplied.
|
---|
2465 | _.delay = function(func, wait) {
|
---|
2466 | var args = slice.call(arguments, 2);
|
---|
2467 | return setTimeout(function(){ return func.apply(null, args); }, wait);
|
---|
2468 | };
|
---|
2469 |
|
---|
2470 | // Defers a function, scheduling it to run after the current call stack has
|
---|
2471 | // cleared.
|
---|
2472 | _.defer = function(func) {
|
---|
2473 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
---|
2474 | };
|
---|
2475 |
|
---|
2476 | // Returns a function, that, when invoked, will only be triggered at most once
|
---|
2477 | // during a given window of time. Normally, the throttled function will run
|
---|
2478 | // as much as it can, without ever going more than once per `wait` duration;
|
---|
2479 | // but if you'd like to disable the execution on the leading edge, pass
|
---|
2480 | // `{leading: false}`. To disable execution on the trailing edge, ditto.
|
---|
2481 | _.throttle = function(func, wait, options) {
|
---|
2482 | var context, args, result;
|
---|
2483 | var timeout = null;
|
---|
2484 | var previous = 0;
|
---|
2485 | options || (options = {});
|
---|
2486 | var later = function() {
|
---|
2487 | previous = options.leading === false ? 0 : new Date;
|
---|
2488 | timeout = null;
|
---|
2489 | result = func.apply(context, args);
|
---|
2490 | };
|
---|
2491 | return function() {
|
---|
2492 | var now = new Date;
|
---|
2493 | if (!previous && options.leading === false) previous = now;
|
---|
2494 | var remaining = wait - (now - previous);
|
---|
2495 | context = this;
|
---|
2496 | args = arguments;
|
---|
2497 | if (remaining <= 0) {
|
---|
2498 | clearTimeout(timeout);
|
---|
2499 | timeout = null;
|
---|
2500 | previous = now;
|
---|
2501 | result = func.apply(context, args);
|
---|
2502 | } else if (!timeout && options.trailing !== false) {
|
---|
2503 | timeout = setTimeout(later, remaining);
|
---|
2504 | }
|
---|
2505 | return result;
|
---|
2506 | };
|
---|
2507 | };
|
---|
2508 |
|
---|
2509 | // Returns a function, that, as long as it continues to be invoked, will not
|
---|
2510 | // be triggered. The function will be called after it stops being called for
|
---|
2511 | // N milliseconds. If `immediate` is passed, trigger the function on the
|
---|
2512 | // leading edge, instead of the trailing.
|
---|
2513 | _.debounce = function(func, wait, immediate) {
|
---|
2514 | var timeout, args, context, timestamp, result;
|
---|
2515 | return function() {
|
---|
2516 | context = this;
|
---|
2517 | args = arguments;
|
---|
2518 | timestamp = new Date();
|
---|
2519 | var later = function() {
|
---|
2520 | var last = (new Date()) - timestamp;
|
---|
2521 | if (last < wait) {
|
---|
2522 | timeout = setTimeout(later, wait - last);
|
---|
2523 | } else {
|
---|
2524 | timeout = null;
|
---|
2525 | if (!immediate) result = func.apply(context, args);
|
---|
2526 | }
|
---|
2527 | };
|
---|
2528 | var callNow = immediate && !timeout;
|
---|
2529 | if (!timeout) {
|
---|
2530 | timeout = setTimeout(later, wait);
|
---|
2531 | }
|
---|
2532 | if (callNow) result = func.apply(context, args);
|
---|
2533 | return result;
|
---|
2534 | };
|
---|
2535 | };
|
---|
2536 |
|
---|
2537 | // Returns a function that will be executed at most one time, no matter how
|
---|
2538 | // often you call it. Useful for lazy initialization.
|
---|
2539 | _.once = function(func) {
|
---|
2540 | var ran = false, memo;
|
---|
2541 | return function() {
|
---|
2542 | if (ran) return memo;
|
---|
2543 | ran = true;
|
---|
2544 | memo = func.apply(this, arguments);
|
---|
2545 | func = null;
|
---|
2546 | return memo;
|
---|
2547 | };
|
---|
2548 | };
|
---|
2549 |
|
---|
2550 | // Returns the first function passed as an argument to the second,
|
---|
2551 | // allowing you to adjust arguments, run code before and after, and
|
---|
2552 | // conditionally execute the original function.
|
---|
2553 | _.wrap = function(func, wrapper) {
|
---|
2554 | return function() {
|
---|
2555 | var args = [func];
|
---|
2556 | push.apply(args, arguments);
|
---|
2557 | return wrapper.apply(this, args);
|
---|
2558 | };
|
---|
2559 | };
|
---|
2560 |
|
---|
2561 | // Returns a function that is the composition of a list of functions, each
|
---|
2562 | // consuming the return value of the function that follows.
|
---|
2563 | _.compose = function() {
|
---|
2564 | var funcs = arguments;
|
---|
2565 | return function() {
|
---|
2566 | var args = arguments;
|
---|
2567 | for (var i = funcs.length - 1; i >= 0; i--) {
|
---|
2568 | args = [funcs[i].apply(this, args)];
|
---|
2569 | }
|
---|
2570 | return args[0];
|
---|
2571 | };
|
---|
2572 | };
|
---|
2573 |
|
---|
2574 | // Returns a function that will only be executed after being called N times.
|
---|
2575 | _.after = function(times, func) {
|
---|
2576 | return function() {
|
---|
2577 | if (--times < 1) {
|
---|
2578 | return func.apply(this, arguments);
|
---|
2579 | }
|
---|
2580 | };
|
---|
2581 | };
|
---|
2582 |
|
---|
2583 | // Object Functions
|
---|
2584 | // ----------------
|
---|
2585 |
|
---|
2586 | // Retrieve the names of an object's properties.
|
---|
2587 | // Delegates to **ECMAScript 5**'s native `Object.keys`
|
---|
2588 | _.keys = nativeKeys || function(obj) {
|
---|
2589 | if (obj !== Object(obj)) throw new TypeError('Invalid object');
|
---|
2590 | var keys = [];
|
---|
2591 | for (var key in obj) if (_.has(obj, key)) keys.push(key);
|
---|
2592 | return keys;
|
---|
2593 | };
|
---|
2594 |
|
---|
2595 | // Retrieve the values of an object's properties.
|
---|
2596 | _.values = function(obj) {
|
---|
2597 | var keys = _.keys(obj);
|
---|
2598 | var length = keys.length;
|
---|
2599 | var values = new Array(length);
|
---|
2600 | for (var i = 0; i < length; i++) {
|
---|
2601 | values[i] = obj[keys[i]];
|
---|
2602 | }
|
---|
2603 | return values;
|
---|
2604 | };
|
---|
2605 |
|
---|
2606 | // Convert an object into a list of `[key, value]` pairs.
|
---|
2607 | _.pairs = function(obj) {
|
---|
2608 | var keys = _.keys(obj);
|
---|
2609 | var length = keys.length;
|
---|
2610 | var pairs = new Array(length);
|
---|
2611 | for (var i = 0; i < length; i++) {
|
---|
2612 | pairs[i] = [keys[i], obj[keys[i]]];
|
---|
2613 | }
|
---|
2614 | return pairs;
|
---|
2615 | };
|
---|
2616 |
|
---|
2617 | // Invert the keys and values of an object. The values must be serializable.
|
---|
2618 | _.invert = function(obj) {
|
---|
2619 | var result = {};
|
---|
2620 | var keys = _.keys(obj);
|
---|
2621 | for (var i = 0, length = keys.length; i < length; i++) {
|
---|
2622 | result[obj[keys[i]]] = keys[i];
|
---|
2623 | }
|
---|
2624 | return result;
|
---|
2625 | };
|
---|
2626 |
|
---|
2627 | // Return a sorted list of the function names available on the object.
|
---|
2628 | // Aliased as `methods`
|
---|
2629 | _.functions = _.methods = function(obj) {
|
---|
2630 | var names = [];
|
---|
2631 | for (var key in obj) {
|
---|
2632 | if (_.isFunction(obj[key])) names.push(key);
|
---|
2633 | }
|
---|
2634 | return names.sort();
|
---|
2635 | };
|
---|
2636 |
|
---|
2637 | // Extend a given object with all the properties in passed-in object(s).
|
---|
2638 | _.extend = function(obj) {
|
---|
2639 | each(slice.call(arguments, 1), function(source) {
|
---|
2640 | if (source) {
|
---|
2641 | for (var prop in source) {
|
---|
2642 | obj[prop] = source[prop];
|
---|
2643 | }
|
---|
2644 | }
|
---|
2645 | });
|
---|
2646 | return obj;
|
---|
2647 | };
|
---|
2648 |
|
---|
2649 | // Return a copy of the object only containing the whitelisted properties.
|
---|
2650 | _.pick = function(obj) {
|
---|
2651 | var copy = {};
|
---|
2652 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
---|
2653 | each(keys, function(key) {
|
---|
2654 | if (key in obj) copy[key] = obj[key];
|
---|
2655 | });
|
---|
2656 | return copy;
|
---|
2657 | };
|
---|
2658 |
|
---|
2659 | // Return a copy of the object without the blacklisted properties.
|
---|
2660 | _.omit = function(obj) {
|
---|
2661 | var copy = {};
|
---|
2662 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
---|
2663 | for (var key in obj) {
|
---|
2664 | if (!_.contains(keys, key)) copy[key] = obj[key];
|
---|
2665 | }
|
---|
2666 | return copy;
|
---|
2667 | };
|
---|
2668 |
|
---|
2669 | // Fill in a given object with default properties.
|
---|
2670 | _.defaults = function(obj) {
|
---|
2671 | each(slice.call(arguments, 1), function(source) {
|
---|
2672 | if (source) {
|
---|
2673 | for (var prop in source) {
|
---|
2674 | if (obj[prop] === void 0) obj[prop] = source[prop];
|
---|
2675 | }
|
---|
2676 | }
|
---|
2677 | });
|
---|
2678 | return obj;
|
---|
2679 | };
|
---|
2680 |
|
---|
2681 | // Create a (shallow-cloned) duplicate of an object.
|
---|
2682 | _.clone = function(obj) {
|
---|
2683 | if (!_.isObject(obj)) return obj;
|
---|
2684 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
---|
2685 | };
|
---|
2686 |
|
---|
2687 | // Invokes interceptor with the obj, and then returns obj.
|
---|
2688 | // The primary purpose of this method is to "tap into" a method chain, in
|
---|
2689 | // order to perform operations on intermediate results within the chain.
|
---|
2690 | _.tap = function(obj, interceptor) {
|
---|
2691 | interceptor(obj);
|
---|
2692 | return obj;
|
---|
2693 | };
|
---|
2694 |
|
---|
2695 | // Internal recursive comparison function for `isEqual`.
|
---|
2696 | var eq = function(a, b, aStack, bStack) {
|
---|
2697 | // Identical objects are equal. `0 === -0`, but they aren't identical.
|
---|
2698 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
---|
2699 | if (a === b) return a !== 0 || 1 / a == 1 / b;
|
---|
2700 | // A strict comparison is necessary because `null == undefined`.
|
---|
2701 | if (a == null || b == null) return a === b;
|
---|
2702 | // Unwrap any wrapped objects.
|
---|
2703 | if (a instanceof _) a = a._wrapped;
|
---|
2704 | if (b instanceof _) b = b._wrapped;
|
---|
2705 | // Compare `[[Class]]` names.
|
---|
2706 | var className = toString.call(a);
|
---|
2707 | if (className != toString.call(b)) return false;
|
---|
2708 | switch (className) {
|
---|
2709 | // Strings, numbers, dates, and booleans are compared by value.
|
---|
2710 | case '[object String]':
|
---|
2711 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
---|
2712 | // equivalent to `new String("5")`.
|
---|
2713 | return a == String(b);
|
---|
2714 | case '[object Number]':
|
---|
2715 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
|
---|
2716 | // other numeric values.
|
---|
2717 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
|
---|
2718 | case '[object Date]':
|
---|
2719 | case '[object Boolean]':
|
---|
2720 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
---|
2721 | // millisecond representations. Note that invalid dates with millisecond representations
|
---|
2722 | // of `NaN` are not equivalent.
|
---|
2723 | return +a == +b;
|
---|
2724 | // RegExps are compared by their source patterns and flags.
|
---|
2725 | case '[object RegExp]':
|
---|
2726 | return a.source == b.source &&
|
---|
2727 | a.global == b.global &&
|
---|
2728 | a.multiline == b.multiline &&
|
---|
2729 | a.ignoreCase == b.ignoreCase;
|
---|
2730 | }
|
---|
2731 | if (typeof a != 'object' || typeof b != 'object') return false;
|
---|
2732 | // Assume equality for cyclic structures. The algorithm for detecting cyclic
|
---|
2733 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
---|
2734 | var length = aStack.length;
|
---|
2735 | while (length--) {
|
---|
2736 | // Linear search. Performance is inversely proportional to the number of
|
---|
2737 | // unique nested structures.
|
---|
2738 | if (aStack[length] == a) return bStack[length] == b;
|
---|
2739 | }
|
---|
2740 | // Objects with different constructors are not equivalent, but `Object`s
|
---|
2741 | // from different frames are.
|
---|
2742 | var aCtor = a.constructor, bCtor = b.constructor;
|
---|
2743 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
|
---|
2744 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
|
---|
2745 | return false;
|
---|
2746 | }
|
---|
2747 | // Add the first object to the stack of traversed objects.
|
---|
2748 | aStack.push(a);
|
---|
2749 | bStack.push(b);
|
---|
2750 | var size = 0, result = true;
|
---|
2751 | // Recursively compare objects and arrays.
|
---|
2752 | if (className == '[object Array]') {
|
---|
2753 | // Compare array lengths to determine if a deep comparison is necessary.
|
---|
2754 | size = a.length;
|
---|
2755 | result = size == b.length;
|
---|
2756 | if (result) {
|
---|
2757 | // Deep compare the contents, ignoring non-numeric properties.
|
---|
2758 | while (size--) {
|
---|
2759 | if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
---|
2760 | }
|
---|
2761 | }
|
---|
2762 | } else {
|
---|
2763 | // Deep compare objects.
|
---|
2764 | for (var key in a) {
|
---|
2765 | if (_.has(a, key)) {
|
---|
2766 | // Count the expected number of properties.
|
---|
2767 | size++;
|
---|
2768 | // Deep compare each member.
|
---|
2769 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
|
---|
2770 | }
|
---|
2771 | }
|
---|
2772 | // Ensure that both objects contain the same number of properties.
|
---|
2773 | if (result) {
|
---|
2774 | for (key in b) {
|
---|
2775 | if (_.has(b, key) && !(size--)) break;
|
---|
2776 | }
|
---|
2777 | result = !size;
|
---|
2778 | }
|
---|
2779 | }
|
---|
2780 | // Remove the first object from the stack of traversed objects.
|
---|
2781 | aStack.pop();
|
---|
2782 | bStack.pop();
|
---|
2783 | return result;
|
---|
2784 | };
|
---|
2785 |
|
---|
2786 | // Perform a deep comparison to check if two objects are equal.
|
---|
2787 | _.isEqual = function(a, b) {
|
---|
2788 | return eq(a, b, [], []);
|
---|
2789 | };
|
---|
2790 |
|
---|
2791 | // Is a given array, string, or object empty?
|
---|
2792 | // An "empty" object has no enumerable own-properties.
|
---|
2793 | _.isEmpty = function(obj) {
|
---|
2794 | if (obj == null) return true;
|
---|
2795 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
|
---|
2796 | for (var key in obj) if (_.has(obj, key)) return false;
|
---|
2797 | return true;
|
---|
2798 | };
|
---|
2799 |
|
---|
2800 | // Is a given value a DOM element?
|
---|
2801 | _.isElement = function(obj) {
|
---|
2802 | return !!(obj && obj.nodeType === 1);
|
---|
2803 | };
|
---|
2804 |
|
---|
2805 | // Is a given value an array?
|
---|
2806 | // Delegates to ECMA5's native Array.isArray
|
---|
2807 | _.isArray = nativeIsArray || function(obj) {
|
---|
2808 | return toString.call(obj) == '[object Array]';
|
---|
2809 | };
|
---|
2810 |
|
---|
2811 | // Is a given variable an object?
|
---|
2812 | _.isObject = function(obj) {
|
---|
2813 | return obj === Object(obj);
|
---|
2814 | };
|
---|
2815 |
|
---|
2816 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
|
---|
2817 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
|
---|
2818 | _['is' + name] = function(obj) {
|
---|
2819 | return toString.call(obj) == '[object ' + name + ']';
|
---|
2820 | };
|
---|
2821 | });
|
---|
2822 |
|
---|
2823 | // Define a fallback version of the method in browsers (ahem, IE), where
|
---|
2824 | // there isn't any inspectable "Arguments" type.
|
---|
2825 | if (!_.isArguments(arguments)) {
|
---|
2826 | _.isArguments = function(obj) {
|
---|
2827 | return !!(obj && _.has(obj, 'callee'));
|
---|
2828 | };
|
---|
2829 | }
|
---|
2830 |
|
---|
2831 | // Optimize `isFunction` if appropriate.
|
---|
2832 | if (typeof (/./) !== 'function') {
|
---|
2833 | _.isFunction = function(obj) {
|
---|
2834 | return typeof obj === 'function';
|
---|
2835 | };
|
---|
2836 | }
|
---|
2837 |
|
---|
2838 | // Is a given object a finite number?
|
---|
2839 | _.isFinite = function(obj) {
|
---|
2840 | return isFinite(obj) && !isNaN(parseFloat(obj));
|
---|
2841 | };
|
---|
2842 |
|
---|
2843 | // Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
---|
2844 | _.isNaN = function(obj) {
|
---|
2845 | return _.isNumber(obj) && obj != +obj;
|
---|
2846 | };
|
---|
2847 |
|
---|
2848 | // Is a given value a boolean?
|
---|
2849 | _.isBoolean = function(obj) {
|
---|
2850 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
---|
2851 | };
|
---|
2852 |
|
---|
2853 | // Is a given value equal to null?
|
---|
2854 | _.isNull = function(obj) {
|
---|
2855 | return obj === null;
|
---|
2856 | };
|
---|
2857 |
|
---|
2858 | // Is a given variable undefined?
|
---|
2859 | _.isUndefined = function(obj) {
|
---|
2860 | return obj === void 0;
|
---|
2861 | };
|
---|
2862 |
|
---|
2863 | // Shortcut function for checking if an object has a given property directly
|
---|
2864 | // on itself (in other words, not on a prototype).
|
---|
2865 | _.has = function(obj, key) {
|
---|
2866 | return hasOwnProperty.call(obj, key);
|
---|
2867 | };
|
---|
2868 |
|
---|
2869 | // Utility Functions
|
---|
2870 | // -----------------
|
---|
2871 |
|
---|
2872 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
---|
2873 | // previous owner. Returns a reference to the Underscore object.
|
---|
2874 | _.noConflict = function() {
|
---|
2875 | root._ = previousUnderscore;
|
---|
2876 | return this;
|
---|
2877 | };
|
---|
2878 |
|
---|
2879 | // Keep the identity function around for default iterators.
|
---|
2880 | _.identity = function(value) {
|
---|
2881 | return value;
|
---|
2882 | };
|
---|
2883 |
|
---|
2884 | // Run a function **n** times.
|
---|
2885 | _.times = function(n, iterator, context) {
|
---|
2886 | var accum = Array(Math.max(0, n));
|
---|
2887 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
|
---|
2888 | return accum;
|
---|
2889 | };
|
---|
2890 |
|
---|
2891 | // Return a random integer between min and max (inclusive).
|
---|
2892 | _.random = function(min, max) {
|
---|
2893 | if (max == null) {
|
---|
2894 | max = min;
|
---|
2895 | min = 0;
|
---|
2896 | }
|
---|
2897 | return min + Math.floor(Math.random() * (max - min + 1));
|
---|
2898 | };
|
---|
2899 |
|
---|
2900 | // List of HTML entities for escaping.
|
---|
2901 | var entityMap = {
|
---|
2902 | escape: {
|
---|
2903 | '&': '&',
|
---|
2904 | '<': '<',
|
---|
2905 | '>': '>',
|
---|
2906 | '"': '"',
|
---|
2907 | "'": '''
|
---|
2908 | }
|
---|
2909 | };
|
---|
2910 | entityMap.unescape = _.invert(entityMap.escape);
|
---|
2911 |
|
---|
2912 | // Regexes containing the keys and values listed immediately above.
|
---|
2913 | var entityRegexes = {
|
---|
2914 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
|
---|
2915 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
|
---|
2916 | };
|
---|
2917 |
|
---|
2918 | // Functions for escaping and unescaping strings to/from HTML interpolation.
|
---|
2919 | _.each(['escape', 'unescape'], function(method) {
|
---|
2920 | _[method] = function(string) {
|
---|
2921 | if (string == null) return '';
|
---|
2922 | return ('' + string).replace(entityRegexes[method], function(match) {
|
---|
2923 | return entityMap[method][match];
|
---|
2924 | });
|
---|
2925 | };
|
---|
2926 | });
|
---|
2927 |
|
---|
2928 | // If the value of the named `property` is a function then invoke it with the
|
---|
2929 | // `object` as context; otherwise, return it.
|
---|
2930 | _.result = function(object, property) {
|
---|
2931 | if (object == null) return void 0;
|
---|
2932 | var value = object[property];
|
---|
2933 | return _.isFunction(value) ? value.call(object) : value;
|
---|
2934 | };
|
---|
2935 |
|
---|
2936 | // Add your own custom functions to the Underscore object.
|
---|
2937 | _.mixin = function(obj) {
|
---|
2938 | each(_.functions(obj), function(name) {
|
---|
2939 | var func = _[name] = obj[name];
|
---|
2940 | _.prototype[name] = function() {
|
---|
2941 | var args = [this._wrapped];
|
---|
2942 | push.apply(args, arguments);
|
---|
2943 | return result.call(this, func.apply(_, args));
|
---|
2944 | };
|
---|
2945 | });
|
---|
2946 | };
|
---|
2947 |
|
---|
2948 | // Generate a unique integer id (unique within the entire client session).
|
---|
2949 | // Useful for temporary DOM ids.
|
---|
2950 | var idCounter = 0;
|
---|
2951 | _.uniqueId = function(prefix) {
|
---|
2952 | var id = ++idCounter + '';
|
---|
2953 | return prefix ? prefix + id : id;
|
---|
2954 | };
|
---|
2955 |
|
---|
2956 | // By default, Underscore uses ERB-style template delimiters, change the
|
---|
2957 | // following template settings to use alternative delimiters.
|
---|
2958 | _.templateSettings = {
|
---|
2959 | evaluate : /<%([\s\S]+?)%>/g,
|
---|
2960 | interpolate : /<%=([\s\S]+?)%>/g,
|
---|
2961 | escape : /<%-([\s\S]+?)%>/g
|
---|
2962 | };
|
---|
2963 |
|
---|
2964 | // When customizing `templateSettings`, if you don't want to define an
|
---|
2965 | // interpolation, evaluation or escaping regex, we need one that is
|
---|
2966 | // guaranteed not to match.
|
---|
2967 | var noMatch = /(.)^/;
|
---|
2968 |
|
---|
2969 | // Certain characters need to be escaped so that they can be put into a
|
---|
2970 | // string literal.
|
---|
2971 | var escapes = {
|
---|
2972 | "'": "'",
|
---|
2973 | '\\': '\\',
|
---|
2974 | '\r': 'r',
|
---|
2975 | '\n': 'n',
|
---|
2976 | '\t': 't',
|
---|
2977 | '\u2028': 'u2028',
|
---|
2978 | '\u2029': 'u2029'
|
---|
2979 | };
|
---|
2980 |
|
---|
2981 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
|
---|
2982 |
|
---|
2983 | // JavaScript micro-templating, similar to John Resig's implementation.
|
---|
2984 | // Underscore templating handles arbitrary delimiters, preserves whitespace,
|
---|
2985 | // and correctly escapes quotes within interpolated code.
|
---|
2986 | _.template = function(text, data, settings) {
|
---|
2987 | var render;
|
---|
2988 | settings = _.defaults({}, settings, _.templateSettings);
|
---|
2989 |
|
---|
2990 | // Combine delimiters into one regular expression via alternation.
|
---|
2991 | var matcher = new RegExp([
|
---|
2992 | (settings.escape || noMatch).source,
|
---|
2993 | (settings.interpolate || noMatch).source,
|
---|
2994 | (settings.evaluate || noMatch).source
|
---|
2995 | ].join('|') + '|$', 'g');
|
---|
2996 |
|
---|
2997 | // Compile the template source, escaping string literals appropriately.
|
---|
2998 | var index = 0;
|
---|
2999 | var source = "__p+='";
|
---|
3000 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
---|
3001 | source += text.slice(index, offset)
|
---|
3002 | .replace(escaper, function(match) { return '\\' + escapes[match]; });
|
---|
3003 |
|
---|
3004 | if (escape) {
|
---|
3005 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
---|
3006 | }
|
---|
3007 | if (interpolate) {
|
---|
3008 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
---|
3009 | }
|
---|
3010 | if (evaluate) {
|
---|
3011 | source += "';\n" + evaluate + "\n__p+='";
|
---|
3012 | }
|
---|
3013 | index = offset + match.length;
|
---|
3014 | return match;
|
---|
3015 | });
|
---|
3016 | source += "';\n";
|
---|
3017 |
|
---|
3018 | // If a variable is not specified, place data values in local scope.
|
---|
3019 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
---|
3020 |
|
---|
3021 | source = "var __t,__p='',__j=Array.prototype.join," +
|
---|
3022 | "print=function(){__p+=__j.call(arguments,'');};\n" +
|
---|
3023 | source + "return __p;\n";
|
---|
3024 |
|
---|
3025 | try {
|
---|
3026 | render = new Function(settings.variable || 'obj', '_', source);
|
---|
3027 | } catch (e) {
|
---|
3028 | e.source = source;
|
---|
3029 | throw e;
|
---|
3030 | }
|
---|
3031 |
|
---|
3032 | if (data) return render(data, _);
|
---|
3033 | var template = function(data) {
|
---|
3034 | return render.call(this, data, _);
|
---|
3035 | };
|
---|
3036 |
|
---|
3037 | // Provide the compiled function source as a convenience for precompilation.
|
---|
3038 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
|
---|
3039 |
|
---|
3040 | return template;
|
---|
3041 | };
|
---|
3042 |
|
---|
3043 | // Add a "chain" function, which will delegate to the wrapper.
|
---|
3044 | _.chain = function(obj) {
|
---|
3045 | return _(obj).chain();
|
---|
3046 | };
|
---|
3047 |
|
---|
3048 | // OOP
|
---|
3049 | // ---------------
|
---|
3050 | // If Underscore is called as a function, it returns a wrapped object that
|
---|
3051 | // can be used OO-style. This wrapper holds altered versions of all the
|
---|
3052 | // underscore functions. Wrapped objects may be chained.
|
---|
3053 |
|
---|
3054 | // Helper function to continue chaining intermediate results.
|
---|
3055 | var result = function(obj) {
|
---|
3056 | return this._chain ? _(obj).chain() : obj;
|
---|
3057 | };
|
---|
3058 |
|
---|
3059 | // Add all of the Underscore functions to the wrapper object.
|
---|
3060 | _.mixin(_);
|
---|
3061 |
|
---|
3062 | // Add all mutator Array functions to the wrapper.
|
---|
3063 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
---|
3064 | var method = ArrayProto[name];
|
---|
3065 | _.prototype[name] = function() {
|
---|
3066 | var obj = this._wrapped;
|
---|
3067 | method.apply(obj, arguments);
|
---|
3068 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
|
---|
3069 | return result.call(this, obj);
|
---|
3070 | };
|
---|
3071 | });
|
---|
3072 |
|
---|
3073 | // Add all accessor Array functions to the wrapper.
|
---|
3074 | each(['concat', 'join', 'slice'], function(name) {
|
---|
3075 | var method = ArrayProto[name];
|
---|
3076 | _.prototype[name] = function() {
|
---|
3077 | return result.call(this, method.apply(this._wrapped, arguments));
|
---|
3078 | };
|
---|
3079 | });
|
---|
3080 |
|
---|
3081 | _.extend(_.prototype, {
|
---|
3082 |
|
---|
3083 | // Start chaining a wrapped Underscore object.
|
---|
3084 | chain: function() {
|
---|
3085 | this._chain = true;
|
---|
3086 | return this;
|
---|
3087 | },
|
---|
3088 |
|
---|
3089 | // Extracts the result from a wrapped and chained object.
|
---|
3090 | value: function() {
|
---|
3091 | return this._wrapped;
|
---|
3092 | }
|
---|
3093 |
|
---|
3094 | });
|
---|
3095 |
|
---|
3096 | }).call(this);
|
---|
3097 |
|
---|
3098 | },{}],15:[function(require,module,exports){
|
---|
3099 |
|
---|
3100 | (function() {
|
---|
3101 |
|
---|
3102 | // Baseline setup
|
---|
3103 | // --------------
|
---|
3104 |
|
---|
3105 | // Establish the root object, `window` in the browser, or `global` on the server.
|
---|
3106 | var root = this;
|
---|
3107 |
|
---|
3108 | // Save the previous value of the `humanize` variable.
|
---|
3109 | var previousHumanize = root.humanize;
|
---|
3110 |
|
---|
3111 | var humanize = {};
|
---|
3112 |
|
---|
3113 | if (typeof exports !== 'undefined') {
|
---|
3114 | if (typeof module !== 'undefined' && module.exports) {
|
---|
3115 | exports = module.exports = humanize;
|
---|
3116 | }
|
---|
3117 | exports.humanize = humanize;
|
---|
3118 | } else {
|
---|
3119 | if (typeof define === 'function' && define.amd) {
|
---|
3120 | define('humanize', function() {
|
---|
3121 | return humanize;
|
---|
3122 | });
|
---|
3123 | }
|
---|
3124 | root.humanize = humanize;
|
---|
3125 | }
|
---|
3126 |
|
---|
3127 | humanize.noConflict = function() {
|
---|
3128 | root.humanize = previousHumanize;
|
---|
3129 | return this;
|
---|
3130 | };
|
---|
3131 |
|
---|
3132 | humanize.pad = function(str, count, padChar, type) {
|
---|
3133 | str += '';
|
---|
3134 | if (!padChar) {
|
---|
3135 | padChar = ' ';
|
---|
3136 | } else if (padChar.length > 1) {
|
---|
3137 | padChar = padChar.charAt(0);
|
---|
3138 | }
|
---|
3139 | type = (type === undefined) ? 'left' : 'right';
|
---|
3140 |
|
---|
3141 | if (type === 'right') {
|
---|
3142 | while (str.length < count) {
|
---|
3143 | str = str + padChar;
|
---|
3144 | }
|
---|
3145 | } else {
|
---|
3146 | // default to left
|
---|
3147 | while (str.length < count) {
|
---|
3148 | str = padChar + str;
|
---|
3149 | }
|
---|
3150 | }
|
---|
3151 |
|
---|
3152 | return str;
|
---|
3153 | };
|
---|
3154 |
|
---|
3155 | // gets current unix time
|
---|
3156 | humanize.time = function() {
|
---|
3157 | return new Date().getTime() / 1000;
|
---|
3158 | };
|
---|
3159 |
|
---|
3160 | /**
|
---|
3161 | * PHP-inspired date
|
---|
3162 | */
|
---|
3163 |
|
---|
3164 | /* jan feb mar apr may jun jul aug sep oct nov dec */
|
---|
3165 | var dayTableCommon = [ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ];
|
---|
3166 | var dayTableLeap = [ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ];
|
---|
3167 | // var mtable_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
---|
3168 | // static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
---|
3169 |
|
---|
3170 |
|
---|
3171 | humanize.date = function(format, timestamp) {
|
---|
3172 | var jsdate = ((timestamp === undefined) ? new Date() : // Not provided
|
---|
3173 | (timestamp instanceof Date) ? new Date(timestamp) : // JS Date()
|
---|
3174 | new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int)
|
---|
3175 | );
|
---|
3176 |
|
---|
3177 | var formatChr = /\\?([a-z])/gi;
|
---|
3178 | var formatChrCb = function (t, s) {
|
---|
3179 | return f[t] ? f[t]() : s;
|
---|
3180 | };
|
---|
3181 |
|
---|
3182 | var shortDayTxt = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
---|
3183 | var monthTxt = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
|
---|
3184 |
|
---|
3185 | var f = {
|
---|
3186 | /* Day */
|
---|
3187 | // Day of month w/leading 0; 01..31
|
---|
3188 | d: function () { return humanize.pad(f.j(), 2, '0'); },
|
---|
3189 |
|
---|
3190 | // Shorthand day name; Mon..Sun
|
---|
3191 | D: function () { return f.l().slice(0, 3); },
|
---|
3192 |
|
---|
3193 | // Day of month; 1..31
|
---|
3194 | j: function () { return jsdate.getDate(); },
|
---|
3195 |
|
---|
3196 | // Full day name; Monday..Sunday
|
---|
3197 | l: function () { return shortDayTxt[f.w()]; },
|
---|
3198 |
|
---|
3199 | // ISO-8601 day of week; 1[Mon]..7[Sun]
|
---|
3200 | N: function () { return f.w() || 7; },
|
---|
3201 |
|
---|
3202 | // Ordinal suffix for day of month; st, nd, rd, th
|
---|
3203 | S: function () {
|
---|
3204 | var j = f.j();
|
---|
3205 | return j > 4 && j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th';
|
---|
3206 | },
|
---|
3207 |
|
---|
3208 | // Day of week; 0[Sun]..6[Sat]
|
---|
3209 | w: function () { return jsdate.getDay(); },
|
---|
3210 |
|
---|
3211 | // Day of year; 0..365
|
---|
3212 | z: function () {
|
---|
3213 | return (f.L() ? dayTableLeap[f.n()] : dayTableCommon[f.n()]) + f.j() - 1;
|
---|
3214 | },
|
---|
3215 |
|
---|
3216 | /* Week */
|
---|
3217 | // ISO-8601 week number
|
---|
3218 | W: function () {
|
---|
3219 | // days between midweek of this week and jan 4
|
---|
3220 | // (f.z() - f.N() + 1 + 3.5) - 3
|
---|
3221 | var midWeekDaysFromJan4 = f.z() - f.N() + 1.5;
|
---|
3222 | // 1 + number of weeks + rounded week
|
---|
3223 | return humanize.pad(1 + Math.floor(Math.abs(midWeekDaysFromJan4) / 7) + (midWeekDaysFromJan4 % 7 > 3.5 ? 1 : 0), 2, '0');
|
---|
3224 | },
|
---|
3225 |
|
---|
3226 | /* Month */
|
---|
3227 | // Full month name; January..December
|
---|
3228 | F: function () { return monthTxt[jsdate.getMonth()]; },
|
---|
3229 |
|
---|
3230 | // Month w/leading 0; 01..12
|
---|
3231 | m: function () { return humanize.pad(f.n(), 2, '0'); },
|
---|
3232 |
|
---|
3233 | // Shorthand month name; Jan..Dec
|
---|
3234 | M: function () { return f.F().slice(0, 3); },
|
---|
3235 |
|
---|
3236 | // Month; 1..12
|
---|
3237 | n: function () { return jsdate.getMonth() + 1; },
|
---|
3238 |
|
---|
3239 | // Days in month; 28..31
|
---|
3240 | t: function () { return (new Date(f.Y(), f.n(), 0)).getDate(); },
|
---|
3241 |
|
---|
3242 | /* Year */
|
---|
3243 | // Is leap year?; 0 or 1
|
---|
3244 | L: function () { return new Date(f.Y(), 1, 29).getMonth() === 1 ? 1 : 0; },
|
---|
3245 |
|
---|
3246 | // ISO-8601 year
|
---|
3247 | o: function () {
|
---|
3248 | var n = f.n();
|
---|
3249 | var W = f.W();
|
---|
3250 | return f.Y() + (n === 12 && W < 9 ? -1 : n === 1 && W > 9);
|
---|
3251 | },
|
---|
3252 |
|
---|
3253 | // Full year; e.g. 1980..2010
|
---|
3254 | Y: function () { return jsdate.getFullYear(); },
|
---|
3255 |
|
---|
3256 | // Last two digits of year; 00..99
|
---|
3257 | y: function () { return (String(f.Y())).slice(-2); },
|
---|
3258 |
|
---|
3259 | /* Time */
|
---|
3260 | // am or pm
|
---|
3261 | a: function () { return jsdate.getHours() > 11 ? 'pm' : 'am'; },
|
---|
3262 |
|
---|
3263 | // AM or PM
|
---|
3264 | A: function () { return f.a().toUpperCase(); },
|
---|
3265 |
|
---|
3266 | // Swatch Internet time; 000..999
|
---|
3267 | B: function () {
|
---|
3268 | var unixTime = jsdate.getTime() / 1000;
|
---|
3269 | var secondsPassedToday = unixTime % 86400 + 3600; // since it's based off of UTC+1
|
---|
3270 | if (secondsPassedToday < 0) { secondsPassedToday += 86400; }
|
---|
3271 | var beats = ((secondsPassedToday) / 86.4) % 1000;
|
---|
3272 | if (unixTime < 0) {
|
---|
3273 | return Math.ceil(beats);
|
---|
3274 | }
|
---|
3275 | return Math.floor(beats);
|
---|
3276 | },
|
---|
3277 |
|
---|
3278 | // 12-Hours; 1..12
|
---|
3279 | g: function () { return f.G() % 12 || 12; },
|
---|
3280 |
|
---|
3281 | // 24-Hours; 0..23
|
---|
3282 | G: function () { return jsdate.getHours(); },
|
---|
3283 |
|
---|
3284 | // 12-Hours w/leading 0; 01..12
|
---|
3285 | h: function () { return humanize.pad(f.g(), 2, '0'); },
|
---|
3286 |
|
---|
3287 | // 24-Hours w/leading 0; 00..23
|
---|
3288 | H: function () { return humanize.pad(f.G(), 2, '0'); },
|
---|
3289 |
|
---|
3290 | // Minutes w/leading 0; 00..59
|
---|
3291 | i: function () { return humanize.pad(jsdate.getMinutes(), 2, '0'); },
|
---|
3292 |
|
---|
3293 | // Seconds w/leading 0; 00..59
|
---|
3294 | s: function () { return humanize.pad(jsdate.getSeconds(), 2, '0'); },
|
---|
3295 |
|
---|
3296 | // Microseconds; 000000-999000
|
---|
3297 | u: function () { return humanize.pad(jsdate.getMilliseconds() * 1000, 6, '0'); },
|
---|
3298 |
|
---|
3299 | // Whether or not the date is in daylight savings time
|
---|
3300 | /*
|
---|
3301 | I: function () {
|
---|
3302 | // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC.
|
---|
3303 | // If they are not equal, then DST is observed.
|
---|
3304 | var Y = f.Y();
|
---|
3305 | return 0 + ((new Date(Y, 0) - Date.UTC(Y, 0)) !== (new Date(Y, 6) - Date.UTC(Y, 6)));
|
---|
3306 | },
|
---|
3307 | */
|
---|
3308 |
|
---|
3309 | // Difference to GMT in hour format; e.g. +0200
|
---|
3310 | O: function () {
|
---|
3311 | var tzo = jsdate.getTimezoneOffset();
|
---|
3312 | var tzoNum = Math.abs(tzo);
|
---|
3313 | return (tzo > 0 ? '-' : '+') + humanize.pad(Math.floor(tzoNum / 60) * 100 + tzoNum % 60, 4, '0');
|
---|
3314 | },
|
---|
3315 |
|
---|
3316 | // Difference to GMT w/colon; e.g. +02:00
|
---|
3317 | P: function () {
|
---|
3318 | var O = f.O();
|
---|
3319 | return (O.substr(0, 3) + ':' + O.substr(3, 2));
|
---|
3320 | },
|
---|
3321 |
|
---|
3322 | // Timezone offset in seconds (-43200..50400)
|
---|
3323 | Z: function () { return -jsdate.getTimezoneOffset() * 60; },
|
---|
3324 |
|
---|
3325 | // Full Date/Time, ISO-8601 date
|
---|
3326 | c: function () { return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); },
|
---|
3327 |
|
---|
3328 | // RFC 2822
|
---|
3329 | r: function () { return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); },
|
---|
3330 |
|
---|
3331 | // Seconds since UNIX epoch
|
---|
3332 | U: function () { return jsdate.getTime() / 1000 || 0; }
|
---|
3333 | };
|
---|
3334 |
|
---|
3335 | return format.replace(formatChr, formatChrCb);
|
---|
3336 | };
|
---|
3337 |
|
---|
3338 |
|
---|
3339 | /**
|
---|
3340 | * format number by adding thousands separaters and significant digits while rounding
|
---|
3341 | */
|
---|
3342 | humanize.numberFormat = function(number, decimals, decPoint, thousandsSep) {
|
---|
3343 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals);
|
---|
3344 | decPoint = (decPoint === undefined) ? '.' : decPoint;
|
---|
3345 | thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep;
|
---|
3346 |
|
---|
3347 | var sign = number < 0 ? '-' : '';
|
---|
3348 | number = Math.abs(+number || 0);
|
---|
3349 |
|
---|
3350 | var intPart = parseInt(number.toFixed(decimals), 10) + '';
|
---|
3351 | var j = intPart.length > 3 ? intPart.length % 3 : 0;
|
---|
3352 |
|
---|
3353 | return sign + (j ? intPart.substr(0, j) + thousandsSep : '') + intPart.substr(j).replace(/(\d{3})(?=\d)/g, '$1' + thousandsSep) + (decimals ? decPoint + Math.abs(number - intPart).toFixed(decimals).slice(2) : '');
|
---|
3354 | };
|
---|
3355 |
|
---|
3356 |
|
---|
3357 | /**
|
---|
3358 | * For dates that are the current day or within one day, return 'today', 'tomorrow' or 'yesterday', as appropriate.
|
---|
3359 | * Otherwise, format the date using the passed in format string.
|
---|
3360 | *
|
---|
3361 | * Examples (when 'today' is 17 Feb 2007):
|
---|
3362 | * 16 Feb 2007 becomes yesterday.
|
---|
3363 | * 17 Feb 2007 becomes today.
|
---|
3364 | * 18 Feb 2007 becomes tomorrow.
|
---|
3365 | * Any other day is formatted according to given argument or the DATE_FORMAT setting if no argument is given.
|
---|
3366 | */
|
---|
3367 | humanize.naturalDay = function(timestamp, format) {
|
---|
3368 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
|
---|
3369 | format = (format === undefined) ? 'Y-m-d' : format;
|
---|
3370 |
|
---|
3371 | var oneDay = 86400;
|
---|
3372 | var d = new Date();
|
---|
3373 | var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000;
|
---|
3374 |
|
---|
3375 | if (timestamp < today && timestamp >= today - oneDay) {
|
---|
3376 | return 'yesterday';
|
---|
3377 | } else if (timestamp >= today && timestamp < today + oneDay) {
|
---|
3378 | return 'today';
|
---|
3379 | } else if (timestamp >= today + oneDay && timestamp < today + 2 * oneDay) {
|
---|
3380 | return 'tomorrow';
|
---|
3381 | }
|
---|
3382 |
|
---|
3383 | return humanize.date(format, timestamp);
|
---|
3384 | };
|
---|
3385 |
|
---|
3386 | /**
|
---|
3387 | * returns a string representing how many seconds, minutes or hours ago it was or will be in the future
|
---|
3388 | * Will always return a relative time, most granular of seconds to least granular of years. See unit tests for more details
|
---|
3389 | */
|
---|
3390 | humanize.relativeTime = function(timestamp) {
|
---|
3391 | timestamp = (timestamp === undefined) ? humanize.time() : timestamp;
|
---|
3392 |
|
---|
3393 | var currTime = humanize.time();
|
---|
3394 | var timeDiff = currTime - timestamp;
|
---|
3395 |
|
---|
3396 | // within 2 seconds
|
---|
3397 | if (timeDiff < 2 && timeDiff > -2) {
|
---|
3398 | return (timeDiff >= 0 ? 'just ' : '') + 'now';
|
---|
3399 | }
|
---|
3400 |
|
---|
3401 | // within a minute
|
---|
3402 | if (timeDiff < 60 && timeDiff > -60) {
|
---|
3403 | return (timeDiff >= 0 ? Math.floor(timeDiff) + ' seconds ago' : 'in ' + Math.floor(-timeDiff) + ' seconds');
|
---|
3404 | }
|
---|
3405 |
|
---|
3406 | // within 2 minutes
|
---|
3407 | if (timeDiff < 120 && timeDiff > -120) {
|
---|
3408 | return (timeDiff >= 0 ? 'about a minute ago' : 'in about a minute');
|
---|
3409 | }
|
---|
3410 |
|
---|
3411 | // within an hour
|
---|
3412 | if (timeDiff < 3600 && timeDiff > -3600) {
|
---|
3413 | return (timeDiff >= 0 ? Math.floor(timeDiff / 60) + ' minutes ago' : 'in ' + Math.floor(-timeDiff / 60) + ' minutes');
|
---|
3414 | }
|
---|
3415 |
|
---|
3416 | // within 2 hours
|
---|
3417 | if (timeDiff < 7200 && timeDiff > -7200) {
|
---|
3418 | return (timeDiff >= 0 ? 'about an hour ago' : 'in about an hour');
|
---|
3419 | }
|
---|
3420 |
|
---|
3421 | // within 24 hours
|
---|
3422 | if (timeDiff < 86400 && timeDiff > -86400) {
|
---|
3423 | return (timeDiff >= 0 ? Math.floor(timeDiff / 3600) + ' hours ago' : 'in ' + Math.floor(-timeDiff / 3600) + ' hours');
|
---|
3424 | }
|
---|
3425 |
|
---|
3426 | // within 2 days
|
---|
3427 | var days2 = 2 * 86400;
|
---|
3428 | if (timeDiff < days2 && timeDiff > -days2) {
|
---|
3429 | return (timeDiff >= 0 ? '1 day ago' : 'in 1 day');
|
---|
3430 | }
|
---|
3431 |
|
---|
3432 | // within 29 days
|
---|
3433 | var days29 = 29 * 86400;
|
---|
3434 | if (timeDiff < days29 && timeDiff > -days29) {
|
---|
3435 | return (timeDiff >= 0 ? Math.floor(timeDiff / 86400) + ' days ago' : 'in ' + Math.floor(-timeDiff / 86400) + ' days');
|
---|
3436 | }
|
---|
3437 |
|
---|
3438 | // within 60 days
|
---|
3439 | var days60 = 60 * 86400;
|
---|
3440 | if (timeDiff < days60 && timeDiff > -days60) {
|
---|
3441 | return (timeDiff >= 0 ? 'about a month ago' : 'in about a month');
|
---|
3442 | }
|
---|
3443 |
|
---|
3444 | var currTimeYears = parseInt(humanize.date('Y', currTime), 10);
|
---|
3445 | var timestampYears = parseInt(humanize.date('Y', timestamp), 10);
|
---|
3446 | var currTimeMonths = currTimeYears * 12 + parseInt(humanize.date('n', currTime), 10);
|
---|
3447 | var timestampMonths = timestampYears * 12 + parseInt(humanize.date('n', timestamp), 10);
|
---|
3448 |
|
---|
3449 | // within a year
|
---|
3450 | var monthDiff = currTimeMonths - timestampMonths;
|
---|
3451 | if (monthDiff < 12 && monthDiff > -12) {
|
---|
3452 | return (monthDiff >= 0 ? monthDiff + ' months ago' : 'in ' + (-monthDiff) + ' months');
|
---|
3453 | }
|
---|
3454 |
|
---|
3455 | var yearDiff = currTimeYears - timestampYears;
|
---|
3456 | if (yearDiff < 2 && yearDiff > -2) {
|
---|
3457 | return (yearDiff >= 0 ? 'a year ago' : 'in a year');
|
---|
3458 | }
|
---|
3459 |
|
---|
3460 | return (yearDiff >= 0 ? yearDiff + ' years ago' : 'in ' + (-yearDiff) + ' years');
|
---|
3461 | };
|
---|
3462 |
|
---|
3463 | /**
|
---|
3464 | * Converts an integer to its ordinal as a string.
|
---|
3465 | *
|
---|
3466 | * 1 becomes 1st
|
---|
3467 | * 2 becomes 2nd
|
---|
3468 | * 3 becomes 3rd etc
|
---|
3469 | */
|
---|
3470 | humanize.ordinal = function(number) {
|
---|
3471 | number = parseInt(number, 10);
|
---|
3472 | number = isNaN(number) ? 0 : number;
|
---|
3473 | var sign = number < 0 ? '-' : '';
|
---|
3474 | number = Math.abs(number);
|
---|
3475 | var tens = number % 100;
|
---|
3476 |
|
---|
3477 | return sign + number + (tens > 4 && tens < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[number % 10] || 'th');
|
---|
3478 | };
|
---|
3479 |
|
---|
3480 | /**
|
---|
3481 | * Formats the value like a 'human-readable' file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc).
|
---|
3482 | *
|
---|
3483 | * For example:
|
---|
3484 | * If value is 123456789, the output would be 117.7 MB.
|
---|
3485 | */
|
---|
3486 | humanize.filesize = function(filesize, kilo, decimals, decPoint, thousandsSep, suffixSep) {
|
---|
3487 | kilo = (kilo === undefined) ? 1024 : kilo;
|
---|
3488 | if (filesize <= 0) { return '0 bytes'; }
|
---|
3489 | if (filesize < kilo && decimals === undefined) { decimals = 0; }
|
---|
3490 | if (suffixSep === undefined) { suffixSep = ' '; }
|
---|
3491 | return humanize.intword(filesize, ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'], kilo, decimals, decPoint, thousandsSep, suffixSep);
|
---|
3492 | };
|
---|
3493 |
|
---|
3494 | /**
|
---|
3495 | * Formats the value like a 'human-readable' number (i.e. '13 K', '4.1 M', '102', etc).
|
---|
3496 | *
|
---|
3497 | * For example:
|
---|
3498 | * If value is 123456789, the output would be 117.7 M.
|
---|
3499 | */
|
---|
3500 | humanize.intword = function(number, units, kilo, decimals, decPoint, thousandsSep, suffixSep) {
|
---|
3501 | var humanized, unit;
|
---|
3502 |
|
---|
3503 | units = units || ['', 'K', 'M', 'B', 'T'],
|
---|
3504 | unit = units.length - 1,
|
---|
3505 | kilo = kilo || 1000,
|
---|
3506 | decimals = isNaN(decimals) ? 2 : Math.abs(decimals),
|
---|
3507 | decPoint = decPoint || '.',
|
---|
3508 | thousandsSep = thousandsSep || ',',
|
---|
3509 | suffixSep = suffixSep || '';
|
---|
3510 |
|
---|
3511 | for (var i=0; i < units.length; i++) {
|
---|
3512 | if (number < Math.pow(kilo, i+1)) {
|
---|
3513 | unit = i;
|
---|
3514 | break;
|
---|
3515 | }
|
---|
3516 | }
|
---|
3517 | humanized = number / Math.pow(kilo, unit);
|
---|
3518 |
|
---|
3519 | var suffix = units[unit] ? suffixSep + units[unit] : '';
|
---|
3520 | return humanize.numberFormat(humanized, decimals, decPoint, thousandsSep) + suffix;
|
---|
3521 | };
|
---|
3522 |
|
---|
3523 | /**
|
---|
3524 | * Replaces line breaks in plain text with appropriate HTML
|
---|
3525 | * A single newline becomes an HTML line break (<br />) and a new line followed by a blank line becomes a paragraph break (</p>).
|
---|
3526 | *
|
---|
3527 | * For example:
|
---|
3528 | * If value is Joel\nis a\n\nslug, the output will be <p>Joel<br />is a</p><p>slug</p>
|
---|
3529 | */
|
---|
3530 | humanize.linebreaks = function(str) {
|
---|
3531 | // remove beginning and ending newlines
|
---|
3532 | str = str.replace(/^([\n|\r]*)/, '');
|
---|
3533 | str = str.replace(/([\n|\r]*)$/, '');
|
---|
3534 |
|
---|
3535 | // normalize all to \n
|
---|
3536 | str = str.replace(/(\r\n|\n|\r)/g, "\n");
|
---|
3537 |
|
---|
3538 | // any consecutive new lines more than 2 gets turned into p tags
|
---|
3539 | str = str.replace(/(\n{2,})/g, '</p><p>');
|
---|
3540 |
|
---|
3541 | // any that are singletons get turned into br
|
---|
3542 | str = str.replace(/\n/g, '<br />');
|
---|
3543 | return '<p>' + str + '</p>';
|
---|
3544 | };
|
---|
3545 |
|
---|
3546 | /**
|
---|
3547 | * Converts all newlines in a piece of plain text to HTML line breaks (<br />).
|
---|
3548 | */
|
---|
3549 | humanize.nl2br = function(str) {
|
---|
3550 | return str.replace(/(\r\n|\n|\r)/g, '<br />');
|
---|
3551 | };
|
---|
3552 |
|
---|
3553 | /**
|
---|
3554 | * Truncates a string if it is longer than the specified number of characters.
|
---|
3555 | * Truncated strings will end with a translatable ellipsis sequence ('…').
|
---|
3556 | */
|
---|
3557 | humanize.truncatechars = function(string, length) {
|
---|
3558 | if (string.length <= length) { return string; }
|
---|
3559 | return string.substr(0, length) + '…';
|
---|
3560 | };
|
---|
3561 |
|
---|
3562 | /**
|
---|
3563 | * Truncates a string after a certain number of words.
|
---|
3564 | * Newlines within the string will be removed.
|
---|
3565 | */
|
---|
3566 | humanize.truncatewords = function(string, numWords) {
|
---|
3567 | var words = string.split(' ');
|
---|
3568 | if (words.length < numWords) { return string; }
|
---|
3569 | return words.slice(0, numWords).join(' ') + '…';
|
---|
3570 | };
|
---|
3571 |
|
---|
3572 | }).call(this);
|
---|
3573 |
|
---|
3574 | },{}],16:[function(require,module,exports){
|
---|
3575 | // Underscore.js 1.7.0
|
---|
3576 | // http://underscorejs.org
|
---|
3577 | // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
---|
3578 | // Underscore may be freely distributed under the MIT license.
|
---|
3579 |
|
---|
3580 | (function() {
|
---|
3581 |
|
---|
3582 | // Baseline setup
|
---|
3583 | // --------------
|
---|
3584 |
|
---|
3585 | // Establish the root object, `window` in the browser, or `exports` on the server.
|
---|
3586 | var root = this;
|
---|
3587 |
|
---|
3588 | // Save the previous value of the `_` variable.
|
---|
3589 | var previousUnderscore = root._;
|
---|
3590 |
|
---|
3591 | // Save bytes in the minified (but not gzipped) version:
|
---|
3592 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
---|
3593 |
|
---|
3594 | // Create quick reference variables for speed access to core prototypes.
|
---|
3595 | var
|
---|
3596 | push = ArrayProto.push,
|
---|
3597 | slice = ArrayProto.slice,
|
---|
3598 | concat = ArrayProto.concat,
|
---|
3599 | toString = ObjProto.toString,
|
---|
3600 | hasOwnProperty = ObjProto.hasOwnProperty;
|
---|
3601 |
|
---|
3602 | // All **ECMAScript 5** native function implementations that we hope to use
|
---|
3603 | // are declared here.
|
---|
3604 | var
|
---|
3605 | nativeIsArray = Array.isArray,
|
---|
3606 | nativeKeys = Object.keys,
|
---|
3607 | nativeBind = FuncProto.bind;
|
---|
3608 |
|
---|
3609 | // Create a safe reference to the Underscore object for use below.
|
---|
3610 | var _ = function(obj) {
|
---|
3611 | if (obj instanceof _) return obj;
|
---|
3612 | if (!(this instanceof _)) return new _(obj);
|
---|
3613 | this._wrapped = obj;
|
---|
3614 | };
|
---|
3615 |
|
---|
3616 | // Export the Underscore object for **Node.js**, with
|
---|
3617 | // backwards-compatibility for the old `require()` API. If we're in
|
---|
3618 | // the browser, add `_` as a global object.
|
---|
3619 | if (typeof exports !== 'undefined') {
|
---|
3620 | if (typeof module !== 'undefined' && module.exports) {
|
---|
3621 | exports = module.exports = _;
|
---|
3622 | }
|
---|
3623 | exports._ = _;
|
---|
3624 | } else {
|
---|
3625 | root._ = _;
|
---|
3626 | }
|
---|
3627 |
|
---|
3628 | // Current version.
|
---|
3629 | _.VERSION = '1.7.0';
|
---|
3630 |
|
---|
3631 | // Internal function that returns an efficient (for current engines) version
|
---|
3632 | // of the passed-in callback, to be repeatedly applied in other Underscore
|
---|
3633 | // functions.
|
---|
3634 | var createCallback = function(func, context, argCount) {
|
---|
3635 | if (context === void 0) return func;
|
---|
3636 | switch (argCount == null ? 3 : argCount) {
|
---|
3637 | case 1: return function(value) {
|
---|
3638 | return func.call(context, value);
|
---|
3639 | };
|
---|
3640 | case 2: return function(value, other) {
|
---|
3641 | return func.call(context, value, other);
|
---|
3642 | };
|
---|
3643 | case 3: return function(value, index, collection) {
|
---|
3644 | return func.call(context, value, index, collection);
|
---|
3645 | };
|
---|
3646 | case 4: return function(accumulator, value, index, collection) {
|
---|
3647 | return func.call(context, accumulator, value, index, collection);
|
---|
3648 | };
|
---|
3649 | }
|
---|
3650 | return function() {
|
---|
3651 | return func.apply(context, arguments);
|
---|
3652 | };
|
---|
3653 | };
|
---|
3654 |
|
---|
3655 | // A mostly-internal function to generate callbacks that can be applied
|
---|
3656 | // to each element in a collection, returning the desired result — either
|
---|
3657 | // identity, an arbitrary callback, a property matcher, or a property accessor.
|
---|
3658 | _.iteratee = function(value, context, argCount) {
|
---|
3659 | if (value == null) return _.identity;
|
---|
3660 | if (_.isFunction(value)) return createCallback(value, context, argCount);
|
---|
3661 | if (_.isObject(value)) return _.matches(value);
|
---|
3662 | return _.property(value);
|
---|
3663 | };
|
---|
3664 |
|
---|
3665 | // Collection Functions
|
---|
3666 | // --------------------
|
---|
3667 |
|
---|
3668 | // The cornerstone, an `each` implementation, aka `forEach`.
|
---|
3669 | // Handles raw objects in addition to array-likes. Treats all
|
---|
3670 | // sparse array-likes as if they were dense.
|
---|
3671 | _.each = _.forEach = function(obj, iteratee, context) {
|
---|
3672 | if (obj == null) return obj;
|
---|
3673 | iteratee = createCallback(iteratee, context);
|
---|
3674 | var i, length = obj.length;
|
---|
3675 | if (length === +length) {
|
---|
3676 | for (i = 0; i < length; i++) {
|
---|
3677 | iteratee(obj[i], i, obj);
|
---|
3678 | }
|
---|
3679 | } else {
|
---|
3680 | var keys = _.keys(obj);
|
---|
3681 | for (i = 0, length = keys.length; i < length; i++) {
|
---|
3682 | iteratee(obj[keys[i]], keys[i], obj);
|
---|
3683 | }
|
---|
3684 | }
|
---|
3685 | return obj;
|
---|
3686 | };
|
---|
3687 |
|
---|
3688 | // Return the results of applying the iteratee to each element.
|
---|
3689 | _.map = _.collect = function(obj, iteratee, context) {
|
---|
3690 | if (obj == null) return [];
|
---|
3691 | iteratee = _.iteratee(iteratee, context);
|
---|
3692 | var keys = obj.length !== +obj.length && _.keys(obj),
|
---|
3693 | length = (keys || obj).length,
|
---|
3694 | results = Array(length),
|
---|
3695 | currentKey;
|
---|
3696 | for (var index = 0; index < length; index++) {
|
---|
3697 | currentKey = keys ? keys[index] : index;
|
---|
3698 | results[index] = iteratee(obj[currentKey], currentKey, obj);
|
---|
3699 | }
|
---|
3700 | return results;
|
---|
3701 | };
|
---|
3702 |
|
---|
3703 | var reduceError = 'Reduce of empty array with no initial value';
|
---|
3704 |
|
---|
3705 | // **Reduce** builds up a single result from a list of values, aka `inject`,
|
---|
3706 | // or `foldl`.
|
---|
3707 | _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) {
|
---|
3708 | if (obj == null) obj = [];
|
---|
3709 | iteratee = createCallback(iteratee, context, 4);
|
---|
3710 | var keys = obj.length !== +obj.length && _.keys(obj),
|
---|
3711 | length = (keys || obj).length,
|
---|
3712 | index = 0, currentKey;
|
---|
3713 | if (arguments.length < 3) {
|
---|
3714 | if (!length) throw new TypeError(reduceError);
|
---|
3715 | memo = obj[keys ? keys[index++] : index++];
|
---|
3716 | }
|
---|
3717 | for (; index < length; index++) {
|
---|
3718 | currentKey = keys ? keys[index] : index;
|
---|
3719 | memo = iteratee(memo, obj[currentKey], currentKey, obj);
|
---|
3720 | }
|
---|
3721 | return memo;
|
---|
3722 | };
|
---|
3723 |
|
---|
3724 | // The right-associative version of reduce, also known as `foldr`.
|
---|
3725 | _.reduceRight = _.foldr = function(obj, iteratee, memo, context) {
|
---|
3726 | if (obj == null) obj = [];
|
---|
3727 | iteratee = createCallback(iteratee, context, 4);
|
---|
3728 | var keys = obj.length !== + obj.length && _.keys(obj),
|
---|
3729 | index = (keys || obj).length,
|
---|
3730 | currentKey;
|
---|
3731 | if (arguments.length < 3) {
|
---|
3732 | if (!index) throw new TypeError(reduceError);
|
---|
3733 | memo = obj[keys ? keys[--index] : --index];
|
---|
3734 | }
|
---|
3735 | while (index--) {
|
---|
3736 | currentKey = keys ? keys[index] : index;
|
---|
3737 | memo = iteratee(memo, obj[currentKey], currentKey, obj);
|
---|
3738 | }
|
---|
3739 | return memo;
|
---|
3740 | };
|
---|
3741 |
|
---|
3742 | // Return the first value which passes a truth test. Aliased as `detect`.
|
---|
3743 | _.find = _.detect = function(obj, predicate, context) {
|
---|
3744 | var result;
|
---|
3745 | predicate = _.iteratee(predicate, context);
|
---|
3746 | _.some(obj, function(value, index, list) {
|
---|
3747 | if (predicate(value, index, list)) {
|
---|
3748 | result = value;
|
---|
3749 | return true;
|
---|
3750 | }
|
---|
3751 | });
|
---|
3752 | return result;
|
---|
3753 | };
|
---|
3754 |
|
---|
3755 | // Return all the elements that pass a truth test.
|
---|
3756 | // Aliased as `select`.
|
---|
3757 | _.filter = _.select = function(obj, predicate, context) {
|
---|
3758 | var results = [];
|
---|
3759 | if (obj == null) return results;
|
---|
3760 | predicate = _.iteratee(predicate, context);
|
---|
3761 | _.each(obj, function(value, index, list) {
|
---|
3762 | if (predicate(value, index, list)) results.push(value);
|
---|
3763 | });
|
---|
3764 | return results;
|
---|
3765 | };
|
---|
3766 |
|
---|
3767 | // Return all the elements for which a truth test fails.
|
---|
3768 | _.reject = function(obj, predicate, context) {
|
---|
3769 | return _.filter(obj, _.negate(_.iteratee(predicate)), context);
|
---|
3770 | };
|
---|
3771 |
|
---|
3772 | // Determine whether all of the elements match a truth test.
|
---|
3773 | // Aliased as `all`.
|
---|
3774 | _.every = _.all = function(obj, predicate, context) {
|
---|
3775 | if (obj == null) return true;
|
---|
3776 | predicate = _.iteratee(predicate, context);
|
---|
3777 | var keys = obj.length !== +obj.length && _.keys(obj),
|
---|
3778 | length = (keys || obj).length,
|
---|
3779 | index, currentKey;
|
---|
3780 | for (index = 0; index < length; index++) {
|
---|
3781 | currentKey = keys ? keys[index] : index;
|
---|
3782 | if (!predicate(obj[currentKey], currentKey, obj)) return false;
|
---|
3783 | }
|
---|
3784 | return true;
|
---|
3785 | };
|
---|
3786 |
|
---|
3787 | // Determine if at least one element in the object matches a truth test.
|
---|
3788 | // Aliased as `any`.
|
---|
3789 | _.some = _.any = function(obj, predicate, context) {
|
---|
3790 | if (obj == null) return false;
|
---|
3791 | predicate = _.iteratee(predicate, context);
|
---|
3792 | var keys = obj.length !== +obj.length && _.keys(obj),
|
---|
3793 | length = (keys || obj).length,
|
---|
3794 | index, currentKey;
|
---|
3795 | for (index = 0; index < length; index++) {
|
---|
3796 | currentKey = keys ? keys[index] : index;
|
---|
3797 | if (predicate(obj[currentKey], currentKey, obj)) return true;
|
---|
3798 | }
|
---|
3799 | return false;
|
---|
3800 | };
|
---|
3801 |
|
---|
3802 | // Determine if the array or object contains a given value (using `===`).
|
---|
3803 | // Aliased as `include`.
|
---|
3804 | _.contains = _.include = function(obj, target) {
|
---|
3805 | if (obj == null) return false;
|
---|
3806 | if (obj.length !== +obj.length) obj = _.values(obj);
|
---|
3807 | return _.indexOf(obj, target) >= 0;
|
---|
3808 | };
|
---|
3809 |
|
---|
3810 | // Invoke a method (with arguments) on every item in a collection.
|
---|
3811 | _.invoke = function(obj, method) {
|
---|
3812 | var args = slice.call(arguments, 2);
|
---|
3813 | var isFunc = _.isFunction(method);
|
---|
3814 | return _.map(obj, function(value) {
|
---|
3815 | return (isFunc ? method : value[method]).apply(value, args);
|
---|
3816 | });
|
---|
3817 | };
|
---|
3818 |
|
---|
3819 | // Convenience version of a common use case of `map`: fetching a property.
|
---|
3820 | _.pluck = function(obj, key) {
|
---|
3821 | return _.map(obj, _.property(key));
|
---|
3822 | };
|
---|
3823 |
|
---|
3824 | // Convenience version of a common use case of `filter`: selecting only objects
|
---|
3825 | // containing specific `key:value` pairs.
|
---|
3826 | _.where = function(obj, attrs) {
|
---|
3827 | return _.filter(obj, _.matches(attrs));
|
---|
3828 | };
|
---|
3829 |
|
---|
3830 | // Convenience version of a common use case of `find`: getting the first object
|
---|
3831 | // containing specific `key:value` pairs.
|
---|
3832 | _.findWhere = function(obj, attrs) {
|
---|
3833 | return _.find(obj, _.matches(attrs));
|
---|
3834 | };
|
---|
3835 |
|
---|
3836 | // Return the maximum element (or element-based computation).
|
---|
3837 | _.max = function(obj, iteratee, context) {
|
---|
3838 | var result = -Infinity, lastComputed = -Infinity,
|
---|
3839 | value, computed;
|
---|
3840 | if (iteratee == null && obj != null) {
|
---|
3841 | obj = obj.length === +obj.length ? obj : _.values(obj);
|
---|
3842 | for (var i = 0, length = obj.length; i < length; i++) {
|
---|
3843 | value = obj[i];
|
---|
3844 | if (value > result) {
|
---|
3845 | result = value;
|
---|
3846 | }
|
---|
3847 | }
|
---|
3848 | } else {
|
---|
3849 | iteratee = _.iteratee(iteratee, context);
|
---|
3850 | _.each(obj, function(value, index, list) {
|
---|
3851 | computed = iteratee(value, index, list);
|
---|
3852 | if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
|
---|
3853 | result = value;
|
---|
3854 | lastComputed = computed;
|
---|
3855 | }
|
---|
3856 | });
|
---|
3857 | }
|
---|
3858 | return result;
|
---|
3859 | };
|
---|
3860 |
|
---|
3861 | // Return the minimum element (or element-based computation).
|
---|
3862 | _.min = function(obj, iteratee, context) {
|
---|
3863 | var result = Infinity, lastComputed = Infinity,
|
---|
3864 | value, computed;
|
---|
3865 | if (iteratee == null && obj != null) {
|
---|
3866 | obj = obj.length === +obj.length ? obj : _.values(obj);
|
---|
3867 | for (var i = 0, length = obj.length; i < length; i++) {
|
---|
3868 | value = obj[i];
|
---|
3869 | if (value < result) {
|
---|
3870 | result = value;
|
---|
3871 | }
|
---|
3872 | }
|
---|
3873 | } else {
|
---|
3874 | iteratee = _.iteratee(iteratee, context);
|
---|
3875 | _.each(obj, function(value, index, list) {
|
---|
3876 | computed = iteratee(value, index, list);
|
---|
3877 | if (computed < lastComputed || computed === Infinity && result === Infinity) {
|
---|
3878 | result = value;
|
---|
3879 | lastComputed = computed;
|
---|
3880 | }
|
---|
3881 | });
|
---|
3882 | }
|
---|
3883 | return result;
|
---|
3884 | };
|
---|
3885 |
|
---|
3886 | // Shuffle a collection, using the modern version of the
|
---|
3887 | // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
|
---|
3888 | _.shuffle = function(obj) {
|
---|
3889 | var set = obj && obj.length === +obj.length ? obj : _.values(obj);
|
---|
3890 | var length = set.length;
|
---|
3891 | var shuffled = Array(length);
|
---|
3892 | for (var index = 0, rand; index < length; index++) {
|
---|
3893 | rand = _.random(0, index);
|
---|
3894 | if (rand !== index) shuffled[index] = shuffled[rand];
|
---|
3895 | shuffled[rand] = set[index];
|
---|
3896 | }
|
---|
3897 | return shuffled;
|
---|
3898 | };
|
---|
3899 |
|
---|
3900 | // Sample **n** random values from a collection.
|
---|
3901 | // If **n** is not specified, returns a single random element.
|
---|
3902 | // The internal `guard` argument allows it to work with `map`.
|
---|
3903 | _.sample = function(obj, n, guard) {
|
---|
3904 | if (n == null || guard) {
|
---|
3905 | if (obj.length !== +obj.length) obj = _.values(obj);
|
---|
3906 | return obj[_.random(obj.length - 1)];
|
---|
3907 | }
|
---|
3908 | return _.shuffle(obj).slice(0, Math.max(0, n));
|
---|
3909 | };
|
---|
3910 |
|
---|
3911 | // Sort the object's values by a criterion produced by an iteratee.
|
---|
3912 | _.sortBy = function(obj, iteratee, context) {
|
---|
3913 | iteratee = _.iteratee(iteratee, context);
|
---|
3914 | return _.pluck(_.map(obj, function(value, index, list) {
|
---|
3915 | return {
|
---|
3916 | value: value,
|
---|
3917 | index: index,
|
---|
3918 | criteria: iteratee(value, index, list)
|
---|
3919 | };
|
---|
3920 | }).sort(function(left, right) {
|
---|
3921 | var a = left.criteria;
|
---|
3922 | var b = right.criteria;
|
---|
3923 | if (a !== b) {
|
---|
3924 | if (a > b || a === void 0) return 1;
|
---|
3925 | if (a < b || b === void 0) return -1;
|
---|
3926 | }
|
---|
3927 | return left.index - right.index;
|
---|
3928 | }), 'value');
|
---|
3929 | };
|
---|
3930 |
|
---|
3931 | // An internal function used for aggregate "group by" operations.
|
---|
3932 | var group = function(behavior) {
|
---|
3933 | return function(obj, iteratee, context) {
|
---|
3934 | var result = {};
|
---|
3935 | iteratee = _.iteratee(iteratee, context);
|
---|
3936 | _.each(obj, function(value, index) {
|
---|
3937 | var key = iteratee(value, index, obj);
|
---|
3938 | behavior(result, value, key);
|
---|
3939 | });
|
---|
3940 | return result;
|
---|
3941 | };
|
---|
3942 | };
|
---|
3943 |
|
---|
3944 | // Groups the object's values by a criterion. Pass either a string attribute
|
---|
3945 | // to group by, or a function that returns the criterion.
|
---|
3946 | _.groupBy = group(function(result, value, key) {
|
---|
3947 | if (_.has(result, key)) result[key].push(value); else result[key] = [value];
|
---|
3948 | });
|
---|
3949 |
|
---|
3950 | // Indexes the object's values by a criterion, similar to `groupBy`, but for
|
---|
3951 | // when you know that your index values will be unique.
|
---|
3952 | _.indexBy = group(function(result, value, key) {
|
---|
3953 | result[key] = value;
|
---|
3954 | });
|
---|
3955 |
|
---|
3956 | // Counts instances of an object that group by a certain criterion. Pass
|
---|
3957 | // either a string attribute to count by, or a function that returns the
|
---|
3958 | // criterion.
|
---|
3959 | _.countBy = group(function(result, value, key) {
|
---|
3960 | if (_.has(result, key)) result[key]++; else result[key] = 1;
|
---|
3961 | });
|
---|
3962 |
|
---|
3963 | // Use a comparator function to figure out the smallest index at which
|
---|
3964 | // an object should be inserted so as to maintain order. Uses binary search.
|
---|
3965 | _.sortedIndex = function(array, obj, iteratee, context) {
|
---|
3966 | iteratee = _.iteratee(iteratee, context, 1);
|
---|
3967 | var value = iteratee(obj);
|
---|
3968 | var low = 0, high = array.length;
|
---|
3969 | while (low < high) {
|
---|
3970 | var mid = low + high >>> 1;
|
---|
3971 | if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
|
---|
3972 | }
|
---|
3973 | return low;
|
---|
3974 | };
|
---|
3975 |
|
---|
3976 | // Safely create a real, live array from anything iterable.
|
---|
3977 | _.toArray = function(obj) {
|
---|
3978 | if (!obj) return [];
|
---|
3979 | if (_.isArray(obj)) return slice.call(obj);
|
---|
3980 | if (obj.length === +obj.length) return _.map(obj, _.identity);
|
---|
3981 | return _.values(obj);
|
---|
3982 | };
|
---|
3983 |
|
---|
3984 | // Return the number of elements in an object.
|
---|
3985 | _.size = function(obj) {
|
---|
3986 | if (obj == null) return 0;
|
---|
3987 | return obj.length === +obj.length ? obj.length : _.keys(obj).length;
|
---|
3988 | };
|
---|
3989 |
|
---|
3990 | // Split a collection into two arrays: one whose elements all satisfy the given
|
---|
3991 | // predicate, and one whose elements all do not satisfy the predicate.
|
---|
3992 | _.partition = function(obj, predicate, context) {
|
---|
3993 | predicate = _.iteratee(predicate, context);
|
---|
3994 | var pass = [], fail = [];
|
---|
3995 | _.each(obj, function(value, key, obj) {
|
---|
3996 | (predicate(value, key, obj) ? pass : fail).push(value);
|
---|
3997 | });
|
---|
3998 | return [pass, fail];
|
---|
3999 | };
|
---|
4000 |
|
---|
4001 | // Array Functions
|
---|
4002 | // ---------------
|
---|
4003 |
|
---|
4004 | // Get the first element of an array. Passing **n** will return the first N
|
---|
4005 | // values in the array. Aliased as `head` and `take`. The **guard** check
|
---|
4006 | // allows it to work with `_.map`.
|
---|
4007 | _.first = _.head = _.take = function(array, n, guard) {
|
---|
4008 | if (array == null) return void 0;
|
---|
4009 | if (n == null || guard) return array[0];
|
---|
4010 | if (n < 0) return [];
|
---|
4011 | return slice.call(array, 0, n);
|
---|
4012 | };
|
---|
4013 |
|
---|
4014 | // Returns everything but the last entry of the array. Especially useful on
|
---|
4015 | // the arguments object. Passing **n** will return all the values in
|
---|
4016 | // the array, excluding the last N. The **guard** check allows it to work with
|
---|
4017 | // `_.map`.
|
---|
4018 | _.initial = function(array, n, guard) {
|
---|
4019 | return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
|
---|
4020 | };
|
---|
4021 |
|
---|
4022 | // Get the last element of an array. Passing **n** will return the last N
|
---|
4023 | // values in the array. The **guard** check allows it to work with `_.map`.
|
---|
4024 | _.last = function(array, n, guard) {
|
---|
4025 | if (array == null) return void 0;
|
---|
4026 | if (n == null || guard) return array[array.length - 1];
|
---|
4027 | return slice.call(array, Math.max(array.length - n, 0));
|
---|
4028 | };
|
---|
4029 |
|
---|
4030 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
---|
4031 | // Especially useful on the arguments object. Passing an **n** will return
|
---|
4032 | // the rest N values in the array. The **guard**
|
---|
4033 | // check allows it to work with `_.map`.
|
---|
4034 | _.rest = _.tail = _.drop = function(array, n, guard) {
|
---|
4035 | return slice.call(array, n == null || guard ? 1 : n);
|
---|
4036 | };
|
---|
4037 |
|
---|
4038 | // Trim out all falsy values from an array.
|
---|
4039 | _.compact = function(array) {
|
---|
4040 | return _.filter(array, _.identity);
|
---|
4041 | };
|
---|
4042 |
|
---|
4043 | // Internal implementation of a recursive `flatten` function.
|
---|
4044 | var flatten = function(input, shallow, strict, output) {
|
---|
4045 | if (shallow && _.every(input, _.isArray)) {
|
---|
4046 | return concat.apply(output, input);
|
---|
4047 | }
|
---|
4048 | for (var i = 0, length = input.length; i < length; i++) {
|
---|
4049 | var value = input[i];
|
---|
4050 | if (!_.isArray(value) && !_.isArguments(value)) {
|
---|
4051 | if (!strict) output.push(value);
|
---|
4052 | } else if (shallow) {
|
---|
4053 | push.apply(output, value);
|
---|
4054 | } else {
|
---|
4055 | flatten(value, shallow, strict, output);
|
---|
4056 | }
|
---|
4057 | }
|
---|
4058 | return output;
|
---|
4059 | };
|
---|
4060 |
|
---|
4061 | // Flatten out an array, either recursively (by default), or just one level.
|
---|
4062 | _.flatten = function(array, shallow) {
|
---|
4063 | return flatten(array, shallow, false, []);
|
---|
4064 | };
|
---|
4065 |
|
---|
4066 | // Return a version of the array that does not contain the specified value(s).
|
---|
4067 | _.without = function(array) {
|
---|
4068 | return _.difference(array, slice.call(arguments, 1));
|
---|
4069 | };
|
---|
4070 |
|
---|
4071 | // Produce a duplicate-free version of the array. If the array has already
|
---|
4072 | // been sorted, you have the option of using a faster algorithm.
|
---|
4073 | // Aliased as `unique`.
|
---|
4074 | _.uniq = _.unique = function(array, isSorted, iteratee, context) {
|
---|
4075 | if (array == null) return [];
|
---|
4076 | if (!_.isBoolean(isSorted)) {
|
---|
4077 | context = iteratee;
|
---|
4078 | iteratee = isSorted;
|
---|
4079 | isSorted = false;
|
---|
4080 | }
|
---|
4081 | if (iteratee != null) iteratee = _.iteratee(iteratee, context);
|
---|
4082 | var result = [];
|
---|
4083 | var seen = [];
|
---|
4084 | for (var i = 0, length = array.length; i < length; i++) {
|
---|
4085 | var value = array[i];
|
---|
4086 | if (isSorted) {
|
---|
4087 | if (!i || seen !== value) result.push(value);
|
---|
4088 | seen = value;
|
---|
4089 | } else if (iteratee) {
|
---|
4090 | var computed = iteratee(value, i, array);
|
---|
4091 | if (_.indexOf(seen, computed) < 0) {
|
---|
4092 | seen.push(computed);
|
---|
4093 | result.push(value);
|
---|
4094 | }
|
---|
4095 | } else if (_.indexOf(result, value) < 0) {
|
---|
4096 | result.push(value);
|
---|
4097 | }
|
---|
4098 | }
|
---|
4099 | return result;
|
---|
4100 | };
|
---|
4101 |
|
---|
4102 | // Produce an array that contains the union: each distinct element from all of
|
---|
4103 | // the passed-in arrays.
|
---|
4104 | _.union = function() {
|
---|
4105 | return _.uniq(flatten(arguments, true, true, []));
|
---|
4106 | };
|
---|
4107 |
|
---|
4108 | // Produce an array that contains every item shared between all the
|
---|
4109 | // passed-in arrays.
|
---|
4110 | _.intersection = function(array) {
|
---|
4111 | if (array == null) return [];
|
---|
4112 | var result = [];
|
---|
4113 | var argsLength = arguments.length;
|
---|
4114 | for (var i = 0, length = array.length; i < length; i++) {
|
---|
4115 | var item = array[i];
|
---|
4116 | if (_.contains(result, item)) continue;
|
---|
4117 | for (var j = 1; j < argsLength; j++) {
|
---|
4118 | if (!_.contains(arguments[j], item)) break;
|
---|
4119 | }
|
---|
4120 | if (j === argsLength) result.push(item);
|
---|
4121 | }
|
---|
4122 | return result;
|
---|
4123 | };
|
---|
4124 |
|
---|
4125 | // Take the difference between one array and a number of other arrays.
|
---|
4126 | // Only the elements present in just the first array will remain.
|
---|
4127 | _.difference = function(array) {
|
---|
4128 | var rest = flatten(slice.call(arguments, 1), true, true, []);
|
---|
4129 | return _.filter(array, function(value){
|
---|
4130 | return !_.contains(rest, value);
|
---|
4131 | });
|
---|
4132 | };
|
---|
4133 |
|
---|
4134 | // Zip together multiple lists into a single array -- elements that share
|
---|
4135 | // an index go together.
|
---|
4136 | _.zip = function(array) {
|
---|
4137 | if (array == null) return [];
|
---|
4138 | var length = _.max(arguments, 'length').length;
|
---|
4139 | var results = Array(length);
|
---|
4140 | for (var i = 0; i < length; i++) {
|
---|
4141 | results[i] = _.pluck(arguments, i);
|
---|
4142 | }
|
---|
4143 | return results;
|
---|
4144 | };
|
---|
4145 |
|
---|
4146 | // Converts lists into objects. Pass either a single array of `[key, value]`
|
---|
4147 | // pairs, or two parallel arrays of the same length -- one of keys, and one of
|
---|
4148 | // the corresponding values.
|
---|
4149 | _.object = function(list, values) {
|
---|
4150 | if (list == null) return {};
|
---|
4151 | var result = {};
|
---|
4152 | for (var i = 0, length = list.length; i < length; i++) {
|
---|
4153 | if (values) {
|
---|
4154 | result[list[i]] = values[i];
|
---|
4155 | } else {
|
---|
4156 | result[list[i][0]] = list[i][1];
|
---|
4157 | }
|
---|
4158 | }
|
---|
4159 | return result;
|
---|
4160 | };
|
---|
4161 |
|
---|
4162 | // Return the position of the first occurrence of an item in an array,
|
---|
4163 | // or -1 if the item is not included in the array.
|
---|
4164 | // If the array is large and already in sort order, pass `true`
|
---|
4165 | // for **isSorted** to use binary search.
|
---|
4166 | _.indexOf = function(array, item, isSorted) {
|
---|
4167 | if (array == null) return -1;
|
---|
4168 | var i = 0, length = array.length;
|
---|
4169 | if (isSorted) {
|
---|
4170 | if (typeof isSorted == 'number') {
|
---|
4171 | i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
|
---|
4172 | } else {
|
---|
4173 | i = _.sortedIndex(array, item);
|
---|
4174 | return array[i] === item ? i : -1;
|
---|
4175 | }
|
---|
4176 | }
|
---|
4177 | for (; i < length; i++) if (array[i] === item) return i;
|
---|
4178 | return -1;
|
---|
4179 | };
|
---|
4180 |
|
---|
4181 | _.lastIndexOf = function(array, item, from) {
|
---|
4182 | if (array == null) return -1;
|
---|
4183 | var idx = array.length;
|
---|
4184 | if (typeof from == 'number') {
|
---|
4185 | idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
|
---|
4186 | }
|
---|
4187 | while (--idx >= 0) if (array[idx] === item) return idx;
|
---|
4188 | return -1;
|
---|
4189 | };
|
---|
4190 |
|
---|
4191 | // Generate an integer Array containing an arithmetic progression. A port of
|
---|
4192 | // the native Python `range()` function. See
|
---|
4193 | // [the Python documentation](http://docs.python.org/library/functions.html#range).
|
---|
4194 | _.range = function(start, stop, step) {
|
---|
4195 | if (arguments.length <= 1) {
|
---|
4196 | stop = start || 0;
|
---|
4197 | start = 0;
|
---|
4198 | }
|
---|
4199 | step = step || 1;
|
---|
4200 |
|
---|
4201 | var length = Math.max(Math.ceil((stop - start) / step), 0);
|
---|
4202 | var range = Array(length);
|
---|
4203 |
|
---|
4204 | for (var idx = 0; idx < length; idx++, start += step) {
|
---|
4205 | range[idx] = start;
|
---|
4206 | }
|
---|
4207 |
|
---|
4208 | return range;
|
---|
4209 | };
|
---|
4210 |
|
---|
4211 | // Function (ahem) Functions
|
---|
4212 | // ------------------
|
---|
4213 |
|
---|
4214 | // Reusable constructor function for prototype setting.
|
---|
4215 | var Ctor = function(){};
|
---|
4216 |
|
---|
4217 | // Create a function bound to a given object (assigning `this`, and arguments,
|
---|
4218 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
---|
4219 | // available.
|
---|
4220 | _.bind = function(func, context) {
|
---|
4221 | var args, bound;
|
---|
4222 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
---|
4223 | if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
|
---|
4224 | args = slice.call(arguments, 2);
|
---|
4225 | bound = function() {
|
---|
4226 | if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
|
---|
4227 | Ctor.prototype = func.prototype;
|
---|
4228 | var self = new Ctor;
|
---|
4229 | Ctor.prototype = null;
|
---|
4230 | var result = func.apply(self, args.concat(slice.call(arguments)));
|
---|
4231 | if (_.isObject(result)) return result;
|
---|
4232 | return self;
|
---|
4233 | };
|
---|
4234 | return bound;
|
---|
4235 | };
|
---|
4236 |
|
---|
4237 | // Partially apply a function by creating a version that has had some of its
|
---|
4238 | // arguments pre-filled, without changing its dynamic `this` context. _ acts
|
---|
4239 | // as a placeholder, allowing any combination of arguments to be pre-filled.
|
---|
4240 | _.partial = function(func) {
|
---|
4241 | var boundArgs = slice.call(arguments, 1);
|
---|
4242 | return function() {
|
---|
4243 | var position = 0;
|
---|
4244 | var args = boundArgs.slice();
|
---|
4245 | for (var i = 0, length = args.length; i < length; i++) {
|
---|
4246 | if (args[i] === _) args[i] = arguments[position++];
|
---|
4247 | }
|
---|
4248 | while (position < arguments.length) args.push(arguments[position++]);
|
---|
4249 | return func.apply(this, args);
|
---|
4250 | };
|
---|
4251 | };
|
---|
4252 |
|
---|
4253 | // Bind a number of an object's methods to that object. Remaining arguments
|
---|
4254 | // are the method names to be bound. Useful for ensuring that all callbacks
|
---|
4255 | // defined on an object belong to it.
|
---|
4256 | _.bindAll = function(obj) {
|
---|
4257 | var i, length = arguments.length, key;
|
---|
4258 | if (length <= 1) throw new Error('bindAll must be passed function names');
|
---|
4259 | for (i = 1; i < length; i++) {
|
---|
4260 | key = arguments[i];
|
---|
4261 | obj[key] = _.bind(obj[key], obj);
|
---|
4262 | }
|
---|
4263 | return obj;
|
---|
4264 | };
|
---|
4265 |
|
---|
4266 | // Memoize an expensive function by storing its results.
|
---|
4267 | _.memoize = function(func, hasher) {
|
---|
4268 | var memoize = function(key) {
|
---|
4269 | var cache = memoize.cache;
|
---|
4270 | var address = hasher ? hasher.apply(this, arguments) : key;
|
---|
4271 | if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
|
---|
4272 | return cache[address];
|
---|
4273 | };
|
---|
4274 | memoize.cache = {};
|
---|
4275 | return memoize;
|
---|
4276 | };
|
---|
4277 |
|
---|
4278 | // Delays a function for the given number of milliseconds, and then calls
|
---|
4279 | // it with the arguments supplied.
|
---|
4280 | _.delay = function(func, wait) {
|
---|
4281 | var args = slice.call(arguments, 2);
|
---|
4282 | return setTimeout(function(){
|
---|
4283 | return func.apply(null, args);
|
---|
4284 | }, wait);
|
---|
4285 | };
|
---|
4286 |
|
---|
4287 | // Defers a function, scheduling it to run after the current call stack has
|
---|
4288 | // cleared.
|
---|
4289 | _.defer = function(func) {
|
---|
4290 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
|
---|
4291 | };
|
---|
4292 |
|
---|
4293 | // Returns a function, that, when invoked, will only be triggered at most once
|
---|
4294 | // during a given window of time. Normally, the throttled function will run
|
---|
4295 | // as much as it can, without ever going more than once per `wait` duration;
|
---|
4296 | // but if you'd like to disable the execution on the leading edge, pass
|
---|
4297 | // `{leading: false}`. To disable execution on the trailing edge, ditto.
|
---|
4298 | _.throttle = function(func, wait, options) {
|
---|
4299 | var context, args, result;
|
---|
4300 | var timeout = null;
|
---|
4301 | var previous = 0;
|
---|
4302 | if (!options) options = {};
|
---|
4303 | var later = function() {
|
---|
4304 | previous = options.leading === false ? 0 : _.now();
|
---|
4305 | timeout = null;
|
---|
4306 | result = func.apply(context, args);
|
---|
4307 | if (!timeout) context = args = null;
|
---|
4308 | };
|
---|
4309 | return function() {
|
---|
4310 | var now = _.now();
|
---|
4311 | if (!previous && options.leading === false) previous = now;
|
---|
4312 | var remaining = wait - (now - previous);
|
---|
4313 | context = this;
|
---|
4314 | args = arguments;
|
---|
4315 | if (remaining <= 0 || remaining > wait) {
|
---|
4316 | clearTimeout(timeout);
|
---|
4317 | timeout = null;
|
---|
4318 | previous = now;
|
---|
4319 | result = func.apply(context, args);
|
---|
4320 | if (!timeout) context = args = null;
|
---|
4321 | } else if (!timeout && options.trailing !== false) {
|
---|
4322 | timeout = setTimeout(later, remaining);
|
---|
4323 | }
|
---|
4324 | return result;
|
---|
4325 | };
|
---|
4326 | };
|
---|
4327 |
|
---|
4328 | // Returns a function, that, as long as it continues to be invoked, will not
|
---|
4329 | // be triggered. The function will be called after it stops being called for
|
---|
4330 | // N milliseconds. If `immediate` is passed, trigger the function on the
|
---|
4331 | // leading edge, instead of the trailing.
|
---|
4332 | _.debounce = function(func, wait, immediate) {
|
---|
4333 | var timeout, args, context, timestamp, result;
|
---|
4334 |
|
---|
4335 | var later = function() {
|
---|
4336 | var last = _.now() - timestamp;
|
---|
4337 |
|
---|
4338 | if (last < wait && last > 0) {
|
---|
4339 | timeout = setTimeout(later, wait - last);
|
---|
4340 | } else {
|
---|
4341 | timeout = null;
|
---|
4342 | if (!immediate) {
|
---|
4343 | result = func.apply(context, args);
|
---|
4344 | if (!timeout) context = args = null;
|
---|
4345 | }
|
---|
4346 | }
|
---|
4347 | };
|
---|
4348 |
|
---|
4349 | return function() {
|
---|
4350 | context = this;
|
---|
4351 | args = arguments;
|
---|
4352 | timestamp = _.now();
|
---|
4353 | var callNow = immediate && !timeout;
|
---|
4354 | if (!timeout) timeout = setTimeout(later, wait);
|
---|
4355 | if (callNow) {
|
---|
4356 | result = func.apply(context, args);
|
---|
4357 | context = args = null;
|
---|
4358 | }
|
---|
4359 |
|
---|
4360 | return result;
|
---|
4361 | };
|
---|
4362 | };
|
---|
4363 |
|
---|
4364 | // Returns the first function passed as an argument to the second,
|
---|
4365 | // allowing you to adjust arguments, run code before and after, and
|
---|
4366 | // conditionally execute the original function.
|
---|
4367 | _.wrap = function(func, wrapper) {
|
---|
4368 | return _.partial(wrapper, func);
|
---|
4369 | };
|
---|
4370 |
|
---|
4371 | // Returns a negated version of the passed-in predicate.
|
---|
4372 | _.negate = function(predicate) {
|
---|
4373 | return function() {
|
---|
4374 | return !predicate.apply(this, arguments);
|
---|
4375 | };
|
---|
4376 | };
|
---|
4377 |
|
---|
4378 | // Returns a function that is the composition of a list of functions, each
|
---|
4379 | // consuming the return value of the function that follows.
|
---|
4380 | _.compose = function() {
|
---|
4381 | var args = arguments;
|
---|
4382 | var start = args.length - 1;
|
---|
4383 | return function() {
|
---|
4384 | var i = start;
|
---|
4385 | var result = args[start].apply(this, arguments);
|
---|
4386 | while (i--) result = args[i].call(this, result);
|
---|
4387 | return result;
|
---|
4388 | };
|
---|
4389 | };
|
---|
4390 |
|
---|
4391 | // Returns a function that will only be executed after being called N times.
|
---|
4392 | _.after = function(times, func) {
|
---|
4393 | return function() {
|
---|
4394 | if (--times < 1) {
|
---|
4395 | return func.apply(this, arguments);
|
---|
4396 | }
|
---|
4397 | };
|
---|
4398 | };
|
---|
4399 |
|
---|
4400 | // Returns a function that will only be executed before being called N times.
|
---|
4401 | _.before = function(times, func) {
|
---|
4402 | var memo;
|
---|
4403 | return function() {
|
---|
4404 | if (--times > 0) {
|
---|
4405 | memo = func.apply(this, arguments);
|
---|
4406 | } else {
|
---|
4407 | func = null;
|
---|
4408 | }
|
---|
4409 | return memo;
|
---|
4410 | };
|
---|
4411 | };
|
---|
4412 |
|
---|
4413 | // Returns a function that will be executed at most one time, no matter how
|
---|
4414 | // often you call it. Useful for lazy initialization.
|
---|
4415 | _.once = _.partial(_.before, 2);
|
---|
4416 |
|
---|
4417 | // Object Functions
|
---|
4418 | // ----------------
|
---|
4419 |
|
---|
4420 | // Retrieve the names of an object's properties.
|
---|
4421 | // Delegates to **ECMAScript 5**'s native `Object.keys`
|
---|
4422 | _.keys = function(obj) {
|
---|
4423 | if (!_.isObject(obj)) return [];
|
---|
4424 | if (nativeKeys) return nativeKeys(obj);
|
---|
4425 | var keys = [];
|
---|
4426 | for (var key in obj) if (_.has(obj, key)) keys.push(key);
|
---|
4427 | return keys;
|
---|
4428 | };
|
---|
4429 |
|
---|
4430 | // Retrieve the values of an object's properties.
|
---|
4431 | _.values = function(obj) {
|
---|
4432 | var keys = _.keys(obj);
|
---|
4433 | var length = keys.length;
|
---|
4434 | var values = Array(length);
|
---|
4435 | for (var i = 0; i < length; i++) {
|
---|
4436 | values[i] = obj[keys[i]];
|
---|
4437 | }
|
---|
4438 | return values;
|
---|
4439 | };
|
---|
4440 |
|
---|
4441 | // Convert an object into a list of `[key, value]` pairs.
|
---|
4442 | _.pairs = function(obj) {
|
---|
4443 | var keys = _.keys(obj);
|
---|
4444 | var length = keys.length;
|
---|
4445 | var pairs = Array(length);
|
---|
4446 | for (var i = 0; i < length; i++) {
|
---|
4447 | pairs[i] = [keys[i], obj[keys[i]]];
|
---|
4448 | }
|
---|
4449 | return pairs;
|
---|
4450 | };
|
---|
4451 |
|
---|
4452 | // Invert the keys and values of an object. The values must be serializable.
|
---|
4453 | _.invert = function(obj) {
|
---|
4454 | var result = {};
|
---|
4455 | var keys = _.keys(obj);
|
---|
4456 | for (var i = 0, length = keys.length; i < length; i++) {
|
---|
4457 | result[obj[keys[i]]] = keys[i];
|
---|
4458 | }
|
---|
4459 | return result;
|
---|
4460 | };
|
---|
4461 |
|
---|
4462 | // Return a sorted list of the function names available on the object.
|
---|
4463 | // Aliased as `methods`
|
---|
4464 | _.functions = _.methods = function(obj) {
|
---|
4465 | var names = [];
|
---|
4466 | for (var key in obj) {
|
---|
4467 | if (_.isFunction(obj[key])) names.push(key);
|
---|
4468 | }
|
---|
4469 | return names.sort();
|
---|
4470 | };
|
---|
4471 |
|
---|
4472 | // Extend a given object with all the properties in passed-in object(s).
|
---|
4473 | _.extend = function(obj) {
|
---|
4474 | if (!_.isObject(obj)) return obj;
|
---|
4475 | var source, prop;
|
---|
4476 | for (var i = 1, length = arguments.length; i < length; i++) {
|
---|
4477 | source = arguments[i];
|
---|
4478 | for (prop in source) {
|
---|
4479 | if (hasOwnProperty.call(source, prop)) {
|
---|
4480 | obj[prop] = source[prop];
|
---|
4481 | }
|
---|
4482 | }
|
---|
4483 | }
|
---|
4484 | return obj;
|
---|
4485 | };
|
---|
4486 |
|
---|
4487 | // Return a copy of the object only containing the whitelisted properties.
|
---|
4488 | _.pick = function(obj, iteratee, context) {
|
---|
4489 | var result = {}, key;
|
---|
4490 | if (obj == null) return result;
|
---|
4491 | if (_.isFunction(iteratee)) {
|
---|
4492 | iteratee = createCallback(iteratee, context);
|
---|
4493 | for (key in obj) {
|
---|
4494 | var value = obj[key];
|
---|
4495 | if (iteratee(value, key, obj)) result[key] = value;
|
---|
4496 | }
|
---|
4497 | } else {
|
---|
4498 | var keys = concat.apply([], slice.call(arguments, 1));
|
---|
4499 | obj = new Object(obj);
|
---|
4500 | for (var i = 0, length = keys.length; i < length; i++) {
|
---|
4501 | key = keys[i];
|
---|
4502 | if (key in obj) result[key] = obj[key];
|
---|
4503 | }
|
---|
4504 | }
|
---|
4505 | return result;
|
---|
4506 | };
|
---|
4507 |
|
---|
4508 | // Return a copy of the object without the blacklisted properties.
|
---|
4509 | _.omit = function(obj, iteratee, context) {
|
---|
4510 | if (_.isFunction(iteratee)) {
|
---|
4511 | iteratee = _.negate(iteratee);
|
---|
4512 | } else {
|
---|
4513 | var keys = _.map(concat.apply([], slice.call(arguments, 1)), String);
|
---|
4514 | iteratee = function(value, key) {
|
---|
4515 | return !_.contains(keys, key);
|
---|
4516 | };
|
---|
4517 | }
|
---|
4518 | return _.pick(obj, iteratee, context);
|
---|
4519 | };
|
---|
4520 |
|
---|
4521 | // Fill in a given object with default properties.
|
---|
4522 | _.defaults = function(obj) {
|
---|
4523 | if (!_.isObject(obj)) return obj;
|
---|
4524 | for (var i = 1, length = arguments.length; i < length; i++) {
|
---|
4525 | var source = arguments[i];
|
---|
4526 | for (var prop in source) {
|
---|
4527 | if (obj[prop] === void 0) obj[prop] = source[prop];
|
---|
4528 | }
|
---|
4529 | }
|
---|
4530 | return obj;
|
---|
4531 | };
|
---|
4532 |
|
---|
4533 | // Create a (shallow-cloned) duplicate of an object.
|
---|
4534 | _.clone = function(obj) {
|
---|
4535 | if (!_.isObject(obj)) return obj;
|
---|
4536 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
|
---|
4537 | };
|
---|
4538 |
|
---|
4539 | // Invokes interceptor with the obj, and then returns obj.
|
---|
4540 | // The primary purpose of this method is to "tap into" a method chain, in
|
---|
4541 | // order to perform operations on intermediate results within the chain.
|
---|
4542 | _.tap = function(obj, interceptor) {
|
---|
4543 | interceptor(obj);
|
---|
4544 | return obj;
|
---|
4545 | };
|
---|
4546 |
|
---|
4547 | // Internal recursive comparison function for `isEqual`.
|
---|
4548 | var eq = function(a, b, aStack, bStack) {
|
---|
4549 | // Identical objects are equal. `0 === -0`, but they aren't identical.
|
---|
4550 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
---|
4551 | if (a === b) return a !== 0 || 1 / a === 1 / b;
|
---|
4552 | // A strict comparison is necessary because `null == undefined`.
|
---|
4553 | if (a == null || b == null) return a === b;
|
---|
4554 | // Unwrap any wrapped objects.
|
---|
4555 | if (a instanceof _) a = a._wrapped;
|
---|
4556 | if (b instanceof _) b = b._wrapped;
|
---|
4557 | // Compare `[[Class]]` names.
|
---|
4558 | var className = toString.call(a);
|
---|
4559 | if (className !== toString.call(b)) return false;
|
---|
4560 | switch (className) {
|
---|
4561 | // Strings, numbers, regular expressions, dates, and booleans are compared by value.
|
---|
4562 | case '[object RegExp]':
|
---|
4563 | // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
|
---|
4564 | case '[object String]':
|
---|
4565 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
|
---|
4566 | // equivalent to `new String("5")`.
|
---|
4567 | return '' + a === '' + b;
|
---|
4568 | case '[object Number]':
|
---|
4569 | // `NaN`s are equivalent, but non-reflexive.
|
---|
4570 | // Object(NaN) is equivalent to NaN
|
---|
4571 | if (+a !== +a) return +b !== +b;
|
---|
4572 | // An `egal` comparison is performed for other numeric values.
|
---|
4573 | return +a === 0 ? 1 / +a === 1 / b : +a === +b;
|
---|
4574 | case '[object Date]':
|
---|
4575 | case '[object Boolean]':
|
---|
4576 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their
|
---|
4577 | // millisecond representations. Note that invalid dates with millisecond representations
|
---|
4578 | // of `NaN` are not equivalent.
|
---|
4579 | return +a === +b;
|
---|
4580 | }
|
---|
4581 | if (typeof a != 'object' || typeof b != 'object') return false;
|
---|
4582 | // Assume equality for cyclic structures. The algorithm for detecting cyclic
|
---|
4583 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
---|
4584 | var length = aStack.length;
|
---|
4585 | while (length--) {
|
---|
4586 | // Linear search. Performance is inversely proportional to the number of
|
---|
4587 | // unique nested structures.
|
---|
4588 | if (aStack[length] === a) return bStack[length] === b;
|
---|
4589 | }
|
---|
4590 | // Objects with different constructors are not equivalent, but `Object`s
|
---|
4591 | // from different frames are.
|
---|
4592 | var aCtor = a.constructor, bCtor = b.constructor;
|
---|
4593 | if (
|
---|
4594 | aCtor !== bCtor &&
|
---|
4595 | // Handle Object.create(x) cases
|
---|
4596 | 'constructor' in a && 'constructor' in b &&
|
---|
4597 | !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
|
---|
4598 | _.isFunction(bCtor) && bCtor instanceof bCtor)
|
---|
4599 | ) {
|
---|
4600 | return false;
|
---|
4601 | }
|
---|
4602 | // Add the first object to the stack of traversed objects.
|
---|
4603 | aStack.push(a);
|
---|
4604 | bStack.push(b);
|
---|
4605 | var size, result;
|
---|
4606 | // Recursively compare objects and arrays.
|
---|
4607 | if (className === '[object Array]') {
|
---|
4608 | // Compare array lengths to determine if a deep comparison is necessary.
|
---|
4609 | size = a.length;
|
---|
4610 | result = size === b.length;
|
---|
4611 | if (result) {
|
---|
4612 | // Deep compare the contents, ignoring non-numeric properties.
|
---|
4613 | while (size--) {
|
---|
4614 | if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
---|
4615 | }
|
---|
4616 | }
|
---|
4617 | } else {
|
---|
4618 | // Deep compare objects.
|
---|
4619 | var keys = _.keys(a), key;
|
---|
4620 | size = keys.length;
|
---|
4621 | // Ensure that both objects contain the same number of properties before comparing deep equality.
|
---|
4622 | result = _.keys(b).length === size;
|
---|
4623 | if (result) {
|
---|
4624 | while (size--) {
|
---|
4625 | // Deep compare each member
|
---|
4626 | key = keys[size];
|
---|
4627 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
|
---|
4628 | }
|
---|
4629 | }
|
---|
4630 | }
|
---|
4631 | // Remove the first object from the stack of traversed objects.
|
---|
4632 | aStack.pop();
|
---|
4633 | bStack.pop();
|
---|
4634 | return result;
|
---|
4635 | };
|
---|
4636 |
|
---|
4637 | // Perform a deep comparison to check if two objects are equal.
|
---|
4638 | _.isEqual = function(a, b) {
|
---|
4639 | return eq(a, b, [], []);
|
---|
4640 | };
|
---|
4641 |
|
---|
4642 | // Is a given array, string, or object empty?
|
---|
4643 | // An "empty" object has no enumerable own-properties.
|
---|
4644 | _.isEmpty = function(obj) {
|
---|
4645 | if (obj == null) return true;
|
---|
4646 | if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0;
|
---|
4647 | for (var key in obj) if (_.has(obj, key)) return false;
|
---|
4648 | return true;
|
---|
4649 | };
|
---|
4650 |
|
---|
4651 | // Is a given value a DOM element?
|
---|
4652 | _.isElement = function(obj) {
|
---|
4653 | return !!(obj && obj.nodeType === 1);
|
---|
4654 | };
|
---|
4655 |
|
---|
4656 | // Is a given value an array?
|
---|
4657 | // Delegates to ECMA5's native Array.isArray
|
---|
4658 | _.isArray = nativeIsArray || function(obj) {
|
---|
4659 | return toString.call(obj) === '[object Array]';
|
---|
4660 | };
|
---|
4661 |
|
---|
4662 | // Is a given variable an object?
|
---|
4663 | _.isObject = function(obj) {
|
---|
4664 | var type = typeof obj;
|
---|
4665 | return type === 'function' || type === 'object' && !!obj;
|
---|
4666 | };
|
---|
4667 |
|
---|
4668 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
|
---|
4669 | _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
|
---|
4670 | _['is' + name] = function(obj) {
|
---|
4671 | return toString.call(obj) === '[object ' + name + ']';
|
---|
4672 | };
|
---|
4673 | });
|
---|
4674 |
|
---|
4675 | // Define a fallback version of the method in browsers (ahem, IE), where
|
---|
4676 | // there isn't any inspectable "Arguments" type.
|
---|
4677 | if (!_.isArguments(arguments)) {
|
---|
4678 | _.isArguments = function(obj) {
|
---|
4679 | return _.has(obj, 'callee');
|
---|
4680 | };
|
---|
4681 | }
|
---|
4682 |
|
---|
4683 | // Optimize `isFunction` if appropriate. Work around an IE 11 bug.
|
---|
4684 | if (typeof /./ !== 'function') {
|
---|
4685 | _.isFunction = function(obj) {
|
---|
4686 | return typeof obj == 'function' || false;
|
---|
4687 | };
|
---|
4688 | }
|
---|
4689 |
|
---|
4690 | // Is a given object a finite number?
|
---|
4691 | _.isFinite = function(obj) {
|
---|
4692 | return isFinite(obj) && !isNaN(parseFloat(obj));
|
---|
4693 | };
|
---|
4694 |
|
---|
4695 | // Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
---|
4696 | _.isNaN = function(obj) {
|
---|
4697 | return _.isNumber(obj) && obj !== +obj;
|
---|
4698 | };
|
---|
4699 |
|
---|
4700 | // Is a given value a boolean?
|
---|
4701 | _.isBoolean = function(obj) {
|
---|
4702 | return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
|
---|
4703 | };
|
---|
4704 |
|
---|
4705 | // Is a given value equal to null?
|
---|
4706 | _.isNull = function(obj) {
|
---|
4707 | return obj === null;
|
---|
4708 | };
|
---|
4709 |
|
---|
4710 | // Is a given variable undefined?
|
---|
4711 | _.isUndefined = function(obj) {
|
---|
4712 | return obj === void 0;
|
---|
4713 | };
|
---|
4714 |
|
---|
4715 | // Shortcut function for checking if an object has a given property directly
|
---|
4716 | // on itself (in other words, not on a prototype).
|
---|
4717 | _.has = function(obj, key) {
|
---|
4718 | return obj != null && hasOwnProperty.call(obj, key);
|
---|
4719 | };
|
---|
4720 |
|
---|
4721 | // Utility Functions
|
---|
4722 | // -----------------
|
---|
4723 |
|
---|
4724 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
|
---|
4725 | // previous owner. Returns a reference to the Underscore object.
|
---|
4726 | _.noConflict = function() {
|
---|
4727 | root._ = previousUnderscore;
|
---|
4728 | return this;
|
---|
4729 | };
|
---|
4730 |
|
---|
4731 | // Keep the identity function around for default iteratees.
|
---|
4732 | _.identity = function(value) {
|
---|
4733 | return value;
|
---|
4734 | };
|
---|
4735 |
|
---|
4736 | _.constant = function(value) {
|
---|
4737 | return function() {
|
---|
4738 | return value;
|
---|
4739 | };
|
---|
4740 | };
|
---|
4741 |
|
---|
4742 | _.noop = function(){};
|
---|
4743 |
|
---|
4744 | _.property = function(key) {
|
---|
4745 | return function(obj) {
|
---|
4746 | return obj[key];
|
---|
4747 | };
|
---|
4748 | };
|
---|
4749 |
|
---|
4750 | // Returns a predicate for checking whether an object has a given set of `key:value` pairs.
|
---|
4751 | _.matches = function(attrs) {
|
---|
4752 | var pairs = _.pairs(attrs), length = pairs.length;
|
---|
4753 | return function(obj) {
|
---|
4754 | if (obj == null) return !length;
|
---|
4755 | obj = new Object(obj);
|
---|
4756 | for (var i = 0; i < length; i++) {
|
---|
4757 | var pair = pairs[i], key = pair[0];
|
---|
4758 | if (pair[1] !== obj[key] || !(key in obj)) return false;
|
---|
4759 | }
|
---|
4760 | return true;
|
---|
4761 | };
|
---|
4762 | };
|
---|
4763 |
|
---|
4764 | // Run a function **n** times.
|
---|
4765 | _.times = function(n, iteratee, context) {
|
---|
4766 | var accum = Array(Math.max(0, n));
|
---|
4767 | iteratee = createCallback(iteratee, context, 1);
|
---|
4768 | for (var i = 0; i < n; i++) accum[i] = iteratee(i);
|
---|
4769 | return accum;
|
---|
4770 | };
|
---|
4771 |
|
---|
4772 | // Return a random integer between min and max (inclusive).
|
---|
4773 | _.random = function(min, max) {
|
---|
4774 | if (max == null) {
|
---|
4775 | max = min;
|
---|
4776 | min = 0;
|
---|
4777 | }
|
---|
4778 | return min + Math.floor(Math.random() * (max - min + 1));
|
---|
4779 | };
|
---|
4780 |
|
---|
4781 | // A (possibly faster) way to get the current timestamp as an integer.
|
---|
4782 | _.now = Date.now || function() {
|
---|
4783 | return new Date().getTime();
|
---|
4784 | };
|
---|
4785 |
|
---|
4786 | // List of HTML entities for escaping.
|
---|
4787 | var escapeMap = {
|
---|
4788 | '&': '&',
|
---|
4789 | '<': '<',
|
---|
4790 | '>': '>',
|
---|
4791 | '"': '"',
|
---|
4792 | "'": ''',
|
---|
4793 | '`': '`'
|
---|
4794 | };
|
---|
4795 | var unescapeMap = _.invert(escapeMap);
|
---|
4796 |
|
---|
4797 | // Functions for escaping and unescaping strings to/from HTML interpolation.
|
---|
4798 | var createEscaper = function(map) {
|
---|
4799 | var escaper = function(match) {
|
---|
4800 | return map[match];
|
---|
4801 | };
|
---|
4802 | // Regexes for identifying a key that needs to be escaped
|
---|
4803 | var source = '(?:' + _.keys(map).join('|') + ')';
|
---|
4804 | var testRegexp = RegExp(source);
|
---|
4805 | var replaceRegexp = RegExp(source, 'g');
|
---|
4806 | return function(string) {
|
---|
4807 | string = string == null ? '' : '' + string;
|
---|
4808 | return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
|
---|
4809 | };
|
---|
4810 | };
|
---|
4811 | _.escape = createEscaper(escapeMap);
|
---|
4812 | _.unescape = createEscaper(unescapeMap);
|
---|
4813 |
|
---|
4814 | // If the value of the named `property` is a function then invoke it with the
|
---|
4815 | // `object` as context; otherwise, return it.
|
---|
4816 | _.result = function(object, property) {
|
---|
4817 | if (object == null) return void 0;
|
---|
4818 | var value = object[property];
|
---|
4819 | return _.isFunction(value) ? object[property]() : value;
|
---|
4820 | };
|
---|
4821 |
|
---|
4822 | // Generate a unique integer id (unique within the entire client session).
|
---|
4823 | // Useful for temporary DOM ids.
|
---|
4824 | var idCounter = 0;
|
---|
4825 | _.uniqueId = function(prefix) {
|
---|
4826 | var id = ++idCounter + '';
|
---|
4827 | return prefix ? prefix + id : id;
|
---|
4828 | };
|
---|
4829 |
|
---|
4830 | // By default, Underscore uses ERB-style template delimiters, change the
|
---|
4831 | // following template settings to use alternative delimiters.
|
---|
4832 | _.templateSettings = {
|
---|
4833 | evaluate : /<%([\s\S]+?)%>/g,
|
---|
4834 | interpolate : /<%=([\s\S]+?)%>/g,
|
---|
4835 | escape : /<%-([\s\S]+?)%>/g
|
---|
4836 | };
|
---|
4837 |
|
---|
4838 | // When customizing `templateSettings`, if you don't want to define an
|
---|
4839 | // interpolation, evaluation or escaping regex, we need one that is
|
---|
4840 | // guaranteed not to match.
|
---|
4841 | var noMatch = /(.)^/;
|
---|
4842 |
|
---|
4843 | // Certain characters need to be escaped so that they can be put into a
|
---|
4844 | // string literal.
|
---|
4845 | var escapes = {
|
---|
4846 | "'": "'",
|
---|
4847 | '\\': '\\',
|
---|
4848 | '\r': 'r',
|
---|
4849 | '\n': 'n',
|
---|
4850 | '\u2028': 'u2028',
|
---|
4851 | '\u2029': 'u2029'
|
---|
4852 | };
|
---|
4853 |
|
---|
4854 | var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
|
---|
4855 |
|
---|
4856 | var escapeChar = function(match) {
|
---|
4857 | return '\\' + escapes[match];
|
---|
4858 | };
|
---|
4859 |
|
---|
4860 | // JavaScript micro-templating, similar to John Resig's implementation.
|
---|
4861 | // Underscore templating handles arbitrary delimiters, preserves whitespace,
|
---|
4862 | // and correctly escapes quotes within interpolated code.
|
---|
4863 | // NB: `oldSettings` only exists for backwards compatibility.
|
---|
4864 | _.template = function(text, settings, oldSettings) {
|
---|
4865 | if (!settings && oldSettings) settings = oldSettings;
|
---|
4866 | settings = _.defaults({}, settings, _.templateSettings);
|
---|
4867 |
|
---|
4868 | // Combine delimiters into one regular expression via alternation.
|
---|
4869 | var matcher = RegExp([
|
---|
4870 | (settings.escape || noMatch).source,
|
---|
4871 | (settings.interpolate || noMatch).source,
|
---|
4872 | (settings.evaluate || noMatch).source
|
---|
4873 | ].join('|') + '|$', 'g');
|
---|
4874 |
|
---|
4875 | // Compile the template source, escaping string literals appropriately.
|
---|
4876 | var index = 0;
|
---|
4877 | var source = "__p+='";
|
---|
4878 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
---|
4879 | source += text.slice(index, offset).replace(escaper, escapeChar);
|
---|
4880 | index = offset + match.length;
|
---|
4881 |
|
---|
4882 | if (escape) {
|
---|
4883 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
---|
4884 | } else if (interpolate) {
|
---|
4885 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
---|
4886 | } else if (evaluate) {
|
---|
4887 | source += "';\n" + evaluate + "\n__p+='";
|
---|
4888 | }
|
---|
4889 |
|
---|
4890 | // Adobe VMs need the match returned to produce the correct offest.
|
---|
4891 | return match;
|
---|
4892 | });
|
---|
4893 | source += "';\n";
|
---|
4894 |
|
---|
4895 | // If a variable is not specified, place data values in local scope.
|
---|
4896 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
---|
4897 |
|
---|
4898 | source = "var __t,__p='',__j=Array.prototype.join," +
|
---|
4899 | "print=function(){__p+=__j.call(arguments,'');};\n" +
|
---|
4900 | source + 'return __p;\n';
|
---|
4901 |
|
---|
4902 | try {
|
---|
4903 | var render = new Function(settings.variable || 'obj', '_', source);
|
---|
4904 | } catch (e) {
|
---|
4905 | e.source = source;
|
---|
4906 | throw e;
|
---|
4907 | }
|
---|
4908 |
|
---|
4909 | var template = function(data) {
|
---|
4910 | return render.call(this, data, _);
|
---|
4911 | };
|
---|
4912 |
|
---|
4913 | // Provide the compiled source as a convenience for precompilation.
|
---|
4914 | var argument = settings.variable || 'obj';
|
---|
4915 | template.source = 'function(' + argument + '){\n' + source + '}';
|
---|
4916 |
|
---|
4917 | return template;
|
---|
4918 | };
|
---|
4919 |
|
---|
4920 | // Add a "chain" function. Start chaining a wrapped Underscore object.
|
---|
4921 | _.chain = function(obj) {
|
---|
4922 | var instance = _(obj);
|
---|
4923 | instance._chain = true;
|
---|
4924 | return instance;
|
---|
4925 | };
|
---|
4926 |
|
---|
4927 | // OOP
|
---|
4928 | // ---------------
|
---|
4929 | // If Underscore is called as a function, it returns a wrapped object that
|
---|
4930 | // can be used OO-style. This wrapper holds altered versions of all the
|
---|
4931 | // underscore functions. Wrapped objects may be chained.
|
---|
4932 |
|
---|
4933 | // Helper function to continue chaining intermediate results.
|
---|
4934 | var result = function(obj) {
|
---|
4935 | return this._chain ? _(obj).chain() : obj;
|
---|
4936 | };
|
---|
4937 |
|
---|
4938 | // Add your own custom functions to the Underscore object.
|
---|
4939 | _.mixin = function(obj) {
|
---|
4940 | _.each(_.functions(obj), function(name) {
|
---|
4941 | var func = _[name] = obj[name];
|
---|
4942 | _.prototype[name] = function() {
|
---|
4943 | var args = [this._wrapped];
|
---|
4944 | push.apply(args, arguments);
|
---|
4945 | return result.call(this, func.apply(_, args));
|
---|
4946 | };
|
---|
4947 | });
|
---|
4948 | };
|
---|
4949 |
|
---|
4950 | // Add all of the Underscore functions to the wrapper object.
|
---|
4951 | _.mixin(_);
|
---|
4952 |
|
---|
4953 | // Add all mutator Array functions to the wrapper.
|
---|
4954 | _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
---|
4955 | var method = ArrayProto[name];
|
---|
4956 | _.prototype[name] = function() {
|
---|
4957 | var obj = this._wrapped;
|
---|
4958 | method.apply(obj, arguments);
|
---|
4959 | if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
|
---|
4960 | return result.call(this, obj);
|
---|
4961 | };
|
---|
4962 | });
|
---|
4963 |
|
---|
4964 | // Add all accessor Array functions to the wrapper.
|
---|
4965 | _.each(['concat', 'join', 'slice'], function(name) {
|
---|
4966 | var method = ArrayProto[name];
|
---|
4967 | _.prototype[name] = function() {
|
---|
4968 | return result.call(this, method.apply(this._wrapped, arguments));
|
---|
4969 | };
|
---|
4970 | });
|
---|
4971 |
|
---|
4972 | // Extracts the result from a wrapped and chained object.
|
---|
4973 | _.prototype.value = function() {
|
---|
4974 | return this._wrapped;
|
---|
4975 | };
|
---|
4976 |
|
---|
4977 | // AMD registration happens at the end for compatibility with AMD loaders
|
---|
4978 | // that may not enforce next-turn semantics on modules. Even though general
|
---|
4979 | // practice for AMD registration is to be anonymous, underscore registers
|
---|
4980 | // as a named module because, like jQuery, it is a base library that is
|
---|
4981 | // popular enough to be bundled in a third party lib, but not be part of
|
---|
4982 | // an AMD load request. Those cases could generate an error when an
|
---|
4983 | // anonymous define() is called outside of a loader request.
|
---|
4984 | if (typeof define === 'function' && define.amd) {
|
---|
4985 | define('underscore', [], function() {
|
---|
4986 | return _;
|
---|
4987 | });
|
---|
4988 | }
|
---|
4989 | }.call(this));
|
---|
4990 |
|
---|
4991 | },{}],17:[function(require,module,exports){
|
---|
4992 | // calc.js
|
---|
4993 | // measure calculations
|
---|
4994 |
|
---|
4995 | var _ = require('underscore');
|
---|
4996 | var geocrunch = require('geocrunch');
|
---|
4997 |
|
---|
4998 | var pad = function (num) {
|
---|
4999 | return num < 10 ? '0' + num.toString() : num.toString();
|
---|
5000 | };
|
---|
5001 |
|
---|
5002 | var ddToDms = function (coordinate, posSymbol, negSymbol) {
|
---|
5003 | var dd = Math.abs(coordinate),
|
---|
5004 | d = Math.floor(dd),
|
---|
5005 | m = Math.floor((dd - d) * 60),
|
---|
5006 | s = Math.round((dd - d - (m/60)) * 3600 * 100)/100,
|
---|
5007 | directionSymbol = dd === coordinate ? posSymbol : negSymbol;
|
---|
5008 | return pad(d) + '° ' + pad(m) + '\' ' + pad(s) + '" ' + directionSymbol;
|
---|
5009 | };
|
---|
5010 |
|
---|
5011 | var measure = function (latlngs) {
|
---|
5012 | var last = _.last(latlngs);
|
---|
5013 | var path = geocrunch.path(_.map(latlngs, function (latlng) {
|
---|
5014 | return [latlng.lng, latlng.lat];
|
---|
5015 | }));
|
---|
5016 |
|
---|
5017 | var meters = path.distance({
|
---|
5018 | units: 'meters'
|
---|
5019 | });
|
---|
5020 | var sqMeters = path.area({
|
---|
5021 | units: 'sqmeters'
|
---|
5022 | });
|
---|
5023 |
|
---|
5024 | return {
|
---|
5025 | lastCoord: {
|
---|
5026 | dd: {
|
---|
5027 | x: last.lng,
|
---|
5028 | y: last.lat
|
---|
5029 | },
|
---|
5030 | dms: {
|
---|
5031 | x: ddToDms(last.lng, 'E', 'W'),
|
---|
5032 | y: ddToDms(last.lat, 'N', 'S')
|
---|
5033 | }
|
---|
5034 | },
|
---|
5035 | length: meters,
|
---|
5036 | area: sqMeters
|
---|
5037 | };
|
---|
5038 | };
|
---|
5039 |
|
---|
5040 | module.exports = {
|
---|
5041 | measure: measure // `measure(latLngArray)` - returns object with calced measurements for passed points
|
---|
5042 | };
|
---|
5043 | },{"geocrunch":6,"underscore":16}],18:[function(require,module,exports){
|
---|
5044 | // dom.js
|
---|
5045 | // utility functions for managing DOM elements
|
---|
5046 |
|
---|
5047 | var selectOne = function (selector, el) {
|
---|
5048 | if (!el) {
|
---|
5049 | el = document;
|
---|
5050 | }
|
---|
5051 | return el.querySelector(selector);
|
---|
5052 | };
|
---|
5053 |
|
---|
5054 | var selectAll = function (selector, el) {
|
---|
5055 | if (!el) {
|
---|
5056 | el = document;
|
---|
5057 | }
|
---|
5058 | return Array.prototype.slice.call(el.querySelectorAll(selector));
|
---|
5059 | };
|
---|
5060 |
|
---|
5061 | var hide = function (el) {
|
---|
5062 | if (el) {
|
---|
5063 | el.setAttribute('style', 'display:none;');
|
---|
5064 | return el;
|
---|
5065 | }
|
---|
5066 | };
|
---|
5067 |
|
---|
5068 | var show = function (el) {
|
---|
5069 | if (el) {
|
---|
5070 | el.removeAttribute('style');
|
---|
5071 | return el;
|
---|
5072 | }
|
---|
5073 | };
|
---|
5074 |
|
---|
5075 | module.exports = {
|
---|
5076 | $: selectOne, // `$('.myclass', baseElement)` - returns selected element or undefined
|
---|
5077 | $$: selectAll, // `$$('.myclass', baseElement)` - returns array of selected elements
|
---|
5078 | hide: hide, // `hide(someElement)` - hide passed dom element
|
---|
5079 | show: show // `show(someElement)` - show passed dom element
|
---|
5080 | };
|
---|
5081 | },{}],19:[function(require,module,exports){
|
---|
5082 | (function (global){
|
---|
5083 | // leaflet-measure.js
|
---|
5084 |
|
---|
5085 | var _ = require('underscore');
|
---|
5086 | var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null);
|
---|
5087 | var humanize = require('humanize');
|
---|
5088 |
|
---|
5089 | var units = require('./units');
|
---|
5090 | var calc = require('./calc');
|
---|
5091 | var dom = require('./dom');
|
---|
5092 | var $ = dom.$;
|
---|
5093 |
|
---|
5094 | var Symbology = require('./mapsymbology');
|
---|
5095 |
|
---|
5096 |
|
---|
5097 | var controlTemplate = _.template("<a class=\"<%= model.className %>-toggle js-toggle\" href=\"#\" title=\"Measure distances and areas\">Measure</a>\n<div class=\"<%= model.className %>-interaction js-interaction\">\n <div class=\"js-startprompt startprompt\">\n <h3>Measure Distances and Areas</h3>\n <ul class=\"tasks\">\n <a href=\"#\" class=\"js-start start\">Create a new measurement</a>\n </ul>\n </div>\n <div class=\"js-measuringprompt\">\n <h3>Measure Distances and Areas</h3>\n <p class=\"js-starthelp\">Start creating a measurement by adding points to the map</h3>\n <div class=\"js-results results\"></div>\n <ul class=\"js-measuretasks tasks\">\n <li><a href=\"#\" class=\"js-cancel cancel\">Cancel</a></li>\n <li><a href=\"#\" class=\"js-finish finish\">Finish Measurement</a></li>\n </ul>\n </div>\n</div>");
|
---|
5098 | var resultsTemplate = _.template("<div class=\"group\">\n<p class=\"lastpoint heading\">Last Point</p>\n<p><%= model.lastCoord.dms.y %> <span class=\"coorddivider\">/</span> <%= model.lastCoord.dms.x %></p>\n<p><%= humanize.numberFormat(model.lastCoord.dd.y, 6) %> <span class=\"coorddivider\">/</span> <%= humanize.numberFormat(model.lastCoord.dd.x, 6) %></p>\n</div>\n<% if (model.pointCount > 1) { %>\n<div class=\"group\">\n<p><span class=\"heading\">Path Distance</span> <%= model.lengthDisplay %></p>\n</div>\n<% } %>\n<% if (model.pointCount > 2) { %>\n<div class=\"group\">\n<p><span class=\"heading\">Area</span> <%= model.areaDisplay %></p>\n</div>\n<% } %>");
|
---|
5099 | var pointPopupTemplate = _.template("<h3>Point Location</h3>\n<p><%= model.lastCoord.dms.y %> <span class=\"coorddivider\">/</span> <%= model.lastCoord.dms.x %></p>\n<p><%= humanize.numberFormat(model.lastCoord.dd.y, 6) %> <span class=\"coorddivider\">/</span> <%= humanize.numberFormat(model.lastCoord.dd.x, 6) %></p>\n<ul class=\"tasks\">\n <li><a href=\"#\" class=\"js-zoomto zoomto\">Center on this Location</a></li>\n <li><a href=\"#\" class=\"js-deletemarkup deletemarkup\">Delete</a></li>\n</ul>");
|
---|
5100 | var linePopupTemplate = _.template("<h3>Linear Measurement</h3>\n<p><%= model.lengthDisplay %></p>\n<ul class=\"tasks\">\n <li><a href=\"#\" class=\"js-zoomto zoomto\">Center on this Line</a></li>\n <li><a href=\"#\" class=\"js-deletemarkup deletemarkup\">Delete</a></li>\n</ul>");
|
---|
5101 | var areaPopupTemplate = _.template("<h3>Area Measurement</h3>\n<p><%= model.areaDisplay %></p>\n<p><%= model.lengthDisplay %> Perimeter</p>\n<ul class=\"tasks\">\n <li><a href=\"#\" class=\"js-zoomto zoomto\">Center on this Area</a></li>\n <li><a href=\"#\" class=\"js-deletemarkup deletemarkup\">Delete</a></li>\n</ul>");
|
---|
5102 |
|
---|
5103 | L.Control.Measure = L.Control.extend({
|
---|
5104 | _className: 'leaflet-control-measure',
|
---|
5105 | options: {
|
---|
5106 | units: {},
|
---|
5107 | position: 'topright',
|
---|
5108 | primaryLengthUnit: 'feet',
|
---|
5109 | secondaryLengthUnit: 'miles',
|
---|
5110 | primaryAreaUnit: 'acres',
|
---|
5111 | activeColor: '#ABE67E', // base color for map features while actively measuring
|
---|
5112 | completedColor: '#C8F2BE', // base color for permenant features generated from completed measure
|
---|
5113 | popupOptions: { // standard leaflet popup options http://leafletjs.com/reference.html#popup-options
|
---|
5114 | className: 'leaflet-measure-resultpopup',
|
---|
5115 | autoPanPadding: [10, 10]
|
---|
5116 | }
|
---|
5117 | },
|
---|
5118 | initialize: function (options) {
|
---|
5119 | L.setOptions(this, options);
|
---|
5120 | this.options.units = L.extend({}, units, this.options.units);
|
---|
5121 | this._symbols = new Symbology(_.pick(this.options, 'activeColor', 'completedColor'));
|
---|
5122 | },
|
---|
5123 | onAdd: function (map) {
|
---|
5124 | this._map = map;
|
---|
5125 | this._latlngs = [];
|
---|
5126 | this._initLayout();
|
---|
5127 | map.on('click', this._collapse, this);
|
---|
5128 | this._layer = L.layerGroup().addTo(map);
|
---|
5129 | return this._container;
|
---|
5130 | },
|
---|
5131 | onRemove: function (map) {
|
---|
5132 | map.off('click', this._collapse, this);
|
---|
5133 | map.removeLayer(this._layer);
|
---|
5134 | },
|
---|
5135 | _initLayout: function () {
|
---|
5136 | var className = this._className, container = this._container = L.DomUtil.create('div', className);
|
---|
5137 | var $toggle, $start, $cancel, $finish;
|
---|
5138 |
|
---|
5139 | container.innerHTML = controlTemplate({
|
---|
5140 | model: {
|
---|
5141 | className: className
|
---|
5142 | }
|
---|
5143 | });
|
---|
5144 |
|
---|
5145 | // copied from leaflet
|
---|
5146 | // https://bitbucket.org/ljagis/js-mapbootstrap/src/4ab1e9e896c08bdbc8164d4053b2f945143f4f3a/app/components/measure/leaflet-measure-control.js?at=master#cl-30
|
---|
5147 | container.setAttribute('aria-haspopup', true);
|
---|
5148 | if (!L.Browser.touch) {
|
---|
5149 | L.DomEvent.disableClickPropagation(container);
|
---|
5150 | L.DomEvent.disableScrollPropagation(container);
|
---|
5151 | } else {
|
---|
5152 | L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation);
|
---|
5153 | }
|
---|
5154 |
|
---|
5155 | $toggle = this.$toggle = $('.js-toggle', container); // collapsed content
|
---|
5156 | this.$interaction = $('.js-interaction', container); // expanded content
|
---|
5157 | $start = $('.js-start', container); // start button
|
---|
5158 | $cancel = $('.js-cancel', container); // cancel button
|
---|
5159 | $finish = $('.js-finish', container); // finish button
|
---|
5160 | this.$startPrompt = $('.js-startprompt', container); // full area with button to start measurment
|
---|
5161 | this.$measuringPrompt = $('.js-measuringprompt', container); // full area with all stuff for active measurement
|
---|
5162 | this.$startHelp = $('.js-starthelp', container); // "Start creating a measurement by adding points"
|
---|
5163 | this.$results = $('.js-results', container); // div with coordinate, linear, area results
|
---|
5164 | this.$measureTasks = $('.js-measuretasks', container); // active measure buttons container
|
---|
5165 |
|
---|
5166 | this._collapse();
|
---|
5167 | this._updateMeasureNotStarted();
|
---|
5168 |
|
---|
5169 | if (!L.Browser.android) {
|
---|
5170 | L.DomEvent.on(container, 'mouseenter', this._expand, this);
|
---|
5171 | L.DomEvent.on(container, 'mouseleave', this._collapse, this);
|
---|
5172 | }
|
---|
5173 | L.DomEvent.on($toggle, 'click', L.DomEvent.stop);
|
---|
5174 | if (L.Browser.touch) {
|
---|
5175 | L.DomEvent.on($toggle, 'click', this._expand, this);
|
---|
5176 | } else {
|
---|
5177 | L.DomEvent.on($toggle, 'focus', this._expand, this);
|
---|
5178 | }
|
---|
5179 | L.DomEvent.on($start, 'click', L.DomEvent.stop);
|
---|
5180 | L.DomEvent.on($start, 'click', this._startMeasure, this);
|
---|
5181 | L.DomEvent.on($cancel, 'click', L.DomEvent.stop);
|
---|
5182 | L.DomEvent.on($cancel, 'click', this._finishMeasure, this);
|
---|
5183 | L.DomEvent.on($finish, 'click', L.DomEvent.stop);
|
---|
5184 | L.DomEvent.on($finish, 'click', this._handleMeasureDoubleClick, this);
|
---|
5185 | },
|
---|
5186 | _expand: function () {
|
---|
5187 | dom.hide(this.$toggle);
|
---|
5188 | dom.show(this.$interaction);
|
---|
5189 | },
|
---|
5190 | _collapse: function () {
|
---|
5191 | if (!this._locked) {
|
---|
5192 | dom.hide(this.$interaction);
|
---|
5193 | dom.show(this.$toggle);
|
---|
5194 | }
|
---|
5195 | },
|
---|
5196 | // move between basic states:
|
---|
5197 | // measure not started, started/in progress but no points added, in progress and with points
|
---|
5198 | _updateMeasureNotStarted: function () {
|
---|
5199 | dom.hide(this.$startHelp);
|
---|
5200 | dom.hide(this.$results);
|
---|
5201 | dom.hide(this.$measureTasks);
|
---|
5202 | dom.hide(this.$measuringPrompt);
|
---|
5203 | dom.show(this.$startPrompt);
|
---|
5204 | },
|
---|
5205 | _updateMeasureStartedNoPoints: function () {
|
---|
5206 | dom.hide(this.$results);
|
---|
5207 | dom.show(this.$startHelp);
|
---|
5208 | dom.show(this.$measureTasks);
|
---|
5209 | dom.hide(this.$startPrompt);
|
---|
5210 | dom.show(this.$measuringPrompt);
|
---|
5211 | },
|
---|
5212 | _updateMeasureStartedWithPoints: function () {
|
---|
5213 | dom.hide(this.$startHelp);
|
---|
5214 | dom.show(this.$results);
|
---|
5215 | dom.show(this.$measureTasks);
|
---|
5216 | dom.hide(this.$startPrompt);
|
---|
5217 | dom.show(this.$measuringPrompt);
|
---|
5218 | },
|
---|
5219 | // get state vars and interface ready for measure
|
---|
5220 | _startMeasure: function () {
|
---|
5221 | this._locked = true;
|
---|
5222 |
|
---|
5223 | this._map.doubleClickZoom.disable(); // double click now finishes measure
|
---|
5224 | this._map.on('mouseout', this._handleMapMouseOut, this);
|
---|
5225 |
|
---|
5226 | L.DomEvent.on(this._container, 'mouseenter', this._handleMapMouseOut, this);
|
---|
5227 |
|
---|
5228 | this._map.on('mousemove', this._handleMeasureMove, this);
|
---|
5229 | this._map.on('dblclick', this._handleMeasureDoubleClick, this);
|
---|
5230 | this._map.on('click', this._handleMeasureClick, this);
|
---|
5231 |
|
---|
5232 | this._measureVertexes = L.featureGroup().addTo(this._layer);
|
---|
5233 |
|
---|
5234 | this._updateMeasureStartedNoPoints();
|
---|
5235 | },
|
---|
5236 | // return to state with no measure in progress, undo `this._startMeasure`
|
---|
5237 | _finishMeasure: function () {
|
---|
5238 | this._locked = false;
|
---|
5239 |
|
---|
5240 | this._map.doubleClickZoom.enable();
|
---|
5241 | this._map.off('mouseout', this._handleMapMouseOut, this);
|
---|
5242 |
|
---|
5243 | L.DomEvent.off(this._container, 'mouseover', this._handleMapMouseOut, this);
|
---|
5244 |
|
---|
5245 | this._clearMeasure();
|
---|
5246 |
|
---|
5247 | this._map.off('mousemove', this._handleMeasureMove, this);
|
---|
5248 | this._map.off('dblclick', this._handleMeasureDoubleClick, this);
|
---|
5249 | this._map.off('click', this._handleMeasureClick, this);
|
---|
5250 |
|
---|
5251 | this._layer.removeLayer(this._measureVertexes);
|
---|
5252 | this._measureVertexes = null;
|
---|
5253 |
|
---|
5254 | this._updateMeasureNotStarted();
|
---|
5255 | this._collapse();
|
---|
5256 | },
|
---|
5257 | // clear all running measure data
|
---|
5258 | _clearMeasure: function () {
|
---|
5259 | this._latlngs = [];
|
---|
5260 | this._measureVertexes.clearLayers();
|
---|
5261 | if (this._measureDrag) {
|
---|
5262 | this._layer.removeLayer(this._measureDrag);
|
---|
5263 | }
|
---|
5264 | if (this._measureArea) {
|
---|
5265 | this._layer.removeLayer(this._measureArea);
|
---|
5266 | }
|
---|
5267 | if (this._measureBoundary) {
|
---|
5268 | this._layer.removeLayer(this._measureBoundary);
|
---|
5269 | }
|
---|
5270 | this._measureDrag = null;
|
---|
5271 | this._measureArea = null;
|
---|
5272 | this._measureBoundary = null;
|
---|
5273 | },
|
---|
5274 | // format measurements to nice display string based on units in options. `{ lengthDisplay: '100 Feet (0.02 Miles)', areaDisplay: ... }`
|
---|
5275 | _getMeasurementDisplayStrings: function (measurement) {
|
---|
5276 | var unitDefinitions = this.options.units;
|
---|
5277 |
|
---|
5278 | return {
|
---|
5279 | lengthDisplay: buildDisplay(measurement.length, this.options.primaryLengthUnit, this.options.secondaryLengthUnit),
|
---|
5280 | areaDisplay: buildDisplay(measurement.area, this.options.primaryAreaUnit, this.options.secondaryAreaUnit)
|
---|
5281 | };
|
---|
5282 |
|
---|
5283 | function buildDisplay (val, primaryUnit, secondaryUnit) {
|
---|
5284 | var display;
|
---|
5285 | if (primaryUnit && unitDefinitions[primaryUnit]) {
|
---|
5286 | display = formatMeasure(val, unitDefinitions[primaryUnit]);
|
---|
5287 | if (secondaryUnit && unitDefinitions[secondaryUnit]) {
|
---|
5288 | display = display + ' (' + formatMeasure(val, unitDefinitions[secondaryUnit]) + ')';
|
---|
5289 | }
|
---|
5290 | } else {
|
---|
5291 | display = formatMeasure(val);
|
---|
5292 | }
|
---|
5293 | return display;
|
---|
5294 | }
|
---|
5295 |
|
---|
5296 | function formatMeasure (val, unit) {
|
---|
5297 | return unit && unit.factor && unit.display ?
|
---|
5298 | humanize.numberFormat(val * unit.factor, unit.decimals || 0) + ' ' + unit.display :
|
---|
5299 | humanize.numberFormat(val, 0);
|
---|
5300 | }
|
---|
5301 | },
|
---|
5302 | // update results area of dom with calced measure from `this._latlngs`
|
---|
5303 | _updateResults: function () {
|
---|
5304 | var calced = calc.measure(this._latlngs);
|
---|
5305 | this.$results.innerHTML = resultsTemplate({
|
---|
5306 | model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced), {
|
---|
5307 | pointCount: this._latlngs.length
|
---|
5308 | }),
|
---|
5309 | humanize: humanize
|
---|
5310 | });
|
---|
5311 | },
|
---|
5312 | // mouse move handler while measure in progress
|
---|
5313 | // adds floating measure marker under cursor
|
---|
5314 | _handleMeasureMove: function (evt) {
|
---|
5315 | if (!this._measureDrag) {
|
---|
5316 | this._measureDrag = L.circleMarker(evt.latlng, this._symbols.getSymbol('measureDrag')).addTo(this._layer);
|
---|
5317 | } else {
|
---|
5318 | this._measureDrag.setLatLng(evt.latlng);
|
---|
5319 | }
|
---|
5320 | this._measureDrag.bringToFront();
|
---|
5321 | },
|
---|
5322 | // handler for both double click and clicking finish button
|
---|
5323 | // do final calc and finish out current measure, clear dom and internal state, add permanent map features
|
---|
5324 | _handleMeasureDoubleClick: function () {
|
---|
5325 | var latlngs = this._latlngs, calced, resultFeature, popupContainer, popupContent, zoomLink, deleteLink;
|
---|
5326 |
|
---|
5327 | this._finishMeasure();
|
---|
5328 |
|
---|
5329 | if (!latlngs.length) {
|
---|
5330 | return;
|
---|
5331 | }
|
---|
5332 |
|
---|
5333 | if (latlngs.length > 2) {
|
---|
5334 | latlngs.push(_.first(latlngs)); // close path to get full perimeter measurement for areas
|
---|
5335 | }
|
---|
5336 |
|
---|
5337 | calced = calc.measure(latlngs);
|
---|
5338 |
|
---|
5339 | if (latlngs.length === 1) {
|
---|
5340 | resultFeature = L.circleMarker(latlngs[0], this._symbols.getSymbol('resultPoint'));
|
---|
5341 | popupContent = pointPopupTemplate({
|
---|
5342 | model: calced,
|
---|
5343 | humanize: humanize
|
---|
5344 | });
|
---|
5345 | } else if (latlngs.length === 2) {
|
---|
5346 | resultFeature = L.polyline(latlngs, this._symbols.getSymbol('resultLine')).addTo(this._map);
|
---|
5347 | popupContent = linePopupTemplate({
|
---|
5348 | model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced)),
|
---|
5349 | humanize: humanize
|
---|
5350 | });
|
---|
5351 | } else {
|
---|
5352 | resultFeature = L.polygon(latlngs, this._symbols.getSymbol('resultArea'));
|
---|
5353 | popupContent = areaPopupTemplate({
|
---|
5354 | model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced)),
|
---|
5355 | humanize: humanize,
|
---|
5356 | units: this._units
|
---|
5357 | });
|
---|
5358 | }
|
---|
5359 |
|
---|
5360 | popupContainer = L.DomUtil.create('div', '');
|
---|
5361 | popupContainer.innerHTML = popupContent;
|
---|
5362 |
|
---|
5363 | zoomLink = $('.js-zoomto', popupContainer);
|
---|
5364 | if (zoomLink) {
|
---|
5365 | L.DomEvent.on(zoomLink, 'click', L.DomEvent.stop);
|
---|
5366 | L.DomEvent.on(zoomLink, 'click', function () {
|
---|
5367 | this._map.fitBounds(resultFeature.getBounds(), {
|
---|
5368 | padding: [20, 20],
|
---|
5369 | maxZoom: 17
|
---|
5370 | });
|
---|
5371 | }, this);
|
---|
5372 | }
|
---|
5373 |
|
---|
5374 | deleteLink = $('.js-deletemarkup', popupContainer);
|
---|
5375 | if (deleteLink) {
|
---|
5376 | L.DomEvent.on(deleteLink, 'click', L.DomEvent.stop);
|
---|
5377 | L.DomEvent.on(deleteLink, 'click', function () {
|
---|
5378 | // TODO. maybe remove any event handlers on zoom and delete buttons?
|
---|
5379 | this._map.removeLayer(resultFeature);
|
---|
5380 | }, this);
|
---|
5381 | }
|
---|
5382 |
|
---|
5383 | resultFeature.addTo(this._map);
|
---|
5384 | resultFeature.bindPopup(popupContainer, this.options.popupOptions);
|
---|
5385 | resultFeature.openPopup(resultFeature.getBounds().getCenter());
|
---|
5386 | },
|
---|
5387 | // handle map click during ongoing measurement
|
---|
5388 | // add new clicked point, update measure layers and results ui
|
---|
5389 | _handleMeasureClick: function (evt) {
|
---|
5390 | var latlng = evt.latlng, lastClick = _.last(this._latlngs), vertexSymbol = this._symbols.getSymbol('measureVertex');
|
---|
5391 |
|
---|
5392 | if (!lastClick || !latlng.equals(lastClick)) { // skip if same point as last click, happens on `dblclick`
|
---|
5393 | this._latlngs.push(latlng);
|
---|
5394 | this._addMeasureArea(this._latlngs);
|
---|
5395 | this._addMeasureBoundary(this._latlngs);
|
---|
5396 |
|
---|
5397 | this._measureVertexes.eachLayer(function (layer) {
|
---|
5398 | layer.setStyle(vertexSymbol);
|
---|
5399 | // reset all vertexes to non-active class - only last vertex is active
|
---|
5400 | // `layer.setStyle({ className: 'layer-measurevertex'})` doesn't work. https://github.com/leaflet/leaflet/issues/2662
|
---|
5401 | // set attribute on path directly
|
---|
5402 | layer._path.setAttribute('class', vertexSymbol.className);
|
---|
5403 | });
|
---|
5404 |
|
---|
5405 | this._addNewVertex(latlng);
|
---|
5406 |
|
---|
5407 | if (this._measureBoundary) {
|
---|
5408 | this._measureBoundary.bringToFront();
|
---|
5409 | }
|
---|
5410 | this._measureVertexes.bringToFront();
|
---|
5411 | }
|
---|
5412 |
|
---|
5413 | this._updateResults();
|
---|
5414 | this._updateMeasureStartedWithPoints();
|
---|
5415 | },
|
---|
5416 | // handle map mouse out during ongoing measure
|
---|
5417 | // remove floating cursor vertex from map
|
---|
5418 | _handleMapMouseOut: function () {
|
---|
5419 | if (this._measureDrag) {
|
---|
5420 | this._layer.removeLayer(this._measureDrag);
|
---|
5421 | this._measureDrag = null;
|
---|
5422 | }
|
---|
5423 | },
|
---|
5424 | // add various measure graphics to map - vertex, area, boundary
|
---|
5425 | _addNewVertex: function (latlng) {
|
---|
5426 | L.circleMarker(latlng, this._symbols.getSymbol('measureVertexActive')).addTo(this._measureVertexes);
|
---|
5427 | },
|
---|
5428 | _addMeasureArea: function (latlngs) {
|
---|
5429 | if (latlngs.length < 3) {
|
---|
5430 | if (this._measureArea) {
|
---|
5431 | this._layer.removeLayer(this._measureArea);
|
---|
5432 | this._measureArea = null;
|
---|
5433 | }
|
---|
5434 | return;
|
---|
5435 | }
|
---|
5436 | if (!this._measureArea) {
|
---|
5437 | this._measureArea = L.polygon(latlngs, this._symbols.getSymbol('measureArea')).addTo(this._layer);
|
---|
5438 | } else {
|
---|
5439 | this._measureArea.setLatLngs(latlngs);
|
---|
5440 | }
|
---|
5441 | },
|
---|
5442 | _addMeasureBoundary: function (latlngs) {
|
---|
5443 | if (latlngs.length < 2) {
|
---|
5444 | if (this._measureBoundary) {
|
---|
5445 | this._layer.removeLayer(this._measureBoundary);
|
---|
5446 | this._measureBoundary = null;
|
---|
5447 | }
|
---|
5448 | return;
|
---|
5449 | }
|
---|
5450 | if (!this._measureBoundary) {
|
---|
5451 | this._measureBoundary = L.polyline(latlngs, this._symbols.getSymbol('measureBoundary')).addTo(this._layer);
|
---|
5452 | } else {
|
---|
5453 | this._measureBoundary.setLatLngs(latlngs);
|
---|
5454 | }
|
---|
5455 | }
|
---|
5456 | });
|
---|
5457 |
|
---|
5458 | L.Map.mergeOptions({
|
---|
5459 | measureControl: false
|
---|
5460 | });
|
---|
5461 |
|
---|
5462 | L.Map.addInitHook(function () {
|
---|
5463 | if (this.options.measureControl) {
|
---|
5464 | this.measureControl = (new L.Control.Measure()).addTo(this);
|
---|
5465 | }
|
---|
5466 | });
|
---|
5467 |
|
---|
5468 | L.control.measure = function (options) {
|
---|
5469 | return new L.Control.Measure(options);
|
---|
5470 | };
|
---|
5471 |
|
---|
5472 | }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
---|
5473 | },{"./calc":17,"./dom":18,"./mapsymbology":20,"./units":21,"humanize":15,"underscore":16}],20:[function(require,module,exports){
|
---|
5474 | // mapsymbology.js
|
---|
5475 |
|
---|
5476 | var _ = require('underscore');
|
---|
5477 |
|
---|
5478 | var color = require('color');
|
---|
5479 |
|
---|
5480 | var Symbology = function (options) {
|
---|
5481 | this.setOptions(options);
|
---|
5482 | };
|
---|
5483 |
|
---|
5484 | Symbology.DEFAULTS = {
|
---|
5485 | activeColor: '#ABE67E', // base color for map features while actively measuring
|
---|
5486 | completedColor: '#C8F2BE' // base color for permenant features generated from completed measure
|
---|
5487 | };
|
---|
5488 |
|
---|
5489 | _.extend(Symbology.prototype, {
|
---|
5490 | setOptions: function (options) {
|
---|
5491 | this._options = _.extend({}, Symbology.DEFAULTS, this._options, options);
|
---|
5492 | return this;
|
---|
5493 | },
|
---|
5494 | getSymbol: function (name) {
|
---|
5495 | var symbols = {
|
---|
5496 | measureDrag: {
|
---|
5497 | clickable: false,
|
---|
5498 | radius: 4,
|
---|
5499 | color: this._options.activeColor,
|
---|
5500 | weight: 2,
|
---|
5501 | opacity: 0.7,
|
---|
5502 | fillColor: this._options.activeColor,
|
---|
5503 | fillOpacity: 0.5,
|
---|
5504 | className: 'layer-measuredrag'
|
---|
5505 | },
|
---|
5506 | measureArea: {
|
---|
5507 | clickable: false,
|
---|
5508 | stroke: false,
|
---|
5509 | fillColor: this._options.activeColor,
|
---|
5510 | fillOpacity: 0.2,
|
---|
5511 | className: 'layer-measurearea'
|
---|
5512 | },
|
---|
5513 | measureBoundary: {
|
---|
5514 | clickable: false,
|
---|
5515 | color: this._options.activeColor,
|
---|
5516 | weight: 2,
|
---|
5517 | opacity: 0.9,
|
---|
5518 | fill: false,
|
---|
5519 | className: 'layer-measureboundary'
|
---|
5520 | },
|
---|
5521 | measureVertex: {
|
---|
5522 | clickable: false,
|
---|
5523 | radius: 4,
|
---|
5524 | color: this._options.activeColor,
|
---|
5525 | weight: 2,
|
---|
5526 | opacity: 1,
|
---|
5527 | fillColor: this._options.activeColor,
|
---|
5528 | fillOpacity: 0.7,
|
---|
5529 | className: 'layer-measurevertex'
|
---|
5530 | },
|
---|
5531 | measureVertexActive: {
|
---|
5532 | clickable: false,
|
---|
5533 | radius: 4,
|
---|
5534 | color: this._options.activeColor,
|
---|
5535 | weight: 2,
|
---|
5536 | opacity: 1,
|
---|
5537 | fillColor: color(this._options.activeColor).darken(0.15),
|
---|
5538 | fillOpacity: 0.7,
|
---|
5539 | className: 'layer-measurevertex active'
|
---|
5540 | },
|
---|
5541 | resultArea: {
|
---|
5542 | clickable: true,
|
---|
5543 | color: this._options.completedColor,
|
---|
5544 | weight: 2,
|
---|
5545 | opacity: 0.9,
|
---|
5546 | fillColor: this._options.completedColor,
|
---|
5547 | fillOpacity: 0.2,
|
---|
5548 | className: 'layer-measure-resultarea'
|
---|
5549 | },
|
---|
5550 | resultLine: {
|
---|
5551 | clickable: true,
|
---|
5552 | color: this._options.completedColor,
|
---|
5553 | weight: 3,
|
---|
5554 | opacity: 0.9,
|
---|
5555 | fill: false,
|
---|
5556 | className: 'layer-measure-resultline'
|
---|
5557 | },
|
---|
5558 | resultPoint: {
|
---|
5559 | clickable: true,
|
---|
5560 | radius: 4,
|
---|
5561 | color: this._options.completedColor,
|
---|
5562 | weight: 2,
|
---|
5563 | opacity: 1,
|
---|
5564 | fillColor: this._options.completedColor,
|
---|
5565 | fillOpacity: 0.7,
|
---|
5566 | className: 'layer-measure-resultpoint'
|
---|
5567 | }
|
---|
5568 | };
|
---|
5569 | return symbols[name];
|
---|
5570 | }
|
---|
5571 | });
|
---|
5572 |
|
---|
5573 | module.exports = Symbology;
|
---|
5574 | },{"color":1,"underscore":16}],21:[function(require,module,exports){
|
---|
5575 | // units.js
|
---|
5576 | // Unit configurations
|
---|
5577 | // Factor is with respect to meters/sqmeters
|
---|
5578 |
|
---|
5579 | module.exports = {
|
---|
5580 | acres: {
|
---|
5581 | factor: 0.00024711,
|
---|
5582 | display: 'Acres',
|
---|
5583 | decimals: 2
|
---|
5584 | },
|
---|
5585 | feet: {
|
---|
5586 | factor: 3.2808,
|
---|
5587 | display: 'Feet',
|
---|
5588 | decimals: 0
|
---|
5589 | },
|
---|
5590 | kilometers: {
|
---|
5591 | factor: 0.001,
|
---|
5592 | display: 'Kilometers',
|
---|
5593 | decimals: 2
|
---|
5594 | },
|
---|
5595 | hectares: {
|
---|
5596 | factor: 0.0001,
|
---|
5597 | display: 'Hectares',
|
---|
5598 | decimals: 2
|
---|
5599 | },
|
---|
5600 | meters: {
|
---|
5601 | factor: 1,
|
---|
5602 | display: 'Meters',
|
---|
5603 | decimals: 0
|
---|
5604 | },
|
---|
5605 | miles: {
|
---|
5606 | factor: 3.2808 / 5280,
|
---|
5607 | display: 'Miles',
|
---|
5608 | decimals: 2
|
---|
5609 | },
|
---|
5610 | sqfeet: {
|
---|
5611 | factor: 10.7639,
|
---|
5612 | display: 'Sq Feet',
|
---|
5613 | decimals: 0
|
---|
5614 | },
|
---|
5615 | sqmeters: {
|
---|
5616 | factor: 1,
|
---|
5617 | display: 'Sq Meters',
|
---|
5618 | decimals: 0
|
---|
5619 | },
|
---|
5620 | sqmiles: {
|
---|
5621 | factor: 0.000000386102,
|
---|
5622 | display: 'Sq Miles',
|
---|
5623 | decimals: 2
|
---|
5624 | }
|
---|
5625 | };
|
---|
5626 | },{}]},{},[19]);
|
---|