utils.js 10 KB

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