jui.redefine("util.svg", [ "util.base", "util.math", "util.color", "util.svg.element", "util.svg.element.transform", "util.svg.element.path", "util.svg.element.path.symbol", "util.svg.element.path.rect", "util.svg.element.poly" ], function(_, math, color, Element, TransElement, PathElement, PathSymbolElement, PathRectElement, PolyElement) { /** * @class util.svg * SVG Utility * * @param {Element} rootElem * @param {Object} rootAttr * @extends util.svg.base3d * @requires util.base * @requires util.math * @requires util.color * @requires util.svg.element * @requires util.svg.element.transform * @requires util.svg.element.transform * @requires util.svg.element.path * @requires util.svg.element.path.symbol * @requires util.svg.element.path.rect * @requires util.svg.element.poly * @constructor * @alias SVG */ var SVG = function(rootElem, rootAttr) { var self = this, root = null, main = null, sub = null, parent = {}, depth = 0; var isFirst = false; // 첫번째 렌더링 체크 function init() { self.root = root = new Element(); main = new TransElement(); sub = new TransElement(); root.create("svg", rootAttr); main.create("g"); sub.create("g"); main.translate(0.5, 0.5); sub.translate(0.5, 0.5); rootElem.appendChild(root.element); root.append(main); root.append(sub); } function appendAll(target) { var childs = target.children; // 엘리먼트 렌더링 순서 정하기 if(isOrderingChild(childs)) { childs.sort(function (a, b) { return a.order - b.order; }); } for(var i = 0, len = childs.length; i < len; i++) { var child = childs[i]; if(child) { if(child.children.length > 0) { appendAll(child); } // PathElement & PathSymbolElement & PathRectElement & PolyElement auto join if(child instanceof PathElement || child instanceof PolyElement) { child.join(); } if(child.parent == target) { target.element.appendChild(child.element); } } } } function removeEventAll(target) { var childs = target.children; for(var i = 0, len = childs.length; i < len; i++) { var child = childs[i]; if(child) { child.off(); if(child.children.length > 0) { removeEventAll(child); } } } } function isOrderingChild(childs) { // order가 0 이상인 엘리먼트가 하나라도 있을 경우 for(var i = 0, len = childs.length; i < len; i++) { if(childs[i].order > 0) { return true; } } return false; } this.create = function(obj, type, attr, callback) { obj.create(type, attr); if(depth == 0) { main.append(obj); } else { parent[depth].append(obj); } if(_.typeCheck("function", callback)) { depth++; parent[depth] = obj; callback.call(obj); depth--; } return obj; } this.createChild = function(obj, type, attr, callback) { if(obj.parent == main) { throw new Error("JUI_CRITICAL_ERR: Parents are required elements of the '" + type + "'"); } return this.create(obj, type, attr, callback); } /** * @method size * * if arguments.length is 2, set attribute width, height to root element * if arguments.length is zero, return svg size * * @return {Object} * @return {Integer} width * @return {Integer} height */ this.size = function() { if(arguments.length == 2) { var w = arguments[0], h = arguments[1]; root.attr({ width: w, height: h }); } else { return root.size(); } } /** * @method clear * @param isAll */ this.clear = function(isAll) { main.each(function() { if(this.element.parentNode) { main.element.removeChild(this.element); } }); removeEventAll(main); if(isAll === true) { sub.each(function() { if(this.element.parentNode) { sub.element.removeChild(this.element); } }); removeEventAll(sub); } } /** * @method reset * @param isAll */ this.reset = function(isAll) { this.clear(isAll); main.children = []; if(isAll === true) { sub.children = []; } } /** * @method render * @param isAll */ this.render = function(isAll) { this.clear(); if(isFirst === false || isAll === true) { appendAll(root); } else { appendAll(main); } isFirst = true; } /** * @method * implements svg image file download used by canvas * @param name */ this.download = function(name) { if(_.typeCheck("string", name)) { name = name.split(".")[0]; } var a = document.createElement("a"); a.download = (name) ? name + ".svg" : "svg.svg"; a.href = this.toDataURI()//;_.svgToBase64(rootElem.innerHTML); document.body.appendChild(a); a.click(); a.parentNode.removeChild(a); } this.downloadImage = function(name, type) { type = type || "image/png"; var img = new Image(); var size = this.size(); var uri = this.toDataURI() .replace('width="100%"', 'width="' + size.width + '"') .replace('height="100%"', 'height="' + size.height + '"'); img.onload = function(){ var canvas = document.createElement("canvas"); canvas.width = img.width; canvas.height = img.height; var context = canvas.getContext('2d'); context.drawImage(img, 0, 0); var png = canvas.toDataURL(type); if(_.typeCheck("string", name)) { name = name.split(".")[0]; } var a = document.createElement('a'); a.download = (name) ? name + ".png" : "svg.png"; a.href = png; document.body.appendChild(a); a.click(); a.parentNode.removeChild(a); } img.src = uri; } /** * @method exportCanvas * * convert svg image to canvas * * @param {Canvas} canvas */ this.exportCanvas = function(canvas) { var img = new Image(), size = this.size(); var uri = this.toDataURI() .replace('width="100%"', 'width="' + size.width + '"') .replace('height="100%"', 'height="' + size.height + '"'); img.onload = function() { canvas.width = img.width; canvas.height = img.height; var context = canvas.getContext('2d'); context.drawImage(img, 0, 0); } img.src = uri; } /** * @method toXML * * convert xml string * * @return {String} xml */ this.toXML = function() { var text = rootElem.innerHTML; text = text.replace('xmlns="http://www.w3.org/2000/svg"', ''); return [ '<?xml version="1.0" encoding="utf-8"?>', text.replace("<svg ", '<svg xmlns="http://www.w3.org/2000/svg" ') ].join("\n"); } /** * @method toDataURI * * convert svg to datauri format * * @return {String} */ this.toDataURI = function() { var xml = this.toXML(); if (_.browser.mozilla || _.browser.msie) { xml = encodeURIComponent(xml); } if (_.browser.msie) { return "data:image/svg+xml," + xml; } else { return "data:image/svg+xml;utf8," + xml; } } /** * @method autoRender * * @param {util.svg.element} elem * @param {Boolean} isAuto */ this.autoRender = function(elem, isAuto) { if(depth > 0) return; if(!isAuto) { sub.append(elem); } else { main.append(elem); } } /** * @method getTextSize * * caculate real pixel size of text element * * @param {String} text target text * @return {Object} * @return {Integer} return.width text element's width (px) * @return {Integer} return.height text element's height(px) */ this.getTextSize = function(text, opt) { if (text == "") { return { width : 0, height : 0 }; } opt = opt || {}; var bodyElement = document.body || root.element; var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttributeNS(null, "width", 500); svg.setAttributeNS(null, "height", 100); svg.setAttributeNS(null, "x", -20000); svg.setAttributeNS(null, "y", -20000); var el = document.createElementNS("http://www.w3.org/2000/svg", "text"); el.setAttributeNS(null, "x", -200); el.setAttributeNS(null, "y", -200); el.appendChild(document.createTextNode(text)); if (opt.fontSize) { el.setAttributeNS(null, "font-size", opt.fontSize); } if (opt.fontFamily) { el.setAttributeNS(null, "font-family", opt.fontFamily); } if (opt.bold) { el.setAttributeNS(null, "font-weight", opt.bold); } if (opt.style) { el.setAttributeNS(null, "font-style", opt.style); } svg.appendChild(el); bodyElement.appendChild(svg); var rect = el.getBoundingClientRect(); bodyElement.removeChild(svg); return { width : rect.width, height : rect.height }; } init(); } /** * @method create * * create nested elements by json * * @example * SVG.create({ * tag : "pattern", * attr : { x : 0, y : 0, width : 20, height : 20 }, * children : [ * { tag : 'rect', attr : {width : 20, height : 20, fill : 'black', stroke : 'blue', 'stroke-width' : 2 } , * { tag : 'rect', attr : {width : 20, height : 20, fill : 'black', stroke : 'blue', 'stroke-width' : 2 } , * { tag : 'rect', attr : {width : 20, height : 20, fill : 'black', stroke : 'blue', 'stroke-width' : 2 } , * { tag : 'rect', attr : {width : 20, height : 20, fill : 'black', stroke : 'blue', 'stroke-width' : 2 } * ] * }); * * is equals to * * @example * <pattern x="0" y="0" width="20" height="20"> * <rect width="20" height="20" fill="black" stroke="blue" stroke-width="2" /> * <rect width="20" height="20" fill="black" stroke="blue" stroke-width="2" /> * <rect width="20" height="20" fill="black" stroke="blue" stroke-width="2" /> * <rect width="20" height="20" fill="black" stroke="blue" stroke-width="2" /> * </pattern> * * @param {Object} obj json literal * @param {String} obj.type svg element name * @param {Object} obj.attr svg element's attributes * @param {Array} [obj.children=null] svg element's children * @static * @return {util.svg.element} * */ SVG.createObject = function(obj) { var el = new Element(); el.create(obj.type, obj.attr); if (obj.children instanceof Array) { for(var i = 0, len = obj.children.length; i < len; i++) { el.append(SVG.createObject(obj.children[i])); } } return el; } return SVG; }, "util.svg.base3d", true);