1 | define([ |
---|
2 | "dojo/_base/kernel", |
---|
3 | "dojo/_base/lang", |
---|
4 | "dojo/_base/connect", |
---|
5 | "dojo/_base/html", |
---|
6 | "../gfx", |
---|
7 | "../xml/DomParser", |
---|
8 | "./UndoStack" |
---|
9 | ], function(dojo){ |
---|
10 | dojo.experimental("dojox.sketch"); |
---|
11 | |
---|
12 | var ta=dojox.sketch; |
---|
13 | ta.tools={}; |
---|
14 | ta.registerTool=function(type, fn){ ta.tools[type]=fn; }; |
---|
15 | ta.Figure = function(mixin){ |
---|
16 | var self=this; |
---|
17 | this.annCounter=1; |
---|
18 | |
---|
19 | this.shapes=[]; |
---|
20 | this.image=null; |
---|
21 | this.imageSrc=null; |
---|
22 | this.size={ w:0, h:0 }; |
---|
23 | this.surface=null; |
---|
24 | this.group=null; |
---|
25 | //node should have tabindex set, otherwise keyboard action does not work |
---|
26 | this.node=null; |
---|
27 | |
---|
28 | this.zoomFactor=1; // multiplier for zooming. |
---|
29 | |
---|
30 | this.tools=null; // toolbar reference. |
---|
31 | |
---|
32 | this.obj={}; // lookup table for shapes. Not keen on this solution. |
---|
33 | |
---|
34 | dojo.mixin(this,mixin); |
---|
35 | |
---|
36 | // what is selected. |
---|
37 | this.selected=[]; |
---|
38 | this.hasSelections=function(){ return this.selected.length>0 }; |
---|
39 | this.isSelected=function(obj){ |
---|
40 | for(var i=0; i<self.selected.length; i++){ |
---|
41 | if(self.selected[i]==obj){ return true; } |
---|
42 | } |
---|
43 | return false; |
---|
44 | }; |
---|
45 | this.select=function(obj){ |
---|
46 | // TODO: deal with multiple vs. single selections. |
---|
47 | if(!self.isSelected(obj)){ |
---|
48 | // force single select |
---|
49 | self.clearSelections(); |
---|
50 | self.selected=[ obj ]; |
---|
51 | } |
---|
52 | // force a bbox update, regardless |
---|
53 | obj.setMode(ta.Annotation.Modes.View); |
---|
54 | obj.setMode(ta.Annotation.Modes.Edit); |
---|
55 | }; |
---|
56 | this.deselect=function(obj){ |
---|
57 | var idx=-1; |
---|
58 | for(var i=0; i<self.selected.length; i++){ |
---|
59 | if(self.selected[i]==obj){ |
---|
60 | idx=i; |
---|
61 | break; |
---|
62 | } |
---|
63 | } |
---|
64 | if(idx>-1){ |
---|
65 | obj.setMode(ta.Annotation.Modes.View); |
---|
66 | self.selected.splice(idx,1); |
---|
67 | } |
---|
68 | return obj; |
---|
69 | }; |
---|
70 | this.clearSelections=function(){ |
---|
71 | for(var i=0; i<self.selected.length; i++){ |
---|
72 | self.selected[i].setMode(ta.Annotation.Modes.View); |
---|
73 | } |
---|
74 | self.selected=[]; |
---|
75 | }; |
---|
76 | this.replaceSelection=function(n, o){ |
---|
77 | if(!self.isSelected(o)){ |
---|
78 | self.select(n); |
---|
79 | return; |
---|
80 | } |
---|
81 | var idx=-1; |
---|
82 | for(var i=0; i<self.selected.length; i++){ |
---|
83 | if(self.selected[i]==o){ |
---|
84 | idx=i; |
---|
85 | break; |
---|
86 | } |
---|
87 | } |
---|
88 | if(idx>-1){ |
---|
89 | self.selected.splice(idx,1,n); |
---|
90 | } |
---|
91 | }; |
---|
92 | |
---|
93 | // for the drag and drop handlers. |
---|
94 | this._c=null; // current shape |
---|
95 | this._ctr=null; // container measurements |
---|
96 | this._lp=null; // last position |
---|
97 | this._action=null; |
---|
98 | this._prevState=null; |
---|
99 | this._startPoint=null; // test to record a move. |
---|
100 | |
---|
101 | // if an object isn't selected and we're dragging anyways. |
---|
102 | this._ctool=null; // hard code it. |
---|
103 | this._start=null; |
---|
104 | this._end=null; |
---|
105 | this._absEnd=null; |
---|
106 | this._cshape=null; |
---|
107 | |
---|
108 | this._dblclick=function(e){ |
---|
109 | var o=self._fromEvt(e); |
---|
110 | if(o){ |
---|
111 | self.onDblClickShape(o,e); |
---|
112 | } |
---|
113 | }; |
---|
114 | |
---|
115 | this._keydown=function(e){ |
---|
116 | var prevent=false; |
---|
117 | if(e.ctrlKey){ |
---|
118 | if(e.keyCode===90 || e.keyCode===122){ //ctrl+z |
---|
119 | self.undo(); |
---|
120 | prevent = true; |
---|
121 | } |
---|
122 | else if(e.keyCode===89 || e.keyCode===121){ //ctrl+y |
---|
123 | self.redo(); |
---|
124 | prevent = true; |
---|
125 | } |
---|
126 | } |
---|
127 | |
---|
128 | if(e.keyCode===46 || e.keyCode===8){ //delete or backspace |
---|
129 | self._delete(self.selected); |
---|
130 | prevent = true; |
---|
131 | } |
---|
132 | |
---|
133 | if(prevent){ |
---|
134 | dojo.stopEvent(e); |
---|
135 | } |
---|
136 | }; |
---|
137 | |
---|
138 | // drag handlers. |
---|
139 | this._md=function(e){ |
---|
140 | //in IE, when clicking into the drawing canvas, the node does not get focused, |
---|
141 | //do it manually here to force it, otherwise the keydown event listener is |
---|
142 | //never triggered in IE. |
---|
143 | if(dojox.gfx.renderer=='vml'){ |
---|
144 | self.node.focus(); |
---|
145 | } |
---|
146 | var o=self._fromEvt(e); |
---|
147 | self._startPoint={ x:e.pageX, y:e.pageY }; |
---|
148 | |
---|
149 | self._ctr=dojo.position(self.node); |
---|
150 | // figure out the coordinates taking scroll into account |
---|
151 | var scroll={x:self.node.scrollLeft,y:self.node.scrollTop}; |
---|
152 | //var win = dojo.window.get(self.node.ownerDocument); |
---|
153 | //var scroll=dojo.withGlobal(win,dojo._docScroll); |
---|
154 | self._ctr={x:self._ctr.x-scroll.x, y:self._ctr.y-scroll.y}; |
---|
155 | var X=e.clientX-self._ctr.x, Y=e.clientY-self._ctr.y; |
---|
156 | self._lp={ x:X, y:Y }; |
---|
157 | |
---|
158 | // capture it separately |
---|
159 | self._start={ x:X, y:Y }; |
---|
160 | self._end={ x:X, y:Y }; |
---|
161 | self._absEnd={ x:X, y:Y }; |
---|
162 | if(!o){ |
---|
163 | self.clearSelections(); |
---|
164 | self._ctool.onMouseDown(e); |
---|
165 | }else{ |
---|
166 | if(o.type && o.type()!="Anchor"){ |
---|
167 | if(!self.isSelected(o)){ |
---|
168 | self.select(o); |
---|
169 | self._sameShapeSelected=false; |
---|
170 | }else{ |
---|
171 | self._sameShapeSelected=true; |
---|
172 | } |
---|
173 | } |
---|
174 | o.beginEdit(); |
---|
175 | self._c=o; |
---|
176 | } |
---|
177 | }; |
---|
178 | this._mm=function(e){ |
---|
179 | if(!self._ctr){ return; } |
---|
180 | var x=e.clientX-self._ctr.x; |
---|
181 | var y=e.clientY-self._ctr.y; |
---|
182 | var dx=x-self._lp.x; |
---|
183 | var dy=y-self._lp.y; |
---|
184 | self._absEnd={x:x, y:y}; |
---|
185 | if(self._c){ |
---|
186 | //self._c.doChange({dx:dx, dy:dy}); |
---|
187 | self._c.setBinding({dx:dx/self.zoomFactor, dy:dy/self.zoomFactor}); |
---|
188 | self._lp={x:x, y:y}; |
---|
189 | } else { |
---|
190 | self._end={x:dx, y:dy}; |
---|
191 | var rect={ |
---|
192 | x:Math.min(self._start.x,self._absEnd.x), |
---|
193 | y:Math.min(self._start.y,self._absEnd.y), |
---|
194 | width:Math.abs(self._start.x-self._absEnd.x), |
---|
195 | height:Math.abs(self._start.y-self._absEnd.y) |
---|
196 | } |
---|
197 | if(rect.width && rect.height){ |
---|
198 | self._ctool.onMouseMove(e,rect); |
---|
199 | } |
---|
200 | } |
---|
201 | }; |
---|
202 | this._mu=function(e){ |
---|
203 | if(self._c){ |
---|
204 | // record the event. |
---|
205 | self._c.endEdit(); |
---|
206 | }else{ |
---|
207 | self._ctool.onMouseUp(e); |
---|
208 | } |
---|
209 | |
---|
210 | // clear the stuff out. |
---|
211 | self._c=self._ctr=self._lp=self._action=self._prevState=self._startPoint=null; |
---|
212 | self._cshape=self._start=self._end=self._absEnd=null; |
---|
213 | }; |
---|
214 | |
---|
215 | this.initUndoStack(); |
---|
216 | }; |
---|
217 | |
---|
218 | var p=ta.Figure.prototype; |
---|
219 | p.initUndoStack=function(){ |
---|
220 | this.history=new ta.UndoStack(this); |
---|
221 | }; |
---|
222 | p.setTool=function(/*dojox.sketch._Plugin*/t){ |
---|
223 | this._ctool=t; |
---|
224 | }; |
---|
225 | //gridSize: int |
---|
226 | // if it is greater than 0, all new shapes placed on the drawing will have coordinates |
---|
227 | // snapped to the gridSize. For example, if gridSize is set to 10, all coordinates |
---|
228 | // (only including coordinates which specifies the x/y position of shape are affected |
---|
229 | // by this parameter) will be dividable by 10 |
---|
230 | p.gridSize=0; |
---|
231 | p._calCol=function(v){ |
---|
232 | return this.gridSize?(Math.round(v/this.gridSize)*this.gridSize):v; |
---|
233 | }; |
---|
234 | p._delete=function(arr,noundo){ |
---|
235 | for(var i=0; i<arr.length; i++){ |
---|
236 | //var before=arr[i].serialize(); |
---|
237 | arr[i].setMode(ta.Annotation.Modes.View); |
---|
238 | arr[i].destroy(noundo); |
---|
239 | this.remove(arr[i]); |
---|
240 | this._remove(arr[i]); |
---|
241 | if(!noundo){ |
---|
242 | arr[i].onRemove(); |
---|
243 | } |
---|
244 | } |
---|
245 | arr.splice(0,arr.length); |
---|
246 | }; |
---|
247 | p.onDblClickShape=function(shape,e){ |
---|
248 | if(shape['onDblClick']){ |
---|
249 | shape.onDblClick(e); |
---|
250 | } |
---|
251 | }; |
---|
252 | |
---|
253 | p.onCreateShape=function(shape){}; |
---|
254 | p.onBeforeCreateShape=function(shape){}; |
---|
255 | p.initialize=function(node){ |
---|
256 | this.node=node; |
---|
257 | this.surface=dojox.gfx.createSurface(node, this.size.w, this.size.h); |
---|
258 | //this.backgroundRect=this.surface.createRect({ x:0, y:0, width:this.size.w, height:this.size.h }) |
---|
259 | // .setFill("white"); |
---|
260 | this.group=this.surface.createGroup(); |
---|
261 | |
---|
262 | this._cons=[]; |
---|
263 | var es=this.surface.getEventSource(); |
---|
264 | this._cons.push( |
---|
265 | // kill any dragging events. |
---|
266 | // for FF |
---|
267 | dojo.connect(es, "ondraggesture", dojo.stopEvent), |
---|
268 | dojo.connect(es, "ondragenter", dojo.stopEvent), |
---|
269 | dojo.connect(es, "ondragover", dojo.stopEvent), |
---|
270 | dojo.connect(es, "ondragexit", dojo.stopEvent), |
---|
271 | dojo.connect(es, "ondragstart", dojo.stopEvent), |
---|
272 | // for IE |
---|
273 | dojo.connect(es, "onselectstart", dojo.stopEvent), |
---|
274 | // hook up the drag system. |
---|
275 | dojo.connect(es, 'onmousedown', this._md), |
---|
276 | dojo.connect(es, 'onmousemove', this._mm), |
---|
277 | dojo.connect(es, 'onmouseup', this._mu), |
---|
278 | // misc hooks |
---|
279 | dojo.connect(es, 'onclick', this, 'onClick'), |
---|
280 | dojo.connect(es, 'ondblclick', this._dblclick), |
---|
281 | dojo.connect(node, 'onkeydown', this._keydown)); |
---|
282 | |
---|
283 | this.image=this.group.createImage({ width:this.imageSize.w, height:this.imageSize.h, src:this.imageSrc }); |
---|
284 | }; |
---|
285 | |
---|
286 | p.destroy=function(isLoading){ |
---|
287 | if(!this.node){ return; } |
---|
288 | if(!isLoading){ |
---|
289 | if(this.history){ this.history.destroy(); } |
---|
290 | if(this._subscribed){ |
---|
291 | dojo.unsubscribe(this._subscribed); |
---|
292 | delete this._subscribed; |
---|
293 | } |
---|
294 | } |
---|
295 | dojo.forEach(this._cons, dojo.disconnect); |
---|
296 | this._cons=[]; |
---|
297 | |
---|
298 | //TODO: how to destroy a surface properly? |
---|
299 | dojo.empty(this.node); |
---|
300 | |
---|
301 | //this.node.removeChild(this.surface.getEventSource()); |
---|
302 | this.group=this.surface=null; |
---|
303 | this.obj={}; |
---|
304 | this.shapes=[]; |
---|
305 | }; |
---|
306 | p.nextKey=function(){ return "annotation-"+this.annCounter++; }; |
---|
307 | p.draw=function(){ }; |
---|
308 | p.zoom=function(pct){ |
---|
309 | // first get the new dimensions |
---|
310 | this.zoomFactor=pct/100; |
---|
311 | var w=this.size.w*this.zoomFactor; |
---|
312 | var h=this.size.h*this.zoomFactor; |
---|
313 | this.surface.setDimensions(w, h); |
---|
314 | // then scale it. |
---|
315 | this.group.setTransform(dojox.gfx.matrix.scale(this.zoomFactor, this.zoomFactor)); |
---|
316 | |
---|
317 | for(var i=0; i<this.shapes.length; i++){ |
---|
318 | this.shapes[i].zoom(this.zoomFactor); |
---|
319 | } |
---|
320 | }; |
---|
321 | p.getFit=function(){ |
---|
322 | // assume fitting the parent node. |
---|
323 | // var box=dojo.html.getContentBox(this.node.parentNode); |
---|
324 | //the following should work under IE and FF, not sure about others though |
---|
325 | var wF=(this.node.parentNode.offsetWidth-5)/this.size.w; |
---|
326 | var hF=(this.node.parentNode.offsetHeight-5)/this.size.h; |
---|
327 | return Math.min(wF, hF)*100; |
---|
328 | }; |
---|
329 | p.unzoom=function(){ |
---|
330 | // restore original size. |
---|
331 | this.zoomFactor=1; |
---|
332 | this.surface.setDimensions(this.size.w, this.size.h); |
---|
333 | this.group.setTransform(); |
---|
334 | }; |
---|
335 | |
---|
336 | // object registry for drag handling. |
---|
337 | p._add=function(obj){ this.obj[obj._key]=obj; }; |
---|
338 | p._remove=function(obj){ |
---|
339 | if(this.obj[obj._key]){ |
---|
340 | delete this.obj[obj._key]; |
---|
341 | } |
---|
342 | }; |
---|
343 | p._get=function(key){ |
---|
344 | if(key&&key.indexOf("bounding")>-1){ |
---|
345 | key=key.replace("-boundingBox",""); |
---|
346 | }else if(key&&key.indexOf("-labelShape")>-1){ |
---|
347 | key=key.replace("-labelShape",""); |
---|
348 | } |
---|
349 | return this.obj[key]; |
---|
350 | }; |
---|
351 | p._keyFromEvt=function(e){ |
---|
352 | var key=e.target.id+""; |
---|
353 | if(key.length==0){ |
---|
354 | // ancestor tree until you get to the end (meaning this.surface) |
---|
355 | var p=e.target.parentNode; |
---|
356 | var node=this.surface.getEventSource(); |
---|
357 | while(p && p.id.length==0 && p!=node){ |
---|
358 | p=p.parentNode; |
---|
359 | } |
---|
360 | key=p.id; |
---|
361 | } |
---|
362 | return key; |
---|
363 | }; |
---|
364 | p._fromEvt=function(e){ |
---|
365 | return this._get(this._keyFromEvt(e)); |
---|
366 | }; |
---|
367 | |
---|
368 | p.add=function(annotation){ |
---|
369 | for(var i=0; i<this.shapes.length; i++){ |
---|
370 | if(this.shapes[i]==annotation){ return true; } |
---|
371 | } |
---|
372 | this.shapes.push(annotation); |
---|
373 | return true; |
---|
374 | }; |
---|
375 | p.remove=function(annotation){ |
---|
376 | var idx=-1; |
---|
377 | for(var i=0; i<this.shapes.length; i++){ |
---|
378 | if(this.shapes[i]==annotation){ |
---|
379 | idx=i; |
---|
380 | break; |
---|
381 | } |
---|
382 | } |
---|
383 | if(idx>-1){ this.shapes.splice(idx, 1); } |
---|
384 | return annotation; |
---|
385 | }; |
---|
386 | p.getAnnotator=function(id){ |
---|
387 | for(var i=0; i<this.shapes.length; i++){ |
---|
388 | if(this.shapes[i].id==id) { |
---|
389 | return this.shapes[i]; |
---|
390 | } |
---|
391 | } |
---|
392 | return null; |
---|
393 | }; |
---|
394 | |
---|
395 | p.convert=function(ann, t){ |
---|
396 | // convert an existing annotation to a different kind of annotation |
---|
397 | var ctor=t+"Annotation"; |
---|
398 | if(!ta[ctor]){ return; } |
---|
399 | var type=ann.type(), id=ann.id, label=ann.label, mode=ann.mode, tokenId=ann.tokenId; |
---|
400 | var start, end, control, transform; |
---|
401 | switch(type){ |
---|
402 | case "Preexisting": |
---|
403 | case "Lead":{ |
---|
404 | transform={dx:ann.transform.dx, dy:ann.transform.dy }; |
---|
405 | start={x:ann.start.x, y:ann.start.y}; |
---|
406 | end={x:ann.end.x, y:ann.end.y }; |
---|
407 | var cx=end.x-((end.x-start.x)/2); |
---|
408 | var cy=end.y-((end.y-start.y)/2); |
---|
409 | control={x:cx, y:cy}; |
---|
410 | break; |
---|
411 | } |
---|
412 | case "SingleArrow": |
---|
413 | case "DoubleArrow":{ |
---|
414 | transform={dx:ann.transform.dx, dy:ann.transform.dy }; |
---|
415 | start={x:ann.start.x, y:ann.start.y}; |
---|
416 | end={x:ann.end.x, y:ann.end.y }; |
---|
417 | control={x:ann.control.x, y:ann.control.y}; |
---|
418 | break; |
---|
419 | } |
---|
420 | case "Underline":{ |
---|
421 | transform={dx:ann.transform.dx, dy:ann.transform.dy }; |
---|
422 | start={x:ann.start.x, y:ann.start.y}; |
---|
423 | control={x:start.x+50, y:start.y+50 }; |
---|
424 | end={x:start.x+100, y:start.y+100 }; |
---|
425 | break; |
---|
426 | } |
---|
427 | case "Brace":{ } |
---|
428 | } |
---|
429 | var n=new ta[ctor](this, id); |
---|
430 | |
---|
431 | if(n.type()=="Underline"){ |
---|
432 | // special handling, since we never move the start point. |
---|
433 | n.transform={dx:transform.dx+start.x, dy:transform.dy+start.y }; |
---|
434 | } else { |
---|
435 | if(n.transform){ n.transform=transform; } |
---|
436 | if(n.start){ n.start=start; } |
---|
437 | } |
---|
438 | if(n.end){ n.end=end; } |
---|
439 | if(n.control){ n.control=control; } |
---|
440 | n.label=label; |
---|
441 | n.token=dojo.lang.shallowCopy(ann.token); |
---|
442 | n.initialize(); |
---|
443 | |
---|
444 | this.replaceSelection(n, ann); |
---|
445 | this._remove(ann); |
---|
446 | this.remove(ann); |
---|
447 | ann.destroy(); |
---|
448 | |
---|
449 | // this should do all the things we need it to do for getting it registered. |
---|
450 | n.setMode(mode); |
---|
451 | }; |
---|
452 | p.setValue=function(text){ |
---|
453 | var obj=dojox.xml.DomParser.parse(text); |
---|
454 | var node=this.node; |
---|
455 | this.load(obj,node); |
---|
456 | //this.zoom(this.zoomFactor*100); //zoom to orignal scale |
---|
457 | }; |
---|
458 | p.load=function(obj, n){ |
---|
459 | // create from pseudo-DOM |
---|
460 | if(this.surface){ this.destroy(true); } |
---|
461 | var node=obj.documentElement; // should be either the document or the docElement |
---|
462 | this.size={ |
---|
463 | w:parseFloat(node.getAttribute('width'),10), |
---|
464 | h:parseFloat(node.getAttribute('height'),10) |
---|
465 | }; |
---|
466 | var g=node.childrenByName("g")[0]; |
---|
467 | var img=g.childrenByName("image")[0]; |
---|
468 | this.imageSize={ |
---|
469 | w:parseFloat(img.getAttribute('width'),10), |
---|
470 | h:parseFloat(img.getAttribute('height'),10) |
---|
471 | }; |
---|
472 | this.imageSrc=img.getAttribute("xlink:href"); |
---|
473 | this.initialize(n); |
---|
474 | |
---|
475 | // now let's do the annotations. |
---|
476 | var ann=g.childrenByName("g"); |
---|
477 | for(var i=0; i<ann.length; i++){ this._loadAnnotation(ann[i]); } |
---|
478 | if(this._loadDeferred){ |
---|
479 | this._loadDeferred.callback(this); |
---|
480 | this._loadDeferred=null; |
---|
481 | } |
---|
482 | this.onLoad(); |
---|
483 | }; |
---|
484 | p.onLoad=function(){}; |
---|
485 | p.onClick=function(){}; |
---|
486 | p._loadAnnotation=function(obj){ |
---|
487 | var ctor=obj.getAttribute('dojoxsketch:type')+"Annotation"; |
---|
488 | if(ta[ctor]){ |
---|
489 | var a=new ta[ctor](this, obj.id); |
---|
490 | a.initialize(obj); |
---|
491 | this.nextKey(); |
---|
492 | a.setMode(ta.Annotation.Modes.View); |
---|
493 | this._add(a); |
---|
494 | return a; |
---|
495 | } |
---|
496 | return null; |
---|
497 | }; |
---|
498 | |
---|
499 | p.onUndo=function(){}; |
---|
500 | p.onBeforeUndo=function(){}; |
---|
501 | p.onRedo=function(){}; |
---|
502 | p.onBeforeRedo=function(){}; |
---|
503 | p.undo=function(){ |
---|
504 | if(this.history){ |
---|
505 | this.onBeforeUndo(); |
---|
506 | this.history.undo(); |
---|
507 | this.onUndo(); |
---|
508 | } |
---|
509 | }; |
---|
510 | p.redo=function(){ |
---|
511 | if(this.history){ |
---|
512 | this.onBeforeRedo(); |
---|
513 | this.history.redo(); |
---|
514 | this.onRedo(); |
---|
515 | } |
---|
516 | }; |
---|
517 | p.serialize=function(){ |
---|
518 | var s='<svg xmlns="http://www.w3.org/2000/svg" ' |
---|
519 | + 'xmlns:xlink="http://www.w3.org/1999/xlink" ' |
---|
520 | + 'xmlns:dojoxsketch="http://dojotoolkit.org/dojox/sketch" ' |
---|
521 | + 'width="' + this.size.w + '" height="' + this.size.h + '">' |
---|
522 | + '<g>' |
---|
523 | + '<image xlink:href="' + this.imageSrc + '" x="0" y="0" width="' |
---|
524 | + this.size.w + '" height="' + this.size.h + '" />'; |
---|
525 | for(var i=0; i<this.shapes.length; i++){ s+= this.shapes[i].serialize(); } |
---|
526 | s += '</g></svg>'; |
---|
527 | return s; |
---|
528 | }; |
---|
529 | p.getValue=p.serialize; |
---|
530 | |
---|
531 | return dojox.sketch.Figure; |
---|
532 | }); |
---|