Drawing.js 19 KB

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