[483] | 1 | define(["dojo/_base/kernel","dojo/_base/lang","./_base", "dojo/_base/html","dojo/_base/array", "dojo/_base/window", "dojo/_base/json", |
---|
| 2 | "dojo/_base/Deferred", "dojo/_base/sniff", "require","dojo/_base/config"], |
---|
| 3 | function(kernel, lang, g, html, arr, win, jsonLib, Deferred, has, require, config){ |
---|
| 4 | var gu = g.utils = {}; |
---|
| 5 | |
---|
| 6 | lang.mixin(gu, { |
---|
| 7 | forEach: function( |
---|
| 8 | /*dojox/gfx/shape.Surface|dojox/gfx/shape.Shape*/ object, |
---|
| 9 | /*Function|String|Array*/ f, /*Object?*/ o |
---|
| 10 | ){ |
---|
| 11 | // summary: |
---|
| 12 | // Takes a shape or a surface and applies a function "f" to in the context of "o" |
---|
| 13 | // (or global, if missing). If "shape" was a surface or a group, it applies the same |
---|
| 14 | // function to all children recursively effectively visiting all shapes of the underlying scene graph. |
---|
| 15 | // object: |
---|
| 16 | // The gfx container to iterate. |
---|
| 17 | // f: |
---|
| 18 | // The function to apply. |
---|
| 19 | // o: |
---|
| 20 | // The scope. |
---|
| 21 | o = o || kernel.global; |
---|
| 22 | f.call(o, object); |
---|
| 23 | if(object instanceof g.Surface || object instanceof g.Group){ |
---|
| 24 | arr.forEach(object.children, function(shape){ |
---|
| 25 | gu.forEach(shape, f, o); |
---|
| 26 | }); |
---|
| 27 | } |
---|
| 28 | }, |
---|
| 29 | |
---|
| 30 | serialize: function(object){ |
---|
| 31 | // summary: |
---|
| 32 | // Takes a shape or a surface and returns an object, which describes underlying shapes. |
---|
| 33 | // object: dojox/gfx/shape.Surface|dojox/gfx/shape.Shape |
---|
| 34 | // The container to serialize. |
---|
| 35 | |
---|
| 36 | var t = {}, v, isSurface = object instanceof g.Surface; |
---|
| 37 | if(isSurface || object instanceof g.Group){ |
---|
| 38 | t.children = arr.map(object.children, gu.serialize); |
---|
| 39 | if(isSurface){ |
---|
| 40 | return t.children; // Array |
---|
| 41 | } |
---|
| 42 | }else{ |
---|
| 43 | t.shape = object.getShape(); |
---|
| 44 | } |
---|
| 45 | if(object.getTransform){ |
---|
| 46 | v = object.getTransform(); |
---|
| 47 | if(v){ t.transform = v; } |
---|
| 48 | } |
---|
| 49 | if(object.getStroke){ |
---|
| 50 | v = object.getStroke(); |
---|
| 51 | if(v){ t.stroke = v; } |
---|
| 52 | } |
---|
| 53 | if(object.getFill){ |
---|
| 54 | v = object.getFill(); |
---|
| 55 | if(v){ t.fill = v; } |
---|
| 56 | } |
---|
| 57 | if(object.getFont){ |
---|
| 58 | v = object.getFont(); |
---|
| 59 | if(v){ t.font = v; } |
---|
| 60 | } |
---|
| 61 | return t; // Object |
---|
| 62 | }, |
---|
| 63 | |
---|
| 64 | toJson: function(object, prettyPrint){ |
---|
| 65 | // summary: |
---|
| 66 | // Works just like serialize() but returns a JSON string. If prettyPrint is true, the string is pretty-printed to make it more human-readable. |
---|
| 67 | // object: dojox/gfx/shape.Surface|dojox/gfx/shape.Shape |
---|
| 68 | // The container to serialize. |
---|
| 69 | // prettyPrint: Boolean? |
---|
| 70 | // Indicates whether the output string should be formatted. |
---|
| 71 | // returns: String |
---|
| 72 | |
---|
| 73 | return jsonLib.toJson(gu.serialize(object), prettyPrint); // String |
---|
| 74 | }, |
---|
| 75 | |
---|
| 76 | deserialize: function(parent, object){ |
---|
| 77 | // summary: |
---|
| 78 | // Takes a surface or a shape and populates it with an object produced by serialize(). |
---|
| 79 | // parent: dojox/gfx/shape.Surface|dojox/gfx/shape.Shape |
---|
| 80 | // The destination container for the deserialized shapes. |
---|
| 81 | // object: dojox/gfx/shape.Shape|Array |
---|
| 82 | // The shapes to deserialize. |
---|
| 83 | |
---|
| 84 | if(object instanceof Array){ |
---|
| 85 | return arr.map(object, lang.hitch(null, gu.deserialize, parent)); // Array |
---|
| 86 | } |
---|
| 87 | var shape = ("shape" in object) ? parent.createShape(object.shape) : parent.createGroup(); |
---|
| 88 | if("transform" in object){ |
---|
| 89 | shape.setTransform(object.transform); |
---|
| 90 | } |
---|
| 91 | if("stroke" in object){ |
---|
| 92 | shape.setStroke(object.stroke); |
---|
| 93 | } |
---|
| 94 | if("fill" in object){ |
---|
| 95 | shape.setFill(object.fill); |
---|
| 96 | } |
---|
| 97 | if("font" in object){ |
---|
| 98 | shape.setFont(object.font); |
---|
| 99 | } |
---|
| 100 | if("children" in object){ |
---|
| 101 | arr.forEach(object.children, lang.hitch(null, gu.deserialize, shape)); |
---|
| 102 | } |
---|
| 103 | return shape; // dojox/gfx/shape.Shape |
---|
| 104 | }, |
---|
| 105 | |
---|
| 106 | fromJson: function(parent, json){ |
---|
| 107 | // summary: |
---|
| 108 | // Works just like deserialize() but takes a JSON representation of the object. |
---|
| 109 | // parent: dojox/gfx/shape.Surface|dojox/gfx/shape.Shape |
---|
| 110 | // The destination container for the deserialized shapes. |
---|
| 111 | // json: String |
---|
| 112 | // The shapes to deserialize. |
---|
| 113 | |
---|
| 114 | return gu.deserialize(parent, jsonLib.fromJson(json)); // Array|dojox/gfx/shape.Shape |
---|
| 115 | }, |
---|
| 116 | |
---|
| 117 | toSvg: function(/*dojox/gfx/shape.Surface*/surface){ |
---|
| 118 | // summary: |
---|
| 119 | // Function to serialize a GFX surface to SVG text. |
---|
| 120 | // description: |
---|
| 121 | // Function to serialize a GFX surface to SVG text. The value of this output |
---|
| 122 | // is that there are numerous serverside parser libraries that can render |
---|
| 123 | // SVG into images in various formats. This provides a way that GFX objects |
---|
| 124 | // can be captured in a known format and sent serverside for serialization |
---|
| 125 | // into an image. |
---|
| 126 | // surface: |
---|
| 127 | // The GFX surface to serialize. |
---|
| 128 | // returns: |
---|
| 129 | // Deferred object that will be called when SVG serialization is complete. |
---|
| 130 | |
---|
| 131 | //Since the init and even surface creation can be async, we need to |
---|
| 132 | //return a deferred that will be called when content has serialized. |
---|
| 133 | var deferred = new Deferred(); |
---|
| 134 | |
---|
| 135 | if(g.renderer === "svg"){ |
---|
| 136 | //If we're already in SVG mode, this is easy and quick. |
---|
| 137 | try{ |
---|
| 138 | var svg = gu._cleanSvg(gu._innerXML(surface.rawNode)); |
---|
| 139 | deferred.callback(svg); |
---|
| 140 | }catch(e){ |
---|
| 141 | deferred.errback(e); |
---|
| 142 | } |
---|
| 143 | }else{ |
---|
| 144 | //Okay, now we have to get creative with hidden iframes and the like to |
---|
| 145 | //serialize SVG. |
---|
| 146 | if (!gu._initSvgSerializerDeferred) { |
---|
| 147 | gu._initSvgSerializer(); |
---|
| 148 | } |
---|
| 149 | var jsonForm = gu.toJson(surface); |
---|
| 150 | var serializer = function(){ |
---|
| 151 | try{ |
---|
| 152 | var sDim = surface.getDimensions(); |
---|
| 153 | var width = sDim.width; |
---|
| 154 | var height = sDim.height; |
---|
| 155 | |
---|
| 156 | //Create an attach point in the iframe for the contents. |
---|
| 157 | var node = gu._gfxSvgProxy.document.createElement("div"); |
---|
| 158 | gu._gfxSvgProxy.document.body.appendChild(node); |
---|
| 159 | //Set the node scaling. |
---|
| 160 | win.withDoc(gu._gfxSvgProxy.document, function() { |
---|
| 161 | html.style(node, "width", width); |
---|
| 162 | html.style(node, "height", height); |
---|
| 163 | }, this); |
---|
| 164 | |
---|
| 165 | //Create temp surface to render object to and render. |
---|
| 166 | var ts = gu._gfxSvgProxy[dojox._scopeName].gfx.createSurface(node, width, height); |
---|
| 167 | |
---|
| 168 | //It's apparently possible that a suface creation is async, so we need to use |
---|
| 169 | //the whenLoaded function. Probably not needed for SVG, but making it common |
---|
| 170 | var draw = function(surface) { |
---|
| 171 | try{ |
---|
| 172 | gu._gfxSvgProxy[dojox._scopeName].gfx.utils.fromJson(surface, jsonForm); |
---|
| 173 | |
---|
| 174 | //Get contents and remove temp surface. |
---|
| 175 | var svg = gu._cleanSvg(node.innerHTML); |
---|
| 176 | surface.clear(); |
---|
| 177 | surface.destroy(); |
---|
| 178 | gu._gfxSvgProxy.document.body.removeChild(node); |
---|
| 179 | deferred.callback(svg); |
---|
| 180 | }catch(e){ |
---|
| 181 | deferred.errback(e); |
---|
| 182 | } |
---|
| 183 | }; |
---|
| 184 | ts.whenLoaded(null,draw); |
---|
| 185 | }catch (ex) { |
---|
| 186 | deferred.errback(ex); |
---|
| 187 | } |
---|
| 188 | }; |
---|
| 189 | //See if we can call it directly or pass it to the deferred to be |
---|
| 190 | //called on initialization. |
---|
| 191 | if(gu._initSvgSerializerDeferred.fired > 0){ |
---|
| 192 | serializer(); |
---|
| 193 | }else{ |
---|
| 194 | gu._initSvgSerializerDeferred.addCallback(serializer); |
---|
| 195 | } |
---|
| 196 | } |
---|
| 197 | return deferred; //dojo.Deferred that will be called when serialization finishes. |
---|
| 198 | }, |
---|
| 199 | |
---|
| 200 | //iFrame document used for handling SVG serialization. |
---|
| 201 | _gfxSvgProxy: null, |
---|
| 202 | |
---|
| 203 | //Serializer loaded. |
---|
| 204 | _initSvgSerializerDeferred: null, |
---|
| 205 | |
---|
| 206 | _svgSerializerInitialized: function() { |
---|
| 207 | // summary: |
---|
| 208 | // Internal function to call when the serializer init completed. |
---|
| 209 | // tags: |
---|
| 210 | // private |
---|
| 211 | gu._initSvgSerializerDeferred.callback(true); |
---|
| 212 | }, |
---|
| 213 | |
---|
| 214 | _initSvgSerializer: function(){ |
---|
| 215 | // summary: |
---|
| 216 | // Internal function to initialize the hidden iframe where SVG rendering |
---|
| 217 | // will occur. |
---|
| 218 | // tags: |
---|
| 219 | // private |
---|
| 220 | if(!gu._initSvgSerializerDeferred){ |
---|
| 221 | gu._initSvgSerializerDeferred = new Deferred(); |
---|
| 222 | var f = win.doc.createElement("iframe"); |
---|
| 223 | html.style(f, { |
---|
| 224 | display: "none", |
---|
| 225 | position: "absolute", |
---|
| 226 | width: "1em", |
---|
| 227 | height: "1em", |
---|
| 228 | top: "-10000px" |
---|
| 229 | }); |
---|
| 230 | var intv; |
---|
| 231 | if(has("ie")){ |
---|
| 232 | f.onreadystatechange = function(){ |
---|
| 233 | if(f.contentWindow.document.readyState == "complete"){ |
---|
| 234 | f.onreadystatechange = function() {}; |
---|
| 235 | intv = setInterval(function() { |
---|
| 236 | if(f.contentWindow[kernel.scopeMap["dojo"][1]._scopeName] && |
---|
| 237 | f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx && |
---|
| 238 | f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils){ |
---|
| 239 | clearInterval(intv); |
---|
| 240 | f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._gfxSvgProxy = f.contentWindow; |
---|
| 241 | f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._svgSerializerInitialized(); |
---|
| 242 | } |
---|
| 243 | }, 50); |
---|
| 244 | } |
---|
| 245 | }; |
---|
| 246 | }else{ |
---|
| 247 | f.onload = function(){ |
---|
| 248 | f.onload = function() {}; |
---|
| 249 | intv = setInterval(function() { |
---|
| 250 | if(f.contentWindow[kernel.scopeMap["dojo"][1]._scopeName] && |
---|
| 251 | f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx && |
---|
| 252 | f.contentWindow[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils){ |
---|
| 253 | clearInterval(intv); |
---|
| 254 | f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._gfxSvgProxy = f.contentWindow; |
---|
| 255 | f.contentWindow.parent[kernel.scopeMap["dojox"][1]._scopeName].gfx.utils._svgSerializerInitialized(); |
---|
| 256 | } |
---|
| 257 | }, 50); |
---|
| 258 | }; |
---|
| 259 | } |
---|
| 260 | //We have to load the GFX SVG proxy frame. Default is to use the one packaged in dojox. |
---|
| 261 | var uri = (config["dojoxGfxSvgProxyFrameUrl"]||require.toUrl("dojox/gfx/resources/gfxSvgProxyFrame.html")); |
---|
| 262 | f.setAttribute("src", uri.toString()); |
---|
| 263 | win.body().appendChild(f); |
---|
| 264 | } |
---|
| 265 | }, |
---|
| 266 | |
---|
| 267 | _innerXML: function(/*Node*/node){ |
---|
| 268 | // summary: |
---|
| 269 | // Implementation of MS's innerXML function, borrowed from dojox.xml.parser. |
---|
| 270 | // node: |
---|
| 271 | // The node from which to generate the XML text representation. |
---|
| 272 | // tags: |
---|
| 273 | // private |
---|
| 274 | if(node.innerXML){ |
---|
| 275 | return node.innerXML; //String |
---|
| 276 | }else if(node.xml){ |
---|
| 277 | return node.xml; //String |
---|
| 278 | }else if(typeof XMLSerializer != "undefined"){ |
---|
| 279 | return (new XMLSerializer()).serializeToString(node); //String |
---|
| 280 | } |
---|
| 281 | return null; |
---|
| 282 | }, |
---|
| 283 | |
---|
| 284 | _cleanSvg: function(svg) { |
---|
| 285 | // summary: |
---|
| 286 | // Internal function that cleans up artifacts in extracted SVG content. |
---|
| 287 | // tags: |
---|
| 288 | // private |
---|
| 289 | if(svg){ |
---|
| 290 | //Make sure the namespace is set. |
---|
| 291 | if(svg.indexOf("xmlns=\"http://www.w3.org/2000/svg\"") == -1){ |
---|
| 292 | svg = svg.substring(4, svg.length); |
---|
| 293 | svg = "<svg xmlns=\"http://www.w3.org/2000/svg\"" + svg; |
---|
| 294 | } |
---|
| 295 | //Same for xmlns:xlink (missing in Chrome and Safari) |
---|
| 296 | if(svg.indexOf("xmlns:xlink=\"http://www.w3.org/1999/xlink\"") == -1){ |
---|
| 297 | svg = svg.substring(4, svg.length); |
---|
| 298 | svg = "<svg xmlns:xlink=\"http://www.w3.org/1999/xlink\"" + svg; |
---|
| 299 | } |
---|
| 300 | //and add namespace to href attribute if not done yet |
---|
| 301 | //(FF 5+ adds xlink:href but not the xmlns def) |
---|
| 302 | if(svg.indexOf("xlink:href") === -1){ |
---|
| 303 | svg = svg.replace(/href\s*=/g, "xlink:href="); |
---|
| 304 | } |
---|
| 305 | // in IE, <image are serialized as <img> |
---|
| 306 | svg = svg.replace(/<img\b([^>]*)>/gi,"<image $1 />"); |
---|
| 307 | //Do some other cleanup, like stripping out the |
---|
| 308 | //dojoGfx attributes and quoting ids. |
---|
| 309 | svg = svg.replace(/\bdojoGfx\w*\s*=\s*(['"])\w*\1/g, ""); |
---|
| 310 | svg = svg.replace(/\b__gfxObject__\s*=\s*(['"])\w*\1/g, ""); |
---|
| 311 | svg = svg.replace(/[=]([^"']+?)(\s|>)/g,'="$1"$2'); |
---|
| 312 | } |
---|
| 313 | return svg; //Cleaned SVG text. |
---|
| 314 | } |
---|
| 315 | }); |
---|
| 316 | |
---|
| 317 | return gu; |
---|
| 318 | }); |
---|