/*! * ==================================================== * Kity Formula Render - v1.0.0 - 2014-07-30 * https://github.com/kitygraph/formula * GitHub: https://github.com/kitygraph/formula.git * Copyright (c) 2014 Baidu Kity Group; Licensed MIT * ==================================================== */ (function () { var _p = { r: function(index) { if (_p[index].inited) { return _p[index].value; } if (typeof _p[index].value === "function") { var module = { exports: {} }, returnValue = _p[index].value(null, module.exports, module); _p[index].inited = true; _p[index].value = returnValue; if (returnValue !== undefined) { return returnValue; } else { for (var key in module.exports) { if (module.exports.hasOwnProperty(key)) { _p[index].inited = true; _p[index].value = module.exports; return module.exports; } } } } else { _p[index].inited = true; return _p[index].value; } } }; /*! * canvg库封装 * canvg官网: https://code.google.com/p/canvg/ */ _p[0] = { value: function(require) { /** * A class to parse color values * @author Stoyan Stefanov * @link http://www.phpied.com/rgb-color-parser-in-javascript/ * @license Use it if you like it */ function RGBColor(color_string) { this.ok = false; // strip any leading # if (color_string.charAt(0) == "#") { // remove # if any color_string = color_string.substr(1, 6); } color_string = color_string.replace(/ /g, ""); color_string = color_string.toLowerCase(); // before getting into regexps, try simple matches // and overwrite the input var simple_colors = { aliceblue: "f0f8ff", antiquewhite: "faebd7", aqua: "00ffff", aquamarine: "7fffd4", azure: "f0ffff", beige: "f5f5dc", bisque: "ffe4c4", black: "000000", blanchedalmond: "ffebcd", blue: "0000ff", blueviolet: "8a2be2", brown: "a52a2a", burlywood: "deb887", cadetblue: "5f9ea0", chartreuse: "7fff00", chocolate: "d2691e", coral: "ff7f50", cornflowerblue: "6495ed", cornsilk: "fff8dc", crimson: "dc143c", cyan: "00ffff", darkblue: "00008b", darkcyan: "008b8b", darkgoldenrod: "b8860b", darkgray: "a9a9a9", darkgreen: "006400", darkkhaki: "bdb76b", darkmagenta: "8b008b", darkolivegreen: "556b2f", darkorange: "ff8c00", darkorchid: "9932cc", darkred: "8b0000", darksalmon: "e9967a", darkseagreen: "8fbc8f", darkslateblue: "483d8b", darkslategray: "2f4f4f", darkturquoise: "00ced1", darkviolet: "9400d3", deeppink: "ff1493", deepskyblue: "00bfff", dimgray: "696969", dodgerblue: "1e90ff", feldspar: "d19275", firebrick: "b22222", floralwhite: "fffaf0", forestgreen: "228b22", fuchsia: "ff00ff", gainsboro: "dcdcdc", ghostwhite: "f8f8ff", gold: "ffd700", goldenrod: "daa520", gray: "808080", green: "008000", greenyellow: "adff2f", honeydew: "f0fff0", hotpink: "ff69b4", indianred: "cd5c5c", indigo: "4b0082", ivory: "fffff0", khaki: "f0e68c", lavender: "e6e6fa", lavenderblush: "fff0f5", lawngreen: "7cfc00", lemonchiffon: "fffacd", lightblue: "add8e6", lightcoral: "f08080", lightcyan: "e0ffff", lightgoldenrodyellow: "fafad2", lightgrey: "d3d3d3", lightgreen: "90ee90", lightpink: "ffb6c1", lightsalmon: "ffa07a", lightseagreen: "20b2aa", lightskyblue: "87cefa", lightslateblue: "8470ff", lightslategray: "778899", lightsteelblue: "b0c4de", lightyellow: "ffffe0", lime: "00ff00", limegreen: "32cd32", linen: "faf0e6", magenta: "ff00ff", maroon: "800000", mediumaquamarine: "66cdaa", mediumblue: "0000cd", mediumorchid: "ba55d3", mediumpurple: "9370d8", mediumseagreen: "3cb371", mediumslateblue: "7b68ee", mediumspringgreen: "00fa9a", mediumturquoise: "48d1cc", mediumvioletred: "c71585", midnightblue: "191970", mintcream: "f5fffa", mistyrose: "ffe4e1", moccasin: "ffe4b5", navajowhite: "ffdead", navy: "000080", oldlace: "fdf5e6", olive: "808000", olivedrab: "6b8e23", orange: "ffa500", orangered: "ff4500", orchid: "da70d6", palegoldenrod: "eee8aa", palegreen: "98fb98", paleturquoise: "afeeee", palevioletred: "d87093", papayawhip: "ffefd5", peachpuff: "ffdab9", peru: "cd853f", pink: "ffc0cb", plum: "dda0dd", powderblue: "b0e0e6", purple: "800080", red: "ff0000", rosybrown: "bc8f8f", royalblue: "4169e1", saddlebrown: "8b4513", salmon: "fa8072", sandybrown: "f4a460", seagreen: "2e8b57", seashell: "fff5ee", sienna: "a0522d", silver: "c0c0c0", skyblue: "87ceeb", slateblue: "6a5acd", slategray: "708090", snow: "fffafa", springgreen: "00ff7f", steelblue: "4682b4", tan: "d2b48c", teal: "008080", thistle: "d8bfd8", tomato: "ff6347", turquoise: "40e0d0", violet: "ee82ee", violetred: "d02090", wheat: "f5deb3", white: "ffffff", whitesmoke: "f5f5f5", yellow: "ffff00", yellowgreen: "9acd32" }; for (var key in simple_colors) { if (color_string == key) { color_string = simple_colors[key]; } } // emd of simple type-in colors // array of color definition objects var color_defs = [ { re: /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/, example: [ "rgb(123, 234, 45)", "rgb(255,234,245)" ], process: function(bits) { return [ parseInt(bits[1]), parseInt(bits[2]), parseInt(bits[3]) ]; } }, { re: /^(\w{2})(\w{2})(\w{2})$/, example: [ "#00ff00", "336699" ], process: function(bits) { return [ parseInt(bits[1], 16), parseInt(bits[2], 16), parseInt(bits[3], 16) ]; } }, { re: /^(\w{1})(\w{1})(\w{1})$/, example: [ "#fb0", "f0f" ], process: function(bits) { return [ parseInt(bits[1] + bits[1], 16), parseInt(bits[2] + bits[2], 16), parseInt(bits[3] + bits[3], 16) ]; } } ]; // search through the definitions to find a match for (var i = 0; i < color_defs.length; i++) { var re = color_defs[i].re; var processor = color_defs[i].process; var bits = re.exec(color_string); if (bits) { channels = processor(bits); this.r = channels[0]; this.g = channels[1]; this.b = channels[2]; this.ok = true; } } // validate/cleanup values this.r = this.r < 0 || isNaN(this.r) ? 0 : this.r > 255 ? 255 : this.r; this.g = this.g < 0 || isNaN(this.g) ? 0 : this.g > 255 ? 255 : this.g; this.b = this.b < 0 || isNaN(this.b) ? 0 : this.b > 255 ? 255 : this.b; // some getters this.toRGB = function() { return "rgb(" + this.r + ", " + this.g + ", " + this.b + ")"; }; this.toHex = function() { var r = this.r.toString(16); var g = this.g.toString(16); var b = this.b.toString(16); if (r.length == 1) r = "0" + r; if (g.length == 1) g = "0" + g; if (b.length == 1) b = "0" + b; return "#" + r + g + b; }; // help this.getHelpXML = function() { var examples = new Array(); // add regexps for (var i = 0; i < color_defs.length; i++) { var example = color_defs[i].example; for (var j = 0; j < example.length; j++) { examples[examples.length] = example[j]; } } // add type-in colors for (var sc in simple_colors) { examples[examples.length] = sc; } var xml = document.createElement("ul"); xml.setAttribute("id", "rgbcolor-examples"); for (var i = 0; i < examples.length; i++) { try { var list_item = document.createElement("li"); var list_color = new RGBColor(examples[i]); var example_div = document.createElement("div"); example_div.style.cssText = "margin: 3px; " + "border: 1px solid black; " + "background:" + list_color.toHex() + "; " + "color:" + list_color.toHex(); example_div.appendChild(document.createTextNode("test")); var list_item_value = document.createTextNode(" " + examples[i] + " -> " + list_color.toRGB() + " -> " + list_color.toHex()); list_item.appendChild(example_div); list_item.appendChild(list_item_value); xml.appendChild(list_item); } catch (e) {} } return xml; }; } /* StackBlur - a fast almost Gaussian Blur For Canvas Version: 0.5 Author: Mario Klingemann Contact: mario@quasimondo.com Website: http://www.quasimondo.com/StackBlurForCanvas Twitter: @quasimondo In case you find this class useful - especially in commercial projects - I am not totally unhappy for a small donation to my PayPal account mario@quasimondo.de Or support me on flattr: https://flattr.com/thing/72791/StackBlur-a-fast-almost-Gaussian-Blur-Effect-for-CanvasJavascript Copyright (c) 2010 Mario Klingemann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ var mul_table = [ 512, 512, 456, 512, 328, 456, 335, 512, 405, 328, 271, 456, 388, 335, 292, 512, 454, 405, 364, 328, 298, 271, 496, 456, 420, 388, 360, 335, 312, 292, 273, 512, 482, 454, 428, 405, 383, 364, 345, 328, 312, 298, 284, 271, 259, 496, 475, 456, 437, 420, 404, 388, 374, 360, 347, 335, 323, 312, 302, 292, 282, 273, 265, 512, 497, 482, 468, 454, 441, 428, 417, 405, 394, 383, 373, 364, 354, 345, 337, 328, 320, 312, 305, 298, 291, 284, 278, 271, 265, 259, 507, 496, 485, 475, 465, 456, 446, 437, 428, 420, 412, 404, 396, 388, 381, 374, 367, 360, 354, 347, 341, 335, 329, 323, 318, 312, 307, 302, 297, 292, 287, 282, 278, 273, 269, 265, 261, 512, 505, 497, 489, 482, 475, 468, 461, 454, 447, 441, 435, 428, 422, 417, 411, 405, 399, 394, 389, 383, 378, 373, 368, 364, 359, 354, 350, 345, 341, 337, 332, 328, 324, 320, 316, 312, 309, 305, 301, 298, 294, 291, 287, 284, 281, 278, 274, 271, 268, 265, 262, 259, 257, 507, 501, 496, 491, 485, 480, 475, 470, 465, 460, 456, 451, 446, 442, 437, 433, 428, 424, 420, 416, 412, 408, 404, 400, 396, 392, 388, 385, 381, 377, 374, 370, 367, 363, 360, 357, 354, 350, 347, 344, 341, 338, 335, 332, 329, 326, 323, 320, 318, 315, 312, 310, 307, 304, 302, 299, 297, 294, 292, 289, 287, 285, 282, 280, 278, 275, 273, 271, 269, 267, 265, 263, 261, 259 ]; var shg_table = [ 9, 11, 12, 13, 13, 14, 14, 15, 15, 15, 15, 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24 ]; function stackBlurImage(imageID, canvasID, radius, blurAlphaChannel) { var img = document.getElementById(imageID); var w = img.naturalWidth; var h = img.naturalHeight; var canvas = document.getElementById(canvasID); canvas.style.width = w + "px"; canvas.style.height = h + "px"; canvas.width = w; canvas.height = h; var context = canvas.getContext("2d"); context.clearRect(0, 0, w, h); context.drawImage(img, 0, 0); if (isNaN(radius) || radius < 1) return; if (blurAlphaChannel) stackBlurCanvasRGBA(canvasID, 0, 0, w, h, radius); else stackBlurCanvasRGB(canvasID, 0, 0, w, h, radius); } function stackBlurCanvasRGBA(id, top_x, top_y, width, height, radius) { if (isNaN(radius) || radius < 1) return; radius |= 0; var canvas = document.getElementById(id); var context = canvas.getContext("2d"); var imageData; try { try { imageData = context.getImageData(top_x, top_y, width, height); } catch (e) { // NOTE: this part is supposedly only needed if you want to work with local files // so it might be okay to remove the whole try/catch block and just use // imageData = context.getImageData( top_x, top_y, width, height ); try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); imageData = context.getImageData(top_x, top_y, width, height); } catch (e) { alert("Cannot access local image"); throw new Error("unable to access local image data: " + e); return; } } } catch (e) { alert("Cannot access image"); throw new Error("unable to access image data: " + e); } var pixels = imageData.data; var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, a_sum, r_out_sum, g_out_sum, b_out_sum, a_out_sum, r_in_sum, g_in_sum, b_in_sum, a_in_sum, pr, pg, pb, pa, rbs; var div = radius + radius + 1; var w4 = width << 2; var widthMinus1 = width - 1; var heightMinus1 = height - 1; var radiusPlus1 = radius + 1; var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; var stackStart = new BlurStack(); var stack = stackStart; for (i = 1; i < div; i++) { stack = stack.next = new BlurStack(); if (i == radiusPlus1) var stackEnd = stack; } stack.next = stackStart; var stackIn = null; var stackOut = null; yw = yi = 0; var mul_sum = mul_table[radius]; var shg_sum = shg_table[radius]; for (y = 0; y < height; y++) { r_in_sum = g_in_sum = b_in_sum = a_in_sum = r_sum = g_sum = b_sum = a_sum = 0; r_out_sum = radiusPlus1 * (pr = pixels[yi]); g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]); r_sum += sumFactor * pr; g_sum += sumFactor * pg; b_sum += sumFactor * pb; a_sum += sumFactor * pa; stack = stackStart; for (i = 0; i < radiusPlus1; i++) { stack.r = pr; stack.g = pg; stack.b = pb; stack.a = pa; stack = stack.next; } for (i = 1; i < radiusPlus1; i++) { p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); g_sum += (stack.g = pg = pixels[p + 1]) * rbs; b_sum += (stack.b = pb = pixels[p + 2]) * rbs; a_sum += (stack.a = pa = pixels[p + 3]) * rbs; r_in_sum += pr; g_in_sum += pg; b_in_sum += pb; a_in_sum += pa; stack = stack.next; } stackIn = stackStart; stackOut = stackEnd; for (x = 0; x < width; x++) { pixels[yi + 3] = pa = a_sum * mul_sum >> shg_sum; if (pa != 0) { pa = 255 / pa; pixels[yi] = (r_sum * mul_sum >> shg_sum) * pa; pixels[yi + 1] = (g_sum * mul_sum >> shg_sum) * pa; pixels[yi + 2] = (b_sum * mul_sum >> shg_sum) * pa; } else { pixels[yi] = pixels[yi + 1] = pixels[yi + 2] = 0; } r_sum -= r_out_sum; g_sum -= g_out_sum; b_sum -= b_out_sum; a_sum -= a_out_sum; r_out_sum -= stackIn.r; g_out_sum -= stackIn.g; b_out_sum -= stackIn.b; a_out_sum -= stackIn.a; p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; r_in_sum += stackIn.r = pixels[p]; g_in_sum += stackIn.g = pixels[p + 1]; b_in_sum += stackIn.b = pixels[p + 2]; a_in_sum += stackIn.a = pixels[p + 3]; r_sum += r_in_sum; g_sum += g_in_sum; b_sum += b_in_sum; a_sum += a_in_sum; stackIn = stackIn.next; r_out_sum += pr = stackOut.r; g_out_sum += pg = stackOut.g; b_out_sum += pb = stackOut.b; a_out_sum += pa = stackOut.a; r_in_sum -= pr; g_in_sum -= pg; b_in_sum -= pb; a_in_sum -= pa; stackOut = stackOut.next; yi += 4; } yw += width; } for (x = 0; x < width; x++) { g_in_sum = b_in_sum = a_in_sum = r_in_sum = g_sum = b_sum = a_sum = r_sum = 0; yi = x << 2; r_out_sum = radiusPlus1 * (pr = pixels[yi]); g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); a_out_sum = radiusPlus1 * (pa = pixels[yi + 3]); r_sum += sumFactor * pr; g_sum += sumFactor * pg; b_sum += sumFactor * pb; a_sum += sumFactor * pa; stack = stackStart; for (i = 0; i < radiusPlus1; i++) { stack.r = pr; stack.g = pg; stack.b = pb; stack.a = pa; stack = stack.next; } yp = width; for (i = 1; i <= radius; i++) { yi = yp + x << 2; r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); g_sum += (stack.g = pg = pixels[yi + 1]) * rbs; b_sum += (stack.b = pb = pixels[yi + 2]) * rbs; a_sum += (stack.a = pa = pixels[yi + 3]) * rbs; r_in_sum += pr; g_in_sum += pg; b_in_sum += pb; a_in_sum += pa; stack = stack.next; if (i < heightMinus1) { yp += width; } } yi = x; stackIn = stackStart; stackOut = stackEnd; for (y = 0; y < height; y++) { p = yi << 2; pixels[p + 3] = pa = a_sum * mul_sum >> shg_sum; if (pa > 0) { pa = 255 / pa; pixels[p] = (r_sum * mul_sum >> shg_sum) * pa; pixels[p + 1] = (g_sum * mul_sum >> shg_sum) * pa; pixels[p + 2] = (b_sum * mul_sum >> shg_sum) * pa; } else { pixels[p] = pixels[p + 1] = pixels[p + 2] = 0; } r_sum -= r_out_sum; g_sum -= g_out_sum; b_sum -= b_out_sum; a_sum -= a_out_sum; r_out_sum -= stackIn.r; g_out_sum -= stackIn.g; b_out_sum -= stackIn.b; a_out_sum -= stackIn.a; p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; r_sum += r_in_sum += stackIn.r = pixels[p]; g_sum += g_in_sum += stackIn.g = pixels[p + 1]; b_sum += b_in_sum += stackIn.b = pixels[p + 2]; a_sum += a_in_sum += stackIn.a = pixels[p + 3]; stackIn = stackIn.next; r_out_sum += pr = stackOut.r; g_out_sum += pg = stackOut.g; b_out_sum += pb = stackOut.b; a_out_sum += pa = stackOut.a; r_in_sum -= pr; g_in_sum -= pg; b_in_sum -= pb; a_in_sum -= pa; stackOut = stackOut.next; yi += width; } } context.putImageData(imageData, top_x, top_y); } function stackBlurCanvasRGB(id, top_x, top_y, width, height, radius) { if (isNaN(radius) || radius < 1) return; radius |= 0; var canvas = document.getElementById(id); var context = canvas.getContext("2d"); var imageData; try { try { imageData = context.getImageData(top_x, top_y, width, height); } catch (e) { // NOTE: this part is supposedly only needed if you want to work with local files // so it might be okay to remove the whole try/catch block and just use // imageData = context.getImageData( top_x, top_y, width, height ); try { netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead"); imageData = context.getImageData(top_x, top_y, width, height); } catch (e) { alert("Cannot access local image"); throw new Error("unable to access local image data: " + e); return; } } } catch (e) { alert("Cannot access image"); throw new Error("unable to access image data: " + e); } var pixels = imageData.data; var x, y, i, p, yp, yi, yw, r_sum, g_sum, b_sum, r_out_sum, g_out_sum, b_out_sum, r_in_sum, g_in_sum, b_in_sum, pr, pg, pb, rbs; var div = radius + radius + 1; var w4 = width << 2; var widthMinus1 = width - 1; var heightMinus1 = height - 1; var radiusPlus1 = radius + 1; var sumFactor = radiusPlus1 * (radiusPlus1 + 1) / 2; var stackStart = new BlurStack(); var stack = stackStart; for (i = 1; i < div; i++) { stack = stack.next = new BlurStack(); if (i == radiusPlus1) var stackEnd = stack; } stack.next = stackStart; var stackIn = null; var stackOut = null; yw = yi = 0; var mul_sum = mul_table[radius]; var shg_sum = shg_table[radius]; for (y = 0; y < height; y++) { r_in_sum = g_in_sum = b_in_sum = r_sum = g_sum = b_sum = 0; r_out_sum = radiusPlus1 * (pr = pixels[yi]); g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); r_sum += sumFactor * pr; g_sum += sumFactor * pg; b_sum += sumFactor * pb; stack = stackStart; for (i = 0; i < radiusPlus1; i++) { stack.r = pr; stack.g = pg; stack.b = pb; stack = stack.next; } for (i = 1; i < radiusPlus1; i++) { p = yi + ((widthMinus1 < i ? widthMinus1 : i) << 2); r_sum += (stack.r = pr = pixels[p]) * (rbs = radiusPlus1 - i); g_sum += (stack.g = pg = pixels[p + 1]) * rbs; b_sum += (stack.b = pb = pixels[p + 2]) * rbs; r_in_sum += pr; g_in_sum += pg; b_in_sum += pb; stack = stack.next; } stackIn = stackStart; stackOut = stackEnd; for (x = 0; x < width; x++) { pixels[yi] = r_sum * mul_sum >> shg_sum; pixels[yi + 1] = g_sum * mul_sum >> shg_sum; pixels[yi + 2] = b_sum * mul_sum >> shg_sum; r_sum -= r_out_sum; g_sum -= g_out_sum; b_sum -= b_out_sum; r_out_sum -= stackIn.r; g_out_sum -= stackIn.g; b_out_sum -= stackIn.b; p = yw + ((p = x + radius + 1) < widthMinus1 ? p : widthMinus1) << 2; r_in_sum += stackIn.r = pixels[p]; g_in_sum += stackIn.g = pixels[p + 1]; b_in_sum += stackIn.b = pixels[p + 2]; r_sum += r_in_sum; g_sum += g_in_sum; b_sum += b_in_sum; stackIn = stackIn.next; r_out_sum += pr = stackOut.r; g_out_sum += pg = stackOut.g; b_out_sum += pb = stackOut.b; r_in_sum -= pr; g_in_sum -= pg; b_in_sum -= pb; stackOut = stackOut.next; yi += 4; } yw += width; } for (x = 0; x < width; x++) { g_in_sum = b_in_sum = r_in_sum = g_sum = b_sum = r_sum = 0; yi = x << 2; r_out_sum = radiusPlus1 * (pr = pixels[yi]); g_out_sum = radiusPlus1 * (pg = pixels[yi + 1]); b_out_sum = radiusPlus1 * (pb = pixels[yi + 2]); r_sum += sumFactor * pr; g_sum += sumFactor * pg; b_sum += sumFactor * pb; stack = stackStart; for (i = 0; i < radiusPlus1; i++) { stack.r = pr; stack.g = pg; stack.b = pb; stack = stack.next; } yp = width; for (i = 1; i <= radius; i++) { yi = yp + x << 2; r_sum += (stack.r = pr = pixels[yi]) * (rbs = radiusPlus1 - i); g_sum += (stack.g = pg = pixels[yi + 1]) * rbs; b_sum += (stack.b = pb = pixels[yi + 2]) * rbs; r_in_sum += pr; g_in_sum += pg; b_in_sum += pb; stack = stack.next; if (i < heightMinus1) { yp += width; } } yi = x; stackIn = stackStart; stackOut = stackEnd; for (y = 0; y < height; y++) { p = yi << 2; pixels[p] = r_sum * mul_sum >> shg_sum; pixels[p + 1] = g_sum * mul_sum >> shg_sum; pixels[p + 2] = b_sum * mul_sum >> shg_sum; r_sum -= r_out_sum; g_sum -= g_out_sum; b_sum -= b_out_sum; r_out_sum -= stackIn.r; g_out_sum -= stackIn.g; b_out_sum -= stackIn.b; p = x + ((p = y + radiusPlus1) < heightMinus1 ? p : heightMinus1) * width << 2; r_sum += r_in_sum += stackIn.r = pixels[p]; g_sum += g_in_sum += stackIn.g = pixels[p + 1]; b_sum += b_in_sum += stackIn.b = pixels[p + 2]; stackIn = stackIn.next; r_out_sum += pr = stackOut.r; g_out_sum += pg = stackOut.g; b_out_sum += pb = stackOut.b; r_in_sum -= pr; g_in_sum -= pg; b_in_sum -= pb; stackOut = stackOut.next; yi += width; } } context.putImageData(imageData, top_x, top_y); } function BlurStack() { this.r = 0; this.g = 0; this.b = 0; this.a = 0; this.next = null; } /* * canvg.js - Javascript SVG parser and renderer on Canvas * MIT Licensed * Gabe Lerner (gabelerner@gmail.com) * http://code.google.com/p/canvg/ * * Requires: rgbcolor.js - http://www.phpied.com/rgb-color-parser-in-javascript/ */ (function() { // canvg(target, s) // empty parameters: replace all 'svg' elements on page with 'canvas' elements // target: canvas element or the id of a canvas element // s: svg string, url to svg file, or xml document // opts: optional hash of options // ignoreMouse: true => ignore mouse events // ignoreAnimation: true => ignore animations // ignoreDimensions: true => does not try to resize canvas // ignoreClear: true => does not clear canvas // offsetX: int => draws at a x offset // offsetY: int => draws at a y offset // scaleWidth: int => scales horizontally to width // scaleHeight: int => scales vertically to height // renderCallback: function => will call the function after the first render is completed // forceRedraw: function => will call the function on every frame, if it returns true, will redraw this.canvg = function(target, s, opts) { // no parameters if (target == null && s == null && opts == null) { var svgTags = document.getElementsByTagName("svg"); for (var i = 0; i < svgTags.length; i++) { var svgTag = svgTags[i]; var c = document.createElement("canvas"); c.width = svgTag.clientWidth; c.height = svgTag.clientHeight; svgTag.parentNode.insertBefore(c, svgTag); svgTag.parentNode.removeChild(svgTag); var div = document.createElement("div"); div.appendChild(svgTag); canvg(c, div.innerHTML); } return; } opts = opts || {}; if (typeof target == "string") { target = document.getElementById(target); } // store class on canvas if (target.svg != null) target.svg.stop(); var svg = build(); // on i.e. 8 for flash canvas, we can't assign the property so check for it if (!(target.childNodes.length == 1 && target.childNodes[0].nodeName == "OBJECT")) target.svg = svg; svg.opts = opts; var ctx = target.getContext("2d"); if (typeof s.documentElement != "undefined") { // load from xml doc svg.loadXmlDoc(ctx, s); } else if (s.substr(0, 1) == "<") { // load from xml string svg.loadXml(ctx, s); } else { // load from url svg.load(ctx, s); } }; function build() { var svg = {}; svg.FRAMERATE = 30; svg.MAX_VIRTUAL_PIXELS = 3e4; // globals svg.init = function(ctx) { var uniqueId = 0; svg.UniqueId = function() { uniqueId++; return "canvg" + uniqueId; }; svg.Definitions = {}; svg.Styles = {}; svg.Animations = []; svg.Images = []; svg.ctx = ctx; svg.ViewPort = new function() { this.viewPorts = []; this.Clear = function() { this.viewPorts = []; }; this.SetCurrent = function(width, height) { this.viewPorts.push({ width: width, height: height }); }; this.RemoveCurrent = function() { this.viewPorts.pop(); }; this.Current = function() { return this.viewPorts[this.viewPorts.length - 1]; }; this.width = function() { return this.Current().width; }; this.height = function() { return this.Current().height; }; this.ComputeSize = function(d) { if (d != null && typeof d == "number") return d; if (d == "x") return this.width(); if (d == "y") return this.height(); return Math.sqrt(Math.pow(this.width(), 2) + Math.pow(this.height(), 2)) / Math.sqrt(2); }; }(); }; svg.init(); // images loaded svg.ImagesLoaded = function() { for (var i = 0; i < svg.Images.length; i++) { if (!svg.Images[i].loaded) return false; } return true; }; // trim svg.trim = function(s) { return s.replace(/^\s+|\s+$/g, ""); }; // compress spaces svg.compressSpaces = function(s) { return s.replace(/[\s\r\t\n]+/gm, " "); }; // ajax svg.ajax = function(url) { var AJAX; if (window.XMLHttpRequest) { AJAX = new XMLHttpRequest(); } else { AJAX = new ActiveXObject("Microsoft.XMLHTTP"); } if (AJAX) { AJAX.open("GET", url, false); AJAX.send(null); return AJAX.responseText; } return null; }; // parse xml svg.parseXml = function(xml) { if (window.DOMParser) { var parser = new DOMParser(); return parser.parseFromString(xml, "text/xml"); } else { xml = xml.replace(/]*>/, ""); var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); xmlDoc.async = "false"; xmlDoc.loadXML(xml); return xmlDoc; } }; svg.Property = function(name, value) { this.name = name; this.value = value; }; svg.Property.prototype.getValue = function() { return this.value; }; svg.Property.prototype.hasValue = function() { return this.value != null && this.value !== ""; }; // return the numerical value of the property svg.Property.prototype.numValue = function() { if (!this.hasValue()) return 0; var n = parseFloat(this.value); if ((this.value + "").match(/%$/)) { n = n / 100; } return n; }; svg.Property.prototype.valueOrDefault = function(def) { if (this.hasValue()) return this.value; return def; }; svg.Property.prototype.numValueOrDefault = function(def) { if (this.hasValue()) return this.numValue(); return def; }; // color extensions // augment the current color value with the opacity svg.Property.prototype.addOpacity = function(opacity) { var newValue = this.value; if (opacity != null && opacity != "" && typeof this.value == "string") { // can only add opacity to colors, not patterns var color = new RGBColor(this.value); if (color.ok) { newValue = "rgba(" + color.r + ", " + color.g + ", " + color.b + ", " + opacity + ")"; } } return new svg.Property(this.name, newValue); }; // definition extensions // get the definition from the definitions table svg.Property.prototype.getDefinition = function() { var name = this.value.match(/#([^\)'"]+)/); if (name) { name = name[1]; } if (!name) { name = this.value; } return svg.Definitions[name]; }; svg.Property.prototype.isUrlDefinition = function() { return this.value.indexOf("url(") == 0; }; svg.Property.prototype.getFillStyleDefinition = function(e, opacityProp) { var def = this.getDefinition(); // gradient if (def != null && def.createGradient) { return def.createGradient(svg.ctx, e, opacityProp); } // pattern if (def != null && def.createPattern) { if (def.getHrefAttribute().hasValue()) { var pt = def.attribute("patternTransform"); def = def.getHrefAttribute().getDefinition(); if (pt.hasValue()) { def.attribute("patternTransform", true).value = pt.value; } } return def.createPattern(svg.ctx, e); } return null; }; // length extensions svg.Property.prototype.getDPI = function(viewPort) { return 96; }; svg.Property.prototype.getEM = function(viewPort) { var em = 12; var fontSize = new svg.Property("fontSize", svg.Font.Parse(svg.ctx.font).fontSize); if (fontSize.hasValue()) em = fontSize.toPixels(viewPort); return em; }; svg.Property.prototype.getUnits = function() { var s = this.value + ""; return s.replace(/[0-9\.\-]/g, ""); }; // get the length as pixels svg.Property.prototype.toPixels = function(viewPort, processPercent) { if (!this.hasValue()) return 0; var s = this.value + ""; if (s.match(/em$/)) return this.numValue() * this.getEM(viewPort); if (s.match(/ex$/)) return this.numValue() * this.getEM(viewPort) / 2; if (s.match(/px$/)) return this.numValue(); if (s.match(/pt$/)) return this.numValue() * this.getDPI(viewPort) * (1 / 72); if (s.match(/pc$/)) return this.numValue() * 15; if (s.match(/cm$/)) return this.numValue() * this.getDPI(viewPort) / 2.54; if (s.match(/mm$/)) return this.numValue() * this.getDPI(viewPort) / 25.4; if (s.match(/in$/)) return this.numValue() * this.getDPI(viewPort); if (s.match(/%$/)) return this.numValue() * svg.ViewPort.ComputeSize(viewPort); var n = this.numValue(); if (processPercent && n < 1) return n * svg.ViewPort.ComputeSize(viewPort); return n; }; // time extensions // get the time as milliseconds svg.Property.prototype.toMilliseconds = function() { if (!this.hasValue()) return 0; var s = this.value + ""; if (s.match(/s$/)) return this.numValue() * 1e3; if (s.match(/ms$/)) return this.numValue(); return this.numValue(); }; // angle extensions // get the angle as radians svg.Property.prototype.toRadians = function() { if (!this.hasValue()) return 0; var s = this.value + ""; if (s.match(/deg$/)) return this.numValue() * (Math.PI / 180); if (s.match(/grad$/)) return this.numValue() * (Math.PI / 200); if (s.match(/rad$/)) return this.numValue(); return this.numValue() * (Math.PI / 180); }; // fonts svg.Font = new function() { this.Styles = "normal|italic|oblique|inherit"; this.Variants = "normal|small-caps|inherit"; this.Weights = "normal|bold|bolder|lighter|100|200|300|400|500|600|700|800|900|inherit"; this.CreateFont = function(fontStyle, fontVariant, fontWeight, fontSize, fontFamily, inherit) { var f = inherit != null ? this.Parse(inherit) : this.CreateFont("", "", "", "", "", svg.ctx.font); return { fontFamily: fontFamily || f.fontFamily, fontSize: fontSize || f.fontSize, fontStyle: fontStyle || f.fontStyle, fontWeight: fontWeight || f.fontWeight, fontVariant: fontVariant || f.fontVariant, toString: function() { return [ this.fontStyle, this.fontVariant, this.fontWeight, this.fontSize, this.fontFamily ].join(" "); } }; }; var that = this; this.Parse = function(s) { var f = {}; var d = svg.trim(svg.compressSpaces(s || "")).split(" "); var set = { fontSize: false, fontStyle: false, fontWeight: false, fontVariant: false }; var ff = ""; for (var i = 0; i < d.length; i++) { if (!set.fontStyle && that.Styles.indexOf(d[i]) != -1) { if (d[i] != "inherit") f.fontStyle = d[i]; set.fontStyle = true; } else if (!set.fontVariant && that.Variants.indexOf(d[i]) != -1) { if (d[i] != "inherit") f.fontVariant = d[i]; set.fontStyle = set.fontVariant = true; } else if (!set.fontWeight && that.Weights.indexOf(d[i]) != -1) { if (d[i] != "inherit") f.fontWeight = d[i]; set.fontStyle = set.fontVariant = set.fontWeight = true; } else if (!set.fontSize) { if (d[i] != "inherit") f.fontSize = d[i].split("/")[0]; set.fontStyle = set.fontVariant = set.fontWeight = set.fontSize = true; } else { if (d[i] != "inherit") ff += d[i]; } } if (ff != "") f.fontFamily = ff; return f; }; }(); // points and paths svg.ToNumberArray = function(s) { var a = svg.trim(svg.compressSpaces((s || "").replace(/,/g, " "))).split(" "); for (var i = 0; i < a.length; i++) { a[i] = parseFloat(a[i]); } return a; }; svg.Point = function(x, y) { this.x = x; this.y = y; }; svg.Point.prototype.angleTo = function(p) { return Math.atan2(p.y - this.y, p.x - this.x); }; svg.Point.prototype.applyTransform = function(v) { var xp = this.x * v[0] + this.y * v[2] + v[4]; var yp = this.x * v[1] + this.y * v[3] + v[5]; this.x = xp; this.y = yp; }; svg.CreatePoint = function(s) { var a = svg.ToNumberArray(s); return new svg.Point(a[0], a[1]); }; svg.CreatePath = function(s) { var a = svg.ToNumberArray(s); var path = []; for (var i = 0; i < a.length; i += 2) { path.push(new svg.Point(a[i], a[i + 1])); } return path; }; // bounding box svg.BoundingBox = function(x1, y1, x2, y2) { // pass in initial points if you want this.x1 = Number.NaN; this.y1 = Number.NaN; this.x2 = Number.NaN; this.y2 = Number.NaN; this.x = function() { return this.x1; }; this.y = function() { return this.y1; }; this.width = function() { return this.x2 - this.x1; }; this.height = function() { return this.y2 - this.y1; }; this.addPoint = function(x, y) { if (x != null) { if (isNaN(this.x1) || isNaN(this.x2)) { this.x1 = x; this.x2 = x; } if (x < this.x1) this.x1 = x; if (x > this.x2) this.x2 = x; } if (y != null) { if (isNaN(this.y1) || isNaN(this.y2)) { this.y1 = y; this.y2 = y; } if (y < this.y1) this.y1 = y; if (y > this.y2) this.y2 = y; } }; this.addX = function(x) { this.addPoint(x, null); }; this.addY = function(y) { this.addPoint(null, y); }; this.addBoundingBox = function(bb) { this.addPoint(bb.x1, bb.y1); this.addPoint(bb.x2, bb.y2); }; this.addQuadraticCurve = function(p0x, p0y, p1x, p1y, p2x, p2y) { var cp1x = p0x + 2 / 3 * (p1x - p0x); // CP1 = QP0 + 2/3 *(QP1-QP0) var cp1y = p0y + 2 / 3 * (p1y - p0y); // CP1 = QP0 + 2/3 *(QP1-QP0) var cp2x = cp1x + 1 / 3 * (p2x - p0x); // CP2 = CP1 + 1/3 *(QP2-QP0) var cp2y = cp1y + 1 / 3 * (p2y - p0y); // CP2 = CP1 + 1/3 *(QP2-QP0) this.addBezierCurve(p0x, p0y, cp1x, cp2x, cp1y, cp2y, p2x, p2y); }; this.addBezierCurve = function(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) { // from http://blog.hackers-cafe.net/2009/06/how-to-calculate-bezier-curves-bounding.html var p0 = [ p0x, p0y ], p1 = [ p1x, p1y ], p2 = [ p2x, p2y ], p3 = [ p3x, p3y ]; this.addPoint(p0[0], p0[1]); this.addPoint(p3[0], p3[1]); for (i = 0; i <= 1; i++) { var f = function(t) { return Math.pow(1 - t, 3) * p0[i] + 3 * Math.pow(1 - t, 2) * t * p1[i] + 3 * (1 - t) * Math.pow(t, 2) * p2[i] + Math.pow(t, 3) * p3[i]; }; var b = 6 * p0[i] - 12 * p1[i] + 6 * p2[i]; var a = -3 * p0[i] + 9 * p1[i] - 9 * p2[i] + 3 * p3[i]; var c = 3 * p1[i] - 3 * p0[i]; if (a == 0) { if (b == 0) continue; var t = -c / b; if (0 < t && t < 1) { if (i == 0) this.addX(f(t)); if (i == 1) this.addY(f(t)); } continue; } var b2ac = Math.pow(b, 2) - 4 * c * a; if (b2ac < 0) continue; var t1 = (-b + Math.sqrt(b2ac)) / (2 * a); if (0 < t1 && t1 < 1) { if (i == 0) this.addX(f(t1)); if (i == 1) this.addY(f(t1)); } var t2 = (-b - Math.sqrt(b2ac)) / (2 * a); if (0 < t2 && t2 < 1) { if (i == 0) this.addX(f(t2)); if (i == 1) this.addY(f(t2)); } } }; this.isPointInBox = function(x, y) { return this.x1 <= x && x <= this.x2 && this.y1 <= y && y <= this.y2; }; this.addPoint(x1, y1); this.addPoint(x2, y2); }; // transforms svg.Transform = function(v) { var that = this; this.Type = {}; // translate this.Type.translate = function(s) { this.p = svg.CreatePoint(s); this.apply = function(ctx) { ctx.translate(this.p.x || 0, this.p.y || 0); }; this.unapply = function(ctx) { ctx.translate(-1 * this.p.x || 0, -1 * this.p.y || 0); }; this.applyToPoint = function(p) { p.applyTransform([ 1, 0, 0, 1, this.p.x || 0, this.p.y || 0 ]); }; }; // rotate this.Type.rotate = function(s) { var a = svg.ToNumberArray(s); this.angle = new svg.Property("angle", a[0]); this.cx = a[1] || 0; this.cy = a[2] || 0; this.apply = function(ctx) { ctx.translate(this.cx, this.cy); ctx.rotate(this.angle.toRadians()); ctx.translate(-this.cx, -this.cy); }; this.unapply = function(ctx) { ctx.translate(this.cx, this.cy); ctx.rotate(-1 * this.angle.toRadians()); ctx.translate(-this.cx, -this.cy); }; this.applyToPoint = function(p) { var a = this.angle.toRadians(); p.applyTransform([ 1, 0, 0, 1, this.p.x || 0, this.p.y || 0 ]); p.applyTransform([ Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0 ]); p.applyTransform([ 1, 0, 0, 1, -this.p.x || 0, -this.p.y || 0 ]); }; }; this.Type.scale = function(s) { this.p = svg.CreatePoint(s); this.apply = function(ctx) { ctx.scale(this.p.x || 1, this.p.y || this.p.x || 1); }; this.unapply = function(ctx) { ctx.scale(1 / this.p.x || 1, 1 / this.p.y || this.p.x || 1); }; this.applyToPoint = function(p) { p.applyTransform([ this.p.x || 0, 0, 0, this.p.y || 0, 0, 0 ]); }; }; this.Type.matrix = function(s) { this.m = svg.ToNumberArray(s); this.apply = function(ctx) { ctx.transform(this.m[0], this.m[1], this.m[2], this.m[3], this.m[4], this.m[5]); }; this.applyToPoint = function(p) { p.applyTransform(this.m); }; }; this.Type.SkewBase = function(s) { this.base = that.Type.matrix; this.base(s); this.angle = new svg.Property("angle", s); }; this.Type.SkewBase.prototype = new this.Type.matrix(); this.Type.skewX = function(s) { this.base = that.Type.SkewBase; this.base(s); this.m = [ 1, 0, Math.tan(this.angle.toRadians()), 1, 0, 0 ]; }; this.Type.skewX.prototype = new this.Type.SkewBase(); this.Type.skewY = function(s) { this.base = that.Type.SkewBase; this.base(s); this.m = [ 1, Math.tan(this.angle.toRadians()), 0, 1, 0, 0 ]; }; this.Type.skewY.prototype = new this.Type.SkewBase(); this.transforms = []; this.apply = function(ctx) { for (var i = 0; i < this.transforms.length; i++) { this.transforms[i].apply(ctx); } }; this.unapply = function(ctx) { for (var i = this.transforms.length - 1; i >= 0; i--) { this.transforms[i].unapply(ctx); } }; this.applyToPoint = function(p) { for (var i = 0; i < this.transforms.length; i++) { this.transforms[i].applyToPoint(p); } }; var data = svg.trim(svg.compressSpaces(v)).replace(/\)(\s?,\s?)/g, ") ").split(/\s(?=[a-z])/); for (var i = 0; i < data.length; i++) { var type = svg.trim(data[i].split("(")[0]); var s = data[i].split("(")[1].replace(")", ""); var transform = new this.Type[type](s); transform.type = type; this.transforms.push(transform); } }; // aspect ratio svg.AspectRatio = function(ctx, aspectRatio, width, desiredWidth, height, desiredHeight, minX, minY, refX, refY) { // aspect ratio - http://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute aspectRatio = svg.compressSpaces(aspectRatio); aspectRatio = aspectRatio.replace(/^defer\s/, ""); // ignore defer var align = aspectRatio.split(" ")[0] || "xMidYMid"; var meetOrSlice = aspectRatio.split(" ")[1] || "meet"; // calculate scale var scaleX = width / desiredWidth; var scaleY = height / desiredHeight; var scaleMin = Math.min(scaleX, scaleY); var scaleMax = Math.max(scaleX, scaleY); if (meetOrSlice == "meet") { desiredWidth *= scaleMin; desiredHeight *= scaleMin; } if (meetOrSlice == "slice") { desiredWidth *= scaleMax; desiredHeight *= scaleMax; } refX = new svg.Property("refX", refX); refY = new svg.Property("refY", refY); if (refX.hasValue() && refY.hasValue()) { ctx.translate(-scaleMin * refX.toPixels("x"), -scaleMin * refY.toPixels("y")); } else { // align if (align.match(/^xMid/) && (meetOrSlice == "meet" && scaleMin == scaleY || meetOrSlice == "slice" && scaleMax == scaleY)) ctx.translate(width / 2 - desiredWidth / 2, 0); if (align.match(/YMid$/) && (meetOrSlice == "meet" && scaleMin == scaleX || meetOrSlice == "slice" && scaleMax == scaleX)) ctx.translate(0, height / 2 - desiredHeight / 2); if (align.match(/^xMax/) && (meetOrSlice == "meet" && scaleMin == scaleY || meetOrSlice == "slice" && scaleMax == scaleY)) ctx.translate(width - desiredWidth, 0); if (align.match(/YMax$/) && (meetOrSlice == "meet" && scaleMin == scaleX || meetOrSlice == "slice" && scaleMax == scaleX)) ctx.translate(0, height - desiredHeight); } // scale if (align == "none") ctx.scale(scaleX, scaleY); else if (meetOrSlice == "meet") ctx.scale(scaleMin, scaleMin); else if (meetOrSlice == "slice") ctx.scale(scaleMax, scaleMax); // translate ctx.translate(minX == null ? 0 : -minX, minY == null ? 0 : -minY); }; // elements svg.Element = {}; svg.EmptyProperty = new svg.Property("EMPTY", ""); svg.Element.ElementBase = function(node) { this.attributes = {}; this.styles = {}; this.children = []; // get or create attribute this.attribute = function(name, createIfNotExists) { var a = this.attributes[name]; if (a != null) return a; if (createIfNotExists == true) { a = new svg.Property(name, ""); this.attributes[name] = a; } return a || svg.EmptyProperty; }; this.getHrefAttribute = function() { for (var a in this.attributes) { if (a.match(/:href$/)) { return this.attributes[a]; } } return svg.EmptyProperty; }; // get or create style, crawls up node tree this.style = function(name, createIfNotExists) { var s = this.styles[name]; if (s != null) return s; var a = this.attribute(name); if (a != null && a.hasValue()) { this.styles[name] = a; // move up to me to cache return a; } var p = this.parent; if (p != null) { var ps = p.style(name); if (ps != null && ps.hasValue()) { return ps; } } if (createIfNotExists == true) { s = new svg.Property(name, ""); this.styles[name] = s; } return s || svg.EmptyProperty; }; // base render this.render = function(ctx) { // don't render display=none if (this.style("display").value == "none") return; // don't render visibility=hidden if (this.attribute("visibility").value == "hidden") return; ctx.save(); if (this.attribute("mask").hasValue()) { // mask var mask = this.attribute("mask").getDefinition(); if (mask != null) mask.apply(ctx, this); } else if (this.style("filter").hasValue()) { // filter var filter = this.style("filter").getDefinition(); if (filter != null) filter.apply(ctx, this); } else { this.setContext(ctx); this.renderChildren(ctx); this.clearContext(ctx); } ctx.restore(); }; // base set context this.setContext = function(ctx) {}; // base clear context this.clearContext = function(ctx) {}; // base render children this.renderChildren = function(ctx) { for (var i = 0; i < this.children.length; i++) { this.children[i].render(ctx); } }; this.addChild = function(childNode, create) { var child = childNode; if (create) child = svg.CreateElement(childNode); child.parent = this; this.children.push(child); }; if (node != null && node.nodeType == 1) { //ELEMENT_NODE // add children for (var i = 0; i < node.childNodes.length; i++) { var childNode = node.childNodes[i]; if (childNode.nodeType == 1) this.addChild(childNode, true); //ELEMENT_NODE if (this.captureTextNodes && childNode.nodeType == 3) { var text = childNode.nodeValue || childNode.text || ""; if (svg.trim(svg.compressSpaces(text)) != "") { this.addChild(new svg.Element.tspan(childNode), false); } } } // add attributes for (var i = 0; i < node.attributes.length; i++) { var attribute = node.attributes[i]; this.attributes[attribute.nodeName] = new svg.Property(attribute.nodeName, attribute.nodeValue); } // add tag styles var styles = svg.Styles[node.nodeName]; if (styles != null) { for (var name in styles) { this.styles[name] = styles[name]; } } // add class styles if (this.attribute("class").hasValue()) { var classes = svg.compressSpaces(this.attribute("class").value).split(" "); for (var j = 0; j < classes.length; j++) { styles = svg.Styles["." + classes[j]]; if (styles != null) { for (var name in styles) { this.styles[name] = styles[name]; } } styles = svg.Styles[node.nodeName + "." + classes[j]]; if (styles != null) { for (var name in styles) { this.styles[name] = styles[name]; } } } } // add id styles if (this.attribute("id").hasValue()) { var styles = svg.Styles["#" + this.attribute("id").value]; if (styles != null) { for (var name in styles) { this.styles[name] = styles[name]; } } } // add inline styles if (this.attribute("style").hasValue()) { var styles = this.attribute("style").value.split(";"); for (var i = 0; i < styles.length; i++) { if (svg.trim(styles[i]) != "") { var style = styles[i].split(":"); var name = svg.trim(style[0]); var value = svg.trim(style[1]); this.styles[name] = new svg.Property(name, value); } } } // add id if (this.attribute("id").hasValue()) { if (svg.Definitions[this.attribute("id").value] == null) { svg.Definitions[this.attribute("id").value] = this; } } } }; svg.Element.RenderedElementBase = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.setContext = function(ctx) { // fill if (this.style("fill").isUrlDefinition()) { var fs = this.style("fill").getFillStyleDefinition(this, this.style("fill-opacity")); if (fs != null) ctx.fillStyle = fs; } else if (this.style("fill").hasValue()) { var fillStyle = this.style("fill"); if (fillStyle.value == "currentColor") fillStyle.value = this.style("color").value; ctx.fillStyle = fillStyle.value == "none" ? "rgba(0,0,0,0)" : fillStyle.value; } if (this.style("fill-opacity").hasValue()) { var fillStyle = new svg.Property("fill", ctx.fillStyle); fillStyle = fillStyle.addOpacity(this.style("fill-opacity").value); ctx.fillStyle = fillStyle.value; } // stroke if (this.style("stroke").isUrlDefinition()) { var fs = this.style("stroke").getFillStyleDefinition(this, this.style("stroke-opacity")); if (fs != null) ctx.strokeStyle = fs; } else if (this.style("stroke").hasValue()) { var strokeStyle = this.style("stroke"); if (strokeStyle.value == "currentColor") strokeStyle.value = this.style("color").value; ctx.strokeStyle = strokeStyle.value == "none" ? "rgba(0,0,0,0)" : strokeStyle.value; } if (this.style("stroke-opacity").hasValue()) { var strokeStyle = new svg.Property("stroke", ctx.strokeStyle); strokeStyle = strokeStyle.addOpacity(this.style("stroke-opacity").value); ctx.strokeStyle = strokeStyle.value; } if (this.style("stroke-width").hasValue()) { var newLineWidth = this.style("stroke-width").toPixels(); ctx.lineWidth = newLineWidth == 0 ? .001 : newLineWidth; } if (this.style("stroke-linecap").hasValue()) ctx.lineCap = this.style("stroke-linecap").value; if (this.style("stroke-linejoin").hasValue()) ctx.lineJoin = this.style("stroke-linejoin").value; if (this.style("stroke-miterlimit").hasValue()) ctx.miterLimit = this.style("stroke-miterlimit").value; if (this.style("stroke-dasharray").hasValue()) { var gaps = svg.ToNumberArray(this.style("stroke-dasharray").value); if (typeof ctx.setLineDash != "undefined") { ctx.setLineDash(gaps); } else if (typeof ctx.webkitLineDash != "undefined") { ctx.webkitLineDash = gaps; } else if (typeof ctx.mozDash != "undefined") { ctx.mozDash = gaps; } var offset = this.style("stroke-dashoffset").numValueOrDefault(1); if (typeof ctx.lineDashOffset != "undefined") { ctx.lineDashOffset = offset; } else if (typeof ctx.webkitLineDashOffset != "undefined") { ctx.webkitLineDashOffset = offset; } else if (typeof ctx.mozDashOffset != "undefined") { ctx.mozDashOffset = offset; } } // font if (typeof ctx.font != "undefined") { ctx.font = svg.Font.CreateFont(this.style("font-style").value, this.style("font-variant").value, this.style("font-weight").value, this.style("font-size").hasValue() ? this.style("font-size").toPixels() + "px" : "", this.style("font-family").value).toString(); } // transform if (this.attribute("transform").hasValue()) { var transform = new svg.Transform(this.attribute("transform").value); transform.apply(ctx); } // clip if (this.style("clip-path").hasValue()) { var clip = this.style("clip-path").getDefinition(); if (clip != null) clip.apply(ctx); } // opacity if (this.style("opacity").hasValue()) { ctx.globalAlpha = this.style("opacity").numValue(); } }; }; svg.Element.RenderedElementBase.prototype = new svg.Element.ElementBase(); svg.Element.PathElementBase = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.path = function(ctx) { if (ctx != null) ctx.beginPath(); return new svg.BoundingBox(); }; this.renderChildren = function(ctx) { this.path(ctx); svg.Mouse.checkPath(this, ctx); if (ctx.fillStyle != "") { if (this.attribute("fill-rule").hasValue()) { ctx.fill(this.attribute("fill-rule").value); } else { ctx.fill(); } } if (ctx.strokeStyle != "") ctx.stroke(); var markers = this.getMarkers(); if (markers != null) { if (this.style("marker-start").isUrlDefinition()) { var marker = this.style("marker-start").getDefinition(); marker.render(ctx, markers[0][0], markers[0][1]); } if (this.style("marker-mid").isUrlDefinition()) { var marker = this.style("marker-mid").getDefinition(); for (var i = 1; i < markers.length - 1; i++) { marker.render(ctx, markers[i][0], markers[i][1]); } } if (this.style("marker-end").isUrlDefinition()) { var marker = this.style("marker-end").getDefinition(); marker.render(ctx, markers[markers.length - 1][0], markers[markers.length - 1][1]); } } }; this.getBoundingBox = function() { return this.path(); }; this.getMarkers = function() { return null; }; }; svg.Element.PathElementBase.prototype = new svg.Element.RenderedElementBase(); // svg element svg.Element.svg = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.baseClearContext = this.clearContext; this.clearContext = function(ctx) { this.baseClearContext(ctx); svg.ViewPort.RemoveCurrent(); }; this.baseSetContext = this.setContext; this.setContext = function(ctx) { // initial values ctx.strokeStyle = "rgba(0,0,0,0)"; ctx.lineCap = "butt"; ctx.lineJoin = "miter"; ctx.miterLimit = 4; this.baseSetContext(ctx); // create new view port if (!this.attribute("x").hasValue()) this.attribute("x", true).value = 0; if (!this.attribute("y").hasValue()) this.attribute("y", true).value = 0; ctx.translate(this.attribute("x").toPixels("x"), this.attribute("y").toPixels("y")); var width = svg.ViewPort.width(); var height = svg.ViewPort.height(); if (!this.attribute("width").hasValue()) this.attribute("width", true).value = "100%"; if (!this.attribute("height").hasValue()) this.attribute("height", true).value = "100%"; if (typeof this.root == "undefined") { width = this.attribute("width").toPixels("x"); height = this.attribute("height").toPixels("y"); var x = 0; var y = 0; if (this.attribute("refX").hasValue() && this.attribute("refY").hasValue()) { x = -this.attribute("refX").toPixels("x"); y = -this.attribute("refY").toPixels("y"); } ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(width, y); ctx.lineTo(width, height); ctx.lineTo(x, height); ctx.closePath(); ctx.clip(); } svg.ViewPort.SetCurrent(width, height); // viewbox if (this.attribute("viewBox").hasValue()) { var viewBox = svg.ToNumberArray(this.attribute("viewBox").value); var minX = viewBox[0]; var minY = viewBox[1]; width = viewBox[2]; height = viewBox[3]; svg.AspectRatio(ctx, this.attribute("preserveAspectRatio").value, svg.ViewPort.width(), width, svg.ViewPort.height(), height, minX, minY, this.attribute("refX").value, this.attribute("refY").value); svg.ViewPort.RemoveCurrent(); svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]); } }; }; svg.Element.svg.prototype = new svg.Element.RenderedElementBase(); // rect element svg.Element.rect = function(node) { this.base = svg.Element.PathElementBase; this.base(node); this.path = function(ctx) { var x = this.attribute("x").toPixels("x"); var y = this.attribute("y").toPixels("y"); var width = this.attribute("width").toPixels("x"); var height = this.attribute("height").toPixels("y"); var rx = this.attribute("rx").toPixels("x"); var ry = this.attribute("ry").toPixels("y"); if (this.attribute("rx").hasValue() && !this.attribute("ry").hasValue()) ry = rx; if (this.attribute("ry").hasValue() && !this.attribute("rx").hasValue()) rx = ry; rx = Math.min(rx, width / 2); ry = Math.min(ry, height / 2); if (ctx != null) { ctx.beginPath(); ctx.moveTo(x + rx, y); ctx.lineTo(x + width - rx, y); ctx.quadraticCurveTo(x + width, y, x + width, y + ry); ctx.lineTo(x + width, y + height - ry); ctx.quadraticCurveTo(x + width, y + height, x + width - rx, y + height); ctx.lineTo(x + rx, y + height); ctx.quadraticCurveTo(x, y + height, x, y + height - ry); ctx.lineTo(x, y + ry); ctx.quadraticCurveTo(x, y, x + rx, y); ctx.closePath(); } return new svg.BoundingBox(x, y, x + width, y + height); }; }; svg.Element.rect.prototype = new svg.Element.PathElementBase(); // circle element svg.Element.circle = function(node) { this.base = svg.Element.PathElementBase; this.base(node); this.path = function(ctx) { var cx = this.attribute("cx").toPixels("x"); var cy = this.attribute("cy").toPixels("y"); var r = this.attribute("r").toPixels(); if (ctx != null) { ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2, true); ctx.closePath(); } return new svg.BoundingBox(cx - r, cy - r, cx + r, cy + r); }; }; svg.Element.circle.prototype = new svg.Element.PathElementBase(); // ellipse element svg.Element.ellipse = function(node) { this.base = svg.Element.PathElementBase; this.base(node); this.path = function(ctx) { var KAPPA = 4 * ((Math.sqrt(2) - 1) / 3); var rx = this.attribute("rx").toPixels("x"); var ry = this.attribute("ry").toPixels("y"); var cx = this.attribute("cx").toPixels("x"); var cy = this.attribute("cy").toPixels("y"); if (ctx != null) { ctx.beginPath(); ctx.moveTo(cx, cy - ry); ctx.bezierCurveTo(cx + KAPPA * rx, cy - ry, cx + rx, cy - KAPPA * ry, cx + rx, cy); ctx.bezierCurveTo(cx + rx, cy + KAPPA * ry, cx + KAPPA * rx, cy + ry, cx, cy + ry); ctx.bezierCurveTo(cx - KAPPA * rx, cy + ry, cx - rx, cy + KAPPA * ry, cx - rx, cy); ctx.bezierCurveTo(cx - rx, cy - KAPPA * ry, cx - KAPPA * rx, cy - ry, cx, cy - ry); ctx.closePath(); } return new svg.BoundingBox(cx - rx, cy - ry, cx + rx, cy + ry); }; }; svg.Element.ellipse.prototype = new svg.Element.PathElementBase(); // line element svg.Element.line = function(node) { this.base = svg.Element.PathElementBase; this.base(node); this.getPoints = function() { return [ new svg.Point(this.attribute("x1").toPixels("x"), this.attribute("y1").toPixels("y")), new svg.Point(this.attribute("x2").toPixels("x"), this.attribute("y2").toPixels("y")) ]; }; this.path = function(ctx) { var points = this.getPoints(); if (ctx != null) { ctx.beginPath(); ctx.moveTo(points[0].x, points[0].y); ctx.lineTo(points[1].x, points[1].y); } return new svg.BoundingBox(points[0].x, points[0].y, points[1].x, points[1].y); }; this.getMarkers = function() { var points = this.getPoints(); var a = points[0].angleTo(points[1]); return [ [ points[0], a ], [ points[1], a ] ]; }; }; svg.Element.line.prototype = new svg.Element.PathElementBase(); // polyline element svg.Element.polyline = function(node) { this.base = svg.Element.PathElementBase; this.base(node); this.points = svg.CreatePath(this.attribute("points").value); this.path = function(ctx) { var bb = new svg.BoundingBox(this.points[0].x, this.points[0].y); if (ctx != null) { ctx.beginPath(); ctx.moveTo(this.points[0].x, this.points[0].y); } for (var i = 1; i < this.points.length; i++) { bb.addPoint(this.points[i].x, this.points[i].y); if (ctx != null) ctx.lineTo(this.points[i].x, this.points[i].y); } return bb; }; this.getMarkers = function() { var markers = []; for (var i = 0; i < this.points.length - 1; i++) { markers.push([ this.points[i], this.points[i].angleTo(this.points[i + 1]) ]); } markers.push([ this.points[this.points.length - 1], markers[markers.length - 1][1] ]); return markers; }; }; svg.Element.polyline.prototype = new svg.Element.PathElementBase(); // polygon element svg.Element.polygon = function(node) { this.base = svg.Element.polyline; this.base(node); this.basePath = this.path; this.path = function(ctx) { var bb = this.basePath(ctx); if (ctx != null) { ctx.lineTo(this.points[0].x, this.points[0].y); ctx.closePath(); } return bb; }; }; svg.Element.polygon.prototype = new svg.Element.polyline(); // path element svg.Element.path = function(node) { this.base = svg.Element.PathElementBase; this.base(node); var d = this.attribute("d").value; // TODO: convert to real lexer based on http://www.w3.org/TR/SVG11/paths.html#PathDataBNF d = d.replace(/,/gm, " "); // get rid of all commas d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm, "$1 $2"); // separate commands from commands d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([MmZzLlHhVvCcSsQqTtAa])/gm, "$1 $2"); // separate commands from commands d = d.replace(/([MmZzLlHhVvCcSsQqTtAa])([^\s])/gm, "$1 $2"); // separate commands from points d = d.replace(/([^\s])([MmZzLlHhVvCcSsQqTtAa])/gm, "$1 $2"); // separate commands from points d = d.replace(/([0-9])([+\-])/gm, "$1 $2"); // separate digits when no comma d = d.replace(/(\.[0-9]*)(\.)/gm, "$1 $2"); // separate digits when no comma d = d.replace(/([Aa](\s+[0-9]+){3})\s+([01])\s*([01])/gm, "$1 $3 $4 "); // shorthand elliptical arc path syntax d = svg.compressSpaces(d); // compress multiple spaces d = svg.trim(d); this.PathParser = new function(d) { this.tokens = d.split(" "); this.reset = function() { this.i = -1; this.command = ""; this.previousCommand = ""; this.start = new svg.Point(0, 0); this.control = new svg.Point(0, 0); this.current = new svg.Point(0, 0); this.points = []; this.angles = []; }; this.isEnd = function() { return this.i >= this.tokens.length - 1; }; this.isCommandOrEnd = function() { if (this.isEnd()) return true; return this.tokens[this.i + 1].match(/^[A-Za-z]$/) != null; }; this.isRelativeCommand = function() { switch (this.command) { case "m": case "l": case "h": case "v": case "c": case "s": case "q": case "t": case "a": case "z": return true; break; } return false; }; this.getToken = function() { this.i++; return this.tokens[this.i]; }; this.getScalar = function() { return parseFloat(this.getToken()); }; this.nextCommand = function() { this.previousCommand = this.command; this.command = this.getToken(); }; this.getPoint = function() { var p = new svg.Point(this.getScalar(), this.getScalar()); return this.makeAbsolute(p); }; this.getAsControlPoint = function() { var p = this.getPoint(); this.control = p; return p; }; this.getAsCurrentPoint = function() { var p = this.getPoint(); this.current = p; return p; }; this.getReflectedControlPoint = function() { if (this.previousCommand.toLowerCase() != "c" && this.previousCommand.toLowerCase() != "s" && this.previousCommand.toLowerCase() != "q" && this.previousCommand.toLowerCase() != "t") { return this.current; } // reflect point var p = new svg.Point(2 * this.current.x - this.control.x, 2 * this.current.y - this.control.y); return p; }; this.makeAbsolute = function(p) { if (this.isRelativeCommand()) { p.x += this.current.x; p.y += this.current.y; } return p; }; this.addMarker = function(p, from, priorTo) { // if the last angle isn't filled in because we didn't have this point yet ... if (priorTo != null && this.angles.length > 0 && this.angles[this.angles.length - 1] == null) { this.angles[this.angles.length - 1] = this.points[this.points.length - 1].angleTo(priorTo); } this.addMarkerAngle(p, from == null ? null : from.angleTo(p)); }; this.addMarkerAngle = function(p, a) { this.points.push(p); this.angles.push(a); }; this.getMarkerPoints = function() { return this.points; }; this.getMarkerAngles = function() { for (var i = 0; i < this.angles.length; i++) { if (this.angles[i] == null) { for (var j = i + 1; j < this.angles.length; j++) { if (this.angles[j] != null) { this.angles[i] = this.angles[j]; break; } } } } return this.angles; }; }(d); this.path = function(ctx) { var pp = this.PathParser; pp.reset(); var bb = new svg.BoundingBox(); if (ctx != null) ctx.beginPath(); while (!pp.isEnd()) { pp.nextCommand(); switch (pp.command) { case "M": case "m": var p = pp.getAsCurrentPoint(); pp.addMarker(p); bb.addPoint(p.x, p.y); if (ctx != null) ctx.moveTo(p.x, p.y); pp.start = pp.current; while (!pp.isCommandOrEnd()) { var p = pp.getAsCurrentPoint(); pp.addMarker(p, pp.start); bb.addPoint(p.x, p.y); if (ctx != null) ctx.lineTo(p.x, p.y); } break; case "L": case "l": while (!pp.isCommandOrEnd()) { var c = pp.current; var p = pp.getAsCurrentPoint(); pp.addMarker(p, c); bb.addPoint(p.x, p.y); if (ctx != null) ctx.lineTo(p.x, p.y); } break; case "H": case "h": while (!pp.isCommandOrEnd()) { var newP = new svg.Point((pp.isRelativeCommand() ? pp.current.x : 0) + pp.getScalar(), pp.current.y); pp.addMarker(newP, pp.current); pp.current = newP; bb.addPoint(pp.current.x, pp.current.y); if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); } break; case "V": case "v": while (!pp.isCommandOrEnd()) { var newP = new svg.Point(pp.current.x, (pp.isRelativeCommand() ? pp.current.y : 0) + pp.getScalar()); pp.addMarker(newP, pp.current); pp.current = newP; bb.addPoint(pp.current.x, pp.current.y); if (ctx != null) ctx.lineTo(pp.current.x, pp.current.y); } break; case "C": case "c": while (!pp.isCommandOrEnd()) { var curr = pp.current; var p1 = pp.getPoint(); var cntrl = pp.getAsControlPoint(); var cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, p1); bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); } break; case "S": case "s": while (!pp.isCommandOrEnd()) { var curr = pp.current; var p1 = pp.getReflectedControlPoint(); var cntrl = pp.getAsControlPoint(); var cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, p1); bb.addBezierCurve(curr.x, curr.y, p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); if (ctx != null) ctx.bezierCurveTo(p1.x, p1.y, cntrl.x, cntrl.y, cp.x, cp.y); } break; case "Q": case "q": while (!pp.isCommandOrEnd()) { var curr = pp.current; var cntrl = pp.getAsControlPoint(); var cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, cntrl); bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); } break; case "T": case "t": while (!pp.isCommandOrEnd()) { var curr = pp.current; var cntrl = pp.getReflectedControlPoint(); pp.control = cntrl; var cp = pp.getAsCurrentPoint(); pp.addMarker(cp, cntrl, cntrl); bb.addQuadraticCurve(curr.x, curr.y, cntrl.x, cntrl.y, cp.x, cp.y); if (ctx != null) ctx.quadraticCurveTo(cntrl.x, cntrl.y, cp.x, cp.y); } break; case "A": case "a": while (!pp.isCommandOrEnd()) { var curr = pp.current; var rx = pp.getScalar(); var ry = pp.getScalar(); var xAxisRotation = pp.getScalar() * (Math.PI / 180); var largeArcFlag = pp.getScalar(); var sweepFlag = pp.getScalar(); var cp = pp.getAsCurrentPoint(); // Conversion from endpoint to center parameterization // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes // x1', y1' var currp = new svg.Point(Math.cos(xAxisRotation) * (curr.x - cp.x) / 2 + Math.sin(xAxisRotation) * (curr.y - cp.y) / 2, -Math.sin(xAxisRotation) * (curr.x - cp.x) / 2 + Math.cos(xAxisRotation) * (curr.y - cp.y) / 2); // adjust radii var l = Math.pow(currp.x, 2) / Math.pow(rx, 2) + Math.pow(currp.y, 2) / Math.pow(ry, 2); if (l > 1) { rx *= Math.sqrt(l); ry *= Math.sqrt(l); } // cx', cy' var s = (largeArcFlag == sweepFlag ? -1 : 1) * Math.sqrt((Math.pow(rx, 2) * Math.pow(ry, 2) - Math.pow(rx, 2) * Math.pow(currp.y, 2) - Math.pow(ry, 2) * Math.pow(currp.x, 2)) / (Math.pow(rx, 2) * Math.pow(currp.y, 2) + Math.pow(ry, 2) * Math.pow(currp.x, 2))); if (isNaN(s)) s = 0; var cpp = new svg.Point(s * rx * currp.y / ry, s * -ry * currp.x / rx); // cx, cy var centp = new svg.Point((curr.x + cp.x) / 2 + Math.cos(xAxisRotation) * cpp.x - Math.sin(xAxisRotation) * cpp.y, (curr.y + cp.y) / 2 + Math.sin(xAxisRotation) * cpp.x + Math.cos(xAxisRotation) * cpp.y); // vector magnitude var m = function(v) { return Math.sqrt(Math.pow(v[0], 2) + Math.pow(v[1], 2)); }; // ratio between two vectors var r = function(u, v) { return (u[0] * v[0] + u[1] * v[1]) / (m(u) * m(v)); }; // angle between two vectors var a = function(u, v) { return (u[0] * v[1] < u[1] * v[0] ? -1 : 1) * Math.acos(r(u, v)); }; // initial angle var a1 = a([ 1, 0 ], [ (currp.x - cpp.x) / rx, (currp.y - cpp.y) / ry ]); // angle delta var u = [ (currp.x - cpp.x) / rx, (currp.y - cpp.y) / ry ]; var v = [ (-currp.x - cpp.x) / rx, (-currp.y - cpp.y) / ry ]; var ad = a(u, v); if (r(u, v) <= -1) ad = Math.PI; if (r(u, v) >= 1) ad = 0; // for markers var dir = 1 - sweepFlag ? 1 : -1; var ah = a1 + dir * (ad / 2); var halfWay = new svg.Point(centp.x + rx * Math.cos(ah), centp.y + ry * Math.sin(ah)); pp.addMarkerAngle(halfWay, ah - dir * Math.PI / 2); pp.addMarkerAngle(cp, ah - dir * Math.PI); bb.addPoint(cp.x, cp.y); // TODO: this is too naive, make it better if (ctx != null) { var r = rx > ry ? rx : ry; var sx = rx > ry ? 1 : rx / ry; var sy = rx > ry ? ry / rx : 1; ctx.translate(centp.x, centp.y); ctx.rotate(xAxisRotation); ctx.scale(sx, sy); ctx.arc(0, 0, r, a1, a1 + ad, 1 - sweepFlag); ctx.scale(1 / sx, 1 / sy); ctx.rotate(-xAxisRotation); ctx.translate(-centp.x, -centp.y); } } break; case "Z": case "z": if (ctx != null) ctx.closePath(); pp.current = pp.start; } } return bb; }; this.getMarkers = function() { var points = this.PathParser.getMarkerPoints(); var angles = this.PathParser.getMarkerAngles(); var markers = []; for (var i = 0; i < points.length; i++) { markers.push([ points[i], angles[i] ]); } return markers; }; }; svg.Element.path.prototype = new svg.Element.PathElementBase(); // pattern element svg.Element.pattern = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.createPattern = function(ctx, element) { var width = this.attribute("width").toPixels("x", true); var height = this.attribute("height").toPixels("y", true); // render me using a temporary svg element var tempSvg = new svg.Element.svg(); tempSvg.attributes["viewBox"] = new svg.Property("viewBox", this.attribute("viewBox").value); tempSvg.attributes["width"] = new svg.Property("width", width + "px"); tempSvg.attributes["height"] = new svg.Property("height", height + "px"); tempSvg.attributes["transform"] = new svg.Property("transform", this.attribute("patternTransform").value); tempSvg.children = this.children; var c = document.createElement("canvas"); c.width = width; c.height = height; var cctx = c.getContext("2d"); if (this.attribute("x").hasValue() && this.attribute("y").hasValue()) { cctx.translate(this.attribute("x").toPixels("x", true), this.attribute("y").toPixels("y", true)); } // render 3x3 grid so when we transform there's no white space on edges for (var x = -1; x <= 1; x++) { for (var y = -1; y <= 1; y++) { cctx.save(); cctx.translate(x * c.width, y * c.height); tempSvg.render(cctx); cctx.restore(); } } var pattern = ctx.createPattern(c, "repeat"); return pattern; }; }; svg.Element.pattern.prototype = new svg.Element.ElementBase(); // marker element svg.Element.marker = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.baseRender = this.render; this.render = function(ctx, point, angle) { ctx.translate(point.x, point.y); if (this.attribute("orient").valueOrDefault("auto") == "auto") ctx.rotate(angle); if (this.attribute("markerUnits").valueOrDefault("strokeWidth") == "strokeWidth") ctx.scale(ctx.lineWidth, ctx.lineWidth); ctx.save(); // render me using a temporary svg element var tempSvg = new svg.Element.svg(); tempSvg.attributes["viewBox"] = new svg.Property("viewBox", this.attribute("viewBox").value); tempSvg.attributes["refX"] = new svg.Property("refX", this.attribute("refX").value); tempSvg.attributes["refY"] = new svg.Property("refY", this.attribute("refY").value); tempSvg.attributes["width"] = new svg.Property("width", this.attribute("markerWidth").value); tempSvg.attributes["height"] = new svg.Property("height", this.attribute("markerHeight").value); tempSvg.attributes["fill"] = new svg.Property("fill", this.attribute("fill").valueOrDefault("black")); tempSvg.attributes["stroke"] = new svg.Property("stroke", this.attribute("stroke").valueOrDefault("none")); tempSvg.children = this.children; tempSvg.render(ctx); ctx.restore(); if (this.attribute("markerUnits").valueOrDefault("strokeWidth") == "strokeWidth") ctx.scale(1 / ctx.lineWidth, 1 / ctx.lineWidth); if (this.attribute("orient").valueOrDefault("auto") == "auto") ctx.rotate(-angle); ctx.translate(-point.x, -point.y); }; }; svg.Element.marker.prototype = new svg.Element.ElementBase(); // definitions element svg.Element.defs = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.render = function(ctx) {}; }; svg.Element.defs.prototype = new svg.Element.ElementBase(); // base for gradients svg.Element.GradientBase = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.gradientUnits = this.attribute("gradientUnits").valueOrDefault("objectBoundingBox"); this.stops = []; for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (child.type == "stop") this.stops.push(child); } this.getGradient = function() {}; this.createGradient = function(ctx, element, parentOpacityProp) { var stopsContainer = this; if (this.getHrefAttribute().hasValue()) { stopsContainer = this.getHrefAttribute().getDefinition(); } var addParentOpacity = function(color) { if (parentOpacityProp.hasValue()) { var p = new svg.Property("color", color); return p.addOpacity(parentOpacityProp.value).value; } return color; }; var g = this.getGradient(ctx, element); if (g == null) return addParentOpacity(stopsContainer.stops[stopsContainer.stops.length - 1].color); for (var i = 0; i < stopsContainer.stops.length; i++) { g.addColorStop(stopsContainer.stops[i].offset, addParentOpacity(stopsContainer.stops[i].color)); } if (this.attribute("gradientTransform").hasValue()) { // render as transformed pattern on temporary canvas var rootView = svg.ViewPort.viewPorts[0]; var rect = new svg.Element.rect(); rect.attributes["x"] = new svg.Property("x", -svg.MAX_VIRTUAL_PIXELS / 3); rect.attributes["y"] = new svg.Property("y", -svg.MAX_VIRTUAL_PIXELS / 3); rect.attributes["width"] = new svg.Property("width", svg.MAX_VIRTUAL_PIXELS); rect.attributes["height"] = new svg.Property("height", svg.MAX_VIRTUAL_PIXELS); var group = new svg.Element.g(); group.attributes["transform"] = new svg.Property("transform", this.attribute("gradientTransform").value); group.children = [ rect ]; var tempSvg = new svg.Element.svg(); tempSvg.attributes["x"] = new svg.Property("x", 0); tempSvg.attributes["y"] = new svg.Property("y", 0); tempSvg.attributes["width"] = new svg.Property("width", rootView.width); tempSvg.attributes["height"] = new svg.Property("height", rootView.height); tempSvg.children = [ group ]; var c = document.createElement("canvas"); c.width = rootView.width; c.height = rootView.height; var tempCtx = c.getContext("2d"); tempCtx.fillStyle = g; tempSvg.render(tempCtx); return tempCtx.createPattern(c, "no-repeat"); } return g; }; }; svg.Element.GradientBase.prototype = new svg.Element.ElementBase(); // linear gradient element svg.Element.linearGradient = function(node) { this.base = svg.Element.GradientBase; this.base(node); this.getGradient = function(ctx, element) { var bb = element.getBoundingBox(); if (!this.attribute("x1").hasValue() && !this.attribute("y1").hasValue() && !this.attribute("x2").hasValue() && !this.attribute("y2").hasValue()) { this.attribute("x1", true).value = 0; this.attribute("y1", true).value = 0; this.attribute("x2", true).value = 1; this.attribute("y2", true).value = 0; } var x1 = this.gradientUnits == "objectBoundingBox" ? bb.x() + bb.width() * this.attribute("x1").numValue() : this.attribute("x1").toPixels("x"); var y1 = this.gradientUnits == "objectBoundingBox" ? bb.y() + bb.height() * this.attribute("y1").numValue() : this.attribute("y1").toPixels("y"); var x2 = this.gradientUnits == "objectBoundingBox" ? bb.x() + bb.width() * this.attribute("x2").numValue() : this.attribute("x2").toPixels("x"); var y2 = this.gradientUnits == "objectBoundingBox" ? bb.y() + bb.height() * this.attribute("y2").numValue() : this.attribute("y2").toPixels("y"); if (x1 == x2 && y1 == y2) return null; return ctx.createLinearGradient(x1, y1, x2, y2); }; }; svg.Element.linearGradient.prototype = new svg.Element.GradientBase(); // radial gradient element svg.Element.radialGradient = function(node) { this.base = svg.Element.GradientBase; this.base(node); this.getGradient = function(ctx, element) { var bb = element.getBoundingBox(); if (!this.attribute("cx").hasValue()) this.attribute("cx", true).value = "50%"; if (!this.attribute("cy").hasValue()) this.attribute("cy", true).value = "50%"; if (!this.attribute("r").hasValue()) this.attribute("r", true).value = "50%"; var cx = this.gradientUnits == "objectBoundingBox" ? bb.x() + bb.width() * this.attribute("cx").numValue() : this.attribute("cx").toPixels("x"); var cy = this.gradientUnits == "objectBoundingBox" ? bb.y() + bb.height() * this.attribute("cy").numValue() : this.attribute("cy").toPixels("y"); var fx = cx; var fy = cy; if (this.attribute("fx").hasValue()) { fx = this.gradientUnits == "objectBoundingBox" ? bb.x() + bb.width() * this.attribute("fx").numValue() : this.attribute("fx").toPixels("x"); } if (this.attribute("fy").hasValue()) { fy = this.gradientUnits == "objectBoundingBox" ? bb.y() + bb.height() * this.attribute("fy").numValue() : this.attribute("fy").toPixels("y"); } var r = this.gradientUnits == "objectBoundingBox" ? (bb.width() + bb.height()) / 2 * this.attribute("r").numValue() : this.attribute("r").toPixels(); return ctx.createRadialGradient(fx, fy, 0, cx, cy, r); }; }; svg.Element.radialGradient.prototype = new svg.Element.GradientBase(); // gradient stop element svg.Element.stop = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.offset = this.attribute("offset").numValue(); if (this.offset < 0) this.offset = 0; if (this.offset > 1) this.offset = 1; var stopColor = this.style("stop-color"); if (this.style("stop-opacity").hasValue()) stopColor = stopColor.addOpacity(this.style("stop-opacity").value); this.color = stopColor.value; }; svg.Element.stop.prototype = new svg.Element.ElementBase(); // animation base element svg.Element.AnimateBase = function(node) { this.base = svg.Element.ElementBase; this.base(node); svg.Animations.push(this); this.duration = 0; this.begin = this.attribute("begin").toMilliseconds(); this.maxDuration = this.begin + this.attribute("dur").toMilliseconds(); this.getProperty = function() { var attributeType = this.attribute("attributeType").value; var attributeName = this.attribute("attributeName").value; if (attributeType == "CSS") { return this.parent.style(attributeName, true); } return this.parent.attribute(attributeName, true); }; this.initialValue = null; this.initialUnits = ""; this.removed = false; this.calcValue = function() { // OVERRIDE ME! return ""; }; this.update = function(delta) { // set initial value if (this.initialValue == null) { this.initialValue = this.getProperty().value; this.initialUnits = this.getProperty().getUnits(); } // if we're past the end time if (this.duration > this.maxDuration) { // loop for indefinitely repeating animations if (this.attribute("repeatCount").value == "indefinite" || this.attribute("repeatDur").value == "indefinite") { this.duration = 0; } else if (this.attribute("fill").valueOrDefault("remove") == "remove" && !this.removed) { this.removed = true; this.getProperty().value = this.initialValue; return true; } else { return false; } } this.duration = this.duration + delta; // if we're past the begin time var updated = false; if (this.begin < this.duration) { var newValue = this.calcValue(); // tween if (this.attribute("type").hasValue()) { // for transform, etc. var type = this.attribute("type").value; newValue = type + "(" + newValue + ")"; } this.getProperty().value = newValue; updated = true; } return updated; }; this.from = this.attribute("from"); this.to = this.attribute("to"); this.values = this.attribute("values"); if (this.values.hasValue()) this.values.value = this.values.value.split(";"); // fraction of duration we've covered this.progress = function() { var ret = { progress: (this.duration - this.begin) / (this.maxDuration - this.begin) }; if (this.values.hasValue()) { var p = ret.progress * (this.values.value.length - 1); var lb = Math.floor(p), ub = Math.ceil(p); ret.from = new svg.Property("from", parseFloat(this.values.value[lb])); ret.to = new svg.Property("to", parseFloat(this.values.value[ub])); ret.progress = (p - lb) / (ub - lb); } else { ret.from = this.from; ret.to = this.to; } return ret; }; }; svg.Element.AnimateBase.prototype = new svg.Element.ElementBase(); // animate element svg.Element.animate = function(node) { this.base = svg.Element.AnimateBase; this.base(node); this.calcValue = function() { var p = this.progress(); // tween value linearly var newValue = p.from.numValue() + (p.to.numValue() - p.from.numValue()) * p.progress; return newValue + this.initialUnits; }; }; svg.Element.animate.prototype = new svg.Element.AnimateBase(); // animate color element svg.Element.animateColor = function(node) { this.base = svg.Element.AnimateBase; this.base(node); this.calcValue = function() { var p = this.progress(); var from = new RGBColor(p.from.value); var to = new RGBColor(p.to.value); if (from.ok && to.ok) { // tween color linearly var r = from.r + (to.r - from.r) * p.progress; var g = from.g + (to.g - from.g) * p.progress; var b = from.b + (to.b - from.b) * p.progress; return "rgb(" + parseInt(r, 10) + "," + parseInt(g, 10) + "," + parseInt(b, 10) + ")"; } return this.attribute("from").value; }; }; svg.Element.animateColor.prototype = new svg.Element.AnimateBase(); // animate transform element svg.Element.animateTransform = function(node) { this.base = svg.Element.AnimateBase; this.base(node); this.calcValue = function() { var p = this.progress(); // tween value linearly var from = svg.ToNumberArray(p.from.value); var to = svg.ToNumberArray(p.to.value); var newValue = ""; for (var i = 0; i < from.length; i++) { newValue += from[i] + (to[i] - from[i]) * p.progress + " "; } return newValue; }; }; svg.Element.animateTransform.prototype = new svg.Element.animate(); // font element svg.Element.font = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.horizAdvX = this.attribute("horiz-adv-x").numValue(); this.isRTL = false; this.isArabic = false; this.fontFace = null; this.missingGlyph = null; this.glyphs = []; for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (child.type == "font-face") { this.fontFace = child; if (child.style("font-family").hasValue()) { svg.Definitions[child.style("font-family").value] = this; } } else if (child.type == "missing-glyph") this.missingGlyph = child; else if (child.type == "glyph") { if (child.arabicForm != "") { this.isRTL = true; this.isArabic = true; if (typeof this.glyphs[child.unicode] == "undefined") this.glyphs[child.unicode] = []; this.glyphs[child.unicode][child.arabicForm] = child; } else { this.glyphs[child.unicode] = child; } } } }; svg.Element.font.prototype = new svg.Element.ElementBase(); // font-face element svg.Element.fontface = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.ascent = this.attribute("ascent").value; this.descent = this.attribute("descent").value; this.unitsPerEm = this.attribute("units-per-em").numValue(); }; svg.Element.fontface.prototype = new svg.Element.ElementBase(); // missing-glyph element svg.Element.missingglyph = function(node) { this.base = svg.Element.path; this.base(node); this.horizAdvX = 0; }; svg.Element.missingglyph.prototype = new svg.Element.path(); // glyph element svg.Element.glyph = function(node) { this.base = svg.Element.path; this.base(node); this.horizAdvX = this.attribute("horiz-adv-x").numValue(); this.unicode = this.attribute("unicode").value; this.arabicForm = this.attribute("arabic-form").value; }; svg.Element.glyph.prototype = new svg.Element.path(); // text element svg.Element.text = function(node) { this.captureTextNodes = true; this.base = svg.Element.RenderedElementBase; this.base(node); this.baseSetContext = this.setContext; this.setContext = function(ctx) { this.baseSetContext(ctx); if (this.style("dominant-baseline").hasValue()) ctx.textBaseline = this.style("dominant-baseline").value; if (this.style("alignment-baseline").hasValue()) ctx.textBaseline = this.style("alignment-baseline").value; }; this.getBoundingBox = function() { // TODO: implement return new svg.BoundingBox(this.attribute("x").toPixels("x"), this.attribute("y").toPixels("y"), 0, 0); }; this.renderChildren = function(ctx) { this.x = this.attribute("x").toPixels("x"); this.y = this.attribute("y").toPixels("y"); this.x += this.getAnchorDelta(ctx, this, 0); for (var i = 0; i < this.children.length; i++) { this.renderChild(ctx, this, i); } }; this.getAnchorDelta = function(ctx, parent, startI) { var textAnchor = this.style("text-anchor").valueOrDefault("start"); if (textAnchor != "start") { var width = 0; for (var i = startI; i < parent.children.length; i++) { var child = parent.children[i]; if (i > startI && child.attribute("x").hasValue()) break; // new group width += child.measureTextRecursive(ctx); } return -1 * (textAnchor == "end" ? width : width / 2); } return 0; }; this.renderChild = function(ctx, parent, i) { var child = parent.children[i]; if (child.attribute("x").hasValue()) { child.x = child.attribute("x").toPixels("x") + this.getAnchorDelta(ctx, parent, i); } else { if (this.attribute("dx").hasValue()) this.x += this.attribute("dx").toPixels("x"); if (child.attribute("dx").hasValue()) this.x += child.attribute("dx").toPixels("x"); child.x = this.x; } this.x = child.x + child.measureText(ctx); if (child.attribute("y").hasValue()) { child.y = child.attribute("y").toPixels("y"); } else { if (this.attribute("dy").hasValue()) this.y += this.attribute("dy").toPixels("y"); if (child.attribute("dy").hasValue()) this.y += child.attribute("dy").toPixels("y"); child.y = this.y; } this.y = child.y; child.render(ctx); for (var i = 0; i < child.children.length; i++) { this.renderChild(ctx, child, i); } }; }; svg.Element.text.prototype = new svg.Element.RenderedElementBase(); // text base svg.Element.TextElementBase = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.getGlyph = function(font, text, i) { var c = text[i]; var glyph = null; if (font.isArabic) { var arabicForm = "isolated"; if ((i == 0 || text[i - 1] == " ") && i < text.length - 2 && text[i + 1] != " ") arabicForm = "terminal"; if (i > 0 && text[i - 1] != " " && i < text.length - 2 && text[i + 1] != " ") arabicForm = "medial"; if (i > 0 && text[i - 1] != " " && (i == text.length - 1 || text[i + 1] == " ")) arabicForm = "initial"; if (typeof font.glyphs[c] != "undefined") { glyph = font.glyphs[c][arabicForm]; if (glyph == null && font.glyphs[c].type == "glyph") glyph = font.glyphs[c]; } } else { glyph = font.glyphs[c]; } if (glyph == null) glyph = font.missingGlyph; return glyph; }; this.renderChildren = function(ctx) { var customFont = this.parent.style("font-family").getDefinition(); if (customFont != null) { var fontSize = this.parent.style("font-size").numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); var fontStyle = this.parent.style("font-style").valueOrDefault(svg.Font.Parse(svg.ctx.font).fontStyle); var text = this.getText(); if (customFont.isRTL) text = text.split("").reverse().join(""); var dx = svg.ToNumberArray(this.parent.attribute("dx").value); for (var i = 0; i < text.length; i++) { var glyph = this.getGlyph(customFont, text, i); var scale = fontSize / customFont.fontFace.unitsPerEm; ctx.translate(this.x, this.y); ctx.scale(scale, -scale); var lw = ctx.lineWidth; ctx.lineWidth = ctx.lineWidth * customFont.fontFace.unitsPerEm / fontSize; if (fontStyle == "italic") ctx.transform(1, 0, .4, 1, 0, 0); glyph.render(ctx); if (fontStyle == "italic") ctx.transform(1, 0, -.4, 1, 0, 0); ctx.lineWidth = lw; ctx.scale(1 / scale, -1 / scale); ctx.translate(-this.x, -this.y); this.x += fontSize * (glyph.horizAdvX || customFont.horizAdvX) / customFont.fontFace.unitsPerEm; if (typeof dx[i] != "undefined" && !isNaN(dx[i])) { this.x += dx[i]; } } return; } if (ctx.fillStyle != "") ctx.fillText(svg.compressSpaces(this.getText()), this.x, this.y); if (ctx.strokeStyle != "") ctx.strokeText(svg.compressSpaces(this.getText()), this.x, this.y); }; this.getText = function() {}; this.measureTextRecursive = function(ctx) { var width = this.measureText(ctx); for (var i = 0; i < this.children.length; i++) { width += this.children[i].measureTextRecursive(ctx); } return width; }; this.measureText = function(ctx) { var customFont = this.parent.style("font-family").getDefinition(); if (customFont != null) { var fontSize = this.parent.style("font-size").numValueOrDefault(svg.Font.Parse(svg.ctx.font).fontSize); var measure = 0; var text = this.getText(); if (customFont.isRTL) text = text.split("").reverse().join(""); var dx = svg.ToNumberArray(this.parent.attribute("dx").value); for (var i = 0; i < text.length; i++) { var glyph = this.getGlyph(customFont, text, i); measure += (glyph.horizAdvX || customFont.horizAdvX) * fontSize / customFont.fontFace.unitsPerEm; if (typeof dx[i] != "undefined" && !isNaN(dx[i])) { measure += dx[i]; } } return measure; } var textToMeasure = svg.compressSpaces(this.getText()); if (!ctx.measureText) return textToMeasure.length * 10; ctx.save(); this.setContext(ctx); var width = ctx.measureText(textToMeasure).width; ctx.restore(); return width; }; }; svg.Element.TextElementBase.prototype = new svg.Element.RenderedElementBase(); // tspan svg.Element.tspan = function(node) { this.captureTextNodes = true; this.base = svg.Element.TextElementBase; this.base(node); this.text = node.nodeValue || node.text || ""; this.getText = function() { return this.text; }; }; svg.Element.tspan.prototype = new svg.Element.TextElementBase(); // tref svg.Element.tref = function(node) { this.base = svg.Element.TextElementBase; this.base(node); this.getText = function() { var element = this.getHrefAttribute().getDefinition(); if (element != null) return element.children[0].getText(); }; }; svg.Element.tref.prototype = new svg.Element.TextElementBase(); // a element svg.Element.a = function(node) { this.base = svg.Element.TextElementBase; this.base(node); this.hasText = true; for (var i = 0; i < node.childNodes.length; i++) { if (node.childNodes[i].nodeType != 3) this.hasText = false; } // this might contain text this.text = this.hasText ? node.childNodes[0].nodeValue : ""; this.getText = function() { return this.text; }; this.baseRenderChildren = this.renderChildren; this.renderChildren = function(ctx) { if (this.hasText) { // render as text element this.baseRenderChildren(ctx); var fontSize = new svg.Property("fontSize", svg.Font.Parse(svg.ctx.font).fontSize); svg.Mouse.checkBoundingBox(this, new svg.BoundingBox(this.x, this.y - fontSize.toPixels("y"), this.x + this.measureText(ctx), this.y)); } else { // render as temporary group var g = new svg.Element.g(); g.children = this.children; g.parent = this; g.render(ctx); } }; this.onclick = function() { window.open(this.getHrefAttribute().value); }; this.onmousemove = function() { svg.ctx.canvas.style.cursor = "pointer"; }; }; svg.Element.a.prototype = new svg.Element.TextElementBase(); // image element svg.Element.image = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); var href = this.getHrefAttribute().value; var isSvg = href.match(/\.svg$/); svg.Images.push(this); this.loaded = false; if (!isSvg) { this.img = document.createElement("img"); var self = this; this.img.onload = function() { self.loaded = true; }; this.img.onerror = function() { if (typeof console != "undefined") { console.log('ERROR: image "' + href + '" not found'); self.loaded = true; } }; this.img.src = href; } else { this.img = svg.ajax(href); this.loaded = true; } this.renderChildren = function(ctx) { var x = this.attribute("x").toPixels("x"); var y = this.attribute("y").toPixels("y"); var width = this.attribute("width").toPixels("x"); var height = this.attribute("height").toPixels("y"); if (width == 0 || height == 0) return; ctx.save(); if (isSvg) { ctx.drawSvg(this.img, x, y, width, height); } else { ctx.translate(x, y); svg.AspectRatio(ctx, this.attribute("preserveAspectRatio").value, width, this.img.width, height, this.img.height, 0, 0); ctx.drawImage(this.img, 0, 0); } ctx.restore(); }; this.getBoundingBox = function() { var x = this.attribute("x").toPixels("x"); var y = this.attribute("y").toPixels("y"); var width = this.attribute("width").toPixels("x"); var height = this.attribute("height").toPixels("y"); return new svg.BoundingBox(x, y, x + width, y + height); }; }; svg.Element.image.prototype = new svg.Element.RenderedElementBase(); // group element svg.Element.g = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.getBoundingBox = function() { var bb = new svg.BoundingBox(); for (var i = 0; i < this.children.length; i++) { bb.addBoundingBox(this.children[i].getBoundingBox()); } return bb; }; }; svg.Element.g.prototype = new svg.Element.RenderedElementBase(); // symbol element svg.Element.symbol = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.baseSetContext = this.setContext; this.setContext = function(ctx) { this.baseSetContext(ctx); // viewbox if (this.attribute("viewBox").hasValue()) { var viewBox = svg.ToNumberArray(this.attribute("viewBox").value); var minX = viewBox[0]; var minY = viewBox[1]; width = viewBox[2]; height = viewBox[3]; svg.AspectRatio(ctx, this.attribute("preserveAspectRatio").value, this.attribute("width").toPixels("x"), width, this.attribute("height").toPixels("y"), height, minX, minY); svg.ViewPort.SetCurrent(viewBox[2], viewBox[3]); } }; }; svg.Element.symbol.prototype = new svg.Element.RenderedElementBase(); // style element svg.Element.style = function(node) { this.base = svg.Element.ElementBase; this.base(node); // text, or spaces then CDATA var css = ""; for (var i = 0; i < node.childNodes.length; i++) { css += node.childNodes[i].nodeValue; } css = css.replace(/(\/\*([^*]|[\r\n]|(\*+([^*\/]|[\r\n])))*\*+\/)|(^[\s]*\/\/.*)/gm, ""); // remove comments css = svg.compressSpaces(css); // replace whitespace var cssDefs = css.split("}"); for (var i = 0; i < cssDefs.length; i++) { if (svg.trim(cssDefs[i]) != "") { var cssDef = cssDefs[i].split("{"); var cssClasses = cssDef[0].split(","); var cssProps = cssDef[1].split(";"); for (var j = 0; j < cssClasses.length; j++) { var cssClass = svg.trim(cssClasses[j]); if (cssClass != "") { var props = {}; for (var k = 0; k < cssProps.length; k++) { var prop = cssProps[k].indexOf(":"); var name = cssProps[k].substr(0, prop); var value = cssProps[k].substr(prop + 1, cssProps[k].length - prop); if (name != null && value != null) { props[svg.trim(name)] = new svg.Property(svg.trim(name), svg.trim(value)); } } svg.Styles[cssClass] = props; if (cssClass == "@font-face") { var fontFamily = props["font-family"].value.replace(/"/g, ""); var srcs = props["src"].value.split(","); for (var s = 0; s < srcs.length; s++) { if (srcs[s].indexOf('format("svg")') > 0) { var urlStart = srcs[s].indexOf("url"); var urlEnd = srcs[s].indexOf(")", urlStart); var url = srcs[s].substr(urlStart + 5, urlEnd - urlStart - 6); var doc = svg.parseXml(svg.ajax(url)); var fonts = doc.getElementsByTagName("font"); for (var f = 0; f < fonts.length; f++) { var font = svg.CreateElement(fonts[f]); svg.Definitions[fontFamily] = font; } } } } } } } } }; svg.Element.style.prototype = new svg.Element.ElementBase(); // use element svg.Element.use = function(node) { this.base = svg.Element.RenderedElementBase; this.base(node); this.baseSetContext = this.setContext; this.setContext = function(ctx) { this.baseSetContext(ctx); if (this.attribute("x").hasValue()) ctx.translate(this.attribute("x").toPixels("x"), 0); if (this.attribute("y").hasValue()) ctx.translate(0, this.attribute("y").toPixels("y")); }; this.getDefinition = function() { var element = this.getHrefAttribute().getDefinition(); if (this.attribute("width").hasValue()) element.attribute("width", true).value = this.attribute("width").value; if (this.attribute("height").hasValue()) element.attribute("height", true).value = this.attribute("height").value; return element; }; this.path = function(ctx) { var element = this.getDefinition(); if (element != null) element.path(ctx); }; this.getBoundingBox = function() { var element = this.getDefinition(); if (element != null) return element.getBoundingBox(); }; this.renderChildren = function(ctx) { var element = this.getDefinition(); if (element != null) { // temporarily detach from parent and render var oldParent = element.parent; element.parent = null; element.render(ctx); element.parent = oldParent; } }; }; svg.Element.use.prototype = new svg.Element.RenderedElementBase(); // mask element svg.Element.mask = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.apply = function(ctx, element) { // render as temp svg var x = this.attribute("x").toPixels("x"); var y = this.attribute("y").toPixels("y"); var width = this.attribute("width").toPixels("x"); var height = this.attribute("height").toPixels("y"); if (width == 0 && height == 0) { var bb = new svg.BoundingBox(); for (var i = 0; i < this.children.length; i++) { bb.addBoundingBox(this.children[i].getBoundingBox()); } var x = Math.floor(bb.x1); var y = Math.floor(bb.y1); var width = Math.floor(bb.width()); var height = Math.floor(bb.height()); } // temporarily remove mask to avoid recursion var mask = element.attribute("mask").value; element.attribute("mask").value = ""; var cMask = document.createElement("canvas"); cMask.width = x + width; cMask.height = y + height; var maskCtx = cMask.getContext("2d"); this.renderChildren(maskCtx); var c = document.createElement("canvas"); c.width = x + width; c.height = y + height; var tempCtx = c.getContext("2d"); element.render(tempCtx); tempCtx.globalCompositeOperation = "destination-in"; tempCtx.fillStyle = maskCtx.createPattern(cMask, "no-repeat"); tempCtx.fillRect(0, 0, x + width, y + height); ctx.fillStyle = tempCtx.createPattern(c, "no-repeat"); ctx.fillRect(0, 0, x + width, y + height); // reassign mask element.attribute("mask").value = mask; }; this.render = function(ctx) {}; }; svg.Element.mask.prototype = new svg.Element.ElementBase(); // clip element svg.Element.clipPath = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.apply = function(ctx) { for (var i = 0; i < this.children.length; i++) { var child = this.children[i]; if (typeof child.path != "undefined") { var transform = null; if (child.attribute("transform").hasValue()) { transform = new svg.Transform(child.attribute("transform").value); transform.apply(ctx); } child.path(ctx); ctx.clip(); if (transform) { transform.unapply(ctx); } } } }; this.render = function(ctx) {}; }; svg.Element.clipPath.prototype = new svg.Element.ElementBase(); // filters svg.Element.filter = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.apply = function(ctx, element) { // render as temp svg var bb = element.getBoundingBox(); var x = Math.floor(bb.x1); var y = Math.floor(bb.y1); var width = Math.floor(bb.width()); var height = Math.floor(bb.height()); // temporarily remove filter to avoid recursion var filter = element.style("filter").value; element.style("filter").value = ""; var px = 0, py = 0; for (var i = 0; i < this.children.length; i++) { var efd = this.children[i].extraFilterDistance || 0; px = Math.max(px, efd); py = Math.max(py, efd); } var c = document.createElement("canvas"); c.width = width + 2 * px; c.height = height + 2 * py; var tempCtx = c.getContext("2d"); tempCtx.translate(-x + px, -y + py); element.render(tempCtx); // apply filters for (var i = 0; i < this.children.length; i++) { this.children[i].apply(tempCtx, 0, 0, width + 2 * px, height + 2 * py); } // render on me ctx.drawImage(c, 0, 0, width + 2 * px, height + 2 * py, x - px, y - py, width + 2 * px, height + 2 * py); // reassign filter element.style("filter", true).value = filter; }; this.render = function(ctx) {}; }; svg.Element.filter.prototype = new svg.Element.ElementBase(); svg.Element.feMorphology = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.apply = function(ctx, x, y, width, height) {}; }; svg.Element.feMorphology.prototype = new svg.Element.ElementBase(); svg.Element.feColorMatrix = function(node) { this.base = svg.Element.ElementBase; this.base(node); function imGet(img, x, y, width, height, rgba) { return img[y * width * 4 + x * 4 + rgba]; } function imSet(img, x, y, width, height, rgba, val) { img[y * width * 4 + x * 4 + rgba] = val; } this.apply = function(ctx, x, y, width, height) { // only supporting grayscale for now per Issue 195, need to extend to all matrix // assuming x==0 && y==0 for now var srcData = ctx.getImageData(0, 0, width, height); for (var y = 0; y < height; y++) { for (var x = 0; x < width; x++) { var r = imGet(srcData.data, x, y, width, height, 0); var g = imGet(srcData.data, x, y, width, height, 1); var b = imGet(srcData.data, x, y, width, height, 2); var gray = (r + g + b) / 3; imSet(srcData.data, x, y, width, height, 0, gray); imSet(srcData.data, x, y, width, height, 1, gray); imSet(srcData.data, x, y, width, height, 2, gray); } } ctx.clearRect(0, 0, width, height); ctx.putImageData(srcData, 0, 0); }; }; svg.Element.feColorMatrix.prototype = new svg.Element.ElementBase(); svg.Element.feGaussianBlur = function(node) { this.base = svg.Element.ElementBase; this.base(node); this.blurRadius = Math.floor(this.attribute("stdDeviation").numValue()); this.extraFilterDistance = this.blurRadius; this.apply = function(ctx, x, y, width, height) { if (typeof stackBlurCanvasRGBA == "undefined") { if (typeof console != "undefined") { console.log("ERROR: StackBlur.js must be included for blur to work"); } return; } // StackBlur requires canvas be on document ctx.canvas.id = svg.UniqueId(); ctx.canvas.style.display = "none"; document.body.appendChild(ctx.canvas); stackBlurCanvasRGBA(ctx.canvas.id, x, y, width, height, this.blurRadius); document.body.removeChild(ctx.canvas); }; }; svg.Element.feGaussianBlur.prototype = new svg.Element.ElementBase(); // title element, do nothing svg.Element.title = function(node) {}; svg.Element.title.prototype = new svg.Element.ElementBase(); // desc element, do nothing svg.Element.desc = function(node) {}; svg.Element.desc.prototype = new svg.Element.ElementBase(); svg.Element.MISSING = function(node) { if (typeof console != "undefined") { console.log("ERROR: Element '" + node.nodeName + "' not yet implemented."); } }; svg.Element.MISSING.prototype = new svg.Element.ElementBase(); // element factory svg.CreateElement = function(node) { var className = node.nodeName.replace(/^[^:]+:/, ""); // remove namespace className = className.replace(/\-/g, ""); // remove dashes var e = null; if (typeof svg.Element[className] != "undefined") { e = new svg.Element[className](node); } else { e = new svg.Element.MISSING(node); } e.type = node.nodeName; return e; }; // load from url svg.load = function(ctx, url) { svg.loadXml(ctx, svg.ajax(url)); }; // load from xml svg.loadXml = function(ctx, xml) { svg.loadXmlDoc(ctx, svg.parseXml(xml)); }; svg.loadXmlDoc = function(ctx, dom) { svg.init(ctx); var mapXY = function(p) { var e = ctx.canvas; while (e) { p.x -= e.offsetLeft; p.y -= e.offsetTop; e = e.offsetParent; } if (window.scrollX) p.x += window.scrollX; if (window.scrollY) p.y += window.scrollY; return p; }; // bind mouse if (svg.opts["ignoreMouse"] != true) { ctx.canvas.onclick = function(e) { var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY)); svg.Mouse.onclick(p.x, p.y); }; ctx.canvas.onmousemove = function(e) { var p = mapXY(new svg.Point(e != null ? e.clientX : event.clientX, e != null ? e.clientY : event.clientY)); svg.Mouse.onmousemove(p.x, p.y); }; } var e = svg.CreateElement(dom.documentElement); e.root = true; // render loop var isFirstRender = true; var draw = function() { svg.ViewPort.Clear(); if (ctx.canvas.parentNode) svg.ViewPort.SetCurrent(ctx.canvas.parentNode.clientWidth, ctx.canvas.parentNode.clientHeight); if (svg.opts["ignoreDimensions"] != true) { // set canvas size if (e.style("width").hasValue()) { ctx.canvas.width = e.style("width").toPixels("x"); ctx.canvas.style.width = ctx.canvas.width + "px"; } if (e.style("height").hasValue()) { ctx.canvas.height = e.style("height").toPixels("y"); ctx.canvas.style.height = ctx.canvas.height + "px"; } } var cWidth = ctx.canvas.clientWidth || ctx.canvas.width; var cHeight = ctx.canvas.clientHeight || ctx.canvas.height; if (svg.opts["ignoreDimensions"] == true && e.style("width").hasValue() && e.style("height").hasValue()) { cWidth = e.style("width").toPixels("x"); cHeight = e.style("height").toPixels("y"); } svg.ViewPort.SetCurrent(cWidth, cHeight); if (svg.opts["offsetX"] != null) e.attribute("x", true).value = svg.opts["offsetX"]; if (svg.opts["offsetY"] != null) e.attribute("y", true).value = svg.opts["offsetY"]; if (svg.opts["scaleWidth"] != null && svg.opts["scaleHeight"] != null) { var xRatio = 1, yRatio = 1, viewBox = svg.ToNumberArray(e.attribute("viewBox").value); if (e.attribute("width").hasValue()) xRatio = e.attribute("width").toPixels("x") / svg.opts["scaleWidth"]; else if (!isNaN(viewBox[2])) xRatio = viewBox[2] / svg.opts["scaleWidth"]; if (e.attribute("height").hasValue()) yRatio = e.attribute("height").toPixels("y") / svg.opts["scaleHeight"]; else if (!isNaN(viewBox[3])) yRatio = viewBox[3] / svg.opts["scaleHeight"]; e.attribute("width", true).value = svg.opts["scaleWidth"]; e.attribute("height", true).value = svg.opts["scaleHeight"]; e.attribute("viewBox", true).value = "0 0 " + cWidth * xRatio + " " + cHeight * yRatio; e.attribute("preserveAspectRatio", true).value = "none"; } // clear and render if (svg.opts["ignoreClear"] != true) { ctx.clearRect(0, 0, cWidth, cHeight); } e.render(ctx); if (isFirstRender) { isFirstRender = false; if (typeof svg.opts["renderCallback"] == "function") svg.opts["renderCallback"](dom); } }; var waitingForImages = true; if (svg.ImagesLoaded()) { waitingForImages = false; draw(); } svg.intervalID = setInterval(function() { var needUpdate = false; if (waitingForImages && svg.ImagesLoaded()) { waitingForImages = false; needUpdate = true; } // need update from mouse events? if (svg.opts["ignoreMouse"] != true) { needUpdate = needUpdate | svg.Mouse.hasEvents(); } // need update from animations? if (svg.opts["ignoreAnimation"] != true) { for (var i = 0; i < svg.Animations.length; i++) { needUpdate = needUpdate | svg.Animations[i].update(1e3 / svg.FRAMERATE); } } // need update from redraw? if (typeof svg.opts["forceRedraw"] == "function") { if (svg.opts["forceRedraw"]() == true) needUpdate = true; } // render if needed if (needUpdate) { draw(); svg.Mouse.runEvents(); } }, 1e3 / svg.FRAMERATE); }; svg.stop = function() { if (svg.intervalID) { clearInterval(svg.intervalID); } }; svg.Mouse = new function() { this.events = []; this.hasEvents = function() { return this.events.length != 0; }; this.onclick = function(x, y) { this.events.push({ type: "onclick", x: x, y: y, run: function(e) { if (e.onclick) e.onclick(); } }); }; this.onmousemove = function(x, y) { this.events.push({ type: "onmousemove", x: x, y: y, run: function(e) { if (e.onmousemove) e.onmousemove(); } }); }; this.eventElements = []; this.checkPath = function(element, ctx) { for (var i = 0; i < this.events.length; i++) { var e = this.events[i]; if (ctx.isPointInPath && ctx.isPointInPath(e.x, e.y)) this.eventElements[i] = element; } }; this.checkBoundingBox = function(element, bb) { for (var i = 0; i < this.events.length; i++) { var e = this.events[i]; if (bb.isPointInBox(e.x, e.y)) this.eventElements[i] = element; } }; this.runEvents = function() { svg.ctx.canvas.style.cursor = ""; for (var i = 0; i < this.events.length; i++) { var e = this.events[i]; var element = this.eventElements[i]; while (element) { e.run(element); element = element.parent; } } // done running, clear this.events = []; this.eventElements = []; }; }(); return svg; } })(); if (typeof CanvasRenderingContext2D != "undefined") { CanvasRenderingContext2D.prototype.drawSvg = function(s, dx, dy, dw, dh) { canvg(this.canvas, s, { ignoreMouse: true, ignoreAnimation: true, ignoreDimensions: true, ignoreClear: true, offsetX: dx, offsetY: dy, scaleWidth: dw, scaleHeight: dh }); }; } return canvg; } }; /*! * 输出转换器,提供输出支持 */ _p[1] = { value: function(require) { var kity = _p.r(34), canvg = _p.r(0); return kity.createClass("Output", { constructor: function(formula) { this.formula = formula; }, toJPG: function(cb) { toImage(this.formula, "image/jpeg", cb); }, toPNG: function(cb) { toImage(this.formula, "image/png", cb); } }); function toImage(formula, type, cb) { var rectSpace = formula.container.getRenderBox(); return getBase64DataURL(formula.node.ownerDocument, { width: rectSpace.width, height: rectSpace.height, content: getSVGContent(formula.node) }, type, cb); } function getBase64DataURL(doc, data, type, cb) { var canvas = null, args = arguments, ctx = null; if (true) { drawToCanvas.apply(null, args); } else { canvas = getImageCanvas(doc, data.width, data.height, type); ctx = canvas.getContext("2d"); var image = new Image(); image.onload = function() { try { ctx.drawImage(image, 0, 0); cb(canvas.toDataURL(type)); } catch (e) { drawToCanvas.apply(null, args); } }; image.src = getSVGDataURL(data.content); } } function getSVGContent(svgNode) { var tmp = svgNode.ownerDocument.createElement("div"), start = [ '' ]; tmp.appendChild(svgNode.cloneNode(true)); return tmp.innerHTML.replace(/]+?>/i, start.join("")).replace(/ /g, ""); } function getSVGDataURL(data) { return "data:image/svg+xml;base64," + window.btoa(unescape(encodeURIComponent(data))); } function getImageCanvas(doc, width, height, type) { var canvas = doc.createElement("canvas"), ctx = canvas.getContext("2d"); canvas.width = width; canvas.height = height; if (type !== "image/png") { ctx.fillStyle = "white"; ctx.fillRect(0, 0, canvas.width, canvas.height); } return canvas; } function drawToCanvas(doc, data, type, cb) { var canvas = getImageCanvas(doc, data.width, data.height, type); canvas.style.cssText = "position: absolute; top: 0; left: 100000px; z-index: -1;"; window.setTimeout(function() { doc.body.appendChild(canvas); canvg(canvas, data.content); doc.body.removeChild(canvas); cb(canvas.toDataURL(type)); }, 0); } } }; /*! * 所有字符的列表 */ _p[2] = { value: function() { return [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "ȷ", "ı", "α", "β", "γ", "δ", "ε", "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ", "ς", "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "ϑ", "ϕ", "ϖ", "Ϟ", "Ϝ", "ϵ", "ϱ", "Ϲ", "ℜ", "ℵ", "ℑ", "℧", "ℶ", "ℷ", "ℸ", "ð", "ℏ", "⅁", "ℎ", "∂", "℘", "⅌", "Ⅎ", "∁", "ℓ", "Ⓢ", "(", ")", "Γ", "Δ", "Ε", "Ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν", "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω", "Α", "Β", "#", "!", "$", "%", "&", "∠", "′", "‵", "★", "◆", "■", "▲", "▼", "⊤", "⊥", "♣", "♠", "♢", "♡", "∃", "∄", "♭", "♮", "♯", "∀", "∞", "∡", "∇", "¬", "∢", "√", "△", "▽", "∅", "ø", "◇", "◀", "▸", "[", "]", "{", "}", "〈", "〉", "ϰ", ",", ".", "/", ":", ";", "?", "\\", "⋮", "⋯", "⋰", "…", "@", """, "'", "|", "^", "`", "“", "_", "*", "+", "-", "∐", "⊼", "⊻", "◯", "⊡", "⊟", "⊞", "⊠", "•", "∩", "∪", "⋒", "⋓", "⋐", "⋑", "·", "▪", "◦", "⊛", "⊚", "⊖", "⊙", "⊝", "⊕", "⊗", "⊘", "±", "∓", "⋏", "⋎", "†", "‡", "⋄", "÷", "⋇", "∔", "⌭", "⋗", "⋖", "⋉", "⋊", "⋋", "⋌", "⊓", "⊔", "⊑", "⊒", "⊏", "⊐", "⋆", "×", "⊳", "⊲", "⊵", "⊴", "⊎", "∨", "∧", "≀", "<", "=", ">", "≈", "≇", "≍", "≒", "≓", "≊", "∽", "≁", "≂", "≃", "⋍", "≏", "≎", "≗", "≅", "⋞", "⋟", "≐", "≑", "≖", "⪖", "⪕", "≡", "≥", "≤", "≦", "≧", "⩾", "⩽", "≫", "≪", "≨", "≩", "⋘", "⋙", "⪇", "⪈", "⪉", "⪊", "⋧", "⋦", "⪆", "⪅", "⋛", "⋚", "⪋", "⪌", "≷", "≶", "≳", "≲", "⌮", "⌯", "≯", "≱", "≰", "≮", "⌱", "⌰", "⌲", "⌳", "≬", "⊀", "⊁", "⋠", "⋡", "≺", "≻", "≼", "≽", "≾", "≿", "⊂", "⊃", "⊈", "⊉", "⊆", "⊇", "⊊", "⊋", "⪷", "⪸", "⪯", "⪰", "⪹", "⪺", "⪵", "⪶", "⋨", "⋩", "∼", "≜", "↶", "↷", "↺", "↻", "↾", "↿", "⇂", "⇃", "⇄", "⇆", "⇈", "⇊", "⇋", "⇌", "⇍", "⇎", "⇏", "⇐", "⇑", "⇒", "⇓", "⇔", "⇕", "⇚", "⇛", "⇝", "↫", "↬", "↭", "↮", "←", "↑", "→", "↓", "↔", "↕", "↖", "↗", "↘", "↙", "↞", "↠", "↢", "↣", "↰", "↱", "⊢", "⊣", "⊨", "⊩", "⊪", "⊭", "⊯", "⊸", "⊺", "⋔", "⋪", "⋫", "⋬", "⋭", "⌈", "⌉", "⌊", "⌋", "⫋", "⫌", "⫅", "⫆", "∈", "∋", "∝", "∤", "∦", "∴", "∵", "∍", "⋈", "⌢", "⌣", "∣", "∥", "⏐", "⏑", "⏒", "⏓", "⫇", "⫈", "⊮", "⊬", "⫉", "⏔", "⏕", "⏖", "⏗", "⇇", "⇉", "↼", "↽", "⇀", "⇁", "↚", "↛", "⟵", "⟶", "⟷", "⟹", "⟸", "⟺", "≢", "≠", "∉" ]; } }; /*! * 字符配置 */ _p[3] = { value: function() { return { // 默认字体 defaultFont: "KF AMS MAIN" }; } }; /*! * 工厂方法,创建兼容各浏览器的text实现 */ _p[4] = { value: function(require) { var kity = _p.r(34), divNode = document.createElement("div"), NAMESPACE = "http://www.w3.org/XML/1998/namespace"; function createText(content) { var text = new kity.Text(); // Non-IE if ("innerHTML" in text.node) { text.node.setAttributeNS(NAMESPACE, "xml:space", "preserve"); } else { if (content.indexOf(" ") != -1) { content = convertContent(content); } } text.setContent(content); return text; } /** * 构建节点来转换内容 */ function convertContent(content) { divNode.innerHTML = '' + content.replace(/\s/gi, " ") + ""; return divNode.firstChild.firstChild.textContent; } return { create: function(content) { return createText(content); } }; } }; /** * 文本 */ _p[5] = { value: function(require) { var kity = _p.r(34), FONT_CONF = _p.r(47).font, FontManager = _p.r(25), TextFactory = _p.r(4); return kity.createClass("Text", { base: _p.r(46), constructor: function(content, fontFamily) { this.callBase(); this.fontFamily = fontFamily; this.fontSize = 50; this.content = content || ""; // 移除多余的节点 this.box.remove(); this.translationContent = this.translation(this.content); this.contentShape = new kity.Group(); this.contentNode = this.createContent(); this.contentShape.addShape(this.contentNode); this.addShape(this.contentShape); }, createContent: function() { var contentNode = TextFactory.create(this.translationContent); contentNode.setAttr({ "font-family": this.fontFamily, "font-size": 50, x: 0, y: FONT_CONF.offset }); return contentNode; }, setFamily: function(fontFamily) { this.fontFamily = fontFamily; this.contentNode.setAttr("font-family", fontFamily); }, setFontSize: function(fontSize) { this.fontSize = fontSize; this.contentNode.setAttr("font-size", fontSize + "px"); this.contentNode.setAttr("y", fontSize / 50 * FONT_CONF.offset); }, getBaseHeight: function() { var chars = this.contentShape.getItems(), currentChar = null, index = 0, height = 0; while (currentChar = chars[index]) { height = Math.max(height, currentChar.getHeight()); index++; } return height; }, translation: function(content) { var fontFamily = this.fontFamily; // 首先特殊处理掉两个相连的"`"符号 return content.replace(/``/g, "“").replace(/\\([a-zA-Z,]+)\\/g, function(match, input) { if (input === ",") { return " "; } var data = FontManager.getCharacterValue(input, fontFamily); if (!data) { return ""; } return data; }); } }); } }; /** * 定义公式中各种对象的类型 */ _p[6] = { value: function() { return { UNKNOWN: -1, EXP: 0, COMPOUND_EXP: 1, OP: 2 }; } }; /** * 定义公式中上下标的类型 */ _p[7] = { value: function() { return { SIDE: "side", FOLLOW: "follow" }; } }; /** * 下标表达式 */ _p[8] = { value: function(require) { var kity = _p.r(34); return kity.createClass("SubscriptExpression", { base: _p.r(17), constructor: function(operand, subscript) { this.callBase(operand, null, subscript); this.setFlag("Subscript"); } }); } }; /** * 上标表达式 */ _p[9] = { value: function(require) { var kity = _p.r(34); return kity.createClass("SuperscriptExpression", { base: _p.r(17), constructor: function(operand, superscript) { this.callBase(operand, superscript, null); this.setFlag("Superscript"); } }); } }; /** * 二元操作表达式 */ _p[10] = { value: function(require) { var kity = _p.r(34); return kity.createClass("BinaryExpression", { base: _p.r(19), constructor: function(firstOperand, lastOperand) { this.callBase(); this.setFirstOperand(firstOperand); this.setLastOperand(lastOperand); }, setFirstOperand: function(operand) { return this.setOperand(operand, 0); }, getFirstOperand: function() { return this.getOperand(0); }, setLastOperand: function(operand) { return this.setOperand(operand, 1); }, getLastOperand: function() { return this.getOperand(1); } }); } }; /** * 自动增长括号表达式 */ _p[11] = { value: function(require) { var kity = _p.r(34), BracketsOperator = _p.r(35); return kity.createClass("BracketsExpression", { base: _p.r(19), /** * 构造函数调用方式: * new Constructor( 左括号, 右括号, 表达式 ) * 或者 * new Constructor( 括号, 表达式 ), 该构造函数转换成上面的构造函数,是: new Constructor( 括号, 括号, 表达式 ) * @param left 左括号 * @param right 右括号 * @param exp 表达式 */ constructor: function(left, right, exp) { this.callBase(); this.setFlag("Brackets"); // 参数整理 if (arguments.length === 2) { exp = right; right = left; } this.leftSymbol = left; this.rightSymbol = right; this.setOperator(new BracketsOperator()); this.setOperand(exp, 0); }, getLeftSymbol: function() { return this.leftSymbol; }, getRightSymbol: function() { return this.rightSymbol; } }); } }; /** * 组合表达式 * 可以组合多个表达式 */ _p[12] = { value: function(require) { var kity = _p.r(34), FONT_CONF = _p.r(47).font, CombinationOperator = _p.r(36); return kity.createClass("CombinationExpression", { base: _p.r(19), constructor: function() { this.callBase(); this.setFlag("Combination"); this.setOperator(new CombinationOperator()); kity.Utils.each(arguments, function(operand, index) { this.setOperand(operand, index); }, this); }, getRenderBox: function(refer) { var rectBox = this.callBase(refer); if (this.getOperands().length === 0) { rectBox.height = FONT_CONF.spaceHeight; } return rectBox; }, getBaseline: function(refer) { var maxBaseline = 0, operands = this.getOperands(); if (operands.length === 0) { return this.callBase(refer); } kity.Utils.each(operands, function(operand) { maxBaseline = Math.max(operand.getBaseline(refer), maxBaseline); }); return maxBaseline; }, getMeanline: function(refer) { var minMeanline = 1e7, operands = this.getOperands(); if (operands.length === 0) { return this.callBase(refer); } kity.Utils.each(operands, function(operand) { minMeanline = Math.min(operand.getMeanline(refer), minMeanline); }); return minMeanline; } }); } }; /** * 分数表达式 */ _p[13] = { value: function(require) { var kity = _p.r(34), FractionOperator = _p.r(38); return kity.createClass("FractionExpression", { base: _p.r(10), constructor: function(upOperand, downOperand) { this.callBase(upOperand, downOperand); this.setFlag("Fraction"); this.setOperator(new FractionOperator()); }, /*------- 重写分数结构的baseline和mealine计算方式 */ getBaseline: function(refer) { var downOperand = this.getOperand(1), rectBox = downOperand.getRenderBox(refer); return rectBox.y + downOperand.getBaselineProportion() * rectBox.height; }, getMeanline: function(refer) { var upOperand = this.getOperand(0), rectBox = upOperand.getRenderBox(refer); return upOperand.getMeanlineProportion() * rectBox.height; } }); } }; /** * 函数表达式 */ _p[14] = { value: function(require) { var kity = _p.r(34), FUNC_CONF = _p.r(47).func, FunctionOperator = _p.r(39); return kity.createClass("FunctionExpression", { base: _p.r(19), /** * function表达式构造函数 * @param funcName function名称 * @param expr 函数表达式 * @param sup 上标 * @param sub 下标 */ constructor: function(funcName, expr, sup, sub) { this.callBase(); this.setFlag("Func"); this.funcName = funcName; this.setOperator(new FunctionOperator(funcName)); this.setExpr(expr); this.setSuperscript(sup); this.setSubscript(sub); }, // 当前函数应用的script位置是否是在侧面 isSideScript: function() { return !FUNC_CONF["ud-script"][this.funcName]; }, setExpr: function(expr) { return this.setOperand(expr, 0); }, setSuperscript: function(sub) { return this.setOperand(sub, 1); }, setSubscript: function(sub) { return this.setOperand(sub, 2); } }); } }; /** * 积分表达式 */ _p[15] = { value: function(require) { var kity = _p.r(34), IntegrationOperator = _p.r(40), IntegrationExpression = kity.createClass("IntegrationExpression", { base: _p.r(19), /** * 构造积分表达式 * @param integrand 被积函数 * @param supOperand 上限 * @param subOperand 下限 */ constructor: function(integrand, superscript, subscript) { this.callBase(); this.setFlag("Integration"); this.setOperator(new IntegrationOperator()); this.setIntegrand(integrand); this.setSuperscript(superscript); this.setSubscript(subscript); }, setType: function(type) { this.getOperator().setType(type); return this; }, resetType: function() { this.getOperator().resetType(); return this; }, setIntegrand: function(integrand) { this.setOperand(integrand, 0); }, setSuperscript: function(sup) { this.setOperand(sup, 1); }, setSubscript: function(sub) { this.setOperand(sub, 2); } }); return IntegrationExpression; } }; /** * 方根表达式 */ _p[16] = { value: function(require) { var kity = _p.r(34), RadicalOperator = _p.r(42); return kity.createClass("RadicalExpression", { base: _p.r(10), /** * 构造开方表达式 * @param radicand 被开方数 * @param exponent 指数 */ constructor: function(radicand, exponent) { this.callBase(radicand, exponent); this.setFlag("Radicand"); this.setOperator(new RadicalOperator()); }, setRadicand: function(operand) { return this.setFirstOperand(operand); }, getRadicand: function() { return this.getFirstOperand(); }, setExponent: function(operand) { return this.setLastOperand(operand); }, getExponent: function() { return this.getLastOperand(); } }); } }; /** * 上标表达式 */ _p[17] = { value: function(require) { var kity = _p.r(34), ScriptOperator = _p.r(43); return kity.createClass("ScriptExpression", { base: _p.r(19), constructor: function(operand, superscript, subscript) { this.callBase(); this.setFlag("Script"); this.setOperator(new ScriptOperator()); this.setOpd(operand); this.setSuperscript(superscript); this.setSubscript(subscript); }, setOpd: function(operand) { this.setOperand(operand, 0); }, setSuperscript: function(sup) { this.setOperand(sup, 1); }, setSubscript: function(sub) { this.setOperand(sub, 2); } }); } }; /** * 求和表达式 */ _p[18] = { value: function(require) { var kity = _p.r(34), SummationOperator = _p.r(44); return kity.createClass("SummationExpression", { base: _p.r(19), /** * 构造求和表达式 * @param expr 求和表达式 * @param upOperand 上标 * @param downOperand 下标 */ constructor: function(expr, superscript, subscript) { this.callBase(); this.setFlag("Summation"); this.setOperator(new SummationOperator()); this.setExpr(expr); this.setSuperscript(superscript); this.setSubscript(subscript); }, setExpr: function(expr) { this.setOperand(expr, 0); }, setSuperscript: function(sup) { this.setOperand(sup, 1); }, setSubscript: function(sub) { this.setOperand(sub, 2); } }); } }; /** * 复合表达式 * @abstract */ _p[19] = { value: function(require) { var kity = _p.r(34), GTYPE = _p.r(6), Expression = _p.r(21); return kity.createClass("CompoundExpression", { base: _p.r(21), constructor: function() { this.callBase(); this.type = GTYPE.COMPOUND_EXP; this.operands = []; this.operator = null; this.operatorBox = new kity.Group(); this.operatorBox.setAttr("data-type", "kf-editor-exp-op-box"); this.operandBox = new kity.Group(); this.operandBox.setAttr("data-type", "kf-editor-exp-operand-box"); this.setChildren(0, this.operatorBox); this.setChildren(1, this.operandBox); }, // 操作符存储在第1位置 setOperator: function(operator) { if (operator === undefined) { return this; } if (this.operator) { this.operator.remove(); } this.operatorBox.addShape(operator); this.operator = operator; this.operator.setParentExpression(this); // 表达式关联到操作符 operator.expression = this; return this; }, getOperator: function() { return this.operator; }, // 操作数存储位置是从1开始 setOperand: function(operand, index, isWrap) { // 不包装操作数 if (isWrap === false) { this.operands[index] = operand; return this; } operand = Expression.wrap(operand); if (this.operands[index]) { this.operands[index].remove(); } this.operands[index] = operand; this.operandBox.addShape(operand); return this; }, getOperand: function(index) { return this.operands[index]; }, getOperands: function() { return this.operands; }, addedCall: function() { this.operator.applyOperand.apply(this.operator, this.operands); return this; } }); } }; /** * 空表达式 * 该表达式主要用途是用于站位 */ _p[20] = { value: function(require) { var kity = _p.r(34), FONT_CONF = _p.r(47).font, Expression = _p.r(21), EmptyExpression = kity.createClass("EmptyExpression", { base: Expression, constructor: function() { this.callBase(); this.setFlag("Empty"); }, getRenderBox: function() { return { width: 0, height: FONT_CONF.spaceHeight, x: 0, y: 0 }; } }); EmptyExpression.isEmpty = function(target) { return target instanceof EmptyExpression; }; // 注册打包函数 Expression.registerWrap("empty", function(operand) { if (operand === null || operand === undefined) { return new EmptyExpression(); } }); return EmptyExpression; } }; /** * 基础表达式, 该类是表达式和操作数的高层抽象 * @abstract */ _p[21] = { value: function(require) { var kity = _p.r(34), GTYPE = _p.r(6), FONT_CONF = _p.r(47).font, // 打包函数列表 WRAP_FN = [], // 注册的打包函数的名称与其在注册器列表中的索引之间的对应关系 WRAP_FN_INDEX = {}, Expression = kity.createClass("Expression", { base: _p.r(46), constructor: function() { this.callBase(); this.type = GTYPE.EXP; // 表达式的上下偏移 this._offset = { top: 0, bottom: 0 }; this.children = []; this.box.fill("transparent").setAttr("data-type", "kf-editor-exp-box"); this.box.setAttr("data-type", "kf-editor-exp-bg-box"); this.expContent = new kity.Group(); this.expContent.setAttr("data-type", "kf-editor-exp-content-box"); this.addShape(this.expContent); }, getChildren: function() { return this.children; }, getChild: function(index) { return this.children[index] || null; }, getTopOffset: function() { return this._offset.top; }, getBottomOffset: function() { return this._offset.bottom; }, getOffset: function() { return this._offset; }, setTopOffset: function(val) { this._offset.top = val; }, setBottomOffset: function(val) { this._offset.bottom = val; }, setOffset: function(top, bottom) { this._offset.top = top; this._offset.bottom = bottom; }, setFlag: function(flag) { this.setAttr("data-flag", flag || "Expression"); }, setChildren: function(index, exp) { // 首先清理掉之前的表达式 if (this.children[index]) { this.children[index].remove(); } this.children[index] = exp; this.expContent.addShape(exp); }, getBaselineProportion: function() { return FONT_CONF.baselinePosition; }, getMeanlineProportion: function() { return FONT_CONF.meanlinePosition; }, getBaseline: function(refer) { // 上偏移3px return this.getRenderBox(refer).height * FONT_CONF.baselinePosition - 3; }, getMeanline: function(refer) { // 上偏移1px return this.getRenderBox(refer).height * FONT_CONF.meanlinePosition - 1; }, getAscenderline: function() { return this.getFixRenderBox().height * FONT_CONF.ascenderPosition; }, getDescenderline: function() { return this.getFixRenderBox().height * FONT_CONF.descenderPosition; }, translateElement: function(x, y) { this.expContent.translate(x, y); }, expand: function(width, height) { var renderBox = this.getFixRenderBox(); this.setBoxSize(renderBox.width + width, renderBox.height + height); }, getBaseWidth: function() { return this.getWidth(); }, getBaseHeight: function() { return this.getHeight(); }, updateBoxSize: function() { var renderBox = this.expContent.getFixRenderBox(); this.setBoxSize(renderBox.width, renderBox.height); }, getBox: function() { return this.box; } }); // 表达式自动打包 kity.Utils.extend(Expression, { registerWrap: function(name, fn) { WRAP_FN_INDEX[name] = WRAP_FN.length; WRAP_FN.push(fn); }, revokeWrap: function(name) { var fn = null; if (name in WRAP_FN_INDEX) { fn = WRAP_FN[WRAP_FN_INDEX[name]]; WRAP_FN[WRAP_FN_INDEX[name]] = null; delete WRAP_FN_INDEX[name]; } return fn; }, // 打包函数 wrap: function(operand) { var result; kity.Utils.each(WRAP_FN, function(fn) { if (!fn) { return; } result = fn(operand); if (result) { return false; } }); return result; } }); return Expression; } }; /** * Text表达式 */ _p[22] = { value: function(require) { var Text = _p.r(5), kity = _p.r(34), FONT_CONF = _p.r(3), Expression = _p.r(21), TextExpression = kity.createClass("TextExpression", { base: _p.r(21), constructor: function(content, fontFamily) { this.callBase(); this.fontFamily = fontFamily || FONT_CONF.defaultFont; this.setFlag("Text"); this.content = content + ""; this.textContent = new Text(this.content, this.fontFamily); this.setChildren(0, this.textContent); this.setChildren(1, new kity.Rect(0, 0, 0, 0).fill("transparent")); }, setFamily: function(fontFamily) { this.textContent.setFamily(fontFamily); }, setFontSize: function(fontSize) { this.textContent.setFontSize(fontSize); }, addedCall: function() { var box = this.textContent.getFixRenderBox(); this.getChild(1).setSize(box.width, box.height); this.updateBoxSize(); return this; } }); // 注册文本表达式的打包函数 Expression.registerWrap("text", function(operand) { var operandType = typeof operand; if (operandType === "number" || operandType === "string") { operand = new TextExpression(operand); } return operand; }); return TextExpression; } }; /*! * 字体信息检测模板,用于检测浏览器的字体信息 */ _p[23] = { value: function() { return [ '', 'x', "" ]; } }; /*! * 字体安装器 */ _p[24] = { value: function(require) { var kity = _p.r(34), FontManager = _p.r(25), $ = _p.r(33), FONT_CONF = _p.r(47).font, CHAR_LIST = _p.r(2), NODE_LIST = []; return kity.createClass("FontInstaller", { constructor: function(doc, resource) { this.callBase(); this.resource = resource || "../src/resource/"; this.doc = doc; }, // 挂载字体 mount: function(callback) { var fontList = FontManager.getFontList(), count = 0, _self = this; kity.Utils.each(fontList, function(fontInfo) { count++; fontInfo.meta.src = _self.resource + fontInfo.meta.src; _self.createFontStyle(fontInfo); preload(_self.doc, fontInfo, function() { count--; if (count === 0) { complete(_self.doc, callback); } }); }); }, createFontStyle: function(fontInfo) { var stylesheet = this.doc.createElement("style"), tpl = '@font-face{\nfont-family: "${fontFamily}";\nsrc: url("${src}");\n}'; stylesheet.setAttribute("type", "text/css"); stylesheet.innerHTML = tpl.replace("${fontFamily}", fontInfo.meta.fontFamily).replace("${src}", fontInfo.meta.src); this.doc.head.appendChild(stylesheet); } }); function preload(doc, fontInfo, callback) { $.get(fontInfo.meta.src, function(data, state) { if (state === "success") { applyFonts(doc, fontInfo); } callback(); }); } function complete(doc, callback) { window.setTimeout(function() { initFontSystemInfo(doc); removeTmpNode(); callback(); }, 100); } function applyFonts(doc, fontInfo) { var node = document.createElement("div"), fontFamily = fontInfo.meta.fontFamily; node.style.cssText = "position: absolute; top: -10000px; left: -100000px;"; node.style.fontFamily = fontFamily; node.innerHTML = CHAR_LIST.join(""); doc.body.appendChild(node); NODE_LIST.push(node); } /** * 计算字体系统信息 */ function initFontSystemInfo(doc) { var tmpNode = doc.createElement("div"); tmpNode.style.cssText = "position: absolute; top: 0; left: -100000px;"; tmpNode.innerHTML = _p.r(23).join(""); doc.body.appendChild(tmpNode); var rectBox = tmpNode.getElementsByTagName("text")[0].getBBox(); // text实际占用空间 FONT_CONF.spaceHeight = rectBox.height; // text顶部空间 FONT_CONF.topSpace = -rectBox.y - FONT_CONF.baseline; FONT_CONF.bottomSpace = FONT_CONF.spaceHeight - FONT_CONF.topSpace - FONT_CONF.baseHeight; // text偏移值 FONT_CONF.offset = FONT_CONF.baseline + FONT_CONF.topSpace; // baseline比例 FONT_CONF.baselinePosition = (FONT_CONF.topSpace + FONT_CONF.baseline) / FONT_CONF.spaceHeight; // meanline比例 FONT_CONF.meanlinePosition = (FONT_CONF.topSpace + FONT_CONF.meanline) / FONT_CONF.spaceHeight; // 上下延伸性比例 FONT_CONF.ascenderPosition = FONT_CONF.topSpace / FONT_CONF.spaceHeight; FONT_CONF.descenderPosition = (FONT_CONF.topSpace + FONT_CONF.baseHeight) / FONT_CONF.spaceHeight; doc.body.removeChild(tmpNode); } function removeTmpNode() { kity.Utils.each(NODE_LIST, function(node) { node.parentNode.removeChild(node); }); NODE_LIST = []; } } }; /*! * 字体管理器 */ _p[25] = { value: function(require) { var FONT_LIST = {}, kity = _p.r(34), CONF = _p.r(47).font.list; // init (function() { kity.Utils.each(CONF, function(fontData) { FONT_LIST[fontData.meta.fontFamily] = fontData; }); })(); return { getFontList: function() { return FONT_LIST; }, getCharacterValue: function(key, fontFamily) { if (!FONT_LIST[fontFamily]) { return null; } return FONT_LIST[fontFamily].map[key] || null; } }; } }; /*! * 双线字体 */ _p[26] = { value: function() { return { meta: { fontFamily: "KF AMS BB", src: "KF_AMS_BB.woff" } }; } }; /*! * 手写体 */ _p[27] = { value: function() { return { meta: { fontFamily: "KF AMS CAL", src: "KF_AMS_CAL.woff" } }; } }; /*! * 花体 */ _p[28] = { value: function() { return { meta: { fontFamily: "KF AMS FRAK", src: "KF_AMS_FRAK.woff" } }; } }; /*! * 字体主文件 */ _p[29] = { value: function() { return { meta: { fontFamily: "KF AMS MAIN", src: "KF_AMS_MAIN.woff" }, map: { // char Alpha: "Α", Beta: "Β", Gamma: "Γ", Delta: "Δ", Epsilon: "Ε", Zeta: "Ζ", Eta: "Η", Theta: "Θ", Iota: "Ι", Kappa: "Κ", Lambda: "Λ", Mu: "Μ", Nu: "Ν", Xi: "Ξ", Omicron: "Ο", Pi: "Π", Rho: "Ρ", Sigma: "Σ", Tau: "Τ", Upsilon: "Υ", Phi: "Φ", Chi: "Χ", Psi: "Ψ", Omega: "Ω", alpha: "α", beta: "β", gamma: "γ", delta: "δ", epsilon: "ε", zeta: "ζ", eta: "η", theta: "θ", iota: "ι", kappa: "κ", lambda: "λ", mu: "μ", nu: "ν", xi: "ξ", omicron: "ο", pi: "π", rho: "ρ", sigma: "σ", tau: "τ", upsilon: "υ", phi: "φ", varkappa: "ϰ", chi: "χ", psi: "ψ", omega: "ω", digamma: "Ϝ", varepsilon: "ϵ", varrho: "ϱ", varphi: "ϕ", vartheta: "ϑ", varpi: "ϖ", varsigma: "Ϲ", aleph: "ℵ", beth: "ℶ", daleth: "ℸ", gimel: "ℷ", eth: "ð", hbar: "ℎ", hslash: "ℏ", mho: "℧", partial: "∂", wp: "℘", Game: "⅁", Bbbk: "⅌", Finv: "Ⅎ", Im: "ℑ", Re: "ℜ", complement: "∁", ell: "ℓ", circledS: "Ⓢ", imath: "ı", jmath: "ȷ", // symbol doublecap: "⋒", Cap: "⋒", doublecup: "⋓", Cup: "⋓", ast: "*", divideontimes: "⋇", rightthreetimes: "⋌", leftthreetimes: "⋋", cdot: "·", odot: "⊙", dotplus: "∔", rtimes: "⋊", ltimes: "⋉", centerdot: "▪", doublebarwedge: "⌭", setminus: "⒁", amalg: "∐", circ: "◦", bigcirc: "◯", gtrdot: "⋗", lessdot: "⋖", smallsetminus: "⒅", circledast: "⊛", circledcirc: "⊚", sqcap: "⊓", sqcup: "⊔", barwedge: "⊼", circleddash: "⊝", star: "⋆", bigtriangledown: "▽", bigtriangleup: "△", cup: "∪", cap: "∩", times: "×", mp: "∓", pm: "±", triangleleft: "⊲", triangleright: "⊳", boxdot: "⊡", curlyvee: "⋏", curlywedge: "⋎", boxminus: "⊟", boxtimes: "⊠", ominus: "⊖", oplus: "⊕", oslash: "⊘", otimes: "⊗", uplus: "⊎", boxplus: "⊞", dagger: "†", ddagger: "‡", vee: "∨", lor: "∨", veebar: "⊻", bullet: "•", diamond: "⋄", wedge: "∧", land: "∧", div: "÷", wr: "≀", geqq: "≧", lll: "⋘", llless: "⋘", ggg: "⋙", gggtr: "⋙", preccurlyeq: "≼", geqslant: "⩾", lnapprox: "⪉", preceq: "⪯", gg: "≫", lneq: "⪇", precnapprox: "⪹", approx: "≈", lneqq: "≨", precneqq: "⪵", approxeq: "≊", gnapprox: "⪊", lnsim: "⋦", precnsim: "⋨", asymp: "≍", gneq: "⪈", lvertneqq: "⌮", precsim: "≾", backsim: "∽", gneqq: "≩", ncong: "≇", risingdotseq: "≓", backsimeq: "⋍", gnsim: "⋧", sim: "∼", simeq: "≃", bumpeq: "≏", gtrapprox: "⪆", ngeq: "≱", Bumpeq: "≎", gtreqless: "⋛", ngeqq: "⌱", succ: "≻", circeq: "≗", gtreqqless: "⪌", ngeqslant: "⌳", succapprox: "⪸", cong: "≅", gtrless: "≷", ngtr: "≯", succcurlyeq: "≽", curlyeqprec: "⋞", gtrsim: "≳", nleq: "≰", succeq: "⪰", curlyeqsucc: "⋟", gvertneqq: "⌯", neq: "≠", ne: "≠", nequiv: "≢", nleqq: "⌰", succnapprox: "⪺", doteq: "≐", leq: "≤", le: "≤", nleqslant: "⌲", succneqq: "⪶", doteqdot: "≑", Doteq: "≑", leqq: "≦", nless: "≮", succnsim: "⋩", leqslant: "⩽", nprec: "⊀", succsim: "≿", eqsim: "≂", lessapprox: "⪅", npreceq: "⋠", eqslantgtr: "⪖", lesseqgtr: "⋚", nsim: "≁", eqslantless: "⪕", lesseqqgtr: "⪋", nsucc: "⊁", triangleq: "≜", eqcirc: "≖", equiv: "≡", lessgtr: "≶", nsucceq: "⋡", fallingdotseq: "≒", lesssim: "≲", prec: "≺", geq: "≥", ge: "≥", ll: "≪", precapprox: "⪷", // arrows uparrow: "↑", downarrow: "↓", updownarrow: "↕", Uparrow: "⇑", Downarrow: "⇓", Updownarrow: "⇕", circlearrowleft: "↺", circlearrowright: "↻", curvearrowleft: "↶", curvearrowright: "↷", downdownarrows: "⇊", downharpoonleft: "⇃", downharpoonright: "⇂", leftarrow: "←", gets: "←", Leftarrow: "⇐", leftarrowtail: "↢", leftharpoondown: "↽", leftharpoonup: "↼", leftleftarrows: "⇇", leftrightarrow: "↔", Leftrightarrow: "⇔", leftrightarrows: "⇄", leftrightharpoons: "⇋", leftrightsquigarrow: "↭", Lleftarrow: "⇚", looparrowleft: "↫", looparrowright: "↬", multimap: "⊸", nLeftarrow: "⇍", nRightarrow: "⇏", nLeftrightarrow: "⇎", nearrow: "↗", nleftarrow: "↚", nleftrightarrow: "↮", nrightarrow: "↛", nwarrow: "↖", rightarrow: "→", to: "→", Rightarrow: "⇒", rightarrowtail: "↣", rightharpoondown: "⇁", rightharpoonup: "⇀", rightleftarrows: "⇆", rightleftharpoons: "⇌", rightrightarrows: "⇉", rightsquigarrow: "⇝", Rrightarrow: "⇛", searrow: "↘", swarrow: "↙", twoheadleftarrow: "↞", twoheadrightarrow: "↠", upharpoonleft: "↿", upharpoonright: "↾", restriction: "↾", upuparrows: "⇈", Lsh: "↰", Rsh: "↱", longleftarrow: "⟵", longrightarrow: "⟶", Longleftarrow: "⟸", Longrightarrow: "⟹", implies: "⟹", longleftrightarrow: "⟷", Longleftrightarrow: "⟺", // relation backepsilon: "∍", because: "∵", therefore: "∴", between: "≬", blacktriangleleft: "◀", blacktriangleright: "▸", dashv: "⊣", bowtie: "⋈", frown: "⌢", "in": "∈", notin: "∉", mid: "∣", parallel: "∥", models: "⊨", ni: "∋", owns: "∋", nmid: "∤", nparallel: "∦", nshortmid: "⏒", nshortparallel: "⏓", nsubseteq: "⊈", nsubseteqq: "⫇", nsupseteq: "⊉", nsupseteqq: "⫈", ntriangleleft: "⋪", ntrianglelefteq: "⋬", ntriangleright: "⋫", ntrianglerighteq: "⋭", nvdash: "⊬", nVdash: "⊮", nvDash: "⊭", nVDash: "⊯", perp: "⊥", pitchfork: "⋔", propto: "∝", shortmid: "⏐", shortparallel: "⏑", smile: "⌣", sqsubset: "⊏", sqsubseteq: "⊑", sqsupset: "⊐", sqsupseteq: "⊒", subset: "⊂", Subset: "⋐", subseteq: "⊆", subseteqq: "⫅", subsetneq: "⊊", subsetneqq: "⫋", supset: "⊃", Supset: "⋑", supseteq: "⊇", supseteqq: "⫆", supsetneq: "⊋", supsetneqq: "⫌", trianglelefteq: "⊴", trianglerighteq: "⊵", varpropto: "⫉", varsubsetneq: "⏔", varsubsetneqq: "⏖", varsupsetneq: "⏕", varsupsetneqq: "⏗", vdash: "⊢", Vdash: "⊩", vDash: "⊨", Vvdash: "⊪", vert: "|", Vert: "ǁ", "|": "ǁ", "{": "{", "}": "}", backslash: "\\", langle: "〈", rangle: "〉", lceil: "⌈", rceil: "⌉", lbrace: "{", rbrace: "}", lfloor: "⌊", rfloor: "⌋", cdots: "⋯", ddots: "⋰", vdots: "⋮", dots: "…", ldots: "…", "#": "#", bot: "⊥", angle: "∠", backprime: "‵", bigstar: "★", blacklozenge: "◆", blacksquare: "■", blacktriangle: "▲", blacktriangledown: "▼", clubsuit: "♣", diagdown: "⒁", diagup: "⒂", diamondsuit: "♢", emptyset: "ø", exists: "∃", flat: "♭", forall: "∀", heartsuit: "♡", infty: "∞", lozenge: "◇", measuredangle: "∡", nabla: "∇", natural: "♮", neg: "¬", lnot: "¬", nexists: "∄", prime: "′", sharp: "♯", spadesuit: "♠", sphericalangle: "∢", surd: "√", top: "⊤", varnothing: "∅", triangle: "△", triangledown: "▽" } }; } }; /*! * 罗马字体 */ _p[30] = { value: function() { return { meta: { fontFamily: "KF AMS ROMAN", src: "KF_AMS_ROMAN.woff" } }; } }; /** * 公式对象,表达式容器 */ _p[31] = { value: function(require) { var kity = _p.r(34), GTYPE = _p.r(6), FontManager = _p.r(25), FontInstaller = _p.r(24), DEFAULT_OPTIONS = { fontsize: 50, autoresize: true, padding: [ 0 ] }, Output = _p.r(1), EXPRESSION_INTERVAL = 10, ExpressionWrap = kity.createClass("ExpressionWrap", { constructor: function(exp, config) { this.wrap = new kity.Group(); this.bg = new kity.Rect(0, 0, 0, 0).fill("transparent"); this.exp = exp; this.config = config; this.wrap.setAttr("data-type", "kf-exp-wrap"); this.bg.setAttr("data-type", "kf-exp-wrap-bg"); this.wrap.addShape(this.bg); this.wrap.addShape(this.exp); }, getWrapShape: function() { return this.wrap; }, getExpression: function() { return this.exp; }, getBackground: function() { return this.bg; }, resize: function() { var padding = this.config.padding, expBox = this.exp.getFixRenderBox(); if (padding.length === 1) { padding[1] = padding[0]; } this.bg.setSize(padding[1] * 2 + expBox.width, padding[0] * 2 + expBox.height); this.exp.translate(padding[1], padding[0]); } }), Formula = kity.createClass("Formula", { base: _p.r(32), constructor: function(container, config) { this.callBase(container); this.expressions = []; this.fontInstaller = new FontInstaller(this); this.config = kity.Utils.extend({}, DEFAULT_OPTIONS, config); this.initEnvironment(); this.initInnerFont(); }, getContentContainer: function() { return this.container; }, initEnvironment: function() { this.zoom = this.config.fontsize / 50; if ("width" in this.config) { this.setWidth(this.config.width); } if ("height" in this.config) { this.setHeight(this.config.height); } this.node.setAttribute("font-size", DEFAULT_OPTIONS.fontsize); }, initInnerFont: function() { var fontList = FontManager.getFontList(), _self = this; kity.Utils.each(fontList, function(fontInfo) { createFontStyle(fontInfo); }); function createFontStyle(fontInfo) { var stylesheet = _self.doc.createElement("style"), tpl = '@font-face{font-family: "${fontFamily}";font-style: normal;src: url("${src}") format("woff");}'; stylesheet.setAttribute("type", "text/css"); stylesheet.innerHTML = tpl.replace("${fontFamily}", fontInfo.meta.fontFamily).replace("${src}", fontInfo.meta.src); _self.resourceNode.appendChild(stylesheet); } }, insertExpression: function(expression, index) { var expWrap = this.wrap(expression); // clear zoom this.container.clearTransform(); this.expressions.splice(index, 0, expWrap.getWrapShape()); this.addShape(expWrap.getWrapShape()); notifyExpression.call(this, expWrap.getExpression()); expWrap.resize(); correctOffset.call(this); this.resetZoom(); this.config.autoresize && this.resize(); }, appendExpression: function(expression) { this.insertExpression(expression, this.expressions.length); }, resize: function() { var renderBox = this.container.getRenderBox("paper"); this.node.setAttribute("width", renderBox.width); this.node.setAttribute("height", renderBox.height); }, resetZoom: function() { var zoomLevel = this.zoom / this.getBaseZoom(); if (zoomLevel !== 0) { this.container.scale(zoomLevel); } }, wrap: function(exp) { return new ExpressionWrap(exp, this.config); }, clear: function() { this.callBase(); this.expressions = []; }, clearExpressions: function() { kity.Utils.each(this.expressions, function(exp) { exp.remove(); }); this.expressions = []; }, toJPG: function(cb) { new Output(this).toJPG(cb); }, toPNG: function(cb) { new Output(this).toPNG(cb); } }); kity.Utils.extend(Formula, { registerFont: function(fontData) { FontManager.registerFont(fontData); } }); // 调整表达式之间的偏移 function correctOffset() { var exprOffset = 0; kity.Utils.each(this.expressions, function(expr) { var box = null; if (!expr) { return; } expr.setMatrix(new kity.Matrix(1, 0, 0, 1, 0, 0)); box = expr.getFixRenderBox(); expr.translate(0 - box.x, exprOffset); exprOffset += box.height + EXPRESSION_INTERVAL; }); return this; } // 通知表达式已接入到paper function notifyExpression(expression) { var len = 0; if (!expression) { return; } if (expression.getType() === GTYPE.EXP) { for (var i = 0, len = expression.getChildren().length; i < len; i++) { notifyExpression(expression.getChild(i)); } } else if (expression.getType() === GTYPE.COMPOUND_EXP) { // 操作数处理 for (var i = 0, len = expression.getOperands().length; i < len; i++) { notifyExpression(expression.getOperand(i)); } // 处理操作符 notifyExpression(expression.getOperator()); } expression.addedCall && expression.addedCall(); } return Formula; } }; /** * 公式专用paper */ _p[32] = { value: function(require) { var kity = _p.r(34); return kity.createClass("FPaper", { base: kity.Paper, constructor: function(container) { this.callBase(container); this.doc = container.ownerDocument; this.container = new kity.Group(); this.container.setAttr("data-type", "kf-container"); this.background = new kity.Group(); this.background.setAttr("data-type", "kf-bg"); this.baseZoom = 1; this.zoom = 1; this.base("addShape", this.background); this.base("addShape", this.container); }, getZoom: function() { return this.zoom; }, getBaseZoom: function() { return this.baseZoom; }, addShape: function(shape, pos) { return this.container.addShape(shape, pos); }, getBackground: function() { return this.background; }, removeShape: function(pos) { return this.container.removeShape(pos); }, clear: function() { return this.container.clear(); } }); } }; /** * jquery */ _p[33] = { value: function() { if (!window.jQuery) { throw new Error("Missing jQuery"); } return window.jQuery; } }; /** * kity库封包 */ _p[34] = { value: function() { if (!window.kity) { throw new Error("Missing Kity Graphic Lib"); } return window.kity; } }; /** * 小括号操作符:() */ _p[35] = { value: function(require) { var kity = _p.r(34), Text = _p.r(5); return kity.createClass("BracketsOperator", { base: _p.r(41), constructor: function() { this.callBase("Brackets"); }, applyOperand: function(exp) { generate.call(this, exp); } }); function generate(exp) { var left = this.getParentExpression().getLeftSymbol(), right = this.getParentExpression().getRightSymbol(), fontSize = exp.getFixRenderBox().height, group = new kity.Group(), offset = 0, leftOp = new Text(left, "KF AMS MAIN").fill("black"), rightOp = new Text(right, "KF AMS MAIN").fill("black"); leftOp.setFontSize(fontSize); rightOp.setFontSize(fontSize); this.addOperatorShape(group.addShape(leftOp).addShape(rightOp)); offset += leftOp.getFixRenderBox().width; exp.translate(offset, 0); offset += exp.getFixRenderBox().width; rightOp.translate(offset, 0); } } }; /** * 组合操作符 * 操作多个表达式组合在一起 */ _p[36] = { value: function(require) { var kity = _p.r(34); return kity.createClass("CombinationOperator", { base: _p.r(41), constructor: function() { this.callBase("Combination"); }, applyOperand: function() { // 偏移量 var offsetX = 0, // 操作数 operands = arguments, // 操作对象最大高度 maxHeight = 0, // 垂直距离最大偏移 maxOffsetTop = 0, maxOffsetBottom = 0, cached = [], // 偏移集合 offsets = []; kity.Utils.each(operands, function(operand) { var box = operand.getFixRenderBox(), offsetY = operand.getOffset(); box.height -= offsetY.top + offsetY.bottom; cached.push(box); offsets.push(offsetY); maxOffsetTop = Math.max(offsetY.top, maxOffsetTop); maxOffsetBottom = Math.max(offsetY.bottom, maxOffsetBottom); maxHeight = Math.max(box.height, maxHeight); }); kity.Utils.each(operands, function(operand, index) { var box = cached[index]; operand.translate(offsetX - box.x, (maxHeight - (box.y + box.height)) / 2 + maxOffsetBottom - offsets[index].bottom); offsetX += box.width; }); this.parentExpression.setOffset(maxOffsetTop, maxOffsetBottom); this.parentExpression.updateBoxSize(); } }); } }; /*! * 上下标控制器 */ _p[37] = { value: function(require) { var kity = _p.r(34), EmptyExpression = _p.r(20), defaultOptions = { subOffset: 0, supOffset: 0, // 上下标的默认缩放值 zoom: .66 }; return kity.createClass("ScriptController", { constructor: function(opObj, target, sup, sub, options) { this.observer = opObj.getParentExpression(); this.target = target; this.sup = sup; this.sub = sub; this.options = kity.Utils.extend({}, defaultOptions, options); }, // 上下标记 applyUpDown: function() { var target = this.target, sup = this.sup, sub = this.sub, options = this.options; sup.scale(options.zoom); sub.scale(options.zoom); var targetBox = target.getFixRenderBox(); if (EmptyExpression.isEmpty(sup) && EmptyExpression.isEmpty(sub)) { return { width: targetBox.width, height: targetBox.height, top: 0, bottom: 0 }; } else { // 上标 if (!EmptyExpression.isEmpty(sup) && EmptyExpression.isEmpty(sub)) { return this.applyUp(target, sup); } else if (EmptyExpression.isEmpty(sup) && !EmptyExpression.isEmpty(sub)) { return this.applyDown(target, sub); } else { return this.applyUpDownScript(target, sup, sub); } } }, /** * 返回应用上下标后的空间占用情况,其中的key各自的意义是: * top: 上空间偏移 * bottom: 下空间偏移 * width: 当前整个图形的实际占用空间的width * height: 当前整个图形的实际占用空间的height * @returns {*} */ applySide: function() { var target = this.target, sup = this.sup, sub = this.sub; if (EmptyExpression.isEmpty(sup) && EmptyExpression.isEmpty(sub)) { var targetRectBox = target.getRenderBox(this.observer); return { width: targetRectBox.width, height: targetRectBox.height, top: 0, bottom: 0 }; } else { // 下标处理 if (EmptyExpression.isEmpty(sup) && !EmptyExpression.isEmpty(sub)) { return this.applySideSub(target, sub); } else if (!EmptyExpression.isEmpty(sup) && EmptyExpression.isEmpty(sub)) { return this.applySideSuper(target, sup); } else { return this.applySideScript(target, sup, sub); } } }, applySideSuper: function(target, sup) { sup.scale(this.options.zoom); var targetRectBox = target.getRenderBox(this.observer), supRectBox = sup.getRenderBox(this.observer), targetMeanline = target.getMeanline(this.observer), supBaseline = sup.getBaseline(this.observer), positionline = targetMeanline, diff = supBaseline - positionline, space = { top: 0, bottom: 0, width: targetRectBox.width + supRectBox.width, height: targetRectBox.height }; sup.translate(targetRectBox.width, 0); if (this.options.supOffset) { sup.translate(this.options.supOffset, 0); } if (diff > 0) { target.translate(0, diff); space.bottom = diff; space.height += diff; } else { sup.translate(0, -diff); } return space; }, applySideSub: function(target, sub) { sub.scale(this.options.zoom); var targetRectBox = target.getRenderBox(this.observer), subRectBox = sub.getRenderBox(this.observer), subOffset = sub.getOffset(), targetBaseline = target.getBaseline(this.observer), // 下标定位线 subPosition = (subRectBox.height + subOffset.top + subOffset.bottom) / 2, diff = targetRectBox.height - targetBaseline - subPosition, space = { top: 0, bottom: 0, width: targetRectBox.width + subRectBox.width, height: targetRectBox.height }; // 定位下标位置 sub.translate(targetRectBox.width, subOffset.top + targetBaseline - subPosition); if (this.options.subOffset) { sub.translate(this.options.subOffset, 0); } if (diff < 0) { space.top = -diff; space.height -= diff; } return space; }, applySideScript: function(target, sup, sub) { sup.scale(this.options.zoom); sub.scale(this.options.zoom); var targetRectBox = target.getRenderBox(this.observer), subRectBox = sub.getRenderBox(this.observer), supRectBox = sup.getRenderBox(this.observer), targetMeanline = target.getMeanline(this.observer), targetBaseline = target.getBaseline(this.observer), supBaseline = sup.getBaseline(this.observer), // 上下标都存在时, 下标的定位以上伸线为准 subAscenderline = sub.getAscenderline(this.observer), supPosition = targetMeanline, subPosition = targetMeanline + (targetBaseline - targetMeanline) * 2 / 3, topDiff = supPosition - supBaseline, bottomDiff = targetRectBox.height - subPosition - (subRectBox.height - subAscenderline), space = { top: 0, bottom: 0, width: targetRectBox.width + Math.max(subRectBox.width, supRectBox.width), height: targetRectBox.height }; sup.translate(targetRectBox.width, topDiff); sub.translate(targetRectBox.width, subPosition - subAscenderline); if (this.options.supOffset) { sup.translate(this.options.supOffset, 0); } if (this.options.subOffset) { sub.translate(this.options.subOffset, 0); } // 定位纠正 if (topDiff > 0) { if (bottomDiff < 0) { targetRectBox.height -= bottomDiff; space.top = -bottomDiff; } } else { target.translate(0, -topDiff); sup.translate(0, -topDiff); sub.translate(0, -topDiff); space.height -= topDiff; if (bottomDiff > 0) { space.bottom = -topDiff; } else { space.height -= bottomDiff; // 比较上下偏移, 获取正确的偏移值 topDiff = -topDiff; bottomDiff = -bottomDiff; if (topDiff > bottomDiff) { space.bottom = topDiff - bottomDiff; } else { space.top = bottomDiff - topDiff; } } } return space; }, applyUp: function(target, sup) { var supBox = sup.getFixRenderBox(), targetBox = target.getFixRenderBox(), space = { width: Math.max(targetBox.width, supBox.width), height: supBox.height + targetBox.height, top: 0, bottom: supBox.height }; sup.translate((space.width - supBox.width) / 2, 0); target.translate((space.width - targetBox.width) / 2, supBox.height); return space; }, applyDown: function(target, sub) { var subBox = sub.getFixRenderBox(), targetBox = target.getFixRenderBox(), space = { width: Math.max(targetBox.width, subBox.width), height: subBox.height + targetBox.height, top: subBox.height, bottom: 0 }; sub.translate((space.width - subBox.width) / 2, targetBox.height); target.translate((space.width - targetBox.width) / 2, 0); return space; }, applyUpDownScript: function(target, sup, sub) { var supBox = sup.getFixRenderBox(), subBox = sub.getFixRenderBox(), targetBox = target.getFixRenderBox(), space = { width: Math.max(targetBox.width, supBox.width, subBox.width), height: supBox.height + subBox.height + targetBox.height, top: 0, bottom: 0 }; sup.translate((space.width - supBox.width) / 2, 0); target.translate((space.width - targetBox.width) / 2, supBox.height); sub.translate((space.width - subBox.width) / 2, supBox.height + targetBox.height); return space; } }); } }; /** * 分数操作符 */ _p[38] = { value: function(require) { var kity = _p.r(34), ZOOM = _p.r(47).zoom; return kity.createClass("FractionOperator", { base: _p.r(41), constructor: function() { this.callBase("Fraction"); }, applyOperand: function(upOperand, downOperand) { upOperand.scale(ZOOM); downOperand.scale(ZOOM); var upWidth = Math.ceil(upOperand.getWidth()), downWidth = Math.ceil(downOperand.getWidth()), upHeight = Math.ceil(upOperand.getHeight()), downHeight = Math.ceil(downOperand.getHeight()), // 分数线overflow值 overflow = 3, // 整体padding padding = 1, maxWidth = Math.max(upWidth, downWidth), maxHeight = Math.max(upHeight, downHeight), operatorShape = generateOperator(maxWidth, overflow); this.addOperatorShape(operatorShape); upOperand.translate((maxWidth - upWidth) / 2 + overflow, 0); operatorShape.translate(0, upHeight + 1); // 下部不需要偏移 downOperand.translate((maxWidth - downWidth) / 2 + overflow, upHeight + operatorShape.getHeight() + 1 * 2); this.parentExpression.setOffset(maxHeight - upHeight, maxHeight - downHeight); this.parentExpression.expand(padding * 2, padding * 2); this.parentExpression.translateElement(padding, padding); } }); function generateOperator(width, overflow) { return new kity.Rect(width + overflow * 2, 1).fill("black"); } } }; /** * 函数操作符 */ _p[39] = { value: function(require) { var kity = _p.r(34), Text = _p.r(5), ScriptController = _p.r(37); return kity.createClass("FunctionOperator", { base: _p.r(41), constructor: function(funcName) { this.callBase("Function: " + funcName); this.funcName = funcName; }, /* * 积分操作符应用操作数 * @param expr 函数表达式 * @param sup 上限 * @param sub 下限 */ applyOperand: function(expr, sup, sub) { var opShape = generateOperator.call(this), expBox = expr.getFixRenderBox(), scriptHanlder = this.parentExpression.isSideScript() ? "applySide" : "applyUpDown", space = new ScriptController(this, opShape, sup, sub, { zoom: .5 })[scriptHanlder](), padding = 5, diff = (space.height + space.top + space.bottom - expBox.height) / 2; // 应用偏移, 使图形在正确的位置上 opShape.translate(0, space.top); sup.translate(0, space.top); sub.translate(0, space.top); if (diff >= 0) { expr.translate(space.width + padding, diff); } else { diff = -diff; opShape.translate(0, diff); sup.translate(0, diff); sub.translate(0, diff); expr.translate(space.width + padding, 0); } // 只扩展左边, 不扩展右边, 所以padding不 *2 this.parentExpression.expand(padding, padding * 2); this.parentExpression.translateElement(padding, padding); } }); /* 返回操作符对象 */ function generateOperator() { var opShape = new Text(this.funcName, "KF AMS ROMAN"); this.addOperatorShape(opShape); // 为操作符图形创建baseline和meanline方法 opShape.getBaseline = function() { return opShape.getFixRenderBox().height; }; opShape.getMeanline = function() { return 0; }; return opShape; } } }; /** * 积分操作符:∫ */ _p[40] = { value: function(require) { var kity = _p.r(34), ScriptController = _p.r(37); return kity.createClass("IntegrationOperator", { base: _p.r(41), constructor: function(type) { this.callBase("Integration"); // 默认是普通单重积分 this.opType = type || 1; }, setType: function(type) { this.opType = type | 0; }, // 重置类型 resetType: function() { this.opType = 1; }, applyOperand: function(exp, sup, sub) { var opShape = this.getOperatorShape(), padding = 3, expBox = exp.getFixRenderBox(), space = new ScriptController(this, opShape, sup, sub, { supOffset: 3, subOffset: -15 }).applySide(), diff = (space.height + space.top - expBox.height) / 2; opShape.translate(0, space.top); sup.translate(0, space.top); sub.translate(0, space.top); if (diff >= 0) { exp.translate(space.width + padding, diff); } else { diff = -diff; opShape.translate(0, diff); sup.translate(0, diff); sub.translate(0, diff); exp.translate(space.width + padding, 0); } this.parentExpression.expand(padding, padding * 2); this.parentExpression.translateElement(padding, padding); }, getOperatorShape: function() { var pathData = "M1.318,48.226c0,0,0.044,0.066,0.134,0.134c0.292,0.313,0.626,0.447,1.006,0.447c0.246,0.022,0.358-0.044,0.604-0.268 c0.782-0.782,1.497-2.838,2.324-6.727c0.514-2.369,0.938-4.693,1.586-8.448C8.559,24.068,9.9,17.878,11.978,9.52 c0.917-3.553,1.922-7.576,3.866-8.983C16.247,0.246,16.739,0,17.274,0c1.564,0,2.503,1.162,2.592,2.57 c0,0.827-0.424,1.386-1.273,1.386c-0.671,0-1.229-0.514-1.229-1.251c0-0.805,0.514-1.095,1.185-1.274 c0.022,0-0.291-0.29-0.425-0.379c-0.201-0.134-0.514-0.224-0.737-0.224c-0.067,0-0.112,0-0.157,0.022 c-0.469,0.134-0.983,0.939-1.453,2.234c-0.537,1.475-0.961,3.174-1.631,6.548c-0.424,2.101-0.693,3.464-1.229,6.727 c-1.608,9.185-2.949,15.487-5.006,23.756c-0.514,2.034-0.849,3.24-1.207,4.335c-0.559,1.698-1.162,2.95-1.811,3.799 c-0.514,0.715-1.385,1.408-2.436,1.408c-1.363,0-2.391-1.185-2.458-2.592c0-0.804,0.447-1.363,1.273-1.363 c0.671,0,1.229,0.514,1.229,1.251C2.503,47.757,1.989,48.047,1.318,48.226z", group = new kity.Group(), opGroup = new kity.Group(), opShape = new kity.Path(pathData).fill("black"), opBox = new kity.Rect(0, 0, 0, 0).fill("transparent"), tmpShape = null; opGroup.addShape(opShape); group.addShape(opBox); group.addShape(opGroup); this.addOperatorShape(group); for (var i = 1; i < this.opType; i++) { tmpShape = new kity.Use(opShape).translate(opShape.getWidth() / 2 * i, 0); opGroup.addShape(tmpShape); } opGroup.scale(1.6); tmpShape = null; // 为操作符图形创建baseline和meanline方法 group.getBaseline = function() { return opGroup.getFixRenderBox().height; }; group.getMeanline = function() { return 10; }; return group; } }); } }; /** * 操作符抽象类 * @abstract */ _p[41] = { value: function(require) { var kity = _p.r(34), GTYPE = _p.r(6); return kity.createClass("Operator", { base: _p.r(46), constructor: function(operatorName) { this.callBase(); this.type = GTYPE.OP; // 该操作符所属的表达式 this.parentExpression = null; // 操作符名称 this.operatorName = operatorName; // 操作符图形 this.operatorShape = new kity.Group(); this.addShape(this.operatorShape); }, applyOperand: function() { throw new Error("applyOperand is abstract"); }, setParentExpression: function(exp) { this.parentExpression = exp; }, getParentExpression: function() { return this.parentExpression; }, clearParentExpression: function() { this.parentExpression = null; }, // 提供给具体实现类附加其绘制的操作符图形的接口 addOperatorShape: function(shpae) { return this.operatorShape.addShape(shpae); }, getOperatorShape: function() { return this.operatorShape; } }); } }; /** * 开方操作符 */ _p[42] = { value: function(require) { var kity = _p.r(34), // 符号图形属性 // 线条宽度 SHAPE_DATA_WIDTH = 1, // 计算公式 radians = 2 * Math.PI / 360, sin15 = Math.sin(15 * radians), cos15 = Math.cos(15 * radians), tan15 = Math.tan(15 * radians); return kity.createClass("RadicalOperator", { base: _p.r(41), constructor: function() { this.callBase("Radical"); }, applyOperand: function(radicand, exponent) { generateOperator.call(this, radicand, exponent); } }); // 根据给定的操作数生成操作符的pathData // radicand 表示被开方数 // exponent 表示指数 function generateOperator(radicand, exponent) { var decoration = generateDecoration(radicand), vLine = generateVLine(radicand), padding = 5, hLine = generateHLine(radicand); this.addOperatorShape(decoration); this.addOperatorShape(vLine); this.addOperatorShape(hLine); adjustmentPosition.call(this, mergeShape(decoration, vLine, hLine), this.operatorShape, radicand, exponent); this.parentExpression.expand(0, padding * 2); this.parentExpression.translateElement(0, padding); } // 生成根号中的左边装饰部分 function generateDecoration(radicand) { var shape = new kity.Path(), // 命名为a以便于精简表达式 a = SHAPE_DATA_WIDTH, h = radicand.getHeight() / 3, drawer = shape.getDrawer(); // 根号尾部左上角开始 drawer.moveTo(0, cos15 * a * 6); drawer.lineBy(sin15 * a, cos15 * a); drawer.lineBy(cos15 * a * 3, -sin15 * a * 3); drawer.lineBy(tan15 * h, h); drawer.lineBy(sin15 * a * 3, -cos15 * a * 3); drawer.lineBy(-sin15 * h, -h); drawer.close(); return shape.fill("black"); } // 根据操作数生成根号的竖直线部分 function generateVLine(operand) { var shape = new kity.Path(), // * 0.9 是为了在视觉上使斜线部分不至于太高 h = operand.getHeight() * .9, drawer = shape.getDrawer(); drawer.moveTo(tan15 * h, 0); drawer.lineTo(0, h); drawer.lineBy(sin15 * SHAPE_DATA_WIDTH * 3, cos15 * SHAPE_DATA_WIDTH * 3); drawer.lineBy(tan15 * h + sin15 * SHAPE_DATA_WIDTH * 3, -(h + 3 * SHAPE_DATA_WIDTH * cos15)); drawer.close(); return shape.fill("black"); } // 根据操作数生成根号的水平线部分 function generateHLine(operand) { // 表达式宽度 var w = operand.getWidth() + 2 * SHAPE_DATA_WIDTH; return new kity.Rect(w, 2 * SHAPE_DATA_WIDTH).fill("black"); } // 合并根号的各个部分, 并返回根号的关键点位置数据 function mergeShape(decoration, vLine, hLine) { var decoBox = decoration.getFixRenderBox(), vLineBox = vLine.getFixRenderBox(); vLine.translate(decoBox.width - sin15 * SHAPE_DATA_WIDTH * 3, 0); decoration.translate(0, vLineBox.height - decoBox.height); vLineBox = vLine.getFixRenderBox(); hLine.translate(vLineBox.x + vLineBox.width - SHAPE_DATA_WIDTH / cos15, 0); // 返回关键点数据 return { x: vLineBox.x + vLineBox.width - SHAPE_DATA_WIDTH / cos15, y: 0 }; } // 调整整个根号表达式的各个部分: 位置、操作符、被开方数、指数 function adjustmentPosition(position, operator, radicand, exponent) { var exponentBox = null, opOffset = { x: 0, y: 0 }, opBox = operator.getFixRenderBox(); exponent.scale(.66); exponentBox = exponent.getFixRenderBox(); if (exponentBox.width > 0 && exponentBox.height > 0) { opOffset.y = exponentBox.height - opBox.height / 2; // 指数不超出根号, 则移动指数 if (opOffset.y < 0) { exponent.translate(0, -opOffset.y); opOffset.y = 0; } opOffset.x = exponentBox.width + opBox.height / 2 * tan15 - position.x; } operator.translate(opOffset.x, opOffset.y); radicand.translate(opOffset.x + position.x + SHAPE_DATA_WIDTH, opOffset.y + 2 * SHAPE_DATA_WIDTH); } } }; /** * 上下标操作符 */ _p[43] = { value: function(require) { var kity = _p.r(34), ScriptController = _p.r(37); return kity.createClass("ScriptOperator", { base: _p.r(41), constructor: function(operatorName) { this.callBase(operatorName || "Script"); }, applyOperand: function(operand, sup, sub) { var padding = 1, parent = this.parentExpression, space = new ScriptController(this, operand, sup, sub).applySide(); this.getOperatorShape(); space && parent.setOffset(space.top, space.bottom); parent.expand(4, padding * 2); parent.translateElement(2, padding); } }); } }; /** * 求和操作符:∑ */ _p[44] = { value: function(require) { var kity = _p.r(34), ScriptController = _p.r(37); return kity.createClass("SummationOperator", { base: _p.r(41), constructor: function() { this.callBase("Summation"); this.displayType = "equation"; }, applyOperand: function(expr, sup, sub) { var opShape = this.getOperatorShape(), expBox = expr.getFixRenderBox(), padding = 0, space = new ScriptController(this, opShape, sup, sub).applyUpDown(), diff = (space.height - space.top - space.bottom - expBox.height) / 2; if (diff >= 0) { expr.translate(space.width + padding, diff + space.bottom); } else { diff = -diff; opShape.translate(0, diff); sup.translate(0, diff); sub.translate(0, diff); expr.translate(space.width + padding, space.bottom); } this.parentExpression.setOffset(space.top, space.bottom); this.parentExpression.expand(padding, padding * 2); this.parentExpression.translateElement(padding, padding); }, getOperatorShape: function() { var pathData = "M0.672,33.603c-0.432,0-0.648,0-0.648-0.264c0-0.024,0-0.144,0.24-0.432l12.433-14.569L0,0.96c0-0.264,0-0.72,0.024-0.792 C0.096,0.024,0.12,0,0.672,0h28.371l2.904,6.745h-0.6C30.531,4.8,28.898,3.72,28.298,3.336c-1.896-1.2-3.984-1.608-5.28-1.8 c-0.216-0.048-2.4-0.384-5.617-0.384H4.248l11.185,15.289c0.168,0.24,0.168,0.312,0.168,0.36c0,0.12-0.048,0.192-0.216,0.384 L3.168,31.515h14.474c4.608,0,6.96-0.624,7.464-0.744c2.76-0.72,5.305-2.352,6.241-4.848h0.6l-2.904,7.681H0.672z", operatorShape = new kity.Path(pathData).fill("black"), opBgShape = new kity.Rect(0, 0, 0, 0).fill("transparent"), group = new kity.Group(), opRenderBox = null; group.addShape(opBgShape); group.addShape(operatorShape); operatorShape.scale(1.6); this.addOperatorShape(group); opRenderBox = operatorShape.getFixRenderBox(); if (this.displayType === "inline") { operatorShape.translate(5, 15); opBgShape.setSize(opRenderBox.width + 10, opRenderBox.height + 25); } else { operatorShape.translate(2, 5); opBgShape.setSize(opRenderBox.width + 4, opRenderBox.height + 8); } return group; } }); } }; /*! * 资源管理器 * 负责管理资源的加载,并在资源ready之后提供Formula构造器 */ _p[45] = { value: function(require) { var kity = _p.r(34), cbList = [], RES_CONF = _p.r(47).resource, FontInstall = _p.r(24), Formula = _p.r(31), // 资源管理器就绪状态 __readyState = false, // 资源管理器是否已启动 inited = false; return { // 初始化 ready: function(cb, options) { if (!inited) { inited = true; init(options); } if (__readyState) { window.setTimeout(function() { cb(Formula); }, 0); } else { cbList.push(cb); } } }; /** * 资源初始化 */ function init(options) { options = kity.Utils.extend({}, RES_CONF, options); if (!/^(https?:)?\/\//.test(options.path)) { options.path = getFullPath(options.path); } new FontInstall(document, options.path).mount(complete); } function complete() { kity.Utils.each(cbList, function(cb) { cb(Formula); }); } function getFullPath(path) { var pathname = location.pathname.split("/"), pathPart; pathname.length -= 1; pathname = pathname.join("/") + "/"; pathPart = [ location.protocol, "//", location.host, pathname, path.replace(/^\//, "") ]; return pathPart.join(""); } } }; /*! * 所有符号的基类 * @abstract */ _p[46] = { value: function(require) { var kity = _p.r(34), GTYPE = _p.r(6); return kity.createClass("SignGroup", { base: kity.Group, constructor: function() { this.callBase(); this.box = new kity.Rect(0, 0, 0, 0); this.type = GTYPE.UNKNOWN; this.addShape(this.box); this.zoom = 1; }, setZoom: function(zoom) { this.zoom = zoom; }, getZoom: function() { return this.zoom; }, setBoxSize: function(w, h) { return this.box.setSize(w, h); }, setBoxWidth: function(w) { return this.box.setWidth(w); }, setBoxHeight: function(h) { return this.box.setHeight(h); }, getType: function() { return this.type; }, getBaseHeight: function() { return this.getHeight(); }, getBaseWidth: function() { return this.getWidth(); }, addedCall: function() {} }); } }; /*! * 系统项目配置文件. */ _p[47] = { value: function(require) { return { zoom: .66, font: { meanline: Math.round(380 / 1e3 * 50), baseline: Math.round(800 / 1e3 * 50), baseHeight: 50, // 系统字体列表 list: [ _p.r(29), _p.r(27), _p.r(28), _p.r(26), _p.r(30) ] }, /*------------------------- 资源配置*/ resource: { path: "src/resource/" }, // 函数相关配置 func: { // 上下标在函数名上下两侧的函数列表 "ud-script": { lim: true } } }; } }; /*! * 启动代码 */ _p[48] = { value: function(require) { window.kf = { // base ResourceManager: _p.r(45), Operator: _p.r(41), // expression Expression: _p.r(21), CompoundExpression: _p.r(19), TextExpression: _p.r(22), EmptyExpression: _p.r(20), CombinationExpression: _p.r(12), FunctionExpression: _p.r(14), FractionExpression: _p.r(13), IntegrationExpression: _p.r(15), RadicalExpression: _p.r(16), ScriptExpression: _p.r(17), SuperscriptExpression: _p.r(9), SubscriptExpression: _p.r(8), SummationExpression: _p.r(18), // Brackets expressoin BracketsExpression: _p.r(11) }; } }; var moduleMapping = { "kf.start": 48 }; function use(name) { _p.r([ moduleMapping[name] ]); } /** * 模块暴露 */ ( function ( global ) { var oldGetRenderBox = kity.Shape.getRenderBox; kity.extendClass(kity.Shape, { getFixRenderBox: function () { return this.getRenderBox( this.container.container ); }, getTranslate: function () { return this.transform.translate; } }); // build环境中才含有use try { use( 'kf.start' ); } catch ( e ) { } } )( this ); })();