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