source: Dev/trunk/src/client/dojox/drawing/Drawing.js @ 532

Last change on this file since 532 was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

  • Property svn:executable set to *
File size: 18.5 KB
Line 
1define(["dojo", "./defaults", "./manager/_registry", "./manager/keys", "./manager/Mouse",
2"./manager/Canvas","./manager/Undo","./manager/Anchors","./manager/Stencil","./manager/StencilUI",
3  "./util/common"],
4function(dojo, defaults, registry, keys, Mouse, Canvas, Undo, Anchors, Stencil, StencilUI, utilCommon){
5        return dojo.declare("dojox.drawing.Drawing", [], {
6                // summary:
7                //              Drawing is a project that sits on top of DojoX GFX and uses SVG and
8                //              VML vector graphics to draw and display.
9                // description:
10                //              Drawing is similar to DojoX Sketch, but is designed to be more versatile
11                //              extendable and customizable.
12                //              Drawing currently only initiates from HTML although it's technically not
13                //              a Dijit to keep the file size light. But if Dijit is available, Drawing
14                //              will register itself with it and can be accessed with registry.byId('myDrawing')
15                //              (by requiring dijit/registry)
16                //
17                //              NOTES:
18                //              Although not Drawing and Toolbar, all other objects are created with a custom
19                //              declare. See dojox.drawing.util.oo
20                //
21                //              The files are laid out as such:
22                //
23                //              - Drawing: The master class. More than one instance of a Drawing can be placed
24                //                      on a page at one time (although this has not yet been tested). Plugins
25                //                      can be added in markup.
26                //              - Toolbar: Like Drawing, Toolbar is a psudeo Dijit that does not need Dijit. It is
27                //                      optional. It can be oriented horizontal or vertical by placing one of
28                //                      those params in the class (at least one is required).  Plugins
29                //                      can be added in markup. A drawingId is required to point toolbar to
30                //                      the drawing.
31                //              - defaults: Contains the default styles and dimensions for Stencils. An individual
32                //                      Stencil can be changed by calling stencil.att({color obj}); To change
33                //                      all styles, a custom defaults file should be used.
34                //              - Stencils: Drawing uses a concept of 'Stencils' to avoid confusion between a
35                //                      Dojox Shape and a Drawing Shape. The classes in the 'stencils' package
36                //                      are display only, they are not used for actually drawing (see 'tools').
37                //                      This package contains _Base from which stencils inherit most of their
38                //                      methods. (Path and Image are display only and not found in Tools)
39                //              - Tools: The Tools package contains Stencils that are attached to mouse events
40                //                      and can be used for drawing. Items in this package can also be selected
41                //                      and modified.
42                //              - Tools / Custom: Holds tools that do not directly extend Stencil base classes and often
43                //                      have very custom code.
44                //              - Library (not implemented): The Library package, which is not yet implemented, will be the place to
45                //                      hold stencils that have very specific data points that result in a picture.
46                //                      Flag-like-banners, fancy borders, or other complex shapes would go here.
47                //              - Annotations: Annotations 'decorate' and attach to other Stencils, such as a 'Label'
48                //                      that can show text on a stencil, or an 'Angle' that shows while dragging
49                //                      or modifying a Vector, or an Arrow head that is attached to the beginning
50                //                      or end of a line.
51                //              - Manager: Contains classes that control functionality of a Drawing.
52                //              - Plugins: Contains optional classes that are 'plugged into' a Drawing. There are two
53                //                      types: 'drawing' plugins that modify the canvas, and 'tools' which would
54                //                      show in the toolbar.
55                //              - Util: A collection of common tasks.
56                //
57                // example:
58                //              |       <div data-dojo-type="dojox.drawing.Drawing" id="drawing" defaults="myCustom.defaults"
59                //              |               plugins="[{'name':'dojox.drawing.plugins.drawing.Grid', 'options':{gap:100}}]">
60                //              |   </div>
61                //
62                // example:
63                //              |       <div dojoType="dojox.drawing.Toolbar" drawingId="drawing" class="drawingToolbar vertical">
64                //              |               <div tool="dojox.drawing.tools.Line" selected="false">Line</div>
65                //              |               <div tool="dojox.drawing.tools.Rect" selected="false">Rect</div>
66                //              |               <div tool="dojox.drawing.tools.Ellipse" selected="false">Ellipse</div>
67                //              |               <div tool="dojox.drawing.tools.TextBlock" selected="false">Statement</div>
68                //              |               <div tool="dojox.drawing.tools.custom.Equation" selected="false">Equation</div>
69                //              |               <div plugin="dojox.drawing.plugins.tools.Pan" options="{}">Pan</div>
70                //              |               <div plugin="dojox.drawing.plugins.tools.Zoom" options="{zoomInc:.1,minZoom:.5,maxZoom:2}">Zoom</div>
71                //              |       </div>
72
73                // ready: Boolean
74                //              Whether or not the canvas has been created and Stencils can be added
75                ready:false,
76
77                // mode: [optional] String
78                //              Changes the functionality of the drawing
79                mode: "",
80
81                // width: Number
82                //              Width of the canvas
83                width:0,
84
85                // height: Number
86                //              Height of the canvas
87                height:0,
88
89                // defaults : Object
90                //              Optional replacements for native defaults.
91
92                // plugins: Object
93                //              Key values of plugins that apply to canvas.
94
95                constructor: function(/* Object */props, /* HTMLNode */node){
96                        // summary:
97                        //              Drawing is not a Dijit. This is the master method.
98                        //
99                        //              NOTE:
100                        //              props is always null since this is not a real widget
101                        //              Will change when Drawing can be created programmatically.
102
103                        var def = dojo.attr(node, "defaults");
104                        this.defaults =  def ? (typeof def === 'string' ? dojo.getObject(def) : def) : defaults;
105
106                        this.id = node.id || dijit.getUniqueId('dojox_drawing_Drawing');
107                        registry.register(this, "drawing");
108                        this.mode = (props.mode || dojo.attr(node, "mode") || "").toLowerCase();
109                        var box = dojo.contentBox(node);
110                        this.width = props.width || box.w;
111                        this.height = props.height || box.h;
112                        utilCommon.register(this); // So Toolbar can find this Drawing DEPRECATED
113                        this.mouse = new Mouse({util:utilCommon, keys:keys, id:this.mode=="ui"?"MUI":"mse"});
114                        this.mouse.setEventMode(this.mode);
115
116                        this.tools = {};
117                        this.stencilTypes = {};
118                        this.stencilTypeMap = {};
119                        this.srcRefNode = node; // need this?
120                        this.domNode = node;
121                        if(props.plugins){
122                                this.plugins = eval(props.plugins);
123                        }else{
124                                this.plugins = [];
125                        }
126
127                        this.widgetId = this.id;
128                        dojo.attr(this.domNode, "widgetId", this.widgetId);
129                        // If Dijit is available in the page, register with it
130                        if(dijit && dijit.registry){
131                                dijit.registry.add(this);
132                                console.log("using dijit")
133                        }else{
134                                // else fake dijit.byId
135                                // FIXME: This seems pretty hacky.
136                                // Maybe should just encourage jsId
137                                dijit.registry = {
138                                        objs:{},
139                                        add:function(obj){
140                                                this.objs[obj.id] = obj;
141                                        }
142                                };
143                                dijit.byId = function(id){
144                                        return dijit.registry.objs[id];
145                                };
146                                dijit.registry.add(this);
147                        }
148
149                        var stencils = registry.getRegistered("stencil");
150                        for(var nm in stencils){
151                                this.registerTool(stencils[nm].name);
152                        }
153                        var tools = registry.getRegistered("tool");
154                        for(nm in tools){
155                                this.registerTool(tools[nm].name);
156                        }
157                        var plugs = registry.getRegistered("plugin");
158                        for(nm in plugs){
159                                this.registerTool(plugs[nm].name);
160                        }
161                        this._createCanvas();
162
163                },
164
165                _createCanvas: function(){
166                        console.info("drawing create canvas...");
167                        this.canvas = new Canvas({
168                                srcRefNode:this.domNode,
169                                util:utilCommon,
170                                mouse:this.mouse,
171                                width: this.width, height: this.height,
172                                callback: dojo.hitch(this, "onSurfaceReady")
173                        });
174                        this.initPlugins();
175                },
176
177                resize: function(/* Object */box){
178                        // summary:
179                        //              Resizes the canvas.
180                        //              If within a ContentPane this will get called automatically.
181                        //              Can also be called directly.
182
183                        box && dojo.style(this.domNode, {
184                                width:box.w+"px",
185                                height:box.h+"px"
186                        });
187                        if(!this.canvas){
188                                this._createCanvas();
189                        }else if(box){
190                                this.canvas.resize(box.w, box.h);
191                        }
192                },
193
194                startup: function(){
195                        //console.info("drawing startup")
196                },
197
198                getShapeProps: function(/* Object */data, mode){
199                        // summary:
200                        //              The common objects that are mixed into
201                        //              a new Stencil. Mostly internal, but could be used.
202
203                        var surface = data.stencilType;
204                        var ui = this.mode=="ui" || mode=="ui";
205                        return dojo.mixin({
206                                container: ui && !surface ? this.canvas.overlay.createGroup() : this.canvas.surface.createGroup(),
207                                util:utilCommon,
208                                keys:keys,
209                                mouse:this.mouse,
210                                drawing:this,
211                                drawingType: ui && !surface ? "ui" : "stencil",
212                                style:this.defaults.copy()
213                        }, data || {});
214                },
215
216                addPlugin: function(/* Object */plugin){
217                        // summary:
218                        //              Add a toolbar plugin object to plugins array
219                        //              to be parsed
220                        this.plugins.push(plugin);
221                        if(this.canvas.surfaceReady){
222                                this.initPlugins();
223                        }
224                },
225
226                initPlugins: function(){
227                        // summary:
228                        //              Called from Toolbar after a plugin has been loaded
229                        //              The call to this coming from toolbar is a bit funky as the timing
230                        //              of IE for canvas load is different than other browsers
231                        if(!this.canvas || !this.canvas.surfaceReady){
232                                var c = dojo.connect(this, "onSurfaceReady", this, function(){
233                                        dojo.disconnect(c);
234                                        this.initPlugins();
235                                });
236                                return;
237                        }
238                        dojo.forEach(this.plugins, function(p, i){
239                                var props = dojo.mixin({
240                                        util:utilCommon,
241                                        keys:keys,
242                                        mouse:this.mouse,
243                                        drawing:this,
244                                        stencils:this.stencils,
245                                        anchors:this.anchors,
246                                        canvas:this.canvas
247                                }, p.options || {});
248                                //console.log('drawing.plugin:::', p.name, props)
249                                this.registerTool(p.name, dojo.getObject(p.name));
250                                try{
251                                        this.plugins[i] = new this.tools[p.name](props);
252                                }catch(e){
253                                        console.error("Failed to initilaize plugin:     " +p.name + ". Did you require it?");
254                                }
255                        }, this);
256                        this.plugins = [];
257                        // In IE, because the timing is different we have to get the
258                        // canvas position after everything has drawn. *sigh*
259                        this.mouse.setCanvas();
260                },
261
262                onSurfaceReady: function(){
263                        // summary:
264                        //              Event that to which can be connected.
265                        //              Fired when the canvas is ready and can be drawn to.
266
267                        this.ready = true;
268                        //console.info("Surface ready")
269                        this.mouse.init(this.canvas.domNode);
270                        this.undo = new Undo({keys:keys});
271                        this.anchors = new Anchors({drawing:this, mouse:this.mouse, undo:this.undo, util:utilCommon});
272                        if(this.mode == "ui"){
273                                this.uiStencils = new StencilUI({canvas:this.canvas, surface:this.canvas.surface, mouse:this.mouse, keys:keys});
274                        }else{
275                                this.stencils = new Stencil({canvas:this.canvas, surface:this.canvas.surface, mouse:this.mouse, undo:this.undo, keys:keys, anchors:this.anchors});
276                                this.uiStencils = new StencilUI({canvas:this.canvas, surface:this.canvas.surface, mouse:this.mouse, keys:keys});
277                        }
278                        if(dojox.gfx.renderer=="silverlight"){
279                                try{
280                                new dojox.drawing.plugins.drawing.Silverlight({util:utilCommon, mouse:this.mouse, stencils:this.stencils, anchors:this.anchors, canvas:this.canvas});
281                                }catch(e){
282                                        throw new Error("Attempted to install the Silverlight plugin, but it was not found.");
283                                }
284                        }
285                        dojo.forEach(this.plugins, function(p){
286                                p.onSurfaceReady && p.onSurfaceReady();
287                        });
288
289                },
290
291                addUI: function(/* String */type, /* Object */options){
292                        // summary:
293                        //              Use this method to programmatically add Stencils that display on
294                        //              the canvas.
295                        //
296                        //              FIXME: Currently only supports Stencils that have been registered,
297                        //              which is items in the toolbar, and the additional Stencils at the
298                        //              end of onSurfaceReady. This covers all Stencils, but you can't
299                        //              use 'display only' Stencils for Line, Rect, and Ellipse.
300                        // type: String
301                        //              The final name of the tool, lower case: 'image', 'line', 'textBlock'
302                        // options:
303                        //              - type: Object
304                        //
305                        //              The parameters used to draw the object. See stencil._Base and each
306                        //              tool for specific parameters of teh data or points objects.
307
308                        if(!this.ready){
309                                var c = dojo.connect(this, "onSurfaceReady", this, function(){
310                                        dojo.disconnect(c);
311                                        this.addUI(type, options);
312                                });
313                                return false;
314                        }
315                        if(options && !options.data && !options.points){
316                                options = {data:options}
317                        }
318                        if(!this.stencilTypes[type]){
319                                if(type != "tooltip"){
320                                        console.warn("Not registered:", type);
321                                }
322                                return null;
323                        }
324                        var s = this.uiStencils.register( new this.stencilTypes[type](this.getShapeProps(options, "ui")));
325                        return s;
326                },
327
328
329                addStencil: function(/* String */type, /* Object */options){
330                        // summary:
331                        //              Use this method to programmatically add Stencils that display on
332                        //              the canvas.
333                        //
334                        //              FIXME: Currently only supports Stencils that have been registered,
335                        //              which is items in the toolbar, and the additional Stencils at the
336                        //              end of onSurfaceReady. This covers all Stencils, but you can't
337                        //              use 'display only' Stencils for Line, Rect, and Ellipse.
338                        // type: String
339                        //              The final name of the tool, lower case: 'image', 'line', 'textBlock'
340                        // options: Object
341                        //              The parameters used to draw the object. See stencil._Base and each
342                        //              tool for specific parameters of teh data or points objects.
343
344                        if(!this.ready){
345                                var c = dojo.connect(this, "onSurfaceReady", this, function(){
346                                        dojo.disconnect(c);
347                                        this.addStencil(type, options);
348                                });
349                                return false;
350                        }
351                        if(options && !options.data && !options.points){
352                                options = {data:options}
353                        }
354                        var s = this.stencils.register( new this.stencilTypes[type](this.getShapeProps(options)));
355                        // need this or not?
356                        //s.connect(s, "destroy", this, "onDeleteStencil");
357                        this.currentStencil && this.currentStencil.moveToFront();
358                        return s;
359                },
360
361                removeStencil: function(/* Object */stencil){
362                        // summary:
363                        //              Use this method to programmatically remove Stencils from the canvas.
364                        // stencil: Object
365                        //              The Stencil to be removed
366
367                        this.stencils.unregister(stencil);
368                        stencil.destroy();
369                },
370
371                removeAll: function(){
372                        // summary:
373                        //              Deletes all Stencils on the canvas.
374                        this.stencils.removeAll();
375                },
376
377                selectAll: function(){
378                        // summary:
379                        //              Selects all stencils
380                        this.stencils.selectAll();
381                },
382
383                toSelected: function(/*String*/func /*any...*/){
384                        // summary:
385                        //              Call a function within all selected Stencils
386                        //              like attr()
387                        // example:
388                        //              |       myDrawing.toSelected('attr', {x:10})
389
390                        this.stencils.toSelected.apply(this.stencils, arguments);
391                },
392
393                exporter: function(){
394                        // summary:
395                        //              Collects all Stencil data and returns an
396                        //              Array of objects.
397                        console.log("this.stencils", this.stencils);
398                        return this.stencils.exporter();  //Array
399                },
400
401                importer: function(/* Array */objects){
402                        // summary:
403                        //              Handles an Array of stencil data and imports the objects
404                        //              to the drawing.
405                        dojo.forEach(objects, function(m){
406                                this.addStencil(m.type, m);
407                        }, this);
408                },
409
410                changeDefaults: function(/*Object*/newStyle,/*Boolean*/value){
411                        // summary:
412                        //              Change the defaults so that all Stencils from this
413                        //              point on will use the newly changed style.
414                        // newStyle: Object
415                        //              An object that represents one of the objects in
416                        //              drawing.style that will be mixed in. Not all
417                        //              properties are necessary. Only one object may
418                        //              be changed at a time. The object boolean parameter
419                        //              is not required and if not set objects will automatically
420                        //              be changed.
421                        //              Changing non-objects like angleSnap requires value
422                        //              to be true.
423                        // example:
424                        //              |       myDrawing.changeDefaults({
425                        //              |               norm:{
426                        //              |                       fill:"#0000ff",
427                        //              |                       width:5,
428                        //              |                       color:"#ffff00"
429                        //              |               }
430                        //              |       });
431
432                        //console.log("----->>> changeDefault: ",newStyle, " value?: ",value);
433                        if(value!=undefined && value){
434                                for(var nm in newStyle){
435                                        this.defaults[nm] = newStyle[nm];
436                                }
437                        }else{
438                                for(var nm in newStyle){
439                                        for(var n in newStyle[nm]){
440                                                //console.log("  copy", nm, n, " to: ", newStyle[nm][n]);
441                                                this.defaults[nm][n] = newStyle[nm][n];
442                                        }
443                                }
444                        }
445
446                        if(this.currentStencil!=undefined && (!this.currentStencil.created || this.defaults.clickMode)){
447                                this.unSetTool();
448                                this.setTool(this.currentType);
449                        }
450                },
451
452                onRenderStencil: function(/* Object */stencil){
453                        // summary:
454                        //              Event that fires when a stencil is drawn. Does not fire from
455                        //              'addStencil'.
456
457                        //console.info("--------------------------------------dojox.drawing.onRenderStencil:", stencil.id);
458
459                        this.stencils.register(stencil);
460                        this.unSetTool();
461                        if(!this.defaults.clickMode){
462                                this.setTool(this.currentType);
463                        }else{
464                                this.defaults.clickable = true;
465                        }
466                },
467
468                onDeleteStencil: function(/* Object */stencil){
469                        // summary:
470                        //              Event fired from a stencil that has destroyed itself
471                        //              will also be called when it is removed by "removeStencil"
472                        //              or stencils.onDelete.
473
474                        this.stencils.unregister(stencil);
475                },
476
477                registerTool: function(/* String */type){
478                        // summary:
479                        //               Registers a tool that can be accessed. Internal.
480                        if(this.tools[type]){ return; }
481                        var constr = dojo.getObject(type);
482                        //console.log("constr:", type)
483                        this.tools[type] = constr;
484                        var abbr = utilCommon.abbr(type);
485                        this.stencilTypes[abbr] = constr;
486                        this.stencilTypeMap[abbr] = type;
487                },
488
489                getConstructor: function(/*String*/abbr){
490                        // summary:
491                        //              Returns a Stencil constructor base on
492                        //              abbreviation
493                        return this.stencilTypes[abbr];
494                },
495
496                setTool: function(/* String */type){
497                        // summary:
498                        //              Sets up a new class to be used to draw. Called from Toolbar,
499                        //              and this class... after a tool is used a new one of the same
500                        //              type is initialized. Could be called externally.
501
502                        if(this.mode=="ui"){ return; }
503                        if(!this.canvas || !this.canvas.surface){
504                                var c = dojo.connect(this, "onSurfaceReady", this, function(){
505                                        dojo.disconnect(c);
506                                        this.setTool(type);
507                                });
508                                return;
509                        }
510                        if(this.currentStencil){
511                                this.unSetTool();
512                        }
513
514                        this.currentType = this.tools[type] ? type : this.stencilTypeMap[type];
515                        //console.log("new tool arg:", type, "curr:", this.currentType, "mode:", this.mode, "tools:", this.tools)
516
517                        try{
518                                this.currentStencil = new this.tools[this.currentType]({container:this.canvas.surface.createGroup(), util:utilCommon, mouse:this.mouse, keys:keys});
519                                console.log("new tool is:", this.currentStencil.id, this.currentStencil);
520                                if(this.defaults.clickMode){ this.defaults.clickable = false; }
521                                this.currentStencil.connect(this.currentStencil, "onRender", this, "onRenderStencil");
522                                this.currentStencil.connect(this.currentStencil, "destroy", this, "onDeleteStencil");
523                        }catch(e){
524                                console.error("dojox.drawing.setTool Error:", e);
525                                console.error(this.currentType + " is not a constructor: ", this.tools[this.currentType]);
526                                //console.trace();
527                        }
528                },
529
530                set: function(name, value){
531                        // summary:
532                        //              Drawing registers as a widget and needs to support
533                        //              widget's api.
534                        console.info("Attempting to set ",name," to: ",value,". Set currently not fully supported in Drawing");
535                },
536
537                get: function(name){
538                        return;
539                },
540
541                unSetTool: function(){
542                        // summary:
543                        //              Destroys current tool
544                        if(!this.currentStencil.created){
545                                this.currentStencil.destroy();
546                        }
547
548                }
549        });
550
551});
Note: See TracBrowser for help on using the repository browser.