}
opts.axis = opts.axis || "";
-
var allx = Array.prototype.concat.apply([], opts.xvalues),
ally = Array.prototype.concat.apply([], opts.yvalues),
--- /dev/null
+/*!
+ * g.Raphael 0.51 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009-2012 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+
+if (typeof(Raphael) == 'undefined') {
+ // support for seed/simple browser usage
+ importz = imports['seed/importz.js'].importz;
+
+ Raphael = importz('Raphael');
+ Roo = importz('Roo');
+}
+
+(function () {
+
+ function clearAr(ar) {
+ var ret = new Array();
+ //Roo.log(JSON.stringify(ar));
+ for (var i = 0; i < ar.length; i++) {
+ //print(typeof(ar[i]));
+ if (Raphael.is(ar[i], "object")) {
+ ret.push(clearAr(ar[i]));
+ continue;
+ }
+ ret.push(parseInt(ar[i]));
+ }
+ return ret;
+ }
+
+
+/*\
+ * Paper.vbarchart
+ [ method ]
+ **
+ * Creates a vertical bar chart
+ **
+ > Parameters
+ **
+ - x (number) x coordinate of the chart
+ - y (number) y coordinate of the chart
+ - width (number) width of the chart (respected by all elements in the set)
+ - height (number) height of the chart (respected by all elements in the set)
+ - values (array) values
+ - opts (object) options for the chart
+ o {
+ o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
+ o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
+ o vgutter (number)
+ o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
+ o stacked (boolean) whether or not to tread values as in a stacked bar chart
+ o to
+ o stretch (boolean)
+ o }
+ **
+ = (object) path element of the popup
+ > Usage
+ | r.vbarchart(0, 0, 620, 260, [[76, 70], [67, 71]], {})
+ \*/
+
+
+ function MVBarchart(paper, x, y, width, height, values, opts) {
+ opts = opts || {};
+ values = clearAr(values);
+
+ var chartinst = this,
+ chart = paper.set(),
+ bars = paper.set(),
+ covers = paper.set(),
+ barsteps = opts.barsteps || 20,
+ colors = opts.colors || chartinst.colors,
+ max = 3000;
+
+ opts.axis = opts.axis || "";
+ opts.baroffset = opts.baroffset || 50;
+ opts.asixxheight = opts.asixxheight || 100;
+ opts.asixywidth = opts.asixywidth || 100;
+ opts.axisxlabels = opts.axisxlabels || [];
+
+ // background
+ paper.rect(0, 0, width, height).attr({ stroke: "none", fill: (opts.background || "#F0F4F7") });
+// background.toBack();
+
+ // bar border
+ var barwidth = Math.floor((width - x - opts.asixywidth - opts.baroffset) / barsteps);
+ var barheight = height - y - opts.asixxheight || 100;
+ var path = ["M", x + 1, y, "l", 0, barheight]
+
+ for (var i = 0; i < barsteps; i++) {
+ path = path.concat(["M", x + (i * barwidth), y + 1, "l", barwidth, 0, "l", 0, barheight]);
+ }
+
+ paper.path(path).attr({ stroke: "#fff", "stroke-width": 2 });
+
+ //bars
+ var unit = barheight / max;
+ var indicator = [];
+ values.forEach((value,i) => {
+ if (!Raphael.is(values[0], "array")) {
+ value = [value];
+ }
+
+ value.forEach((v,k) => {
+ paper.rect(x + 1 + (k * barwidth) , y + (max - v) * unit, barwidth - 2, v * unit).attr({ stroke: "none", fill: colors[i%colors.length] });
+ paper.rect(x + 1 + (k * barwidth) + barwidth / 4 , y + (max - v) * unit - 42, barwidth / 2, 30, 5).attr({ stroke: "none", fill: "#0C014D" });
+ indicator = indicator.concat(["M", x + 1 + (k * barwidth) + barwidth / 4 + 10, y + (max - v) * unit - 12, "l", 5, 10, "l", 5, -10]);
+ paper.text(x + 1 + (k * barwidth) + barwidth / 2, y + (max - v) * unit - 28, Roo.util.Format.number(v, 0)).attr({
+ "font-size": "16",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ fill : "#fff"
+ });
+ });
+
+ });
+
+ paper.path(indicator).attr({ stroke: "none", fill: "#0C014D" });
+
+ var ax = (opts.axis + "").split(/[,\s]+/),
+ axis = paper.set();
+
+ // right axis
+ +ax[1] && axis.push(
+ chartinst.rightAxis(
+ x + width - opts.asixywidth ,
+ y + height - opts.asixxheight,
+ height - y - opts.asixxheight,
+ max,
+ opts.axisystep || 10,
+ opts.asixywidth,
+ paper
+ )
+ );
+
+ // bottom axis
+ +ax[2] && axis.push(
+ chartinst.bottomAxis(
+ x ,
+ height - opts.asixxheight,
+ width,
+ opts.axisxlabels.length,
+ opts.axisxlabels,
+ barwidth,
+ opts.asixxheight,
+ paper
+ )
+ );
+
+ chart.push(bars, covers);
+ chart.bars = bars;
+ chart.covers = covers;
+ return chart;
+ };
+
+ //inheritance
+ var F = function() {};
+ F.prototype = Raphael.g;
+ MVBarchart.prototype = new F; //prototype reused by hbarchart
+
+ Raphael.fn.mbarchart = function(x, y, width, height, values, opts) {
+ return new MVBarchart(this, x, y, width, height, values, opts);
+ };
+
+ MVBarchart.prototype.rightAxis = function (x, y, length, max, steps, ewidth, paper)
+ {
+// Roo.log('Right Axis');
+// Roo.log([x, y, length, max, steps, ewidth]);
+
+ var path = [],
+ color = "#bababa",
+ text = paper.set(),
+ d = Math.ceil(max / steps);
+
+ var label = 0,
+ dx = length / steps;
+
+ var Y = y;
+
+ for(var i = 0; i <= steps; i++) {
+
+ if(i != 0) {
+ paper.text(x + (ewidth / 2), Y + 15, label).attr({
+ "font-size": "20",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ "text-anchor": "end",
+ fill : color
+ });
+ path = path.concat(["M", x, Y, "l", (ewidth / 2), 0]);
+
+ }
+
+ label += d;
+ Y -= dx;
+ }
+
+ var res = paper.path(path).attr({ stroke: color, "stroke-width": 2 });
+
+ res.text = text;
+ res.all = paper.set([res, text]);
+ res.remove = function () {
+ this.text.remove();
+ this.constructor.prototype.remove.call(this);
+ };
+
+ return res;
+ }
+
+ MVBarchart.prototype.bottomAxis = function (x, y, length, steps, labels, barwidth, eheight, paper)
+ {
+// Roo.log('Bottom Axis');
+// Roo.log([x, y, length, steps, labels, barwidth]);
+
+ var path = ["M", x, y, "l", length, 0],
+ color = "#bababa",
+ text = paper.set(),
+ offset = Math.round(barwidth / 2);
+
+ labels.forEach((v,k) => {
+ paper.text(x + (k * barwidth) + offset, y + (eheight / 2), v).attr({
+ "font-size": "20",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ fill : color
+ });
+ });
+
+ var res = paper.path(path).attr({ stroke: color, "stroke-width": 2 });
+
+ res.text = text;
+ res.all = paper.set([res, text]);
+ res.remove = function () {
+ this.text.remove();
+ this.constructor.prototype.remove.call(this);
+ };
+
+ return res;
+ }
+
+})();
\ No newline at end of file
--- /dev/null
+/*
+ * g.Raphael 0.5 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael = typeof(Raphael) != 'undefined' ? Raphael : (imports ? imports.seed.Raphael.Raphael : {});
+Roo = typeof(Roo) != 'undefined' ? Roo: (imports ? imports.seed.Roo.Roo: {});
+//chartinst = typeof(chartinst) != 'undefined' ? chartinst: (imports ? imports.chartinst.chartinst : {});
+
+
+
+(function () {
+
+ /**
+ * @param {Raphael} paper to draw on
+ * @param {int} cx - centre X
+ * @param {int} cy - centre Y
+ * @param {int} r - radius
+ * @param {Array} values
+ * @param {Object} opts options
+ * cut : after this meany items - do not show a pie element?
+ *
+ *
+ *
+ */
+
+ function Barsplitchart(paper, width, height, values, opts) {
+
+ opts = opts || {};
+
+ var chartinst = this,
+ chart = paper.set();
+
+ opts.top = opts.top || 50;
+ opts.bottom = opts.bottom || 50;
+ opts.left = opts.left || 20;
+ opts.right = opts.right || 20;
+ opts.barwidth = opts.barwidth || 100;
+
+ paper.rect(0, 0, width, height).attr({ stroke: "none", fill: (opts.background || "#F0F4F7") });
+
+ var cw = width - opts.left - opts.right,
+ ch = height - opts.top - opts.bottom;
+
+ paper.rect(opts.left, opts.top, opts.barwidth, ch).attr({ stroke: "#CCCCCC", "stroke-dasharray": "--" });
+ paper.rect(opts.left + cw / 2, opts.top, opts.barwidth, ch).attr({ stroke: "#CCCCCC", "stroke-dasharray": "--" });
+
+ values.forEach((v,k) => {
+ v = Math.min(100, v);
+ var bh = v * ch / 100,
+ bx = (k == 0) ? opts.left : opts.left + cw / 2,
+ by = opts.top + ch - bh;
+
+ paper.rect(bx, by, opts.barwidth, bh).attr({ stroke: "none", fill: "#0A2BC4" });
+
+ paper.rect(bx + opts.barwidth / 4 , by - 42, opts.barwidth / 2, 30, 5).attr({ stroke: "none", fill: "#0C014D" });
+ paper.path(["M", bx + opts.barwidth / 4 + 10, by - 12, "l", 5, 10, "l", 5, -10]).attr({ stroke: "none", fill: "#0C014D" });
+ paper.text(bx + opts.barwidth / 4 + 25, by - 28, Roo.util.Format.number(v, 0) + '%').attr({
+ "font-size": "16",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ fill : "#fff"
+ });
+
+ paper.text(bx + opts.barwidth + 25, opts.top + 50, opts.legend[k]).attr({
+ "font-size": "22",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ "text-anchor": "start",
+ fill : "#0C024B"
+ });
+ });
+
+ return chart;
+ };
+
+ //inheritance
+ var F = function() {};
+ F.prototype = Raphael.g;
+ Barsplitchart.prototype = new F;
+
+ //public
+ Raphael.fn.barsplitchart = function(width, height, values, opts) {
+ return new Barsplitchart(this, width, height, values, opts);
+ }
+
+})();
--- /dev/null
+/*
+ * g.Raphael 0.5 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael = typeof(Raphael) != 'undefined' ? Raphael : (imports ? imports.seed.Raphael.Raphael : {});
+Roo = typeof(Roo) != 'undefined' ? Roo: (imports ? imports.seed.Roo.Roo: {});
+//chartinst = typeof(chartinst) != 'undefined' ? chartinst: (imports ? imports.chartinst.chartinst : {});
+
+
+
+(function () {
+
+ /**
+ * @param {Raphael} paper to draw on
+ * @param {int} cx - centre X
+ * @param {int} cy - centre Y
+ * @param {int} r - radius
+ * @param {Array} values
+ * @param {Object} opts options
+ * cut : after this meany items - do not show a pie element?
+ *
+ *
+ *
+ */
+
+ function Piecircularchart(paper, width, height, cx, cy, r, values, opts) {
+
+ opts = opts || {};
+
+ var chartinst = this,
+ chart = paper.set(),
+ len = values.length;
+
+ opts.linewidth = opts.linewidth || 200;
+
+ paper.rect(0, 0, width, height).attr({ stroke: "none", fill: (opts.background || "#F0F4F7") });
+
+ for (var i = 0; i < len; i++) {
+ values[i] = {
+ value: values[i],
+ origin: i,
+ valueOf: function () { return this.value; }
+ };
+ }
+
+ values.sort(function (a, b) {
+ return b.value - a.value;
+ });
+
+ var dx = cx,
+ dy = cy,
+ dr = r;
+
+ for (i = 0; i < len; i++) {
+
+ paper.circle(dx, dy, dr).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC", stroke: "#fff", "stroke-width": 2 });
+
+ var nx = dx,
+ ny = dy - dr + 20;
+
+ if(i != 0) {
+ ny = dy - dr - 20;
+ }
+
+ paper.text(nx, ny, Roo.util.Format.number(values[i], 0)).attr({
+ "font-size": "18",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "font-weight": "bold",
+ fill : "#fff"
+ });
+
+ paper.path(["M", dx, dy - dr, "l", opts.linewidth, 0]).attr({ stroke: "#fff", "stroke-width": 2 });
+ paper.text(dx + opts.linewidth + 10, dy - dr, opts.legend[values[i].origin]).attr({
+ "font-size": "18",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "text-anchor": "start",
+ fill : "#0E1587"
+ });
+
+ dy = dy + dr / 4 *3;
+ dr = Math.round(dr / 4);
+
+ }
+
+ chart.cx = cx;
+ chart.cy = cy;
+ chart.r = r;
+ return chart;
+ };
+
+ //inheritance
+ var F = function() {};
+ F.prototype = Raphael.g;
+ Piecircularchart.prototype = new F;
+
+ //public
+ Raphael.fn.piecircularchart = function(width, height, cx, cy, r, values, opts) {
+ return new Piecircularchart(this, width, height, cx, cy, r, values, opts);
+ }
+
+})();
--- /dev/null
+/*
+ * g.Raphael 0.5 - Charting library, based on Raphaël
+ *
+ * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael = typeof(Raphael) != 'undefined' ? Raphael : (imports ? imports.seed.Raphael.Raphael : {});
+Roo = typeof(Roo) != 'undefined' ? Roo: (imports ? imports.seed.Roo.Roo: {});
+//chartinst = typeof(chartinst) != 'undefined' ? chartinst: (imports ? imports.chartinst.chartinst : {});
+
+
+
+(function () {
+
+ /**
+ * @param {Raphael} paper to draw on
+ * @param {int} cx - centre X
+ * @param {int} cy - centre Y
+ * @param {int} r - radius
+ * @param {Array} values
+ * @param {Object} opts options
+ * cut : after this meany items - do not show a pie element?
+ *
+ *
+ *
+ */
+
+ function Piesectorchart(paper, width, height, cx, cy, r, values, opts) {
+
+ opts = opts || {};
+
+ var chartinst = this,
+ chart = paper.set(),
+ len = values.length,
+ angle = opts.start_angle || 90,
+ total = 0,
+ others = 0,
+ cut = opts.cut || 9,
+ defcut = true;
+
+ opts.barwidth = opts.barwidth || 80;
+
+ paper.rect(0, 0, width, height).attr({ stroke: "none", fill: (opts.background || "#F0F4F7") });
+
+ paper.customAttributes.sector = function (cx, cy, startAngle, endAngle, color, R) {
+
+ var rad = Math.PI / 180,
+ x1 = cx + r * Math.cos(-startAngle * rad),
+ x2 = cx + r * Math.cos(-endAngle * rad),
+ y1 = cy + r * Math.sin(-startAngle * rad),
+ y2 = cy + r * Math.sin(-endAngle * rad),
+ path;
+
+ path = [["M", x1, y1], ["A", R, R, 0, +(Math.abs(endAngle - startAngle) > 180), 1, x2, y2]];
+
+ return {path: path, stroke: color};
+ };
+
+ if (len == 1) {
+ total = values[0];
+ paper.circle(cx, cy, r + opts.barwidth / 2).attr({ fill: opts.colors && opts.colors[0] || chartinst.colors[0] || "#3E66BC" });
+ paper.circle(cx, cy, r - opts.barwidth / 2).attr({ fill: opts.background || "#F0F4F7" });
+
+ } else {
+
+ for (var i = 0; i < len; i++) {
+ total += values[i] * 1;
+ values[i] = {
+ value: values[i],
+ valueOf: function () { return this.value; }
+ };
+ }
+
+ if (!opts.no_sort) {
+ values.sort(function (a, b) {
+ return b.value - a.value;
+ });
+ }
+
+ for (i = 0; i < len; i++) {
+ if (defcut && values[i] * 360 / total <= 1.5) {
+ cut = i;
+ defcut = false;
+ }
+
+ if (i > cut) {
+ defcut = false;
+ values[cut].value += values[i];
+ values[cut].others = true;
+ }
+ }
+
+ len = Math.min(cut + 1, values.length);
+
+ for (i = 0; i < len; i++) {
+
+ var p = paper.path().attr({
+ "stroke": "#fff",
+ "stroke-width": opts.barwidth
+ }).attr({sector: [cx, cy, angle, angle -= 360 * values[i] / total, opts.colors && opts.colors[i] || chartinst.colors[i], r]});
+
+ }
+
+ }
+
+ var ix = cx + r + opts.barwidth / 2 + 30,
+ iy = cy - r - 30;
+
+ for (var i = 0; i < len; i++) {
+
+ paper.circle(ix, iy, 6).attr({ fill: opts.colors && opts.colors[i] || chartinst.colors[i] || "#3E66BC" });
+
+ var text = (values[i].others) ? opts.others : opts.legend[i] || values[i];
+
+ if(text.indexOf('#%#') !== -1) {
+ text = text.replace('#%#', Math.round(values[i] / total * 100) + '%');
+ }
+
+ paper.text(ix + 20, iy, text).attr({
+ "font-size": "18",
+ "font-family": "'Fontin Sans', Fontin-Sans, sans-serif",
+ "text-anchor": "start",
+ fill : "#0C014F"
+ });
+
+ iy += 30;
+
+ }
+
+ chart.cx = cx;
+ chart.cy = cy;
+ chart.r = r;
+ return chart;
+ };
+
+ //inheritance
+ var F = function() {};
+ F.prototype = Raphael.g;
+ Piesectorchart.prototype = new F;
+
+ //public
+ Raphael.fn.piesectorchart = function(width, height, cx, cy, r, values, opts) {
+ return new Piesectorchart(this, width, height, cx, cy, r, values, opts);
+ }
+
+})();
FakeDom(o.pattern, {patternTransform: o.matrix.invert() + " translate(" + bbox.x + "," + bbox.y + ")"});
}
-
-
function tuneText (el, params) {
if (el.type != "text" || !(params[has]("text") || params[has]("font") || params[has]("font-size") || params[has]("x") || params[has]("y"))) {
return;
dif && R.is(dif, "finite") && FakeDom(tspans[0], {dy: dif});
}
+function addDashes(o, value, params) {
+
+ var dasharray = {
+ // In Firefox 37.0.1 the value of "stroke-dasharray" attribute `0` make the stroke/border invisible.
+ // The actual issue is setting `none` as the value of `stroke-dasharray` attribute
+ // redraphael internally changes the "none" value to "0", thus the stroke/border becomes invisible
+ // To fix this issue now instead of setting the value as `0` for `stroke-dasharray` attribute
+ // now using `none` string as none is a w3c standard value for stroke-dasharray
+ "": ["none"],
+ "none": ["none"],
+ "-": [3, 1],
+ ".": [1, 1],
+ "-.": [3, 1, 1, 1],
+ "-..": [3, 1, 1, 1, 1, 1],
+ ". ": [1, 3],
+ "- ": [4, 3],
+ "--": [8, 3],
+ "- .": [4, 3, 1, 3],
+ "--.": [8, 3, 1, 3],
+ "--..": [8, 3, 1, 3, 1, 3]
+ };
+
+ var $ = R._createNode = function(el, attr) {
+ if (attr) {
+ if (typeof el == "string") {
+ el = $(el);
+ }
+ for (var key in attr)
+ if (attr.hasOwnProperty(key)) {
+ if (key.substring(0, 6) == "xlink:") {
+ // setAttributeNS setAttribute won't works..
+// el.setAttributeNS("http://www.w3.org/1999/xlink", key.substring(6), String(attr[key]));
+ el.attributes[key.substring(6)] = String(attr[key]);
+ } else {
+// el.setAttribute(key, String(attr[key]));
+ el.attributes[key] = String(attr[key]);
+ }
+ }
+ } else {
+ el = R._g.doc.createElementNS("http://www.w3.org/2000/svg", el);
+ }
+ return el;
+ }
+
+ var predefValue = dasharray[String(value).toLowerCase()],
+ calculatedValues,
+ width,
+ butt,
+ i,
+ l,
+ widthFactor;
+
+ value = predefValue || ((value !== undefined) && [].concat(value));
+ if (value) {
+
+ width = o.attrs["stroke-width"] || 1;
+ butt = {
+ round: width,
+ square: width,
+ butt: 0
+ }[params["stroke-linecap"] || o.attrs["stroke-linecap"]] || 0;
+ l = i = value.length;
+ widthFactor = predefValue ? width : 1;
+
+ if (value[0] == 'none') {
+ calculatedValues = value;
+ } else {
+ calculatedValues = [];
+ while (i--) {
+ calculatedValues[i] = (value[i] * widthFactor + ((i % 2) ? 1 : -1) * butt);
+ calculatedValues[i] <= 0 && (calculatedValues[i] = 0.01 + (width <= 1 ? butt : 0));
+ if (isNaN(calculatedValues[i])) {
+ calculatedValues[i] = 0;
+ }
+ }
+ }
+
+ if (R.is(value, 'array')) {
+ $(o.node, {
+ "stroke-dasharray": calculatedValues.join(",")
+ });
+ }
+ }
+}
+
setFillAndStroke = function (o, params) {
//Roo.log(JSON.stringify(params));
importz = imports['seed/importz.js'].importz;
Raphael = importz('Raphael');
Roo = importz('Roo');
-
- /**
- * Escapes string for XML interpolation
- * @param value string or number value to escape
- * @returns string escaped
- */
- function escapeXML(s) {
- if ( typeof s === 'number' ) return s.toString();
-
- var replace = {
- '&': 'amp',
- '<': 'lt',
- '>': 'gt',
- '"': 'quot',
- '\'': 'apos'
- };
-
- for ( var entity in replace ) {
- s = s.replace(new RegExp(entity, 'g'), '&' + replace[entity] + ';');
- }
-
- return s;
- }
-
- /**
- * Generic map function
- * @param iterable the array or object to be mapped
- * @param callback the callback function(element, key)
- * @returns array
- */
- function map(iterable, callback) {
- var mapped = new Array;
-
- for ( var i in iterable ) {
- if ( iterable.hasOwnProperty(i) ) {
- var value = callback.call(this, iterable[i], i);
-
- if ( value !== null ) mapped.push(value);
- }
- }
-
- return mapped;
- }
-
- /**
- * Generic reduce function
- * @param iterable array or object to be reduced
- * @param callback the callback function(initial, element, i)
- * @param initial the initial value
- * @return the reduced value
- */
- function reduce(iterable, callback, initial) {
- for ( var i in iterable ) {
- if ( iterable.hasOwnProperty(i) ) {
- initial = callback.call(this, initial, iterable[i], i);
- }
- }
-
- return initial;
- }
-
- /**
- * Utility method for creating a tag
- * @param name the tag name, e.g., 'text'
- * @param attrs the attribute string, e.g., name1="val1" name2="val2"
- * or attribute map, e.g., { name1 : 'val1', name2 : 'val2' }
- * @param content the content string inside the tag
- * @returns string of the tag
- */
- function tag(name, attrs, matrix, content) {
- if ( typeof content === 'undefined' || content === null ) {
- content = '';
- }
-
- if ( typeof attrs === 'object' ) {
- attrs = map(attrs, function(element, name) {
- if ( name === 'transform') return;
-
- return name + '="' + escapeXML(element) + '"';
- }).join(' ');
- }
-
- return '<' + name + ( matrix ? ' transform="matrix(' + matrix.toString().replace(/^matrix\(|\)$/g, '') + ')" ' : ' ' ) + attrs + '>' +
- content +
+
+/**
+ * Escapes string for XML interpolation
+ * @param value string or number value to escape
+ * @returns string escaped
+ */
+function escapeXML(s) {
+ if (typeof s === 'number')
+ return s.toString();
+
+ var replace = {
+ '&': 'amp',
+ '<': 'lt',
+ '>': 'gt',
+ '"': 'quot',
+ '\'': 'apos'
+ };
+
+ for (var entity in replace) {
+ s = s.replace(new RegExp(entity, 'g'), '&' + replace[entity] + ';');
+ }
+
+ return s;
+}
+
+/**
+ * Generic map function
+ * @param iterable the array or object to be mapped
+ * @param callback the callback function(element, key)
+ * @returns array
+ */
+function map(iterable, callback) {
+ var mapped = new Array;
+
+ for (var i in iterable) {
+ if (iterable.hasOwnProperty(i)) {
+ var value = callback.call(this, iterable[i], i);
+
+ if (value !== null)
+ mapped.push(value);
+ }
+ }
+
+ return mapped;
+}
+
+/**
+ * Generic reduce function
+ * @param iterable array or object to be reduced
+ * @param callback the callback function(initial, element, i)
+ * @param initial the initial value
+ * @return the reduced value
+ */
+function reduce(iterable, callback, initial) {
+ for (var i in iterable) {
+ if (iterable.hasOwnProperty(i)) {
+ initial = callback.call(this, initial, iterable[i], i);
+ }
+ }
+
+ return initial;
+}
+
+/**
+ * Utility method for creating a tag
+ * @param name the tag name, e.g., 'text'
+ * @param attrs the attribute string, e.g., name1="val1" name2="val2"
+ * or attribute map, e.g., { name1 : 'val1', name2 : 'val2' }
+ * @param content the content string inside the tag
+ * @returns string of the tag
+ */
+function tag(name, attrs, matrix, content) {
+ if (typeof content === 'undefined' || content === null) {
+ content = '';
+ }
+
+ if (typeof attrs === 'object') {
+ attrs = map(attrs, function (element, name) {
+ if (name === 'transform')
+ return;
+
+ return name + '="' + escapeXML(element) + '"';
+ }).join(' ');
+ }
+
+ return '<' + name + (matrix ? ' transform="matrix(' + matrix.toString().replace(/^matrix\(|\)$/g, '') + ')" ' : ' ') + attrs + '>' +
+ content +
'</' + name + '>' + "\n";
- }
-
- /**
- * @return object the style object
- */
- function extractStyle(node) {
- //Roo.log(JSON.stringify(style));
- return {
- font: {
- family: typeof node.attrs['font-family'] === 'undefined' ? null :node.attrs['font-family'],
- size: typeof node.attrs['font-size'] === 'undefined' ? null : (node.attrs['font-size']),
- anchor : typeof node.attrs['text-anchor'] === 'undefined' ? null : node.attrs['text-anchor'],
- }
- };
- }
-
- /**
- * @param style object from style()
- * @return string
- */
- function styleToString(style) {
- // TODO figure out what is 'normal'
- //Roo.log(JSON.stringify(style));
- var r = [
- 'font-family:' + style.font.family,
- 'font-weight:normal',
- 'font-style:normal',
- 'font-stretch:normal',
- 'font-variant:normal'
- ];
- if (style.font.size !== null ) {
- r.push('font-size: ' + style.font.size + 'px')
+}
+
+/**
+ * @return object the style object
+ */
+function extractStyle(node) {
+ //Roo.log(JSON.stringify(style));
+ return {
+ font: {
+ family: typeof node.attrs['font-family'] === 'undefined' ? null : node.attrs['font-family'],
+ size: typeof node.attrs['font-size'] === 'undefined' ? null : (node.attrs['font-size']),
+ anchor: typeof node.attrs['text-anchor'] === 'undefined' ? null : node.attrs['text-anchor'],
}
-
- return r.join(';')
-
- }
-
- /**
- * Computes tspan dy using font size. This formula was empircally determined
- * using a best-fit line. Works well in both VML and SVG browsers.
- * @param fontSize number
- * @return number
- */
- function computeTSpanDy(fontSize, line, lines) {
- if ( fontSize === null ) fontSize = 10;
-
- //return fontSize * 4.5 / 13
- return fontSize * 4.5 / 13 * ( line - .2 - lines / 2 ) * 3.5;
- }
-
- var serializer = {
- 'text': function(node) {
- style = extractStyle(node);
- //Roo.log(JSON.stringify(node, null,4));
- var tags = new Array;
-
- map(node.attrs['text'].split('\n'), function(text, iterable, line) {
- line = line || 0;
- tags.push(tag(
- 'text',
- reduce(
- node.attrs,
- function(initial, value, name) {
- if ( name !== 'text' && name !== 'w' && name !== 'h' ) {
- if ( name === 'font-size') value = value + 'px';
-
- initial[name] = escapeXML(value.toString());
- }
-
- return initial;
- },
- {
- style: 'text-anchor: ' + (style.font.anchor ? (style.font.anchor +';' ): 'middle;') +
- styleToString(style) + ';'
- }
- ),
- node.matrix,
- tag('tspan',
- {
- dy: computeTSpanDy(style.font.size, line + 1, node.attrs['text'].split('\n').length)
- },
- null,
- escapeXML(text)
- )
- ));
- });
-
- return tags;
- },
- 'path' : function(node) {
- var initial = ( node.matrix.a === 1 && node.matrix.d === 1 ) ? {} : { 'transform' : node.matrix.toString() };
-
-
-
- return tag(
- 'path',
- reduce(
- node.attrs,
- function(initial, value, name) {
- if ( name === 'path' ) {
- name = 'd';
- }
+ };
+}
+
+/**
+ * @param style object from style()
+ * @return string
+ */
+function styleToString(style) {
+ // TODO figure out what is 'normal'
+ //Roo.log(JSON.stringify(style));
+ var r = [
+ 'font-family:' + style.font.family,
+ 'font-weight:normal',
+ 'font-style:normal',
+ 'font-stretch:normal',
+ 'font-variant:normal'
+ ];
+ if (style.font.size !== null) {
+ r.push('font-size: ' + style.font.size + 'px')
+ }
+
+ return r.join(';')
+
+}
- initial[name] = (typeof(value) == 'undefined') ? '' : value.toString();
+/**
+ * Computes tspan dy using font size. This formula was empircally determined
+ * using a best-fit line. Works well in both VML and SVG browsers.
+ * @param fontSize number
+ * @return number
+ */
+function computeTSpanDy(fontSize, line, lines) {
+ if (fontSize === null)
+ fontSize = 10;
- return initial;
- },
- {
- style: 'fill:' + Raphael.color(node.attrs.fill).hex + ';'
+ //return fontSize * 4.5 / 13
+ return fontSize * 4.5 / 13 * (line - .2 - lines / 2) * 4.5;
+}
+
+var serializer = {
+ 'text': function (node) {
+ var style = extractStyle(node);
+
+ var tags = new Array;
+
+ var content = [];
+
+ var textArray = node.attrs['text'].split('\n');
+
+ textArray.forEach((v,k) => {
+ content.push(tag('tspan',
+ {
+ x: node.attrs.x,
+ dy: computeTSpanDy(style.font.size, k + 1, textArray.length)
+ },
+ null,
+ escapeXML(v)
+ ))
+
+ });
+
+ tags.push(tag(
+ 'text',
+ reduce(
+ node.attrs,
+ function (initial, value, name) {
+ if (name !== 'text' && name !== 'w' && name !== 'h') {
+ if (name === 'font-size')
+ value = value + 'px';
+
+ initial[name] = escapeXML(value.toString());
}
- ),
- node.matrix
- );
- }
- // Other serializers should go here
- };
+
+ return initial;
+ },
+ {
+ style: 'text-anchor: ' + (style.font.anchor ? (style.font.anchor + ';') : 'middle;') +
+ styleToString(style) + ';'
+ }
+ ),
+ node.matrix,
+ content.join("")
+ ));
+
+ return tags;
+ },
+ 'path': function (node) {
+ var initial = (node.matrix.a === 1 && node.matrix.d === 1) ? {} : {'transform': node.matrix.toString()};
+
+
+
+ return tag(
+ 'path',
+ reduce(
+ node.attrs,
+ function (initial, value, name) {
+ if (name === 'path') {
+ name = 'd';
+ }
+
+ initial[name] = (typeof (value) == 'undefined') ? '' : value.toString();
+
+ return initial;
+ },
+ {
+ style: 'fill:' + Raphael.color(node.attrs.fill).hex + ';'
+ }
+ ),
+ node.matrix
+ );
+ }
+ // Other serializers should go here
+};
function toSVG() {
- var
- paper = this,
-
- svg = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + paper.width + '" version="1.1" height="' + paper.height + '">'
- ;
-
-
- for ( var node = paper.bottom; node != null; node = node.next ) {
+ var paper = this,
+ svg = '<svg style="overflow: hidden; position: relative;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="' + paper.width + '" version="1.1" height="' + paper.height + '">';
+
+
+ for (var node = paper.bottom; node != null; node = node.next) {
//if ( node.node.style.display === 'none' ) continue;
var attrs = '';
-
+
// Use serializer
- if ( typeof serializer[node.type] === 'function' ) {
+ if (typeof serializer[node.type] === 'function') {
svg += serializer[node.type](node);
continue;
}
- switch ( node.type ) {
+ switch (node.type) {
case 'image':
attrs += ' preserveAspectRatio="none"';
break;
}
//Roo.log(JSON.stringify(node, null,4));
-
- for ( i in node.attrs ) {
+
+ for (i in node.attrs) {
var name = i;
-
+
var val = node.attrs[i].toString();
- switch ( i ) {
+ switch (i) {
case 'src':
name = 'xlink:href';
case 'transform':
name = '';
break;
-
+
case 'stroke':
case 'fill':
//s(JSON.stringify(node, null,4));
- val = typeof(node.node.attributes[i]) == 'undefined' ? val : node.node.attributes[i];
-
+ val = typeof (node.node.attributes[i]) == 'undefined' ? val : node.node.attributes[i];
+
val = Raphael.color.getRGB(val).hex;
//Roo.log("fill: " + val);
break;
}
- if ( name ) {
+ if (name) {
attrs += ' ' + name + '="' + escapeXML(val) + '"';
}
}
- svg += '<' + node.type + ' transform="matrix(' + node.matrix.toString().replace(/^matrix\(|\)$/g, '') + ')"' + attrs + '></' + node.type + '>' + "\n";
+ svg += '<' + node.type + ' transform="matrix(' + node.matrix.toString().replace(/^matrix\(|\)$/g, '') + ')"' + attrs + '></' + node.type + '>' + "\n";
}
svg += '</svg>';
-
+
return svg;
};
\ No newline at end of file