(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 lum2) { return (lum1 + 0.05) / (lum2 + 0.05) }; return (lum2 + 0.05) / (lum1 + 0.05); }, level: function(color2) { var contrastRatio = this.contrast(color2); return (contrastRatio >= 7.1) ? 'AAA' : (contrastRatio >= 4.5) ? 'AA' : ''; }, dark: function() { // YIQ equation from http://24ways.org/2010/calculating-color-contrast var rgb = this.values.rgb, yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000; return yiq < 128; }, light: function() { return !this.dark(); }, negate: function() { var rgb = [] for (var i = 0; i < 3; i++) { rgb[i] = 255 - this.values.rgb[i]; } this.setValues("rgb", rgb); return this; }, lighten: function(ratio) { this.values.hsl[2] += this.values.hsl[2] * ratio; this.setValues("hsl", this.values.hsl); return this; }, darken: function(ratio) { this.values.hsl[2] -= this.values.hsl[2] * ratio; this.setValues("hsl", this.values.hsl); return this; }, saturate: function(ratio) { this.values.hsl[1] += this.values.hsl[1] * ratio; this.setValues("hsl", this.values.hsl); return this; }, desaturate: function(ratio) { this.values.hsl[1] -= this.values.hsl[1] * ratio; this.setValues("hsl", this.values.hsl); return this; }, whiten: function(ratio) { this.values.hwb[1] += this.values.hwb[1] * ratio; this.setValues("hwb", this.values.hwb); return this; }, blacken: function(ratio) { this.values.hwb[2] += this.values.hwb[2] * ratio; this.setValues("hwb", this.values.hwb); return this; }, greyscale: function() { var rgb = this.values.rgb; // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11; this.setValues("rgb", [val, val, val]); return this; }, clearer: function(ratio) { this.setValues("alpha", this.values.alpha - (this.values.alpha * ratio)); return this; }, opaquer: function(ratio) { this.setValues("alpha", this.values.alpha + (this.values.alpha * ratio)); return this; }, rotate: function(degrees) { var hue = this.values.hsl[0]; hue = (hue + degrees) % 360; hue = hue < 0 ? 360 + hue : hue; this.values.hsl[0] = hue; this.setValues("hsl", this.values.hsl); return this; }, mix: function(color2, weight) { weight = 1 - (weight == null ? 0.5 : weight); // algorithm from Sass's mix(). Ratio of first color in mix is // determined by the alphas of both colors and the weight var t1 = weight * 2 - 1, d = this.alpha() - color2.alpha(); var weight1 = (((t1 * d == -1) ? t1 : (t1 + d) / (1 + t1 * d)) + 1) / 2; var weight2 = 1 - weight1; var rgb = this.rgbArray(); var rgb2 = color2.rgbArray(); for (var i = 0; i < rgb.length; i++) { rgb[i] = rgb[i] * weight1 + rgb2[i] * weight2; } this.setValues("rgb", rgb); var alpha = this.alpha() * weight + color2.alpha() * (1 - weight); this.setValues("alpha", alpha); return this; }, toJSON: function() { return this.rgb(); }, clone: function() { return new Color(this.rgb()); } } Color.prototype.getValues = function(space) { var vals = {}; for (var i = 0; i < space.length; i++) { vals[space.charAt(i)] = this.values[space][i]; } if (this.values.alpha != 1) { vals["a"] = this.values.alpha; } // {r: 255, g: 255, b: 255, a: 0.4} return vals; } Color.prototype.setValues = function(space, vals) { var spaces = { "rgb": ["red", "green", "blue"], "hsl": ["hue", "saturation", "lightness"], "hsv": ["hue", "saturation", "value"], "hwb": ["hue", "whiteness", "blackness"], "cmyk": ["cyan", "magenta", "yellow", "black"] }; var maxes = { "rgb": [255, 255, 255], "hsl": [360, 100, 100], "hsv": [360, 100, 100], "hwb": [360, 100, 100], "cmyk": [100, 100, 100, 100] }; var alpha = 1; if (space == "alpha") { alpha = vals; } else if (vals.length) { // [10, 10, 10] this.values[space] = vals.slice(0, space.length); alpha = vals[space.length]; } else if (vals[space.charAt(0)] !== undefined) { // {r: 10, g: 10, b: 10} for (var i = 0; i < space.length; i++) { this.values[space][i] = vals[space.charAt(i)]; } alpha = vals.a; } else if (vals[spaces[space][0]] !== undefined) { // {red: 10, green: 10, blue: 10} var chans = spaces[space]; for (var i = 0; i < space.length; i++) { this.values[space][i] = vals[chans[i]]; } alpha = vals.alpha; } this.values.alpha = Math.max(0, Math.min(1, (alpha !== undefined ? alpha : this.values.alpha) )); if (space == "alpha") { return; } // cap values of the space prior converting all values for (var i = 0; i < space.length; i++) { var capped = Math.max(0, Math.min(maxes[space][i], this.values[space][i])); this.values[space][i] = Math.round(capped); } // convert to all the other color spaces for (var sname in spaces) { if (sname != space) { this.values[sname] = convert[space][sname](this.values[space]) } // cap values for (var i = 0; i < sname.length; i++) { var capped = Math.max(0, Math.min(maxes[sname][i], this.values[sname][i])); this.values[sname][i] = Math.round(capped); } } return true; } Color.prototype.setSpace = function(space, args) { var vals = args[0]; if (vals === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof vals == "number") { vals = Array.prototype.slice.call(args); } this.setValues(space, vals); return this; } Color.prototype.setChannel = function(space, index, val) { if (val === undefined) { // color.red() return this.values[space][index]; } // color.red(100) this.values[space][index] = val; this.setValues(space, this.values[space]); return this; } module.exports = Color; },{"color-convert":3,"color-string":4}],2:[function(require,module,exports){ /* MIT license */ module.exports = { rgb2hsl: rgb2hsl, rgb2hsv: rgb2hsv, rgb2hwb: rgb2hwb, rgb2cmyk: rgb2cmyk, rgb2keyword: rgb2keyword, rgb2xyz: rgb2xyz, rgb2lab: rgb2lab, rgb2lch: rgb2lch, hsl2rgb: hsl2rgb, hsl2hsv: hsl2hsv, hsl2hwb: hsl2hwb, hsl2cmyk: hsl2cmyk, hsl2keyword: hsl2keyword, hsv2rgb: hsv2rgb, hsv2hsl: hsv2hsl, hsv2hwb: hsv2hwb, hsv2cmyk: hsv2cmyk, hsv2keyword: hsv2keyword, hwb2rgb: hwb2rgb, hwb2hsl: hwb2hsl, hwb2hsv: hwb2hsv, hwb2cmyk: hwb2cmyk, hwb2keyword: hwb2keyword, cmyk2rgb: cmyk2rgb, cmyk2hsl: cmyk2hsl, cmyk2hsv: cmyk2hsv, cmyk2hwb: cmyk2hwb, cmyk2keyword: cmyk2keyword, keyword2rgb: keyword2rgb, keyword2hsl: keyword2hsl, keyword2hsv: keyword2hsv, keyword2hwb: keyword2hwb, keyword2cmyk: keyword2cmyk, keyword2lab: keyword2lab, keyword2xyz: keyword2xyz, xyz2rgb: xyz2rgb, xyz2lab: xyz2lab, xyz2lch: xyz2lch, lab2xyz: lab2xyz, lab2rgb: lab2rgb, lab2lch: lab2lch, lch2lab: lch2lab, lch2xyz: lch2xyz, lch2rgb: lch2rgb } function rgb2hsl(rgb) { var r = rgb[0]/255, g = rgb[1]/255, b = rgb[2]/255, min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, l; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g)/ delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; l = (min + max) / 2; if (max == min) s = 0; else if (l <= 0.5) s = delta / (max + min); else s = delta / (2 - max - min); return [h, s * 100, l * 100]; } function rgb2hsv(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], min = Math.min(r, g, b), max = Math.max(r, g, b), delta = max - min, h, s, v; if (max == 0) s = 0; else s = (delta/max * 1000)/10; if (max == min) h = 0; else if (r == max) h = (g - b) / delta; else if (g == max) h = 2 + (b - r) / delta; else if (b == max) h = 4 + (r - g) / delta; h = Math.min(h * 60, 360); if (h < 0) h += 360; v = ((max / 255) * 1000) / 10; return [h, s, v]; } function rgb2hwb(rgb) { var r = rgb[0], g = rgb[1], b = rgb[2], h = rgb2hsl(rgb)[0], w = 1/255 * Math.min(r, Math.min(g, b)), b = 1 - 1/255 * Math.max(r, Math.max(g, b)); return [h, w * 100, b * 100]; } function rgb2cmyk(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255, c, m, y, k; k = Math.min(1 - r, 1 - g, 1 - b); c = (1 - r - k) / (1 - k) || 0; m = (1 - g - k) / (1 - k) || 0; y = (1 - b - k) / (1 - k) || 0; return [c * 100, m * 100, y * 100, k * 100]; } function rgb2keyword(rgb) { return reverseKeywords[JSON.stringify(rgb)]; } function rgb2xyz(rgb) { var r = rgb[0] / 255, g = rgb[1] / 255, b = rgb[2] / 255; // assume sRGB r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92); g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92); b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92); var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); return [x * 100, y *100, z * 100]; } function rgb2lab(rgb) { var xyz = rgb2xyz(rgb), x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function rgb2lch(args) { return lab2lch(rgb2lab(args)); } function hsl2rgb(hsl) { var h = hsl[0] / 360, s = hsl[1] / 100, l = hsl[2] / 100, t1, t2, t3, rgb, val; if (s == 0) { val = l * 255; return [val, val, val]; } if (l < 0.5) t2 = l * (1 + s); else t2 = l + s - l * s; t1 = 2 * l - t2; rgb = [0, 0, 0]; for (var i = 0; i < 3; i++) { t3 = h + 1 / 3 * - (i - 1); t3 < 0 && t3++; t3 > 1 && t3--; if (6 * t3 < 1) val = t1 + (t2 - t1) * 6 * t3; else if (2 * t3 < 1) val = t2; else if (3 * t3 < 2) val = t1 + (t2 - t1) * (2 / 3 - t3) * 6; else val = t1; rgb[i] = val * 255; } return rgb; } function hsl2hsv(hsl) { var h = hsl[0], s = hsl[1] / 100, l = hsl[2] / 100, sv, v; l *= 2; s *= (l <= 1) ? l : 2 - l; v = (l + s) / 2; sv = (2 * s) / (l + s); return [h, sv * 100, v * 100]; } function hsl2hwb(args) { return rgb2hwb(hsl2rgb(args)); } function hsl2cmyk(args) { return rgb2cmyk(hsl2rgb(args)); } function hsl2keyword(args) { return rgb2keyword(hsl2rgb(args)); } function hsv2rgb(hsv) { var h = hsv[0] / 60, s = hsv[1] / 100, v = hsv[2] / 100, hi = Math.floor(h) % 6; var f = h - Math.floor(h), p = 255 * v * (1 - s), q = 255 * v * (1 - (s * f)), t = 255 * v * (1 - (s * (1 - f))), v = 255 * v; switch(hi) { case 0: return [v, t, p]; case 1: return [q, v, p]; case 2: return [p, v, t]; case 3: return [p, q, v]; case 4: return [t, p, v]; case 5: return [v, p, q]; } } function hsv2hsl(hsv) { var h = hsv[0], s = hsv[1] / 100, v = hsv[2] / 100, sl, l; l = (2 - s) * v; sl = s * v; sl /= (l <= 1) ? l : 2 - l; sl = sl || 0; l /= 2; return [h, sl * 100, l * 100]; } function hsv2hwb(args) { return rgb2hwb(hsv2rgb(args)) } function hsv2cmyk(args) { return rgb2cmyk(hsv2rgb(args)); } function hsv2keyword(args) { return rgb2keyword(hsv2rgb(args)); } // http://dev.w3.org/csswg/css-color/#hwb-to-rgb function hwb2rgb(hwb) { var h = hwb[0] / 360, wh = hwb[1] / 100, bl = hwb[2] / 100, ratio = wh + bl, i, v, f, n; // wh + bl cant be > 1 if (ratio > 1) { wh /= ratio; bl /= ratio; } i = Math.floor(6 * h); v = 1 - bl; f = 6 * h - i; if ((i & 0x01) != 0) { f = 1 - f; } n = wh + f * (v - wh); // linear interpolation switch (i) { default: case 6: case 0: r = v; g = n; b = wh; break; case 1: r = n; g = v; b = wh; break; case 2: r = wh; g = v; b = n; break; case 3: r = wh; g = n; b = v; break; case 4: r = n; g = wh; b = v; break; case 5: r = v; g = wh; b = n; break; } return [r * 255, g * 255, b * 255]; } function hwb2hsl(args) { return rgb2hsl(hwb2rgb(args)); } function hwb2hsv(args) { return rgb2hsv(hwb2rgb(args)); } function hwb2cmyk(args) { return rgb2cmyk(hwb2rgb(args)); } function hwb2keyword(args) { return rgb2keyword(hwb2rgb(args)); } function cmyk2rgb(cmyk) { var c = cmyk[0] / 100, m = cmyk[1] / 100, y = cmyk[2] / 100, k = cmyk[3] / 100, r, g, b; r = 1 - Math.min(1, c * (1 - k) + k); g = 1 - Math.min(1, m * (1 - k) + k); b = 1 - Math.min(1, y * (1 - k) + k); return [r * 255, g * 255, b * 255]; } function cmyk2hsl(args) { return rgb2hsl(cmyk2rgb(args)); } function cmyk2hsv(args) { return rgb2hsv(cmyk2rgb(args)); } function cmyk2hwb(args) { return rgb2hwb(cmyk2rgb(args)); } function cmyk2keyword(args) { return rgb2keyword(cmyk2rgb(args)); } function xyz2rgb(xyz) { var x = xyz[0] / 100, y = xyz[1] / 100, z = xyz[2] / 100, r, g, b; r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); // assume sRGB r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055) : r = (r * 12.92); g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055) : g = (g * 12.92); b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055) : b = (b * 12.92); r = Math.min(Math.max(0, r), 1); g = Math.min(Math.max(0, g), 1); b = Math.min(Math.max(0, b), 1); return [r * 255, g * 255, b * 255]; } function xyz2lab(xyz) { var x = xyz[0], y = xyz[1], z = xyz[2], l, a, b; x /= 95.047; y /= 100; z /= 108.883; x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116); y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116); z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116); l = (116 * y) - 16; a = 500 * (x - y); b = 200 * (y - z); return [l, a, b]; } function xyz2lch(args) { return lab2lch(xyz2lab(args)); } function lab2xyz(lab) { var l = lab[0], a = lab[1], b = lab[2], x, y, z, y2; if (l <= 8) { y = (l * 100) / 903.3; y2 = (7.787 * (y / 100)) + (16 / 116); } else { y = 100 * Math.pow((l + 16) / 116, 3); y2 = Math.pow(y / 100, 1/3); } x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3); z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3); return [x, y, z]; } function lab2lch(lab) { var l = lab[0], a = lab[1], b = lab[2], hr, h, c; hr = Math.atan2(b, a); h = hr * 360 / 2 / Math.PI; if (h < 0) { h += 360; } c = Math.sqrt(a * a + b * b); return [l, c, h]; } function lab2rgb(args) { return xyz2rgb(lab2xyz(args)); } function lch2lab(lch) { var l = lch[0], c = lch[1], h = lch[2], a, b, hr; hr = h / 360 * 2 * Math.PI; a = c * Math.cos(hr); b = c * Math.sin(hr); return [l, a, b]; } function lch2xyz(args) { return lab2xyz(lch2lab(args)); } function lch2rgb(args) { return lab2rgb(lch2lab(args)); } function keyword2rgb(keyword) { return cssKeywords[keyword]; } function keyword2hsl(args) { return rgb2hsl(keyword2rgb(args)); } function keyword2hsv(args) { return rgb2hsv(keyword2rgb(args)); } function keyword2hwb(args) { return rgb2hwb(keyword2rgb(args)); } function keyword2cmyk(args) { return rgb2cmyk(keyword2rgb(args)); } function keyword2lab(args) { return rgb2lab(keyword2rgb(args)); } function keyword2xyz(args) { return rgb2xyz(keyword2rgb(args)); } var cssKeywords = { aliceblue: [240,248,255], antiquewhite: [250,235,215], aqua: [0,255,255], aquamarine: [127,255,212], azure: [240,255,255], beige: [245,245,220], bisque: [255,228,196], black: [0,0,0], blanchedalmond: [255,235,205], blue: [0,0,255], blueviolet: [138,43,226], brown: [165,42,42], burlywood: [222,184,135], cadetblue: [95,158,160], chartreuse: [127,255,0], chocolate: [210,105,30], coral: [255,127,80], cornflowerblue: [100,149,237], cornsilk: [255,248,220], crimson: [220,20,60], cyan: [0,255,255], darkblue: [0,0,139], darkcyan: [0,139,139], darkgoldenrod: [184,134,11], darkgray: [169,169,169], darkgreen: [0,100,0], darkgrey: [169,169,169], darkkhaki: [189,183,107], darkmagenta: [139,0,139], darkolivegreen: [85,107,47], darkorange: [255,140,0], darkorchid: [153,50,204], darkred: [139,0,0], darksalmon: [233,150,122], darkseagreen: [143,188,143], darkslateblue: [72,61,139], darkslategray: [47,79,79], darkslategrey: [47,79,79], darkturquoise: [0,206,209], darkviolet: [148,0,211], deeppink: [255,20,147], deepskyblue: [0,191,255], dimgray: [105,105,105], dimgrey: [105,105,105], dodgerblue: [30,144,255], firebrick: [178,34,34], floralwhite: [255,250,240], forestgreen: [34,139,34], fuchsia: [255,0,255], gainsboro: [220,220,220], ghostwhite: [248,248,255], gold: [255,215,0], goldenrod: [218,165,32], gray: [128,128,128], green: [0,128,0], greenyellow: [173,255,47], grey: [128,128,128], honeydew: [240,255,240], hotpink: [255,105,180], indianred: [205,92,92], indigo: [75,0,130], ivory: [255,255,240], khaki: [240,230,140], lavender: [230,230,250], lavenderblush: [255,240,245], lawngreen: [124,252,0], lemonchiffon: [255,250,205], lightblue: [173,216,230], lightcoral: [240,128,128], lightcyan: [224,255,255], lightgoldenrodyellow: [250,250,210], lightgray: [211,211,211], lightgreen: [144,238,144], lightgrey: [211,211,211], lightpink: [255,182,193], lightsalmon: [255,160,122], lightseagreen: [32,178,170], lightskyblue: [135,206,250], lightslategray: [119,136,153], lightslategrey: [119,136,153], lightsteelblue: [176,196,222], lightyellow: [255,255,224], lime: [0,255,0], limegreen: [50,205,50], linen: [250,240,230], magenta: [255,0,255], maroon: [128,0,0], mediumaquamarine: [102,205,170], mediumblue: [0,0,205], mediumorchid: [186,85,211], mediumpurple: [147,112,219], mediumseagreen: [60,179,113], mediumslateblue: [123,104,238], mediumspringgreen: [0,250,154], mediumturquoise: [72,209,204], mediumvioletred: [199,21,133], midnightblue: [25,25,112], mintcream: [245,255,250], mistyrose: [255,228,225], moccasin: [255,228,181], navajowhite: [255,222,173], navy: [0,0,128], oldlace: [253,245,230], olive: [128,128,0], olivedrab: [107,142,35], orange: [255,165,0], orangered: [255,69,0], orchid: [218,112,214], palegoldenrod: [238,232,170], palegreen: [152,251,152], paleturquoise: [175,238,238], palevioletred: [219,112,147], papayawhip: [255,239,213], peachpuff: [255,218,185], peru: [205,133,63], pink: [255,192,203], plum: [221,160,221], powderblue: [176,224,230], purple: [128,0,128], rebeccapurple: [102, 51, 153], red: [255,0,0], rosybrown: [188,143,143], royalblue: [65,105,225], saddlebrown: [139,69,19], salmon: [250,128,114], sandybrown: [244,164,96], seagreen: [46,139,87], seashell: [255,245,238], sienna: [160,82,45], silver: [192,192,192], skyblue: [135,206,235], slateblue: [106,90,205], slategray: [112,128,144], slategrey: [112,128,144], snow: [255,250,250], springgreen: [0,255,127], steelblue: [70,130,180], tan: [210,180,140], teal: [0,128,128], thistle: [216,191,216], tomato: [255,99,71], turquoise: [64,224,208], violet: [238,130,238], wheat: [245,222,179], white: [255,255,255], whitesmoke: [245,245,245], yellow: [255,255,0], yellowgreen: [154,205,50] }; var reverseKeywords = {}; for (var key in cssKeywords) { reverseKeywords[JSON.stringify(cssKeywords[key])] = key; } },{}],3:[function(require,module,exports){ var conversions = require("./conversions"); var convert = function() { return new Converter(); } for (var func in conversions) { // export Raw versions convert[func + "Raw"] = (function(func) { // accept array or plain args return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); return conversions[func](arg); } })(func); var pair = /(\w+)2(\w+)/.exec(func), from = pair[1], to = pair[2]; // export rgb2hsl and ["rgb"]["hsl"] convert[from] = convert[from] || {}; convert[from][to] = convert[func] = (function(func) { return function(arg) { if (typeof arg == "number") arg = Array.prototype.slice.call(arguments); var val = conversions[func](arg); if (typeof val == "string" || val === undefined) return val; // keyword for (var i = 0; i < val.length; i++) val[i] = Math.round(val[i]); return val; } })(func); } /* Converter does lazy conversion and caching */ var Converter = function() { this.convs = {}; }; /* Either get the values for a space or set the values for a space, depending on args */ Converter.prototype.routeSpace = function(space, args) { var values = args[0]; if (values === undefined) { // color.rgb() return this.getValues(space); } // color.rgb(10, 10, 10) if (typeof values == "number") { values = Array.prototype.slice.call(args); } return this.setValues(space, values); }; /* Set the values for a space, invalidating cache */ Converter.prototype.setValues = function(space, values) { this.space = space; this.convs = {}; this.convs[space] = values; return this; }; /* Get the values for a space. If there's already a conversion for the space, fetch it, otherwise compute it */ Converter.prototype.getValues = function(space) { var vals = this.convs[space]; if (!vals) { var fspace = this.space, from = this.convs[fspace]; vals = convert[fspace][space](from); this.convs[space] = vals; } return vals; }; ["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) { Converter.prototype[space] = function(vals) { return this.routeSpace(space, arguments); } }); module.exports = convert; },{"./conversions":2}],4:[function(require,module,exports){ /* MIT license */ var colorNames = require('color-name'); module.exports = { getRgba: getRgba, getHsla: getHsla, getRgb: getRgb, getHsl: getHsl, getHwb: getHwb, getAlpha: getAlpha, hexString: hexString, rgbString: rgbString, rgbaString: rgbaString, percentString: percentString, percentaString: percentaString, hslString: hslString, hslaString: hslaString, hwbString: hwbString, keyword: keyword } function getRgba(string) { if (!string) { return; } var abbr = /^#([a-fA-F0-9]{3})$/, hex = /^#([a-fA-F0-9]{6})$/, rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*,\s*([+-]?[\d\.]+)\%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)$/, keyword = /(\D+)/; var rgb = [0, 0, 0], a = 1, match = string.match(abbr); if (match) { match = match[1]; for (var i = 0; i < rgb.length; i++) { rgb[i] = parseInt(match[i] + match[i], 16); } } else if (match = string.match(hex)) { match = match[1]; for (var i = 0; i < rgb.length; i++) { rgb[i] = parseInt(match.slice(i * 2, i * 2 + 2), 16); } } else if (match = string.match(rgba)) { for (var i = 0; i < rgb.length; i++) { rgb[i] = parseInt(match[i + 1]); } a = parseFloat(match[4]); } else if (match = string.match(per)) { for (var i = 0; i < rgb.length; i++) { rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55); } a = parseFloat(match[4]); } else if (match = string.match(keyword)) { if (match[1] == "transparent") { return [0, 0, 0, 0]; } rgb = colorNames[match[1]]; if (!rgb) { return; } } for (var i = 0; i < rgb.length; i++) { rgb[i] = scale(rgb[i], 0, 255); } if (!a && a != 0) { a = 1; } else { a = scale(a, 0, 1); } rgb[3] = a; return rgb; } function getHsla(string) { if (!string) { return; } var hsl = /^hsla?\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; var match = string.match(hsl); if (match) { var alpha = parseFloat(match[4]); var h = scale(parseInt(match[1]), 0, 360), s = scale(parseFloat(match[2]), 0, 100), l = scale(parseFloat(match[3]), 0, 100), a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); return [h, s, l, a]; } } function getHwb(string) { if (!string) { return; } var hwb = /^hwb\(\s*([+-]?\d+)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?[\d\.]+)\s*)?\)/; var match = string.match(hwb); if (match) { var alpha = parseFloat(match[4]); var h = scale(parseInt(match[1]), 0, 360), w = scale(parseFloat(match[2]), 0, 100), b = scale(parseFloat(match[3]), 0, 100), a = scale(isNaN(alpha) ? 1 : alpha, 0, 1); return [h, w, b, a]; } } function getRgb(string) { var rgba = getRgba(string); return rgba && rgba.slice(0, 3); } function getHsl(string) { var hsla = getHsla(string); return hsla && hsla.slice(0, 3); } function getAlpha(string) { var vals = getRgba(string); if (vals) { return vals[3]; } else if (vals = getHsla(string)) { return vals[3]; } else if (vals = getHwb(string)) { return vals[3]; } } // generators function hexString(rgb) { return "#" + hexDouble(rgb[0]) + hexDouble(rgb[1]) + hexDouble(rgb[2]); } function rgbString(rgba, alpha) { if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { return rgbaString(rgba, alpha); } return "rgb(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ")"; } function rgbaString(rgba, alpha) { if (alpha === undefined) { alpha = (rgba[3] !== undefined ? rgba[3] : 1); } return "rgba(" + rgba[0] + ", " + rgba[1] + ", " + rgba[2] + ", " + alpha + ")"; } function percentString(rgba, alpha) { if (alpha < 1 || (rgba[3] && rgba[3] < 1)) { return percentaString(rgba, alpha); } var r = Math.round(rgba[0]/255 * 100), g = Math.round(rgba[1]/255 * 100), b = Math.round(rgba[2]/255 * 100); return "rgb(" + r + "%, " + g + "%, " + b + "%)"; } function percentaString(rgba, alpha) { var r = Math.round(rgba[0]/255 * 100), g = Math.round(rgba[1]/255 * 100), b = Math.round(rgba[2]/255 * 100); return "rgba(" + r + "%, " + g + "%, " + b + "%, " + (alpha || rgba[3] || 1) + ")"; } function hslString(hsla, alpha) { if (alpha < 1 || (hsla[3] && hsla[3] < 1)) { return hslaString(hsla, alpha); } return "hsl(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%)"; } function hslaString(hsla, alpha) { if (alpha === undefined) { alpha = (hsla[3] !== undefined ? hsla[3] : 1); } return "hsla(" + hsla[0] + ", " + hsla[1] + "%, " + hsla[2] + "%, " + alpha + ")"; } // hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax // (hwb have alpha optional & 1 is default value) function hwbString(hwb, alpha) { if (alpha === undefined) { alpha = (hwb[3] !== undefined ? hwb[3] : 1); } return "hwb(" + hwb[0] + ", " + hwb[1] + "%, " + hwb[2] + "%" + (alpha !== undefined && alpha !== 1 ? ", " + alpha : "") + ")"; } function keyword(rgb) { return reverseNames[rgb.slice(0, 3)]; } // helpers function scale(num, min, max) { return Math.min(Math.max(min, num), max); } function hexDouble(num) { var str = num.toString(16).toUpperCase(); return (str.length < 2) ? "0" + str : str; } //create a list of reverse color names var reverseNames = {}; for (var name in colorNames) { reverseNames[colorNames[name]] = name; } },{"color-name":5}],5:[function(require,module,exports){ module.exports={ "aliceblue": [240, 248, 255], "antiquewhite": [250, 235, 215], "aqua": [0, 255, 255], "aquamarine": [127, 255, 212], "azure": [240, 255, 255], "beige": [245, 245, 220], "bisque": [255, 228, 196], "black": [0, 0, 0], "blanchedalmond": [255, 235, 205], "blue": [0, 0, 255], "blueviolet": [138, 43, 226], "brown": [165, 42, 42], "burlywood": [222, 184, 135], "cadetblue": [95, 158, 160], "chartreuse": [127, 255, 0], "chocolate": [210, 105, 30], "coral": [255, 127, 80], "cornflowerblue": [100, 149, 237], "cornsilk": [255, 248, 220], "crimson": [220, 20, 60], "cyan": [0, 255, 255], "darkblue": [0, 0, 139], "darkcyan": [0, 139, 139], "darkgoldenrod": [184, 134, 11], "darkgray": [169, 169, 169], "darkgreen": [0, 100, 0], "darkgrey": [169, 169, 169], "darkkhaki": [189, 183, 107], "darkmagenta": [139, 0, 139], "darkolivegreen": [85, 107, 47], "darkorange": [255, 140, 0], "darkorchid": [153, 50, 204], "darkred": [139, 0, 0], "darksalmon": [233, 150, 122], "darkseagreen": [143, 188, 143], "darkslateblue": [72, 61, 139], "darkslategray": [47, 79, 79], "darkslategrey": [47, 79, 79], "darkturquoise": [0, 206, 209], "darkviolet": [148, 0, 211], "deeppink": [255, 20, 147], "deepskyblue": [0, 191, 255], "dimgray": [105, 105, 105], "dimgrey": [105, 105, 105], "dodgerblue": [30, 144, 255], "firebrick": [178, 34, 34], "floralwhite": [255, 250, 240], "forestgreen": [34, 139, 34], "fuchsia": [255, 0, 255], "gainsboro": [220, 220, 220], "ghostwhite": [248, 248, 255], "gold": [255, 215, 0], "goldenrod": [218, 165, 32], "gray": [128, 128, 128], "green": [0, 128, 0], "greenyellow": [173, 255, 47], "grey": [128, 128, 128], "honeydew": [240, 255, 240], "hotpink": [255, 105, 180], "indianred": [205, 92, 92], "indigo": [75, 0, 130], "ivory": [255, 255, 240], "khaki": [240, 230, 140], "lavender": [230, 230, 250], "lavenderblush": [255, 240, 245], "lawngreen": [124, 252, 0], "lemonchiffon": [255, 250, 205], "lightblue": [173, 216, 230], "lightcoral": [240, 128, 128], "lightcyan": [224, 255, 255], "lightgoldenrodyellow": [250, 250, 210], "lightgray": [211, 211, 211], "lightgreen": [144, 238, 144], "lightgrey": [211, 211, 211], "lightpink": [255, 182, 193], "lightsalmon": [255, 160, 122], "lightseagreen": [32, 178, 170], "lightskyblue": [135, 206, 250], "lightslategray": [119, 136, 153], "lightslategrey": [119, 136, 153], "lightsteelblue": [176, 196, 222], "lightyellow": [255, 255, 224], "lime": [0, 255, 0], "limegreen": [50, 205, 50], "linen": [250, 240, 230], "magenta": [255, 0, 255], "maroon": [128, 0, 0], "mediumaquamarine": [102, 205, 170], "mediumblue": [0, 0, 205], "mediumorchid": [186, 85, 211], "mediumpurple": [147, 112, 219], "mediumseagreen": [60, 179, 113], "mediumslateblue": [123, 104, 238], "mediumspringgreen": [0, 250, 154], "mediumturquoise": [72, 209, 204], "mediumvioletred": [199, 21, 133], "midnightblue": [25, 25, 112], "mintcream": [245, 255, 250], "mistyrose": [255, 228, 225], "moccasin": [255, 228, 181], "navajowhite": [255, 222, 173], "navy": [0, 0, 128], "oldlace": [253, 245, 230], "olive": [128, 128, 0], "olivedrab": [107, 142, 35], "orange": [255, 165, 0], "orangered": [255, 69, 0], "orchid": [218, 112, 214], "palegoldenrod": [238, 232, 170], "palegreen": [152, 251, 152], "paleturquoise": [175, 238, 238], "palevioletred": [219, 112, 147], "papayawhip": [255, 239, 213], "peachpuff": [255, 218, 185], "peru": [205, 133, 63], "pink": [255, 192, 203], "plum": [221, 160, 221], "powderblue": [176, 224, 230], "purple": [128, 0, 128], "rebeccapurple": [102, 51, 153], "red": [255, 0, 0], "rosybrown": [188, 143, 143], "royalblue": [65, 105, 225], "saddlebrown": [139, 69, 19], "salmon": [250, 128, 114], "sandybrown": [244, 164, 96], "seagreen": [46, 139, 87], "seashell": [255, 245, 238], "sienna": [160, 82, 45], "silver": [192, 192, 192], "skyblue": [135, 206, 235], "slateblue": [106, 90, 205], "slategray": [112, 128, 144], "slategrey": [112, 128, 144], "snow": [255, 250, 250], "springgreen": [0, 255, 127], "steelblue": [70, 130, 180], "tan": [210, 180, 140], "teal": [0, 128, 128], "thistle": [216, 191, 216], "tomato": [255, 99, 71], "turquoise": [64, 224, 208], "violet": [238, 130, 238], "wheat": [245, 222, 179], "white": [255, 255, 255], "whitesmoke": [245, 245, 245], "yellow": [255, 255, 0], "yellowgreen": [154, 205, 50] } },{}],6:[function(require,module,exports){ module.exports = require('./lib/geocrunch'); },{"./lib/geocrunch":11}],7:[function(require,module,exports){ // distance.js - Distance mixins for Paths var _ = require('underscore'); var R = require('./constants').EARTHRADIUS; var units = require('./units'); var flipCoords = require('./flipcoords'); // Area conversions (from sqmeters) var convertFuncs = { sqmeters: function (a) { return a; }, sqmiles: function (a) { return units.sqMeters.toSqMiles(a); }, acres: function (a) { return units.sqMeters.toAcres(a); } }; // Calculates area in square meters // Method taken from OpenLayers API, https://github.com/openlayers/openlayers/blob/master/lib/OpenLayers/Geometry/LinearRing.js#L270 var calcArea = function (coordArray) { var area = 0, i, l, c1, c2; for (i = 0, l = coordArray.length; i < l; i += 1) { c1 = coordArray[i]; c2 = coordArray[(i + 1) % coordArray.length]; // Access next item in array until last item is i, then accesses first (0) area = area + units.degrees.toRadians(c2[0] - c1[0]) * (2 + Math.sin(units.degrees.toRadians(c1[1])) + Math.sin(units.degrees.toRadians(c2[1]))); } return Math.abs(area * R * R / 2); }; var calcCenter = function (coordArray) { var offset = coordArray[0], twiceArea = 0, x = 0, y = 0, i, l, c1, c2, f; if (coordArray.length === 1) { return coordArray[0]; } for (i = 0, l = coordArray.length; i < l; i += 1) { c1 = coordArray[i]; c2 = coordArray[(i + 1) % coordArray.length]; // Access next item in array until last item is i, then accesses first (0) f = (c1[1] - offset[1]) * (c2[0] - offset[0]) - (c2[1] - offset[1]) * (c1[0] - offset[0]); twiceArea = twiceArea + f; x = x + ((c1[0] + c2[0] - 2 * offset[0]) * f); y = y + ((c1[1] + c2[1] - 2 * offset[1]) * f); } f = twiceArea * 3; return [x / f + offset[0], y / f + offset[1]]; }; module.exports = { _internalAreaCalc: function () { // If not set, set this._calcedArea to total area in UNITS // Checks for cache to prevent additional unnecessary calcs if (!this._calcedArea) { if (this._coords.length < 3) { this._calcedArea = 0; } else { this._calcedArea = calcArea(this._coords); } } }, _internalCenterCalc: function () { if (!this._calcedCenter && this._coords.length) { this._calcedCenter = calcCenter(this._coords); } }, area: function (options) { var opts = _.extend({ units: 'sqmeters' }, options); this._internalAreaCalc(); if (_.isFunction(convertFuncs[opts.units])) { return convertFuncs[opts.units](this._calcedArea); } // TODO. Handle non-matching units }, center: function () { this._internalCenterCalc(); return this._options.imBackwards === true ? flipCoords(this._calcedCenter) : this._calcedCenter; } }; },{"./constants":8,"./flipcoords":10,"./units":13,"underscore":14}],8:[function(require,module,exports){ // utils/constants.js module.exports = { EARTHRADIUS: 6371000 // R in meters }; },{}],9:[function(require,module,exports){ // distance.js - Distance mixins for Paths var _ = require('underscore'); var R = require('./constants').EARTHRADIUS; var units = require('./units'); // Distance conversions (from meters) var convertFuncs = { meters: function (d) { return d; }, kilometers: function (d) { return units.meters.toKilometers(d); }, feet: function (d) { return units.meters.toFeet(d); }, miles: function (d) { return units.meters.toMiles(d); } }; // Distance in meters // Always positive regardless of direction // Calculation based on Haversine Formula http://en.wikipedia.org/wiki/Haversine_formula // Another method is @ http://www.movable-type.co.uk/scripts/latlong-vincenty.html but seems way overcomplicated var calcDistance = function (coord1, coord2) { var deltaLng = units.degrees.toRadians(coord1[0] - coord2[0]), deltaLat = units.degrees.toRadians(coord1[1] - coord2[1]), lat1 = units.degrees.toRadians(coord1[1]), lat2 = units.degrees.toRadians(coord2[1]), hvsLng = Math.sin(deltaLng / 2), hvsLat = Math.sin(deltaLat / 2); var a = hvsLat * hvsLat + hvsLng * hvsLng * Math.cos(lat1) * Math.cos(lat2); return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); }; module.exports = { _internalDistanceCalc: function () { // If not set, set this._calcedDistance to total distance in meters // Checks for cache to prevent additional unnecessary calcs var distance = 0, i, l; if (!this._calcedDistance) { for (i = 0, l = this._coords.length; i < l; i += 1) { if (i > 0) { distance = distance + calcDistance(this._coords[i - 1], this._coords[i]); } } this._calcedDistance = distance; } }, distance: function (options) { var opts = _.extend({ units: 'meters' }, options); this._internalDistanceCalc(); if (_.isFunction(convertFuncs[opts.units])) { return convertFuncs[opts.units](this._calcedDistance); } // TODO. Handle non-matching units } }; },{"./constants":8,"./units":13,"underscore":14}],10:[function(require,module,exports){ // utils/flipcoords.js - Util functions for working with backwards coordinates [lat, lng] var _ = require('underscore'); module.exports = function (backwardsCoordArray) { return _.map(backwardsCoordArray, function (backwardsCoord) { return [backwardsCoord[1], backwardsCoord[0]]; }); }; },{"underscore":14}],11:[function(require,module,exports){ // geocrunch.js var _ = require('underscore'); var Path = require('./path'); var distanceMixins = require('./distance'), areaMixins = require('./area'); _.extend(Path.prototype, distanceMixins, areaMixins); exports.path = function (coords, options) { return new Path(coords, options); }; },{"./area":7,"./distance":9,"./path":12,"underscore":14}],12:[function(require,module,exports){ // path.js - Object for working with a linear path of coordinates var flipCoords = require('./flipcoords'); var Path = function (coords, options) { this._options = options || {}; // Set this._coords... Think about flipping at time of calcs for less iterations/better perf. May risk code clarity and mixin ease. coords = coords || []; this._coords = this._options.imBackwards === true ? flipCoords(coords) : coords; }; module.exports = Path; },{"./flipcoords":10}],13:[function(require,module,exports){ // units.js - Standard unit conversions exports.meters = { toFeet: function (m) { return m * 3.28084; }, toKilometers: function (m) { return m * 0.001; }, toMiles: function (m) { return m * 0.000621371; } }; exports.sqMeters = { toSqMiles: function (m) { return m * 0.000000386102; }, toAcres: function (m) { return m * 0.000247105; } }; exports.degrees = { toRadians: function (d) { return d * Math.PI / 180; } }; },{}],14:[function(require,module,exports){ // Underscore.js 1.5.2 // http://underscorejs.org // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Establish the object that gets returned to break out of a loop iteration. var breaker = {}; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, concat = ArrayProto.concat, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeForEach = ArrayProto.forEach, nativeMap = ArrayProto.map, nativeReduce = ArrayProto.reduce, nativeReduceRight = ArrayProto.reduceRight, nativeFilter = ArrayProto.filter, nativeEvery = ArrayProto.every, nativeSome = ArrayProto.some, nativeIndexOf = ArrayProto.indexOf, nativeLastIndexOf = ArrayProto.lastIndexOf, nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object via a string identifier, // for Closure Compiler "advanced" mode. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.5.2'; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles objects with the built-in `forEach`, arrays, and raw objects. // Delegates to **ECMAScript 5**'s native `forEach` if available. var each = _.each = _.forEach = function(obj, iterator, context) { if (obj == null) return; if (nativeForEach && obj.forEach === nativeForEach) { obj.forEach(iterator, context); } else if (obj.length === +obj.length) { for (var i = 0, length = obj.length; i < length; i++) { if (iterator.call(context, obj[i], i, obj) === breaker) return; } } else { var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; } } }; // Return the results of applying the iterator to each element. // Delegates to **ECMAScript 5**'s native `map` if available. _.map = _.collect = function(obj, iterator, context) { var results = []; if (obj == null) return results; if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); each(obj, function(value, index, list) { results.push(iterator.call(context, value, index, list)); }); return results; }; var reduceError = 'Reduce of empty array with no initial value'; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if (obj == null) obj = []; if (nativeReduce && obj.reduce === nativeReduce) { if (context) iterator = _.bind(iterator, context); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); } each(obj, function(value, index, list) { if (!initial) { memo = value; initial = true; } else { memo = iterator.call(context, memo, value, index, list); } }); if (!initial) throw new TypeError(reduceError); return memo; }; // The right-associative version of reduce, also known as `foldr`. // Delegates to **ECMAScript 5**'s native `reduceRight` if available. _.reduceRight = _.foldr = function(obj, iterator, memo, context) { var initial = arguments.length > 2; if (obj == null) obj = []; if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { if (context) iterator = _.bind(iterator, context); return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); } var length = obj.length; if (length !== +length) { var keys = _.keys(obj); length = keys.length; } each(obj, function(value, index, list) { index = keys ? keys[--length] : --length; if (!initial) { memo = obj[index]; initial = true; } else { memo = iterator.call(context, memo, obj[index], index, list); } }); if (!initial) throw new TypeError(reduceError); return memo; }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, iterator, context) { var result; any(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) { result = value; return true; } }); return result; }; // Return all the elements that pass a truth test. // Delegates to **ECMAScript 5**'s native `filter` if available. // Aliased as `select`. _.filter = _.select = function(obj, iterator, context) { var results = []; if (obj == null) return results; if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); each(obj, function(value, index, list) { if (iterator.call(context, value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, iterator, context) { return _.filter(obj, function(value, index, list) { return !iterator.call(context, value, index, list); }, context); }; // Determine whether all of the elements match a truth test. // Delegates to **ECMAScript 5**'s native `every` if available. // Aliased as `all`. _.every = _.all = function(obj, iterator, context) { iterator || (iterator = _.identity); var result = true; if (obj == null) return result; if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); each(obj, function(value, index, list) { if (!(result = result && iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // Determine if at least one element in the object matches a truth test. // Delegates to **ECMAScript 5**'s native `some` if available. // Aliased as `any`. var any = _.some = _.any = function(obj, iterator, context) { iterator || (iterator = _.identity); var result = false; if (obj == null) return result; if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); each(obj, function(value, index, list) { if (result || (result = iterator.call(context, value, index, list))) return breaker; }); return !!result; }; // Determine if the array or object contains a given value (using `===`). // Aliased as `include`. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; return any(obj, function(value) { return value === target; }); }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, function(value){ return value[key]; }); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs, first) { if (_.isEmpty(attrs)) return first ? void 0 : []; return _[first ? 'find' : 'filter'](obj, function(value) { for (var key in attrs) { if (attrs[key] !== value[key]) return false; } return true; }); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.where(obj, attrs, true); }; // Return the maximum element or (element-based computation). // Can't optimize arrays of integers longer than 65,535 elements. // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) _.max = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.max.apply(Math, obj); } if (!iterator && _.isEmpty(obj)) return -Infinity; var result = {computed : -Infinity, value: -Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed > result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Return the minimum element (or element-based computation). _.min = function(obj, iterator, context) { if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { return Math.min.apply(Math, obj); } if (!iterator && _.isEmpty(obj)) return Infinity; var result = {computed : Infinity, value: Infinity}; each(obj, function(value, index, list) { var computed = iterator ? iterator.call(context, value, index, list) : value; computed < result.computed && (result = {value : value, computed : computed}); }); return result.value; }; // Shuffle an array, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var rand; var index = 0; var shuffled = []; each(obj, function(value) { rand = _.random(index++); shuffled[index - 1] = shuffled[rand]; shuffled[rand] = value; }); return shuffled; }; // Sample **n** random values from an array. // If **n** is not specified, returns a single random element from the array. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (arguments.length < 2 || guard) { return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // An internal function to generate lookup iterators. var lookupIterator = function(value) { return _.isFunction(value) ? value : function(obj){ return obj[value]; }; }; // Sort the object's values by a criterion produced by an iterator. _.sortBy = function(obj, value, context) { var iterator = lookupIterator(value); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iterator.call(context, value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, value, context) { var result = {}; var iterator = value == null ? _.identity : lookupIterator(value); each(obj, function(value, index) { var key = iterator.call(context, value, index, obj); behavior(result, key, value); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, key, value) { (_.has(result, key) ? result[key] : (result[key] = [])).push(value); }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, key, value) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, key) { _.has(result, key) ? result[key]++ : result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iterator, context) { iterator = iterator == null ? _.identity : lookupIterator(iterator); var value = iterator.call(context, obj); var low = 0, high = array.length; while (low < high) { var mid = (low + high) >>> 1; iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; return (n == null) || guard ? array[0] : slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if ((n == null) || guard) { return array[array.length - 1]; } else { return slice.call(array, Math.max(array.length - n, 0)); } }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, (n == null) || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } each(input, function(value) { if (_.isArray(value) || _.isArguments(value)) { shallow ? push.apply(output, value) : flatten(value, shallow, output); } else { output.push(value); } }); return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iterator, context) { if (_.isFunction(isSorted)) { context = iterator; iterator = isSorted; isSorted = false; } var initial = iterator ? _.map(array, iterator, context) : array; var results = []; var seen = []; each(initial, function(value, index) { if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { seen.push(value); results.push(array[index]); } }); return results; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(_.flatten(arguments, true)); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { var rest = slice.call(arguments, 1); return _.filter(_.uniq(array), function(item) { return _.every(rest, function(other) { return _.indexOf(other, item) >= 0; }); }); }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function() { var length = _.max(_.pluck(arguments, "length").concat(0)); var results = new Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, '' + i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), // we need this function. Return the position of the first occurrence of an // item in an array, or -1 if the item is not included in the array. // Delegates to **ECMAScript 5**'s native `indexOf` if available. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); for (; i < length; i++) if (array[i] === item) return i; return -1; }; // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var hasIndex = from != null; if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); } var i = (hasIndex ? from : array.length); while (i--) if (array[i] === item) return i; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = arguments[2] || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var idx = 0; var range = new Array(length); while(idx < length) { range[idx++] = start; start += step; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError; args = slice.call(arguments, 2); return bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); ctor.prototype = func.prototype; var self = new ctor; ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (Object(result) === result) return result; return self; }; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _.partial = function(func) { var args = slice.call(arguments, 1); return function() { return func.apply(this, args.concat(slice.call(arguments))); }; }; // Bind all of an object's methods to that object. Useful for ensuring that // all callbacks defined on an object belong to it. _.bindAll = function(obj) { var funcs = slice.call(arguments, 1); if (funcs.length === 0) throw new Error("bindAll must be passed function names"); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memo = {}; hasher || (hasher = _.identity); return function() { var key = hasher.apply(this, arguments); return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); }; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; options || (options = {}); var later = function() { previous = options.leading === false ? 0 : new Date; timeout = null; result = func.apply(context, args); }; return function() { var now = new Date; if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; return function() { context = this; args = arguments; timestamp = new Date(); var later = function() { var last = (new Date()) - timestamp; if (last < wait) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) result = func.apply(context, args); } }; var callNow = immediate && !timeout; if (!timeout) { timeout = setTimeout(later, wait); } if (callNow) result = func.apply(context, args); return result; }; }; // Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = function(func) { var ran = false, memo; return function() { if (ran) return memo; ran = true; memo = func.apply(this, arguments); func = null; return memo; }; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return function() { var args = [func]; push.apply(args, arguments); return wrapper.apply(this, args); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var funcs = arguments; return function() { var args = arguments; for (var i = funcs.length - 1; i >= 0; i--) { args = [funcs[i].apply(this, args)]; } return args[0]; }; }; // Returns a function that will only be executed after being called N times. _.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; }; // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = nativeKeys || function(obj) { if (obj !== Object(obj)) throw new TypeError('Invalid object'); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; var values = new Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; var pairs = new Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { result[obj[keys[i]]] = keys[i]; } return result; }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { obj[prop] = source[prop]; } } }); return obj; }; // Return a copy of the object only containing the whitelisted properties. _.pick = function(obj) { var copy = {}; var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); each(keys, function(key) { if (key in obj) copy[key] = obj[key]; }); return copy; }; // Return a copy of the object without the blacklisted properties. _.omit = function(obj) { var copy = {}; var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); for (var key in obj) { if (!_.contains(keys, key)) copy[key] = obj[key]; } return copy; }; // Fill in a given object with default properties. _.defaults = function(obj) { each(slice.call(arguments, 1), function(source) { if (source) { for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } }); return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { if (!_.isObject(obj)) return obj; return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a == 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className != toString.call(b)) return false; switch (className) { // Strings, numbers, dates, and booleans are compared by value. case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return a == String(b); case '[object Number]': // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for // other numeric values. return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a == +b; // RegExps are compared by their source patterns and flags. case '[object RegExp]': return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase; } if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] == a) return bStack[length] == b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && _.isFunction(bCtor) && (bCtor instanceof bCtor))) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size = 0, result = true; // Recursively compare objects and arrays. if (className == '[object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; result = size == b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { if (!(result = eq(a[size], b[size], aStack, bStack))) break; } } } else { // Deep compare objects. for (var key in a) { if (_.has(a, key)) { // Count the expected number of properties. size++; // Deep compare each member. if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; } } // Ensure that both objects contain the same number of properties. if (result) { for (key in b) { if (_.has(b, key) && !(size--)) break; } result = !size; } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { return eq(a, b, [], []); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return toString.call(obj) == '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { return obj === Object(obj); }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) == '[object ' + name + ']'; }; }); // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { return !!(obj && _.has(obj, 'callee')); }; } // Optimize `isFunction` if appropriate. if (typeof (/./) !== 'function') { _.isFunction = function(obj) { return typeof obj === 'function'; }; } // Is a given object a finite number? _.isFinite = function(obj) { return isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? (NaN is the only number which does not equal itself). _.isNaN = function(obj) { return _.isNumber(obj) && obj != +obj; }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return obj === void 0; }; // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). _.has = function(obj, key) { return hasOwnProperty.call(obj, key); }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iterators. _.identity = function(value) { return value; }; // Run a function **n** times. _.times = function(n, iterator, context) { var accum = Array(Math.max(0, n)); for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); return accum; }; // Return a random integer between min and max (inclusive). _.random = function(min, max) { if (max == null) { max = min; min = 0; } return min + Math.floor(Math.random() * (max - min + 1)); }; // List of HTML entities for escaping. var entityMap = { escape: { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' } }; entityMap.unescape = _.invert(entityMap.escape); // Regexes containing the keys and values listed immediately above. var entityRegexes = { escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') }; // Functions for escaping and unescaping strings to/from HTML interpolation. _.each(['escape', 'unescape'], function(method) { _[method] = function(string) { if (string == null) return ''; return ('' + string).replace(entityRegexes[method], function(match) { return entityMap[method][match]; }); }; }); // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. _.result = function(object, property) { if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? value.call(object) : value; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result.call(this, func.apply(_, args)); }; }); }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/; // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\t': 't', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. _.template = function(text, data, settings) { var render; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. var matcher = new RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset) .replace(escaper, function(match) { return '\\' + escapes[match]; }); if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; } if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } index = offset + match.length; return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + "return __p;\n"; try { render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } if (data) return render(data, _); var template = function(data) { return render.call(this, data, _); }; // Provide the compiled function source as a convenience for precompilation. template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; return template; }; // Add a "chain" function, which will delegate to the wrapper. _.chain = function(obj) { return _(obj).chain(); }; // OOP // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. var result = function(obj) { return this._chain ? _(obj).chain() : obj; }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; return result.call(this, obj); }; }); // Add all accessor Array functions to the wrapper. each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result.call(this, method.apply(this._wrapped, arguments)); }; }); _.extend(_.prototype, { // Start chaining a wrapped Underscore object. chain: function() { this._chain = true; return this; }, // Extracts the result from a wrapped and chained object. value: function() { return this._wrapped; } }); }).call(this); },{}],15:[function(require,module,exports){ (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `global` on the server. var root = this; // Save the previous value of the `humanize` variable. var previousHumanize = root.humanize; var humanize = {}; if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = humanize; } exports.humanize = humanize; } else { if (typeof define === 'function' && define.amd) { define('humanize', function() { return humanize; }); } root.humanize = humanize; } humanize.noConflict = function() { root.humanize = previousHumanize; return this; }; humanize.pad = function(str, count, padChar, type) { str += ''; if (!padChar) { padChar = ' '; } else if (padChar.length > 1) { padChar = padChar.charAt(0); } type = (type === undefined) ? 'left' : 'right'; if (type === 'right') { while (str.length < count) { str = str + padChar; } } else { // default to left while (str.length < count) { str = padChar + str; } } return str; }; // gets current unix time humanize.time = function() { return new Date().getTime() / 1000; }; /** * PHP-inspired date */ /* jan feb mar apr may jun jul aug sep oct nov dec */ var dayTableCommon = [ 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ]; var dayTableLeap = [ 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 ]; // var mtable_common[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; // static int ml_table_leap[13] = { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; humanize.date = function(format, timestamp) { var jsdate = ((timestamp === undefined) ? new Date() : // Not provided (timestamp instanceof Date) ? new Date(timestamp) : // JS Date() new Date(timestamp * 1000) // UNIX timestamp (auto-convert to int) ); var formatChr = /\\?([a-z])/gi; var formatChrCb = function (t, s) { return f[t] ? f[t]() : s; }; var shortDayTxt = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; var monthTxt = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; var f = { /* Day */ // Day of month w/leading 0; 01..31 d: function () { return humanize.pad(f.j(), 2, '0'); }, // Shorthand day name; Mon..Sun D: function () { return f.l().slice(0, 3); }, // Day of month; 1..31 j: function () { return jsdate.getDate(); }, // Full day name; Monday..Sunday l: function () { return shortDayTxt[f.w()]; }, // ISO-8601 day of week; 1[Mon]..7[Sun] N: function () { return f.w() || 7; }, // Ordinal suffix for day of month; st, nd, rd, th S: function () { var j = f.j(); return j > 4 && j < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[j % 10] || 'th'; }, // Day of week; 0[Sun]..6[Sat] w: function () { return jsdate.getDay(); }, // Day of year; 0..365 z: function () { return (f.L() ? dayTableLeap[f.n()] : dayTableCommon[f.n()]) + f.j() - 1; }, /* Week */ // ISO-8601 week number W: function () { // days between midweek of this week and jan 4 // (f.z() - f.N() + 1 + 3.5) - 3 var midWeekDaysFromJan4 = f.z() - f.N() + 1.5; // 1 + number of weeks + rounded week return humanize.pad(1 + Math.floor(Math.abs(midWeekDaysFromJan4) / 7) + (midWeekDaysFromJan4 % 7 > 3.5 ? 1 : 0), 2, '0'); }, /* Month */ // Full month name; January..December F: function () { return monthTxt[jsdate.getMonth()]; }, // Month w/leading 0; 01..12 m: function () { return humanize.pad(f.n(), 2, '0'); }, // Shorthand month name; Jan..Dec M: function () { return f.F().slice(0, 3); }, // Month; 1..12 n: function () { return jsdate.getMonth() + 1; }, // Days in month; 28..31 t: function () { return (new Date(f.Y(), f.n(), 0)).getDate(); }, /* Year */ // Is leap year?; 0 or 1 L: function () { return new Date(f.Y(), 1, 29).getMonth() === 1 ? 1 : 0; }, // ISO-8601 year o: function () { var n = f.n(); var W = f.W(); return f.Y() + (n === 12 && W < 9 ? -1 : n === 1 && W > 9); }, // Full year; e.g. 1980..2010 Y: function () { return jsdate.getFullYear(); }, // Last two digits of year; 00..99 y: function () { return (String(f.Y())).slice(-2); }, /* Time */ // am or pm a: function () { return jsdate.getHours() > 11 ? 'pm' : 'am'; }, // AM or PM A: function () { return f.a().toUpperCase(); }, // Swatch Internet time; 000..999 B: function () { var unixTime = jsdate.getTime() / 1000; var secondsPassedToday = unixTime % 86400 + 3600; // since it's based off of UTC+1 if (secondsPassedToday < 0) { secondsPassedToday += 86400; } var beats = ((secondsPassedToday) / 86.4) % 1000; if (unixTime < 0) { return Math.ceil(beats); } return Math.floor(beats); }, // 12-Hours; 1..12 g: function () { return f.G() % 12 || 12; }, // 24-Hours; 0..23 G: function () { return jsdate.getHours(); }, // 12-Hours w/leading 0; 01..12 h: function () { return humanize.pad(f.g(), 2, '0'); }, // 24-Hours w/leading 0; 00..23 H: function () { return humanize.pad(f.G(), 2, '0'); }, // Minutes w/leading 0; 00..59 i: function () { return humanize.pad(jsdate.getMinutes(), 2, '0'); }, // Seconds w/leading 0; 00..59 s: function () { return humanize.pad(jsdate.getSeconds(), 2, '0'); }, // Microseconds; 000000-999000 u: function () { return humanize.pad(jsdate.getMilliseconds() * 1000, 6, '0'); }, // Whether or not the date is in daylight savings time /* I: function () { // Compares Jan 1 minus Jan 1 UTC to Jul 1 minus Jul 1 UTC. // If they are not equal, then DST is observed. var Y = f.Y(); return 0 + ((new Date(Y, 0) - Date.UTC(Y, 0)) !== (new Date(Y, 6) - Date.UTC(Y, 6))); }, */ // Difference to GMT in hour format; e.g. +0200 O: function () { var tzo = jsdate.getTimezoneOffset(); var tzoNum = Math.abs(tzo); return (tzo > 0 ? '-' : '+') + humanize.pad(Math.floor(tzoNum / 60) * 100 + tzoNum % 60, 4, '0'); }, // Difference to GMT w/colon; e.g. +02:00 P: function () { var O = f.O(); return (O.substr(0, 3) + ':' + O.substr(3, 2)); }, // Timezone offset in seconds (-43200..50400) Z: function () { return -jsdate.getTimezoneOffset() * 60; }, // Full Date/Time, ISO-8601 date c: function () { return 'Y-m-d\\TH:i:sP'.replace(formatChr, formatChrCb); }, // RFC 2822 r: function () { return 'D, d M Y H:i:s O'.replace(formatChr, formatChrCb); }, // Seconds since UNIX epoch U: function () { return jsdate.getTime() / 1000 || 0; } }; return format.replace(formatChr, formatChrCb); }; /** * format number by adding thousands separaters and significant digits while rounding */ humanize.numberFormat = function(number, decimals, decPoint, thousandsSep) { decimals = isNaN(decimals) ? 2 : Math.abs(decimals); decPoint = (decPoint === undefined) ? '.' : decPoint; thousandsSep = (thousandsSep === undefined) ? ',' : thousandsSep; var sign = number < 0 ? '-' : ''; number = Math.abs(+number || 0); var intPart = parseInt(number.toFixed(decimals), 10) + ''; var j = intPart.length > 3 ? intPart.length % 3 : 0; 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) : ''); }; /** * For dates that are the current day or within one day, return 'today', 'tomorrow' or 'yesterday', as appropriate. * Otherwise, format the date using the passed in format string. * * Examples (when 'today' is 17 Feb 2007): * 16 Feb 2007 becomes yesterday. * 17 Feb 2007 becomes today. * 18 Feb 2007 becomes tomorrow. * Any other day is formatted according to given argument or the DATE_FORMAT setting if no argument is given. */ humanize.naturalDay = function(timestamp, format) { timestamp = (timestamp === undefined) ? humanize.time() : timestamp; format = (format === undefined) ? 'Y-m-d' : format; var oneDay = 86400; var d = new Date(); var today = (new Date(d.getFullYear(), d.getMonth(), d.getDate())).getTime() / 1000; if (timestamp < today && timestamp >= today - oneDay) { return 'yesterday'; } else if (timestamp >= today && timestamp < today + oneDay) { return 'today'; } else if (timestamp >= today + oneDay && timestamp < today + 2 * oneDay) { return 'tomorrow'; } return humanize.date(format, timestamp); }; /** * returns a string representing how many seconds, minutes or hours ago it was or will be in the future * Will always return a relative time, most granular of seconds to least granular of years. See unit tests for more details */ humanize.relativeTime = function(timestamp) { timestamp = (timestamp === undefined) ? humanize.time() : timestamp; var currTime = humanize.time(); var timeDiff = currTime - timestamp; // within 2 seconds if (timeDiff < 2 && timeDiff > -2) { return (timeDiff >= 0 ? 'just ' : '') + 'now'; } // within a minute if (timeDiff < 60 && timeDiff > -60) { return (timeDiff >= 0 ? Math.floor(timeDiff) + ' seconds ago' : 'in ' + Math.floor(-timeDiff) + ' seconds'); } // within 2 minutes if (timeDiff < 120 && timeDiff > -120) { return (timeDiff >= 0 ? 'about a minute ago' : 'in about a minute'); } // within an hour if (timeDiff < 3600 && timeDiff > -3600) { return (timeDiff >= 0 ? Math.floor(timeDiff / 60) + ' minutes ago' : 'in ' + Math.floor(-timeDiff / 60) + ' minutes'); } // within 2 hours if (timeDiff < 7200 && timeDiff > -7200) { return (timeDiff >= 0 ? 'about an hour ago' : 'in about an hour'); } // within 24 hours if (timeDiff < 86400 && timeDiff > -86400) { return (timeDiff >= 0 ? Math.floor(timeDiff / 3600) + ' hours ago' : 'in ' + Math.floor(-timeDiff / 3600) + ' hours'); } // within 2 days var days2 = 2 * 86400; if (timeDiff < days2 && timeDiff > -days2) { return (timeDiff >= 0 ? '1 day ago' : 'in 1 day'); } // within 29 days var days29 = 29 * 86400; if (timeDiff < days29 && timeDiff > -days29) { return (timeDiff >= 0 ? Math.floor(timeDiff / 86400) + ' days ago' : 'in ' + Math.floor(-timeDiff / 86400) + ' days'); } // within 60 days var days60 = 60 * 86400; if (timeDiff < days60 && timeDiff > -days60) { return (timeDiff >= 0 ? 'about a month ago' : 'in about a month'); } var currTimeYears = parseInt(humanize.date('Y', currTime), 10); var timestampYears = parseInt(humanize.date('Y', timestamp), 10); var currTimeMonths = currTimeYears * 12 + parseInt(humanize.date('n', currTime), 10); var timestampMonths = timestampYears * 12 + parseInt(humanize.date('n', timestamp), 10); // within a year var monthDiff = currTimeMonths - timestampMonths; if (monthDiff < 12 && monthDiff > -12) { return (monthDiff >= 0 ? monthDiff + ' months ago' : 'in ' + (-monthDiff) + ' months'); } var yearDiff = currTimeYears - timestampYears; if (yearDiff < 2 && yearDiff > -2) { return (yearDiff >= 0 ? 'a year ago' : 'in a year'); } return (yearDiff >= 0 ? yearDiff + ' years ago' : 'in ' + (-yearDiff) + ' years'); }; /** * Converts an integer to its ordinal as a string. * * 1 becomes 1st * 2 becomes 2nd * 3 becomes 3rd etc */ humanize.ordinal = function(number) { number = parseInt(number, 10); number = isNaN(number) ? 0 : number; var sign = number < 0 ? '-' : ''; number = Math.abs(number); var tens = number % 100; return sign + number + (tens > 4 && tens < 21 ? 'th' : {1: 'st', 2: 'nd', 3: 'rd'}[number % 10] || 'th'); }; /** * Formats the value like a 'human-readable' file size (i.e. '13 KB', '4.1 MB', '102 bytes', etc). * * For example: * If value is 123456789, the output would be 117.7 MB. */ humanize.filesize = function(filesize, kilo, decimals, decPoint, thousandsSep, suffixSep) { kilo = (kilo === undefined) ? 1024 : kilo; if (filesize <= 0) { return '0 bytes'; } if (filesize < kilo && decimals === undefined) { decimals = 0; } if (suffixSep === undefined) { suffixSep = ' '; } return humanize.intword(filesize, ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'], kilo, decimals, decPoint, thousandsSep, suffixSep); }; /** * Formats the value like a 'human-readable' number (i.e. '13 K', '4.1 M', '102', etc). * * For example: * If value is 123456789, the output would be 117.7 M. */ humanize.intword = function(number, units, kilo, decimals, decPoint, thousandsSep, suffixSep) { var humanized, unit; units = units || ['', 'K', 'M', 'B', 'T'], unit = units.length - 1, kilo = kilo || 1000, decimals = isNaN(decimals) ? 2 : Math.abs(decimals), decPoint = decPoint || '.', thousandsSep = thousandsSep || ',', suffixSep = suffixSep || ''; for (var i=0; i < units.length; i++) { if (number < Math.pow(kilo, i+1)) { unit = i; break; } } humanized = number / Math.pow(kilo, unit); var suffix = units[unit] ? suffixSep + units[unit] : ''; return humanize.numberFormat(humanized, decimals, decPoint, thousandsSep) + suffix; }; /** * Replaces line breaks in plain text with appropriate HTML * A single newline becomes an HTML line break (
) and a new line followed by a blank line becomes a paragraph break (

). * * For example: * If value is Joel\nis a\n\nslug, the output will be

Joel
is a

slug

*/ humanize.linebreaks = function(str) { // remove beginning and ending newlines str = str.replace(/^([\n|\r]*)/, ''); str = str.replace(/([\n|\r]*)$/, ''); // normalize all to \n str = str.replace(/(\r\n|\n|\r)/g, "\n"); // any consecutive new lines more than 2 gets turned into p tags str = str.replace(/(\n{2,})/g, '

'); // any that are singletons get turned into br str = str.replace(/\n/g, '
'); return '

' + str + '

'; }; /** * Converts all newlines in a piece of plain text to HTML line breaks (
). */ humanize.nl2br = function(str) { return str.replace(/(\r\n|\n|\r)/g, '
'); }; /** * Truncates a string if it is longer than the specified number of characters. * Truncated strings will end with a translatable ellipsis sequence ('…'). */ humanize.truncatechars = function(string, length) { if (string.length <= length) { return string; } return string.substr(0, length) + '…'; }; /** * Truncates a string after a certain number of words. * Newlines within the string will be removed. */ humanize.truncatewords = function(string, numWords) { var words = string.split(' '); if (words.length < numWords) { return string; } return words.slice(0, numWords).join(' ') + '…'; }; }).call(this); },{}],16:[function(require,module,exports){ // Underscore.js 1.7.0 // http://underscorejs.org // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function() { // Baseline setup // -------------- // Establish the root object, `window` in the browser, or `exports` on the server. var root = this; // Save the previous value of the `_` variable. var previousUnderscore = root._; // Save bytes in the minified (but not gzipped) version: var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; // Create quick reference variables for speed access to core prototypes. var push = ArrayProto.push, slice = ArrayProto.slice, concat = ArrayProto.concat, toString = ObjProto.toString, hasOwnProperty = ObjProto.hasOwnProperty; // All **ECMAScript 5** native function implementations that we hope to use // are declared here. var nativeIsArray = Array.isArray, nativeKeys = Object.keys, nativeBind = FuncProto.bind; // Create a safe reference to the Underscore object for use below. var _ = function(obj) { if (obj instanceof _) return obj; if (!(this instanceof _)) return new _(obj); this._wrapped = obj; }; // Export the Underscore object for **Node.js**, with // backwards-compatibility for the old `require()` API. If we're in // the browser, add `_` as a global object. if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = _; } exports._ = _; } else { root._ = _; } // Current version. _.VERSION = '1.7.0'; // Internal function that returns an efficient (for current engines) version // of the passed-in callback, to be repeatedly applied in other Underscore // functions. var createCallback = function(func, context, argCount) { if (context === void 0) return func; switch (argCount == null ? 3 : argCount) { case 1: return function(value) { return func.call(context, value); }; case 2: return function(value, other) { return func.call(context, value, other); }; case 3: return function(value, index, collection) { return func.call(context, value, index, collection); }; case 4: return function(accumulator, value, index, collection) { return func.call(context, accumulator, value, index, collection); }; } return function() { return func.apply(context, arguments); }; }; // A mostly-internal function to generate callbacks that can be applied // to each element in a collection, returning the desired result — either // identity, an arbitrary callback, a property matcher, or a property accessor. _.iteratee = function(value, context, argCount) { if (value == null) return _.identity; if (_.isFunction(value)) return createCallback(value, context, argCount); if (_.isObject(value)) return _.matches(value); return _.property(value); }; // Collection Functions // -------------------- // The cornerstone, an `each` implementation, aka `forEach`. // Handles raw objects in addition to array-likes. Treats all // sparse array-likes as if they were dense. _.each = _.forEach = function(obj, iteratee, context) { if (obj == null) return obj; iteratee = createCallback(iteratee, context); var i, length = obj.length; if (length === +length) { for (i = 0; i < length; i++) { iteratee(obj[i], i, obj); } } else { var keys = _.keys(obj); for (i = 0, length = keys.length; i < length; i++) { iteratee(obj[keys[i]], keys[i], obj); } } return obj; }; // Return the results of applying the iteratee to each element. _.map = _.collect = function(obj, iteratee, context) { if (obj == null) return []; iteratee = _.iteratee(iteratee, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, results = Array(length), currentKey; for (var index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results; }; var reduceError = 'Reduce of empty array with no initial value'; // **Reduce** builds up a single result from a list of values, aka `inject`, // or `foldl`. _.reduce = _.foldl = _.inject = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index = 0, currentKey; if (arguments.length < 3) { if (!length) throw new TypeError(reduceError); memo = obj[keys ? keys[index++] : index++]; } for (; index < length; index++) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // The right-associative version of reduce, also known as `foldr`. _.reduceRight = _.foldr = function(obj, iteratee, memo, context) { if (obj == null) obj = []; iteratee = createCallback(iteratee, context, 4); var keys = obj.length !== + obj.length && _.keys(obj), index = (keys || obj).length, currentKey; if (arguments.length < 3) { if (!index) throw new TypeError(reduceError); memo = obj[keys ? keys[--index] : --index]; } while (index--) { currentKey = keys ? keys[index] : index; memo = iteratee(memo, obj[currentKey], currentKey, obj); } return memo; }; // Return the first value which passes a truth test. Aliased as `detect`. _.find = _.detect = function(obj, predicate, context) { var result; predicate = _.iteratee(predicate, context); _.some(obj, function(value, index, list) { if (predicate(value, index, list)) { result = value; return true; } }); return result; }; // Return all the elements that pass a truth test. // Aliased as `select`. _.filter = _.select = function(obj, predicate, context) { var results = []; if (obj == null) return results; predicate = _.iteratee(predicate, context); _.each(obj, function(value, index, list) { if (predicate(value, index, list)) results.push(value); }); return results; }; // Return all the elements for which a truth test fails. _.reject = function(obj, predicate, context) { return _.filter(obj, _.negate(_.iteratee(predicate)), context); }; // Determine whether all of the elements match a truth test. // Aliased as `all`. _.every = _.all = function(obj, predicate, context) { if (obj == null) return true; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (!predicate(obj[currentKey], currentKey, obj)) return false; } return true; }; // Determine if at least one element in the object matches a truth test. // Aliased as `any`. _.some = _.any = function(obj, predicate, context) { if (obj == null) return false; predicate = _.iteratee(predicate, context); var keys = obj.length !== +obj.length && _.keys(obj), length = (keys || obj).length, index, currentKey; for (index = 0; index < length; index++) { currentKey = keys ? keys[index] : index; if (predicate(obj[currentKey], currentKey, obj)) return true; } return false; }; // Determine if the array or object contains a given value (using `===`). // Aliased as `include`. _.contains = _.include = function(obj, target) { if (obj == null) return false; if (obj.length !== +obj.length) obj = _.values(obj); return _.indexOf(obj, target) >= 0; }; // Invoke a method (with arguments) on every item in a collection. _.invoke = function(obj, method) { var args = slice.call(arguments, 2); var isFunc = _.isFunction(method); return _.map(obj, function(value) { return (isFunc ? method : value[method]).apply(value, args); }); }; // Convenience version of a common use case of `map`: fetching a property. _.pluck = function(obj, key) { return _.map(obj, _.property(key)); }; // Convenience version of a common use case of `filter`: selecting only objects // containing specific `key:value` pairs. _.where = function(obj, attrs) { return _.filter(obj, _.matches(attrs)); }; // Convenience version of a common use case of `find`: getting the first object // containing specific `key:value` pairs. _.findWhere = function(obj, attrs) { return _.find(obj, _.matches(attrs)); }; // Return the maximum element (or element-based computation). _.max = function(obj, iteratee, context) { var result = -Infinity, lastComputed = -Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value > result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed > lastComputed || computed === -Infinity && result === -Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Return the minimum element (or element-based computation). _.min = function(obj, iteratee, context) { var result = Infinity, lastComputed = Infinity, value, computed; if (iteratee == null && obj != null) { obj = obj.length === +obj.length ? obj : _.values(obj); for (var i = 0, length = obj.length; i < length; i++) { value = obj[i]; if (value < result) { result = value; } } } else { iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index, list) { computed = iteratee(value, index, list); if (computed < lastComputed || computed === Infinity && result === Infinity) { result = value; lastComputed = computed; } }); } return result; }; // Shuffle a collection, using the modern version of the // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). _.shuffle = function(obj) { var set = obj && obj.length === +obj.length ? obj : _.values(obj); var length = set.length; var shuffled = Array(length); for (var index = 0, rand; index < length; index++) { rand = _.random(0, index); if (rand !== index) shuffled[index] = shuffled[rand]; shuffled[rand] = set[index]; } return shuffled; }; // Sample **n** random values from a collection. // If **n** is not specified, returns a single random element. // The internal `guard` argument allows it to work with `map`. _.sample = function(obj, n, guard) { if (n == null || guard) { if (obj.length !== +obj.length) obj = _.values(obj); return obj[_.random(obj.length - 1)]; } return _.shuffle(obj).slice(0, Math.max(0, n)); }; // Sort the object's values by a criterion produced by an iteratee. _.sortBy = function(obj, iteratee, context) { iteratee = _.iteratee(iteratee, context); return _.pluck(_.map(obj, function(value, index, list) { return { value: value, index: index, criteria: iteratee(value, index, list) }; }).sort(function(left, right) { var a = left.criteria; var b = right.criteria; if (a !== b) { if (a > b || a === void 0) return 1; if (a < b || b === void 0) return -1; } return left.index - right.index; }), 'value'); }; // An internal function used for aggregate "group by" operations. var group = function(behavior) { return function(obj, iteratee, context) { var result = {}; iteratee = _.iteratee(iteratee, context); _.each(obj, function(value, index) { var key = iteratee(value, index, obj); behavior(result, value, key); }); return result; }; }; // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = group(function(result, value, key) { if (_.has(result, key)) result[key].push(value); else result[key] = [value]; }); // Indexes the object's values by a criterion, similar to `groupBy`, but for // when you know that your index values will be unique. _.indexBy = group(function(result, value, key) { result[key] = value; }); // Counts instances of an object that group by a certain criterion. Pass // either a string attribute to count by, or a function that returns the // criterion. _.countBy = group(function(result, value, key) { if (_.has(result, key)) result[key]++; else result[key] = 1; }); // Use a comparator function to figure out the smallest index at which // an object should be inserted so as to maintain order. Uses binary search. _.sortedIndex = function(array, obj, iteratee, context) { iteratee = _.iteratee(iteratee, context, 1); var value = iteratee(obj); var low = 0, high = array.length; while (low < high) { var mid = low + high >>> 1; if (iteratee(array[mid]) < value) low = mid + 1; else high = mid; } return low; }; // Safely create a real, live array from anything iterable. _.toArray = function(obj) { if (!obj) return []; if (_.isArray(obj)) return slice.call(obj); if (obj.length === +obj.length) return _.map(obj, _.identity); return _.values(obj); }; // Return the number of elements in an object. _.size = function(obj) { if (obj == null) return 0; return obj.length === +obj.length ? obj.length : _.keys(obj).length; }; // Split a collection into two arrays: one whose elements all satisfy the given // predicate, and one whose elements all do not satisfy the predicate. _.partition = function(obj, predicate, context) { predicate = _.iteratee(predicate, context); var pass = [], fail = []; _.each(obj, function(value, key, obj) { (predicate(value, key, obj) ? pass : fail).push(value); }); return [pass, fail]; }; // Array Functions // --------------- // Get the first element of an array. Passing **n** will return the first N // values in the array. Aliased as `head` and `take`. The **guard** check // allows it to work with `_.map`. _.first = _.head = _.take = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[0]; if (n < 0) return []; return slice.call(array, 0, n); }; // Returns everything but the last entry of the array. Especially useful on // the arguments object. Passing **n** will return all the values in // the array, excluding the last N. The **guard** check allows it to work with // `_.map`. _.initial = function(array, n, guard) { return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n))); }; // Get the last element of an array. Passing **n** will return the last N // values in the array. The **guard** check allows it to work with `_.map`. _.last = function(array, n, guard) { if (array == null) return void 0; if (n == null || guard) return array[array.length - 1]; return slice.call(array, Math.max(array.length - n, 0)); }; // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. // Especially useful on the arguments object. Passing an **n** will return // the rest N values in the array. The **guard** // check allows it to work with `_.map`. _.rest = _.tail = _.drop = function(array, n, guard) { return slice.call(array, n == null || guard ? 1 : n); }; // Trim out all falsy values from an array. _.compact = function(array) { return _.filter(array, _.identity); }; // Internal implementation of a recursive `flatten` function. var flatten = function(input, shallow, strict, output) { if (shallow && _.every(input, _.isArray)) { return concat.apply(output, input); } for (var i = 0, length = input.length; i < length; i++) { var value = input[i]; if (!_.isArray(value) && !_.isArguments(value)) { if (!strict) output.push(value); } else if (shallow) { push.apply(output, value); } else { flatten(value, shallow, strict, output); } } return output; }; // Flatten out an array, either recursively (by default), or just one level. _.flatten = function(array, shallow) { return flatten(array, shallow, false, []); }; // Return a version of the array that does not contain the specified value(s). _.without = function(array) { return _.difference(array, slice.call(arguments, 1)); }; // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. // Aliased as `unique`. _.uniq = _.unique = function(array, isSorted, iteratee, context) { if (array == null) return []; if (!_.isBoolean(isSorted)) { context = iteratee; iteratee = isSorted; isSorted = false; } if (iteratee != null) iteratee = _.iteratee(iteratee, context); var result = []; var seen = []; for (var i = 0, length = array.length; i < length; i++) { var value = array[i]; if (isSorted) { if (!i || seen !== value) result.push(value); seen = value; } else if (iteratee) { var computed = iteratee(value, i, array); if (_.indexOf(seen, computed) < 0) { seen.push(computed); result.push(value); } } else if (_.indexOf(result, value) < 0) { result.push(value); } } return result; }; // Produce an array that contains the union: each distinct element from all of // the passed-in arrays. _.union = function() { return _.uniq(flatten(arguments, true, true, [])); }; // Produce an array that contains every item shared between all the // passed-in arrays. _.intersection = function(array) { if (array == null) return []; var result = []; var argsLength = arguments.length; for (var i = 0, length = array.length; i < length; i++) { var item = array[i]; if (_.contains(result, item)) continue; for (var j = 1; j < argsLength; j++) { if (!_.contains(arguments[j], item)) break; } if (j === argsLength) result.push(item); } return result; }; // Take the difference between one array and a number of other arrays. // Only the elements present in just the first array will remain. _.difference = function(array) { var rest = flatten(slice.call(arguments, 1), true, true, []); return _.filter(array, function(value){ return !_.contains(rest, value); }); }; // Zip together multiple lists into a single array -- elements that share // an index go together. _.zip = function(array) { if (array == null) return []; var length = _.max(arguments, 'length').length; var results = Array(length); for (var i = 0; i < length; i++) { results[i] = _.pluck(arguments, i); } return results; }; // Converts lists into objects. Pass either a single array of `[key, value]` // pairs, or two parallel arrays of the same length -- one of keys, and one of // the corresponding values. _.object = function(list, values) { if (list == null) return {}; var result = {}; for (var i = 0, length = list.length; i < length; i++) { if (values) { result[list[i]] = values[i]; } else { result[list[i][0]] = list[i][1]; } } return result; }; // Return the position of the first occurrence of an item in an array, // or -1 if the item is not included in the array. // If the array is large and already in sort order, pass `true` // for **isSorted** to use binary search. _.indexOf = function(array, item, isSorted) { if (array == null) return -1; var i = 0, length = array.length; if (isSorted) { if (typeof isSorted == 'number') { i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted; } else { i = _.sortedIndex(array, item); return array[i] === item ? i : -1; } } for (; i < length; i++) if (array[i] === item) return i; return -1; }; _.lastIndexOf = function(array, item, from) { if (array == null) return -1; var idx = array.length; if (typeof from == 'number') { idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1); } while (--idx >= 0) if (array[idx] === item) return idx; return -1; }; // Generate an integer Array containing an arithmetic progression. A port of // the native Python `range()` function. See // [the Python documentation](http://docs.python.org/library/functions.html#range). _.range = function(start, stop, step) { if (arguments.length <= 1) { stop = start || 0; start = 0; } step = step || 1; var length = Math.max(Math.ceil((stop - start) / step), 0); var range = Array(length); for (var idx = 0; idx < length; idx++, start += step) { range[idx] = start; } return range; }; // Function (ahem) Functions // ------------------ // Reusable constructor function for prototype setting. var Ctor = function(){}; // Create a function bound to a given object (assigning `this`, and arguments, // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if // available. _.bind = function(func, context) { var args, bound; if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function'); args = slice.call(arguments, 2); bound = function() { if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); Ctor.prototype = func.prototype; var self = new Ctor; Ctor.prototype = null; var result = func.apply(self, args.concat(slice.call(arguments))); if (_.isObject(result)) return result; return self; }; return bound; }; // Partially apply a function by creating a version that has had some of its // arguments pre-filled, without changing its dynamic `this` context. _ acts // as a placeholder, allowing any combination of arguments to be pre-filled. _.partial = function(func) { var boundArgs = slice.call(arguments, 1); return function() { var position = 0; var args = boundArgs.slice(); for (var i = 0, length = args.length; i < length; i++) { if (args[i] === _) args[i] = arguments[position++]; } while (position < arguments.length) args.push(arguments[position++]); return func.apply(this, args); }; }; // Bind a number of an object's methods to that object. Remaining arguments // are the method names to be bound. Useful for ensuring that all callbacks // defined on an object belong to it. _.bindAll = function(obj) { var i, length = arguments.length, key; if (length <= 1) throw new Error('bindAll must be passed function names'); for (i = 1; i < length; i++) { key = arguments[i]; obj[key] = _.bind(obj[key], obj); } return obj; }; // Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memoize = function(key) { var cache = memoize.cache; var address = hasher ? hasher.apply(this, arguments) : key; if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); return cache[address]; }; memoize.cache = {}; return memoize; }; // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; // Defers a function, scheduling it to run after the current call stack has // cleared. _.defer = function(func) { return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); }; // Returns a function, that, when invoked, will only be triggered at most once // during a given window of time. Normally, the throttled function will run // as much as it can, without ever going more than once per `wait` duration; // but if you'd like to disable the execution on the leading edge, pass // `{leading: false}`. To disable execution on the trailing edge, ditto. _.throttle = function(func, wait, options) { var context, args, result; var timeout = null; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : _.now(); timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; return function() { var now = _.now(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // N milliseconds. If `immediate` is passed, trigger the function on the // leading edge, instead of the trailing. _.debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = _.now() - timestamp; if (last < wait && last > 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = _.now(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; }; // Returns the first function passed as an argument to the second, // allowing you to adjust arguments, run code before and after, and // conditionally execute the original function. _.wrap = function(func, wrapper) { return _.partial(wrapper, func); }; // Returns a negated version of the passed-in predicate. _.negate = function(predicate) { return function() { return !predicate.apply(this, arguments); }; }; // Returns a function that is the composition of a list of functions, each // consuming the return value of the function that follows. _.compose = function() { var args = arguments; var start = args.length - 1; return function() { var i = start; var result = args[start].apply(this, arguments); while (i--) result = args[i].call(this, result); return result; }; }; // Returns a function that will only be executed after being called N times. _.after = function(times, func) { return function() { if (--times < 1) { return func.apply(this, arguments); } }; }; // Returns a function that will only be executed before being called N times. _.before = function(times, func) { var memo; return function() { if (--times > 0) { memo = func.apply(this, arguments); } else { func = null; } return memo; }; }; // Returns a function that will be executed at most one time, no matter how // often you call it. Useful for lazy initialization. _.once = _.partial(_.before, 2); // Object Functions // ---------------- // Retrieve the names of an object's properties. // Delegates to **ECMAScript 5**'s native `Object.keys` _.keys = function(obj) { if (!_.isObject(obj)) return []; if (nativeKeys) return nativeKeys(obj); var keys = []; for (var key in obj) if (_.has(obj, key)) keys.push(key); return keys; }; // Retrieve the values of an object's properties. _.values = function(obj) { var keys = _.keys(obj); var length = keys.length; var values = Array(length); for (var i = 0; i < length; i++) { values[i] = obj[keys[i]]; } return values; }; // Convert an object into a list of `[key, value]` pairs. _.pairs = function(obj) { var keys = _.keys(obj); var length = keys.length; var pairs = Array(length); for (var i = 0; i < length; i++) { pairs[i] = [keys[i], obj[keys[i]]]; } return pairs; }; // Invert the keys and values of an object. The values must be serializable. _.invert = function(obj) { var result = {}; var keys = _.keys(obj); for (var i = 0, length = keys.length; i < length; i++) { result[obj[keys[i]]] = keys[i]; } return result; }; // Return a sorted list of the function names available on the object. // Aliased as `methods` _.functions = _.methods = function(obj) { var names = []; for (var key in obj) { if (_.isFunction(obj[key])) names.push(key); } return names.sort(); }; // Extend a given object with all the properties in passed-in object(s). _.extend = function(obj) { if (!_.isObject(obj)) return obj; var source, prop; for (var i = 1, length = arguments.length; i < length; i++) { source = arguments[i]; for (prop in source) { if (hasOwnProperty.call(source, prop)) { obj[prop] = source[prop]; } } } return obj; }; // Return a copy of the object only containing the whitelisted properties. _.pick = function(obj, iteratee, context) { var result = {}, key; if (obj == null) return result; if (_.isFunction(iteratee)) { iteratee = createCallback(iteratee, context); for (key in obj) { var value = obj[key]; if (iteratee(value, key, obj)) result[key] = value; } } else { var keys = concat.apply([], slice.call(arguments, 1)); obj = new Object(obj); for (var i = 0, length = keys.length; i < length; i++) { key = keys[i]; if (key in obj) result[key] = obj[key]; } } return result; }; // Return a copy of the object without the blacklisted properties. _.omit = function(obj, iteratee, context) { if (_.isFunction(iteratee)) { iteratee = _.negate(iteratee); } else { var keys = _.map(concat.apply([], slice.call(arguments, 1)), String); iteratee = function(value, key) { return !_.contains(keys, key); }; } return _.pick(obj, iteratee, context); }; // Fill in a given object with default properties. _.defaults = function(obj) { if (!_.isObject(obj)) return obj; for (var i = 1, length = arguments.length; i < length; i++) { var source = arguments[i]; for (var prop in source) { if (obj[prop] === void 0) obj[prop] = source[prop]; } } return obj; }; // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { if (!_.isObject(obj)) return obj; return _.isArray(obj) ? obj.slice() : _.extend({}, obj); }; // Invokes interceptor with the obj, and then returns obj. // The primary purpose of this method is to "tap into" a method chain, in // order to perform operations on intermediate results within the chain. _.tap = function(obj, interceptor) { interceptor(obj); return obj; }; // Internal recursive comparison function for `isEqual`. var eq = function(a, b, aStack, bStack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). if (a === b) return a !== 0 || 1 / a === 1 / b; // A strict comparison is necessary because `null == undefined`. if (a == null || b == null) return a === b; // Unwrap any wrapped objects. if (a instanceof _) a = a._wrapped; if (b instanceof _) b = b._wrapped; // Compare `[[Class]]` names. var className = toString.call(a); if (className !== toString.call(b)) return false; switch (className) { // Strings, numbers, regular expressions, dates, and booleans are compared by value. case '[object RegExp]': // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i') case '[object String]': // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is // equivalent to `new String("5")`. return '' + a === '' + b; case '[object Number]': // `NaN`s are equivalent, but non-reflexive. // Object(NaN) is equivalent to NaN if (+a !== +a) return +b !== +b; // An `egal` comparison is performed for other numeric values. return +a === 0 ? 1 / +a === 1 / b : +a === +b; case '[object Date]': case '[object Boolean]': // Coerce dates and booleans to numeric primitive values. Dates are compared by their // millisecond representations. Note that invalid dates with millisecond representations // of `NaN` are not equivalent. return +a === +b; } if (typeof a != 'object' || typeof b != 'object') return false; // Assume equality for cyclic structures. The algorithm for detecting cyclic // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. var length = aStack.length; while (length--) { // Linear search. Performance is inversely proportional to the number of // unique nested structures. if (aStack[length] === a) return bStack[length] === b; } // Objects with different constructors are not equivalent, but `Object`s // from different frames are. var aCtor = a.constructor, bCtor = b.constructor; if ( aCtor !== bCtor && // Handle Object.create(x) cases 'constructor' in a && 'constructor' in b && !(_.isFunction(aCtor) && aCtor instanceof aCtor && _.isFunction(bCtor) && bCtor instanceof bCtor) ) { return false; } // Add the first object to the stack of traversed objects. aStack.push(a); bStack.push(b); var size, result; // Recursively compare objects and arrays. if (className === '[object Array]') { // Compare array lengths to determine if a deep comparison is necessary. size = a.length; result = size === b.length; if (result) { // Deep compare the contents, ignoring non-numeric properties. while (size--) { if (!(result = eq(a[size], b[size], aStack, bStack))) break; } } } else { // Deep compare objects. var keys = _.keys(a), key; size = keys.length; // Ensure that both objects contain the same number of properties before comparing deep equality. result = _.keys(b).length === size; if (result) { while (size--) { // Deep compare each member key = keys[size]; if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; } } } // Remove the first object from the stack of traversed objects. aStack.pop(); bStack.pop(); return result; }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { return eq(a, b, [], []); }; // Is a given array, string, or object empty? // An "empty" object has no enumerable own-properties. _.isEmpty = function(obj) { if (obj == null) return true; if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0; for (var key in obj) if (_.has(obj, key)) return false; return true; }; // Is a given value a DOM element? _.isElement = function(obj) { return !!(obj && obj.nodeType === 1); }; // Is a given value an array? // Delegates to ECMA5's native Array.isArray _.isArray = nativeIsArray || function(obj) { return toString.call(obj) === '[object Array]'; }; // Is a given variable an object? _.isObject = function(obj) { var type = typeof obj; return type === 'function' || type === 'object' && !!obj; }; // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { _['is' + name] = function(obj) { return toString.call(obj) === '[object ' + name + ']'; }; }); // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) { _.isArguments = function(obj) { return _.has(obj, 'callee'); }; } // Optimize `isFunction` if appropriate. Work around an IE 11 bug. if (typeof /./ !== 'function') { _.isFunction = function(obj) { return typeof obj == 'function' || false; }; } // Is a given object a finite number? _.isFinite = function(obj) { return isFinite(obj) && !isNaN(parseFloat(obj)); }; // Is the given value `NaN`? (NaN is the only number which does not equal itself). _.isNaN = function(obj) { return _.isNumber(obj) && obj !== +obj; }; // Is a given value a boolean? _.isBoolean = function(obj) { return obj === true || obj === false || toString.call(obj) === '[object Boolean]'; }; // Is a given value equal to null? _.isNull = function(obj) { return obj === null; }; // Is a given variable undefined? _.isUndefined = function(obj) { return obj === void 0; }; // Shortcut function for checking if an object has a given property directly // on itself (in other words, not on a prototype). _.has = function(obj, key) { return obj != null && hasOwnProperty.call(obj, key); }; // Utility Functions // ----------------- // Run Underscore.js in *noConflict* mode, returning the `_` variable to its // previous owner. Returns a reference to the Underscore object. _.noConflict = function() { root._ = previousUnderscore; return this; }; // Keep the identity function around for default iteratees. _.identity = function(value) { return value; }; _.constant = function(value) { return function() { return value; }; }; _.noop = function(){}; _.property = function(key) { return function(obj) { return obj[key]; }; }; // Returns a predicate for checking whether an object has a given set of `key:value` pairs. _.matches = function(attrs) { var pairs = _.pairs(attrs), length = pairs.length; return function(obj) { if (obj == null) return !length; obj = new Object(obj); for (var i = 0; i < length; i++) { var pair = pairs[i], key = pair[0]; if (pair[1] !== obj[key] || !(key in obj)) return false; } return true; }; }; // Run a function **n** times. _.times = function(n, iteratee, context) { var accum = Array(Math.max(0, n)); iteratee = createCallback(iteratee, context, 1); for (var i = 0; i < n; i++) accum[i] = iteratee(i); return accum; }; // Return a random integer between min and max (inclusive). _.random = function(min, max) { if (max == null) { max = min; min = 0; } return min + Math.floor(Math.random() * (max - min + 1)); }; // A (possibly faster) way to get the current timestamp as an integer. _.now = Date.now || function() { return new Date().getTime(); }; // List of HTML entities for escaping. var escapeMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '`': '`' }; var unescapeMap = _.invert(escapeMap); // Functions for escaping and unescaping strings to/from HTML interpolation. var createEscaper = function(map) { var escaper = function(match) { return map[match]; }; // Regexes for identifying a key that needs to be escaped var source = '(?:' + _.keys(map).join('|') + ')'; var testRegexp = RegExp(source); var replaceRegexp = RegExp(source, 'g'); return function(string) { string = string == null ? '' : '' + string; return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string; }; }; _.escape = createEscaper(escapeMap); _.unescape = createEscaper(unescapeMap); // If the value of the named `property` is a function then invoke it with the // `object` as context; otherwise, return it. _.result = function(object, property) { if (object == null) return void 0; var value = object[property]; return _.isFunction(value) ? object[property]() : value; }; // Generate a unique integer id (unique within the entire client session). // Useful for temporary DOM ids. var idCounter = 0; _.uniqueId = function(prefix) { var id = ++idCounter + ''; return prefix ? prefix + id : id; }; // By default, Underscore uses ERB-style template delimiters, change the // following template settings to use alternative delimiters. _.templateSettings = { evaluate : /<%([\s\S]+?)%>/g, interpolate : /<%=([\s\S]+?)%>/g, escape : /<%-([\s\S]+?)%>/g }; // When customizing `templateSettings`, if you don't want to define an // interpolation, evaluation or escaping regex, we need one that is // guaranteed not to match. var noMatch = /(.)^/; // Certain characters need to be escaped so that they can be put into a // string literal. var escapes = { "'": "'", '\\': '\\', '\r': 'r', '\n': 'n', '\u2028': 'u2028', '\u2029': 'u2029' }; var escaper = /\\|'|\r|\n|\u2028|\u2029/g; var escapeChar = function(match) { return '\\' + escapes[match]; }; // JavaScript micro-templating, similar to John Resig's implementation. // Underscore templating handles arbitrary delimiters, preserves whitespace, // and correctly escapes quotes within interpolated code. // NB: `oldSettings` only exists for backwards compatibility. _.template = function(text, settings, oldSettings) { if (!settings && oldSettings) settings = oldSettings; settings = _.defaults({}, settings, _.templateSettings); // Combine delimiters into one regular expression via alternation. var matcher = RegExp([ (settings.escape || noMatch).source, (settings.interpolate || noMatch).source, (settings.evaluate || noMatch).source ].join('|') + '|$', 'g'); // Compile the template source, escaping string literals appropriately. var index = 0; var source = "__p+='"; text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { source += text.slice(index, offset).replace(escaper, escapeChar); index = offset + match.length; if (escape) { source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; } else if (interpolate) { source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; } else if (evaluate) { source += "';\n" + evaluate + "\n__p+='"; } // Adobe VMs need the match returned to produce the correct offest. return match; }); source += "';\n"; // If a variable is not specified, place data values in local scope. if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; source = "var __t,__p='',__j=Array.prototype.join," + "print=function(){__p+=__j.call(arguments,'');};\n" + source + 'return __p;\n'; try { var render = new Function(settings.variable || 'obj', '_', source); } catch (e) { e.source = source; throw e; } var template = function(data) { return render.call(this, data, _); }; // Provide the compiled source as a convenience for precompilation. var argument = settings.variable || 'obj'; template.source = 'function(' + argument + '){\n' + source + '}'; return template; }; // Add a "chain" function. Start chaining a wrapped Underscore object. _.chain = function(obj) { var instance = _(obj); instance._chain = true; return instance; }; // OOP // --------------- // If Underscore is called as a function, it returns a wrapped object that // can be used OO-style. This wrapper holds altered versions of all the // underscore functions. Wrapped objects may be chained. // Helper function to continue chaining intermediate results. var result = function(obj) { return this._chain ? _(obj).chain() : obj; }; // Add your own custom functions to the Underscore object. _.mixin = function(obj) { _.each(_.functions(obj), function(name) { var func = _[name] = obj[name]; _.prototype[name] = function() { var args = [this._wrapped]; push.apply(args, arguments); return result.call(this, func.apply(_, args)); }; }); }; // Add all of the Underscore functions to the wrapper object. _.mixin(_); // Add all mutator Array functions to the wrapper. _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { var obj = this._wrapped; method.apply(obj, arguments); if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0]; return result.call(this, obj); }; }); // Add all accessor Array functions to the wrapper. _.each(['concat', 'join', 'slice'], function(name) { var method = ArrayProto[name]; _.prototype[name] = function() { return result.call(this, method.apply(this._wrapped, arguments)); }; }); // Extracts the result from a wrapped and chained object. _.prototype.value = function() { return this._wrapped; }; // AMD registration happens at the end for compatibility with AMD loaders // that may not enforce next-turn semantics on modules. Even though general // practice for AMD registration is to be anonymous, underscore registers // as a named module because, like jQuery, it is a base library that is // popular enough to be bundled in a third party lib, but not be part of // an AMD load request. Those cases could generate an error when an // anonymous define() is called outside of a loader request. if (typeof define === 'function' && define.amd) { define('underscore', [], function() { return _; }); } }.call(this)); },{}],17:[function(require,module,exports){ // calc.js // measure calculations var _ = require('underscore'); var geocrunch = require('geocrunch'); var pad = function (num) { return num < 10 ? '0' + num.toString() : num.toString(); }; var ddToDms = function (coordinate, posSymbol, negSymbol) { var dd = Math.abs(coordinate), d = Math.floor(dd), m = Math.floor((dd - d) * 60), s = Math.round((dd - d - (m/60)) * 3600 * 100)/100, directionSymbol = dd === coordinate ? posSymbol : negSymbol; return pad(d) + '° ' + pad(m) + '\' ' + pad(s) + '" ' + directionSymbol; }; var measure = function (latlngs) { var last = _.last(latlngs), feet, meters, miles, kilometers, sqMeters, acres, hectares, sqMiles; var path = geocrunch.path(_.map(latlngs, function (latlng) { return [latlng.lng, latlng.lat]; })); feet = path.distance({ units: 'feet' }); meters = feet / 3.2808; miles = feet / 5280; kilometers = meters / 1000; sqMeters = path.area({ units: 'sqmeters' }); acres = sqMeters * 0.00024711; hectares = sqMeters / 10000; sqMiles = acres * 0.0015625; return { lastCoord: { dd: { x: last.lng, y: last.lat }, dms: { x: ddToDms(last.lng, 'E', 'W'), y: ddToDms(last.lat, 'N', 'S') } }, length: { feet: feet, meters: meters, miles: miles, kilometers: kilometers }, area: { acres: acres, hectares: hectares, sqmeters: sqMeters, sqmiles: sqMiles } }; }; module.exports = { measure: measure // `measure(latLngArray)` - returns object with calced measurements for passed points }; },{"geocrunch":6,"underscore":16}],18:[function(require,module,exports){ // dom.js // utility functions for managing DOM elements var selectOne = function (selector, el) { if (!el) { el = document; } return el.querySelector(selector); }; var selectAll = function (selector, el) { if (!el) { el = document; } return Array.prototype.slice.call(el.querySelectorAll(selector)); }; var hide = function (el) { if (el) { el.setAttribute('style', 'display:none;'); return el; } }; var show = function (el) { if (el) { el.removeAttribute('style'); return el; } }; module.exports = { $: selectOne, // `$('.myclass', baseElement)` - returns selected element or undefined $$: selectAll, // `$$('.myclass', baseElement)` - returns array of selected elements hide: hide, // `hide(someElement)` - hide passed dom element show: show // `show(someElement)` - show passed dom element }; },{}],19:[function(require,module,exports){ (function (global){ // leaflet-measure.js var _ = require('underscore'); var L = (typeof window !== "undefined" ? window.L : typeof global !== "undefined" ? global.L : null); var humanize = require('humanize'); var units = require('./units'); var calc = require('./calc'); var dom = require('./dom'); var $ = dom.$; var Symbology = require('./mapsymbology'); var controlTemplate = _.template("-toggle js-toggle\" href=\"#\" title=\"Measure distances and areas\">Measure\n
-interaction js-interaction\">\n
\n

Measure Distances and Areas

\n \n
\n
\n

Measure Distances and Areas

\n

Start creating a measurement by adding points to the map\n

\n \n
\n
"); var resultsTemplate = _.template("
\n

Last Point

\n

<%= model.lastCoord.dms.y %> / <%= model.lastCoord.dms.x %>

\n

<%= humanize.numberFormat(model.lastCoord.dd.y, 6) %> / <%= humanize.numberFormat(model.lastCoord.dd.x, 6) %>

\n
\n<% if (model.pointCount > 1) { %>\n
\n

Path Distance <%= model.lengthDisplay %>

\n
\n<% } %>\n<% if (model.pointCount > 2) { %>\n
\n

Area <%= model.areaDisplay %>

\n
\n<% } %>"); var pointPopupTemplate = _.template("

Point Location

\n

<%= model.lastCoord.dms.y %> / <%= model.lastCoord.dms.x %>

\n

<%= humanize.numberFormat(model.lastCoord.dd.y, 6) %> / <%= humanize.numberFormat(model.lastCoord.dd.x, 6) %>

\n"); var linePopupTemplate = _.template("

Linear Measurement

\n

<%= model.lengthDisplay %>

\n"); var areaPopupTemplate = _.template("

Area Measurement

\n

<%= model.areaDisplay %>

\n

<%= model.lengthDisplay %> Perimeter

\n"); L.Control.Measure = L.Control.extend({ _className: 'leaflet-control-measure', options: { position: 'topright', primaryLengthUnit: 'feet', secondaryLengthUnit: 'miles', primaryAreaUnit: 'acres', activeColor: '#ABE67E', // base color for map features while actively measuring completedColor: '#C8F2BE', // base color for permenant features generated from completed measure popupOptions: { // standard leaflet popup options http://leafletjs.com/reference.html#popup-options className: 'leaflet-measure-resultpopup', autoPanPadding: [10, 10] } }, initialize: function (options) { L.setOptions(this, options); this._symbols = new Symbology(_.pick(this.options, 'activeColor', 'completedColor')); }, onAdd: function (map) { this._map = map; this._latlngs = []; this._initLayout(); map.on('click', this._collapse, this); this._layer = L.layerGroup().addTo(map); return this._container; }, onRemove: function (map) { map.off('click', this._collapse, this); map.removeLayer(this._layer); }, _initLayout: function () { var className = this._className, container = this._container = L.DomUtil.create('div', className); var $toggle, $start, $cancel, $finish; container.innerHTML = controlTemplate({ model: { className: className } }); // copied from leaflet // https://bitbucket.org/ljagis/js-mapbootstrap/src/4ab1e9e896c08bdbc8164d4053b2f945143f4f3a/app/components/measure/leaflet-measure-control.js?at=master#cl-30 container.setAttribute('aria-haspopup', true); if (!L.Browser.touch) { L.DomEvent.disableClickPropagation(container); L.DomEvent.disableScrollPropagation(container); } else { L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); } $toggle = this.$toggle = $('.js-toggle', container); // collapsed content this.$interaction = $('.js-interaction', container); // expanded content $start = $('.js-start', container); // start button $cancel = $('.js-cancel', container); // cancel button $finish = $('.js-finish', container); // finish button this.$startPrompt = $('.js-startprompt', container); // full area with button to start measurment this.$measuringPrompt = $('.js-measuringprompt', container); // full area with all stuff for active measurement this.$startHelp = $('.js-starthelp', container); // "Start creating a measurement by adding points" this.$results = $('.js-results', container); // div with coordinate, linear, area results this.$measureTasks = $('.js-measuretasks', container); // active measure buttons container this._collapse(); this._updateMeasureNotStarted(); if (!L.Browser.android) { L.DomEvent.on(container, 'mouseenter', this._expand, this); L.DomEvent.on(container, 'mouseleave', this._collapse, this); } L.DomEvent.on($toggle, 'click', L.DomEvent.stop); if (L.Browser.touch) { L.DomEvent.on($toggle, 'click', this._expand, this); } else { L.DomEvent.on($toggle, 'focus', this._expand, this); } L.DomEvent.on($start, 'click', L.DomEvent.stop); L.DomEvent.on($start, 'click', this._startMeasure, this); L.DomEvent.on($cancel, 'click', L.DomEvent.stop); L.DomEvent.on($cancel, 'click', this._finishMeasure, this); L.DomEvent.on($finish, 'click', L.DomEvent.stop); L.DomEvent.on($finish, 'click', this._handleMeasureDoubleClick, this); }, _expand: function () { dom.hide(this.$toggle); dom.show(this.$interaction); }, _collapse: function () { if (!this._locked) { dom.hide(this.$interaction); dom.show(this.$toggle); } }, // move between basic states: // measure not started, started/in progress but no points added, in progress and with points _updateMeasureNotStarted: function () { dom.hide(this.$startHelp); dom.hide(this.$results); dom.hide(this.$measureTasks); dom.hide(this.$measuringPrompt); dom.show(this.$startPrompt); }, _updateMeasureStartedNoPoints: function () { dom.hide(this.$results); dom.show(this.$startHelp); dom.show(this.$measureTasks); dom.hide(this.$startPrompt); dom.show(this.$measuringPrompt); }, _updateMeasureStartedWithPoints: function () { dom.hide(this.$startHelp); dom.show(this.$results); dom.show(this.$measureTasks); dom.hide(this.$startPrompt); dom.show(this.$measuringPrompt); }, // get state vars and interface ready for measure _startMeasure: function () { this._locked = true; this._map.doubleClickZoom.disable(); // double click now finishes measure this._map.on('mouseout', this._handleMapMouseOut, this); L.DomEvent.on(this._container, 'mouseenter', this._handleMapMouseOut, this); if (!this._measureCollector) { // polygon to cover all other layers and collection measure move and click events this._measureCollector = L.polygon([[90, -180], [90, 180], [-90, 180], [-90, -180]], this._symbols.getSymbol('measureCollector')).addTo(this._layer); this._measureCollector.on('mousemove', this._handleMeasureMove, this); this._measureCollector.on('dblclick', this._handleMeasureDoubleClick, this); this._measureCollector.on('click', this._handleMeasureClick, this); } this._measureCollector.bringToFront(); this._measureVertexes = L.featureGroup().addTo(this._layer); this._updateMeasureStartedNoPoints(); }, // return to state with no measure in progress, undo `this._startMeasure` _finishMeasure: function () { this._locked = false; this._map.doubleClickZoom.enable(); this._map.off('mouseout', this._handleMapMouseOut, this); L.DomEvent.off(this._container, 'mouseover', this._handleMapMouseOut, this); this._clearMeasure(); this._measureCollector.off(); this._layer.removeLayer(this._measureCollector); this._measureCollector = null; this._layer.removeLayer(this._measureVertexes); this._measureVertexes = null; this._updateMeasureNotStarted(); this._collapse(); }, // clear all running measure data _clearMeasure: function () { this._latlngs = []; this._measureVertexes.clearLayers(); if (this._measureDrag) { this._layer.removeLayer(this._measureDrag); } if (this._measureArea) { this._layer.removeLayer(this._measureArea); } if (this._measureBoundary) { this._layer.removeLayer(this._measureBoundary); } this._measureDrag = null; this._measureArea = null; this._measureBoundary = null; }, // format measurements to nice display string based on units in options. `{ lengthDisplay: '100 Feet (0.02 Miles)', areaDisplay: ... }` _getMeasurementDisplayStrings: function (measurement) { var result = {}; if (this.options.primaryLengthUnit && units[this.options.primaryLengthUnit]) { result.lengthDisplay = humanize.numberFormat(measurement.length[this.options.primaryLengthUnit], units[this.options.primaryLengthUnit].decimals) + ' ' + units[this.options.primaryLengthUnit].display; if (this.options.secondaryLengthUnit && units[this.options.secondaryLengthUnit]) { result.lengthDisplay = result.lengthDisplay + ' (' + humanize.numberFormat(measurement.length[this.options.secondaryLengthUnit], units[this.options.secondaryLengthUnit].decimals) + ' ' + units[this.options.secondaryLengthUnit].display + ')'; } } if (this.options.primaryAreaUnit && units[this.options.primaryAreaUnit]) { result.areaDisplay = humanize.numberFormat(measurement.area[this.options.primaryAreaUnit], units[this.options.primaryAreaUnit].decimals) + ' ' + units[this.options.primaryAreaUnit].display; if (this.options.secondaryAreaUnit && units[this.options.secondaryAreaUnit]) { result.areaDisplay = result.areaDisplay + ' (' + humanize.numberFormat(measurement.area[this.options.secondaryAreaUnit], units[this.options.secondaryAreaUnit].decimals) + ' ' + units[this.options.secondaryAreaUnit].display + ')'; } } return result; }, // update results area of dom with calced measure from `this._latlngs` _updateResults: function () { var calced = calc.measure(this._latlngs); this.$results.innerHTML = resultsTemplate({ model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced), { pointCount: this._latlngs.length }), humanize: humanize }); }, // mouse move handler while measure in progress // adds floating measure marker under cursor _handleMeasureMove: function (evt) { if (!this._measureDrag) { this._measureDrag = L.circleMarker(evt.latlng, this._symbols.getSymbol('measureDrag')).addTo(this._layer); } else { this._measureDrag.setLatLng(evt.latlng); } this._measureDrag.bringToFront(); }, // handler for both double click and clicking finish button // do final calc and finish out current measure, clear dom and internal state, add permanent map features _handleMeasureDoubleClick: function () { var latlngs = this._latlngs, calced, resultFeature, popupContainer, popupContent, zoomLink, deleteLink; this._finishMeasure(); if (!latlngs.length) { return; } if (latlngs.length > 2) { latlngs.push(_.first(latlngs)); // close path to get full perimeter measurement for areas } calced = calc.measure(latlngs); if (latlngs.length === 1) { resultFeature = L.circleMarker(latlngs[0], this._symbols.getSymbol('resultPoint')); popupContent = pointPopupTemplate({ model: calced, humanize: humanize }); } else if (latlngs.length === 2) { resultFeature = L.polyline(latlngs, this._symbols.getSymbol('resultLine')).addTo(this._map); popupContent = linePopupTemplate({ model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced)), humanize: humanize }); } else { resultFeature = L.polygon(latlngs, this._symbols.getSymbol('resultArea')); popupContent = areaPopupTemplate({ model: _.extend({}, calced, this._getMeasurementDisplayStrings(calced)), humanize: humanize, units: this._units }); } popupContainer = L.DomUtil.create('div', ''); popupContainer.innerHTML = popupContent; zoomLink = $('.js-zoomto', popupContainer); if (zoomLink) { L.DomEvent.on(zoomLink, 'click', L.DomEvent.stop); L.DomEvent.on(zoomLink, 'click', function () { this._map.fitBounds(resultFeature.getBounds(), { padding: [20, 20], maxZoom: 17 }); }, this); } deleteLink = $('.js-deletemarkup', popupContainer); if (deleteLink) { L.DomEvent.on(deleteLink, 'click', L.DomEvent.stop); L.DomEvent.on(deleteLink, 'click', function () { // TODO. maybe remove any event handlers on zoom and delete buttons? this._map.removeLayer(resultFeature); }, this); } resultFeature.addTo(this._map); resultFeature.bindPopup(popupContainer, this.options.popupOptions); resultFeature.openPopup(resultFeature.getBounds().getCenter()); }, // handle map click during ongoing measurement // add new clicked point, update measure layers and results ui _handleMeasureClick: function (evt) { var latlng = evt.latlng, lastClick = _.last(this._latlngs), vertexSymbol = this._symbols.getSymbol('measureVertex'); this._map.closePopup(); // open popups aren't closed on click. may be bug. close popup manually just in case. if (!lastClick || !latlng.equals(lastClick)) { // skip if same point as last click, happens on `dblclick` this._latlngs.push(latlng); this._addMeasureArea(this._latlngs); this._addMeasureBoundary(this._latlngs); this._measureVertexes.eachLayer(function (layer) { layer.setStyle(vertexSymbol); // reset all vertexes to non-active class - only last vertex is active // `layer.setStyle({ className: 'layer-measurevertex'})` doesn't work. https://github.com/leaflet/leaflet/issues/2662 // set attribute on path directly layer._path.setAttribute('class', vertexSymbol.className); }); this._addNewVertex(latlng); if (this._measureBoundary) { this._measureBoundary.bringToFront(); } this._measureVertexes.bringToFront(); } this._updateResults(); this._updateMeasureStartedWithPoints(); }, // handle map mouse out during ongoing measure // remove floating cursor vertex from map _handleMapMouseOut: function () { if (this._measureDrag) { this._layer.removeLayer(this._measureDrag); this._measureDrag = null; } }, // add various measure graphics to map - vertex, area, boundary _addNewVertex: function (latlng) { L.circleMarker(latlng, this._symbols.getSymbol('measureVertexActive')).addTo(this._measureVertexes); }, _addMeasureArea: function (latlngs) { if (latlngs.length < 3) { if (this._measureArea) { this._layer.removeLayer(this._measureArea); this._measureArea = null; } return; } if (!this._measureArea) { this._measureArea = L.polygon(latlngs, this._symbols.getSymbol('measureArea')).addTo(this._layer); } else { this._measureArea.setLatLngs(latlngs); } }, _addMeasureBoundary: function (latlngs) { if (latlngs.length < 2) { if (this._measureBoundary) { this._layer.removeLayer(this._measureBoundary); this._measureBoundary = null; } return; } if (!this._measureBoundary) { this._measureBoundary = L.polyline(latlngs, this._symbols.getSymbol('measureBoundary')).addTo(this._layer); } else { this._measureBoundary.setLatLngs(latlngs); } } }); L.Map.mergeOptions({ measureControl: false }); L.Map.addInitHook(function () { if (this.options.measureControl) { this.measureControl = (new L.Control.Measure()).addTo(this); } }); L.control.measure = function (options) { return new L.Control.Measure(options); }; }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{"./calc":17,"./dom":18,"./mapsymbology":20,"./units":21,"humanize":15,"underscore":16}],20:[function(require,module,exports){ // mapsymbology.js var _ = require('underscore'); var color = require('color'); var Symbology = function (options) { this.setOptions(options); }; Symbology.DEFAULTS = { activeColor: '#ABE67E', // base color for map features while actively measuring completedColor: '#C8F2BE' // base color for permenant features generated from completed measure }; _.extend(Symbology.prototype, { setOptions: function (options) { this._options = _.extend({}, Symbology.DEFAULTS, this._options, options); return this; }, getSymbol: function (name) { var symbols = { measureCollector: { clickable: true, stroke: false, fillOpacity: 0.0, className: 'layer-measurecollector' }, measureDrag: { clickable: false, radius: 4, color: this._options.activeColor, weight: 2, opacity: 0.7, fillColor: this._options.activeColor, fillOpacity: 0.5, className: 'layer-measuredrag' }, measureArea: { clickable: false, stroke: false, fillColor: this._options.activeColor, fillOpacity: 0.2, className: 'layer-measurearea' }, measureBoundary: { clickable: false, color: this._options.activeColor, weight: 2, opacity: 0.9, fill: false, className: 'layer-measureboundary' }, measureVertex: { clickable: false, radius: 4, color: this._options.activeColor, weight: 2, opacity: 1, fillColor: this._options.activeColor, fillOpacity: 0.7, className: 'layer-measurevertex' }, measureVertexActive: { clickable: false, radius: 4, color: this._options.activeColor, weight: 2, opacity: 1, fillColor: color(this._options.activeColor).darken(0.15), fillOpacity: 0.7, className: 'layer-measurevertex active' }, resultArea: { clickable: true, color: this._options.completedColor, weight: 2, opacity: 0.9, fillColor: this._options.completedColor, fillOpacity: 0.2, className: 'layer-measure-resultarea' }, resultLine: { clickable: true, color: this._options.completedColor, weight: 3, opacity: 0.9, fill: false, className: 'layer-measure-resultline' }, resultPoint: { clickable: true, radius: 4, color: this._options.completedColor, weight: 2, opacity: 1, fillColor: this._options.completedColor, fillOpacity: 0.7, className: 'layer-measure-resultpoint' } }; return symbols[name]; } }); module.exports = Symbology; },{"color":1,"underscore":16}],21:[function(require,module,exports){ // units.js // Unit configurations module.exports = { acres: { display: 'Acres', decimals: 2 }, feet: { display: 'Feet', decimals: 0 }, kilometers: { display: 'Kilometers', decimals: 2 }, hectares: { display: 'Hectares', decimals: 2 }, meters: { display: 'Meters', decimals: 0 }, miles: { display: 'Miles', decimals: 2 }, sqmeters: { display: 'Sq Meters', decimals: 0 }, sqmiles: { display: 'Sq Miles', decimals: 2 } }; },{}]},{},[19]);