diff options
Diffstat (limited to 'js/dojo/dojox/drawing')
58 files changed, 11802 insertions, 0 deletions
diff --git a/js/dojo/dojox/drawing/Drawing.js b/js/dojo/dojox/drawing/Drawing.js new file mode 100644 index 0000000..2dcb6b0 --- /dev/null +++ b/js/dojo/dojox/drawing/Drawing.js @@ -0,0 +1,566 @@ +//>>built +// wrapped by build app +define("dojox/drawing/Drawing", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.Drawing"); + +(function(){ + + var _plugsInitialized = false; + + 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 dijit.byId('myDrawing') + // + // 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 dojoType="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"); + if(def){ + dojox.drawing.defaults = dojo.getObject(def); + } + this.defaults = dojox.drawing.defaults; + + this.id = node.id; + dojox.drawing.register(this, "drawing"); + this.mode = (props.mode || dojo.attr(node, "mode") || "").toLowerCase(); + var box = dojo.contentBox(node); + this.width = box.w; + this.height = box.h; + this.util = dojox.drawing.util.common; + this.util.register(this); // So Toolbar can find this Drawing DEPRECATED + this.keys = dojox.drawing.manager.keys; + this.mouse = new dojox.drawing.manager.Mouse({util:this.util, keys:this.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 = dojox.drawing.getRegistered("stencil"); + for(var nm in stencils){ + this.registerTool(stencils[nm].name); + } + var tools = dojox.drawing.getRegistered("tool"); + for(nm in tools){ + this.registerTool(tools[nm].name); + } + var plugs = dojox.drawing.getRegistered("plugin"); + for(nm in plugs){ + this.registerTool(plugs[nm].name); + } + this._createCanvas(); + + }, + + _createCanvas: function(){ + console.info("drawing create canvas..."); + this.canvas = new dojox.drawing.manager.Canvas({ + srcRefNode:this.domNode, + util:this.util, + mouse:this.mouse, + 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:this.util, + keys:this.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 toobar 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:this.util, + keys:this.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 = []; + _plugsInitialized = true; + // 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 dojox.drawing.manager.Undo({keys:this.keys}); + this.anchors = new dojox.drawing.manager.Anchors({drawing:this, mouse:this.mouse, undo:this.undo, util:this.util}); + if(this.mode == "ui"){ + this.uiStencils = new dojox.drawing.manager.StencilUI({canvas:this.canvas, surface:this.canvas.surface, mouse:this.mouse, keys:this.keys}); + }else{ + 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}); + this.uiStencils = new dojox.drawing.manager.StencilUI({canvas:this.canvas, surface:this.canvas.surface, mouse:this.mouse, keys:this.keys}); + } + if(dojox.gfx.renderer=="silverlight"){ + try{ + new dojox.drawing.plugins.drawing.Silverlight({util:this.util, 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. + // arguments: + // 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. + // arguments: + // 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.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. + // arguments: + // 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 /*[args, ...]*/){ + // 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. + // arguments: + // 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 = this.util.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:this.util, mouse:this.mouse, keys:this.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"); + }, + + unSetTool: function(){ + // summary: + // Destroys current tool + if(!this.currentStencil.created){ + this.currentStencil.destroy(); + } + + } + }); + +})(); +}); diff --git a/js/dojo/dojox/drawing/README b/js/dojo/dojox/drawing/README new file mode 100644 index 0000000..e14f3a2 --- /dev/null +++ b/js/dojo/dojox/drawing/README @@ -0,0 +1,36 @@ +------------------------------------------------------------------------------- +Drawing +------------------------------------------------------------------------------- +Version 0.1 +Release date: August 22, 2009 +------------------------------------------------------------------------------- +Project state: alpha + The code is very well tested with relatively few bugs. But it is also in a + state where APIs and methods can still change. +------------------------------------------------------------------------------- +Project author + Mike Wilcox anm8tr AT yahoo.com +------------------------------------------------------------------------------- +Project description + +Drawing is a project that sits on top of DojoX GFX and uses SVG and VML vector +graphics to draw and display. +------------------------------------------------------------------------------- +Dependencies: + +dojo.gfx +------------------------------------------------------------------------------- +Documentation + +http://dojotoolkit.org/reference-guide/dojox/drawing.html +------------------------------------------------------------------------------- +Installation instructions + +Grab the following from the Dojo SVN Repository: +http://svn.dojotoolkit.org/dojo/dojox/trunk/drawing.js +http://svn.dojotoolkit.org/dojo/dojox/trunk/drawing/* + +Install into the following directory structure: +/dojox/drawing/ + +...which should be at the same level as your Dojo checkout. diff --git a/js/dojo/dojox/drawing/_base.js b/js/dojo/dojox/drawing/_base.js new file mode 100644 index 0000000..7a21efd --- /dev/null +++ b/js/dojo/dojox/drawing/_base.js @@ -0,0 +1,40 @@ +//>>built +// wrapped by build app +define("dojox/drawing/_base", ["dijit","dojo","dojox","dojo/require!dojox/drawing/manager/_registry,dojox/gfx,dojox/drawing/Drawing,dojox/drawing/util/oo,dojox/drawing/util/common,dojox/drawing/util/typeset,dojox/drawing/defaults,dojox/drawing/manager/Canvas,dojox/drawing/manager/Undo,dojox/drawing/manager/keys,dojox/drawing/manager/Mouse,dojox/drawing/manager/Stencil,dojox/drawing/manager/StencilUI,dojox/drawing/manager/Anchors,dojox/drawing/stencil/_Base,dojox/drawing/stencil/Line,dojox/drawing/stencil/Rect,dojox/drawing/stencil/Ellipse,dojox/drawing/stencil/Path,dojox/drawing/stencil/Text,dojox/drawing/stencil/Image,dojox/drawing/annotations/Label,dojox/drawing/annotations/Angle,dojox/drawing/annotations/Arrow,dojox/drawing/annotations/BoxShadow"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing._base"); +dojo.experimental("dojox.drawing"); + +dojo.require("dojox.drawing.manager._registry"); +dojo.require("dojox.gfx"); +dojo.require("dojox.drawing.Drawing"); +dojo.require("dojox.drawing.util.oo"); +dojo.require("dojox.drawing.util.common"); +dojo.require("dojox.drawing.util.typeset"); +dojo.require("dojox.drawing.defaults"); +dojo.require("dojox.drawing.manager.Canvas"); + +// interactive managers +dojo.require("dojox.drawing.manager.Undo"); +dojo.require("dojox.drawing.manager.keys"); +dojo.require("dojox.drawing.manager.Mouse"); +dojo.require("dojox.drawing.manager.Stencil"); +dojo.require("dojox.drawing.manager.StencilUI"); // plugin? or as a require? good here? in toolbar? +dojo.require("dojox.drawing.manager.Anchors"); + +// standard stencils +dojo.require("dojox.drawing.stencil._Base"); +dojo.require("dojox.drawing.stencil.Line"); +dojo.require("dojox.drawing.stencil.Rect"); +dojo.require("dojox.drawing.stencil.Ellipse"); +dojo.require("dojox.drawing.stencil.Path"); +dojo.require("dojox.drawing.stencil.Text"); +dojo.require("dojox.drawing.stencil.Image"); + +// annotations are built within stencil/_Base.js +// would like to optionally include them, but for +// now it's mandatory. +dojo.require("dojox.drawing.annotations.Label"); +dojo.require("dojox.drawing.annotations.Angle"); +dojo.require("dojox.drawing.annotations.Arrow"); +dojo.require("dojox.drawing.annotations.BoxShadow"); +}); diff --git a/js/dojo/dojox/drawing/annotations/Angle.js b/js/dojo/dojox/drawing/annotations/Angle.js new file mode 100644 index 0000000..6f09f2a --- /dev/null +++ b/js/dojo/dojox/drawing/annotations/Angle.js @@ -0,0 +1,108 @@ +//>>built +// wrapped by build app +define("dojox/drawing/annotations/Angle", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.annotations.Angle"); + +dojox.drawing.annotations.Angle = dojox.drawing.util.oo.declare( + // summary: + // When initiated, an HTML box will hover near the Stencil, + // displaying it's angle while drawn or modified. Currently + // only works with Vector, Line, Arrow, and Axes. + // description: + // Annotation is positioned with dojox.drawing.util.positioning.angle + // That method should be overwritten for custom placement. + // Called internally. To initiaize: + // TODO: currently always on + // + function(/*Object*/options){ + // arguments: + // options: Object + // One key value: the stencil that called this. + // + this.stencil = options.stencil; + this.util = options.stencil.util; + this.mouse = options.stencil.mouse; + + this.stencil.connectMult([ + ["onDrag", this, "showAngle"], + ["onUp", this, "hideAngle"], + ["onTransformBegin", this, "showAngle"], + ["onTransform", this, "showAngle"], + ["onTransformEnd", this, "hideAngle"] + ]); + }, + { + type:"dojox.drawing.tools.custom", + angle:0, + + showAngle: function(){ + // summary: + // Called to display angle + // + if(!this.stencil.selected && this.stencil.created){ return; } + if(this.stencil.getRadius() < this.stencil.minimumSize){ + this.hideAngle(); + return; + } + var node = this.getAngleNode(); + var d = this.stencil.pointsToData(); + var pt = dojox.drawing.util.positioning.angle({x:d.x1,y:d.y1},{x:d.x2,y:d.y2}); + var sc = this.mouse.scrollOffset(); + var mx = this.stencil.getTransform(); + var dx = mx.dx / this.mouse.zoom; + var dy = mx.dy / this.mouse.zoom; + pt.x /= this.mouse.zoom; + pt.y /= this.mouse.zoom; + + // adding _offX & _offY since this is HTML + // and we are from the page corner, not + // the canvas corner + var x = this.stencil._offX + pt.x - sc.left + dx; + var y = this.stencil._offY + pt.y - sc.top + dy; + dojo.style(node, { + left: x + "px", + top: y + "px", + align:pt.align + }); + + var angle=this.stencil.getAngle(); + if(this.stencil.style.zAxis && this.stencil.shortType=="vector"){ + node.innerHTML = this.stencil.data.cosphi > 0 ? "out of" : "into"; + }else if(this.stencil.shortType=="line"){ + node.innerHTML = this.stencil.style.zAxis?"out of":Math.ceil(angle%180); + }else{ + node.innerHTML = Math.ceil(angle); + } + }, + + getAngleNode: function(){ + // summary: + // Gets or creates HTMLNode used for display + if(!this._angleNode){ + this._angleNode = dojo.create("span", null, dojo.body()); + dojo.addClass(this._angleNode, "textAnnotation"); + dojo.style(this._angleNode, "opacity", 1); + } + return this._angleNode; //HTMLNode + }, + + hideAngle: function(){ + // summary: + // Turns display off. + // + if(this._angleNode && dojo.style(this._angleNode, "opacity")>0.9){ + + dojo.fadeOut({node:this._angleNode, + duration:500, + onEnd: function(node){ + dojo.destroy(node); + } + }).play(); + this._angleNode = null; + } + + } + } + +); +}); diff --git a/js/dojo/dojox/drawing/annotations/Arrow.js b/js/dojo/dojox/drawing/annotations/Arrow.js new file mode 100644 index 0000000..d069aa0 --- /dev/null +++ b/js/dojo/dojox/drawing/annotations/Arrow.js @@ -0,0 +1,75 @@ +//>>built +// wrapped by build app +define("dojox/drawing/annotations/Arrow", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Path"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.annotations.Arrow"); +dojo.require("dojox.drawing.stencil.Path"); + +dojox.drawing.annotations.Arrow = dojox.drawing.util.oo.declare( + // summary: + // An annotation called internally to put an arrowhead + // on ether end of a Line. Initiated in Arrow (and Vector) + // with the optional params: arrowStart and arrowEnd. Both + // default true for Axes. + // + dojox.drawing.stencil.Path, + function(/* dojox.__stencilArgs */options){ + // arguments: See stencil._Base + this.stencil.connectMult([ + [this.stencil, "select", this, "select"], + [this.stencil, "deselect", this, "deselect"], + [this.stencil, "render", this, "render"], + [this.stencil, "onDelete", this, "destroy"] + ]); + + this.connect("onBeforeRender", this, function(){ + var o = this.stencil.points[this.idx1]; + var c = this.stencil.points[this.idx2]; + if(this.stencil.getRadius() >= this.minimumSize){ + this.points = this.arrowHead(c.x, c.y, o.x, o.y, this.style); + }else{ + this.points = []; + } + }); + + }, + { + idx1:0, + idx2:1, + + subShape:true, + minimumSize:30, + //annotation:true, NOT! + + arrowHead: function(x1, y1, x2, y2, style){ + // summary: + // Creates data used to draw arrow head. + // + var obj = { + start:{ + x:x1, + y:y1 + }, + x:x2, + y:y2 + } + var angle = this.util.angle(obj); + + var lineLength = this.util.length(obj); + var al = style.arrows.length; + var aw = style.arrows.width/2; + if(lineLength<al){ + al = lineLength/2; + } + var p1 = this.util.pointOnCircle(x2, y2, -al, angle-aw); + var p2 = this.util.pointOnCircle(x2, y2, -al, angle+aw); + + return [ + {x:x2, y:y2}, + p1, + p2 + ]; + } + + } +); +}); diff --git a/js/dojo/dojox/drawing/annotations/BoxShadow.js b/js/dojo/dojox/drawing/annotations/BoxShadow.js new file mode 100644 index 0000000..be39725 --- /dev/null +++ b/js/dojo/dojox/drawing/annotations/BoxShadow.js @@ -0,0 +1,311 @@ +//>>built +// wrapped by build app +define("dojox/drawing/annotations/BoxShadow", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.annotations.BoxShadow"); + +dojox.drawing.annotations.BoxShadow = dojox.drawing.util.oo.declare( + // summary: + // Creates a box shadow under solid objects. Can change the + // shadow direction, color, size, and intensity. Can center + // the shadow and make it a Glow. + // description: + // This is a psuedo shadow, created by duplicating the + // original stencil and increasing the line weight while + // reducing the opacity. Therefore it will not work with + // text. Also won't look very good if the Stencil has no + // fill or is transparent. Can't do knockouts or inner + // shadows. Currently can't do paths - while doable, it + // will most likely choke IE into certain death. + // + function(/*Object*/options){ + this.stencil = options.stencil; + this.util = options.stencil.util; + this.mouse = options.stencil.mouse; + this.style = options.stencil.style; + + var shadowDefaults = { + // summary: + // When passing a shadow object into a stencil, that shadow + // object will be mixed in with these defaults. + // + // size: Number, mult: Number + // These two props work together. Both affect the size and quality + // of the shadow. size affects the actual size and mult affects the + // lineWidths that overlap to make the shadow. Generally you want a + // bigger 'size' than 'mult'. The defaults are good for a shadow, but + // you will want to increase them when making a glow. + // TODO: Make this more clear or use other properties. + size:6, + mult:4, + // alpha: Float + // Affects the alpha of the shadow. Because this is multiple shapes + // overlapped, you want much less than you may think. .1 is pretty + // dark and . is black. Higher numbers also give a sharper edge. + alpha:.05, + // place: String + // Tells the position of the shadow: + // B: bottom + // T: top + // L: left + // R: right + // C: center, or a glow + // Can be used in combinations such as BR, BL, L, T, etc. 'C' should + // be used by itself. + place:"BR", + // color: String + // The color of the shadow or glow. + color:"#646464" + } + + delete options.stencil; + this.options = dojo.mixin(shadowDefaults, options); + this.options.color = new dojo.Color(this.options.color) + this.options.color.a = this.options.alpha; + switch(this.stencil.shortType){ + case "image": + case "rect": + this.method = "createForRect"; break; + case "ellipse": + this.method = "createForEllipse"; break; + case "line": + this.method = "createForLine"; break; + case "path": + this.method = "createForPath"; break; + // path is a bit of a hassle. Plus I think in IE it would be + //slower than the political process. Maybe TODO. + case "vector": + this.method = "createForZArrow"; break; + default: + console.warn("A shadow cannot be made for Stencil type ", this.stencil.type); + } + + if(this.method){ + this.render(); + this.stencil.connectMult([ + [this.stencil, "onTransform", this, "onTransform"], + this.method=="createForZArrow"?[this.stencil, "render", this, "render"]:[this.stencil, "render", this, "onRender"], + [this.stencil, "onDelete", this, "destroy"] + ]); + } + }, + { + showing:true, + render: function(){ + if(this.container){ + this.container.removeShape(); + } + this.container = this.stencil.container.createGroup(); + this.container.moveToBack(); + + var o = this.options, + size = o.size, + mult = o.mult, + d = this.method == "createForPath" + ? this.stencil.points + : this.stencil.data, + r = d.r || 1, + p = o.place, + c = o.color; + + this[this.method](o, size, mult, d, r, p, c); + }, + + hide: function(){ + if(this.showing){ + this.showing = false; + this.container.removeShape(); + } + }, + + show: function(){ + if(!this.showing){ + this.showing = true; + this.stencil.container.add(this.container); + } + }, + + createForPath: function(o, size, mult, pts, r, p, c){ + var sh = size * mult / 4, + shy = /B/.test(p) ? sh : /T/.test(p) ? sh*-1 : 0, + shx = /R/.test(p) ? sh : /L/.test(p) ? sh*-1 : 0; + + var closePath = true; + + for(var i=1;i<=size;i++){ + var lineWidth = i * mult; + //var rect = this.container.createLine({x1:d.x1+shx, y1:d.y1+shy, x2:d.x2+shx, y2:d.y2+shy}) + // .setStroke({width:lineWidth, color:c, cap:"round"}) + + if(dojox.gfx.renderer=="svg"){ + var strAr = []; + dojo.forEach(pts, function(o, i){ + if(i==0){ + strAr.push("M " + (o.x+shx) +" "+ (o.y+shy)); + }else{ + var cmd = o.t || "L "; + strAr.push(cmd + (o.x+shx) +" "+ (o.y+shy)); // Z + undefined works here + } + }, this); + if(closePath){ + strAr.push("Z"); + } + this.container.createPath(strAr.join(", ")).setStroke({width:lineWidth, color:c, cap:"round"}) + + }else{ + // Leaving this code for VML. It seems slightly faster but times vary. + var pth = this.container.createPath({}).setStroke({width:lineWidth, color:c, cap:"round"}) + + dojo.forEach(this.points, function(o, i){ + if(i==0 || o.t=="M"){ + pth.moveTo(o.x+shx, o.y+shy); + }else if(o.t=="Z"){ + closePath && pth.closePath(); + }else{ + pth.lineTo(o.x+shx, o.y+shy); + } + }, this); + + closePath && pth.closePath(); + } + + + } + }, + + createForLine: function(o, size, mult, d, r, p, c){ + + var sh = size * mult / 4, + shy = /B/.test(p) ? sh : /T/.test(p) ? sh*-1 : 0, + shx = /R/.test(p) ? sh : /L/.test(p) ? sh*-1 : 0; + for(var i=1;i<=size;i++){ + var lineWidth = i * mult; + this.container.createLine({x1:d.x1+shx, y1:d.y1+shy, x2:d.x2+shx, y2:d.y2+shy}) + .setStroke({width:lineWidth, color:c, cap:"round"}) + } + }, + createForEllipse: function(o, size, mult, d, r, p, c){ + + var sh = size * mult / 8, + shy = /B/.test(p) ? sh : /T/.test(p) ? sh*-1 : 0, + shx = /R/.test(p) ? sh*.8 : /L/.test(p) ? sh*-.8 : 0; + + for(var i=1;i<=size;i++){ + var lineWidth = i * mult; + this.container.createEllipse({cx:d.cx+shx, cy:d.cy+shy, rx:d.rx-sh, ry:d.ry-sh, r:r}) + .setStroke({width:lineWidth, color:c}) + } + }, + + createForRect: function(o, size, mult, d, r, p, c){ + + var sh = size * mult / 2, + shy = /B/.test(p) ? sh : /T/.test(p) ? 0 : sh /2, + shx = /R/.test(p) ? sh : /L/.test(p) ? 0 : sh /2; + + for(var i=1;i<=size;i++){ + var lineWidth = i * mult; + this.container.createRect({x:d.x+shx, y:d.y+shy, width:d.width-sh, height:d.height-sh, r:r}) + .setStroke({width:lineWidth, color:c}) + } + }, + + arrowPoints: function(){ + // summary: + // Creates data used to draw arrow head. + // + var d = this.stencil.data; + var radius = this.stencil.getRadius(); + var angle = this.style.zAngle + 30; + + var pt = this.util.pointOnCircle(d.x1, d.y1, radius*.75, angle); + + var obj = { + start:{ + x:d.x1, + y:d.y1 + }, + x:pt.x, + y:pt.y + } + var angle = this.util.angle(obj); + var lineLength = this.util.length(obj); + var al = this.style.arrows.length; + var aw = this.style.arrows.width/3; + if(lineLength<al){ + al = lineLength/2; + } + + var p1 = this.util.pointOnCircle(obj.x, obj.y, -al, angle-aw); + var p2 = this.util.pointOnCircle(obj.x, obj.y, -al, angle+aw); + return [ + {x:obj.x, y:obj.y}, + p1, + p2 + ]; + }, + + createForZArrow: function(o, size, mult, pts, r, p, c){ + if(this.stencil.data.cosphi<1 || !this.stencil.points[0]){ return; } + var sh = size * mult / 4, + shy = /B/.test(p) ? sh : /T/.test(p) ? sh*-1 : 0, + shx = /R/.test(p) ? sh : /L/.test(p) ? sh*-1 : 0; + var closePath = true; + for(var i=1;i<=size;i++){ + var lineWidth = i * mult; + pts = this.arrowPoints(); + if(!pts){ return; } + if(dojox.gfx.renderer=="svg"){ + + var strAr = []; + dojo.forEach(pts, function(o, i){ + if(i==0){ + strAr.push("M " + (o.x+shx) +" "+ (o.y+shy)); + }else{ + var cmd = o.t || "L "; + strAr.push(cmd + (o.x+shx) +" "+ (o.y+shy)); // Z + undefined works here + } + }, this); + if(closePath){ + strAr.push("Z"); + } + + this.container.createPath(strAr.join(", ")).setStroke({width:lineWidth, color:c, cap:"round"}).setFill(c); + + }else{ + // Leaving this code for VML. It seems slightly faster but times vary. + var pth = this.container.createPath({}).setStroke({width:lineWidth, color:c, cap:"round"}) + + dojo.forEach(pts, function(o, i){ + if(i==0 || o.t=="M"){ + pth.moveTo(o.x+shx, o.y+shy); + }else if(o.t=="Z"){ + closePath && pth.closePath(); + }else{ + pth.lineTo(o.x+shx, o.y+shy); + } + }, this); + + closePath && pth.closePath(); + } + var sp = this.stencil.points; + this.container.createLine({x1:sp[0].x, y1:sp[0].y, x2:pts[0].x, y2:pts[0].y}) + .setStroke({width:lineWidth, color:c, cap:"round"}); + + } + }, + + + onTransform: function(){ + this.render(); + }, + onRender: function(){ + this.container.moveToBack(); + }, + destroy: function(){ + if(this.container){ + this.container.removeShape(); + } + } + } +); +}); diff --git a/js/dojo/dojox/drawing/annotations/Label.js b/js/dojo/dojox/drawing/annotations/Label.js new file mode 100644 index 0000000..22b9d32 --- /dev/null +++ b/js/dojo/dojox/drawing/annotations/Label.js @@ -0,0 +1,116 @@ +//>>built +// wrapped by build app +define("dojox/drawing/annotations/Label", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Text"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.annotations.Label"); +dojo.require("dojox.drawing.stencil.Text"); + +dojox.drawing.annotations.Label = dojox.drawing.util.oo.declare( + // summary: + // An annotation called internally to label an Stencil. + // description: + // Annotation is positioned with dojox.drawing.util.positioning.label + // That method should be overwritten for custom placement. Or, + // add a 'setLabelCustom' method to the Stencil and it will be used. + // + dojox.drawing.stencil.Text, + function(/*Object*/options){ + // arguments: + // options: Object + // One key value: the stencil that called this. + // + this.master = options.stencil; + this.labelPosition = options.labelPosition || "BR"; // TL, TR, BR, BL, or function + if(dojo.isFunction(this.labelPosition)){ + this.setLabel = this.setLabelCustom; + } + this.setLabel(options.text || ""); + this.connect(this.master, "onTransform", this, "setLabel"); + this.connect(this.master, "destroy", this, "destroy"); + + if(this.style.labelSameColor){ + this.connect(this.master, "attr", this, "beforeAttr"); + } + },{ + _align:"start", + drawingType:"label", + + setLabelCustom: function(/* ? String */text){ + // summary: + // Attaches to custom positioning within a Stencil + // + var d = dojo.hitch(this.master, this.labelPosition)(); + this.setData({ + x:d.x, + y:d.y, + width:d.w || this.style.text.minWidth, + height:d.h || this._lineHeight + }); + + // is an event, not text, so keep the old label: + if(text && !text.split){ text = this.getText(); } + + this.render(this.typesetter(text)); + }, + + setLabel: function(/* String */text){ + // summary: + // Sets the text of the label. Not called directly. Should + // be called within Stencil. See stencil._Base + // + // onTransform will pass an object here + var x, y, box = this.master.getBounds(); + + if(/B/.test(this.labelPosition)){ + y = box.y2 - this._lineHeight; + }else{ + y = box.y1; + } + + if(/R/.test(this.labelPosition)){ + x = box.x2; + }else{ + y = box.y1; + this._align = "end"; + } + + if(!this.labelWidth || (text && text.split && text != this.getText())){ + this.setData({ + x:x, + y:y, + height:this._lineHeight, + width:this.style.text.minWidth + }); + + this.labelWidth = this.style.text.minWidth; + this.render(this.typesetter(text)); + + }else{ + + this.setData({ + x:x, + y:y, + height:this.data.height, + width:this.data.width + }); + + this.render(); + } + + }, + beforeAttr: function(key, value){ + if(value!==undefined){ + // make it an object + var k = key; key = {}; key[k] = value; + } + delete key.x; + delete key.y; + delete key.width; + delete key.height; + this.attr(key); + // FIXME: this.created should already be set, shouldn't it? + !this.created && this.render(); + } + } + +); +}); diff --git a/js/dojo/dojox/drawing/defaults.js b/js/dojo/dojox/drawing/defaults.js new file mode 100644 index 0000000..27cb100 --- /dev/null +++ b/js/dojo/dojox/drawing/defaults.js @@ -0,0 +1,293 @@ +//>>built +// wrapped by build app +define("dojox/drawing/defaults", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.defaults"); + +dojox.drawing.defaults = { + // summary: + // Styles and defaults used for Drawing stencils and text. + // description: + // This object contains defaults for objects used in Drawing. + // To change one item's style, use item.attr(); + // To change all these styles, create a copy of this file + // and point to it in the Drawing properties: + // | <div dojoType="dojox.drawing.Drawing" id="drawing" defaults="MyCustom.defaults"></div> + // See: + // Drawing.changeDefaults + // + // Determines whether in draw or edit mode (whether stencils + // are clickable. If clickMode is false, the original + // functionality of silently switching between select modes + // is enabled. If clickMode is true, it allows powerpoint- + // like functionality. Clickable is used by powerpoint to + // distinguish when things can be selected and when they can't + clickMode:true, + clickable:true, + + // current: Object + // current will point to either null or selected + current:null, + // currentHit: Object + // currentHit will point to either hitNorm or hitSelected + currentHit:null, + + // angleSnap: Number + // Line, arrows, vector and axes will all snap to this angle on mouse up + // shown angle also reflects the snap + // currently cannot accept less than 1 degree + angleSnap:1, + + // zAxis: Boolean + // If true, draw current object in z-direction. + // zAxisEnabled: Boolean + // If true, render axes with Z-axis included, allow objects drawn in z-direction. + // If false the z-axis button will not show up. + zAxis: false, + zAxisEnabled: true, + zAngle: 225, + + // renderHitLines: Boolean + // If true, renders a second, larger layer for lines to make + // them more easily clickable. + renderHitLines: true, + // + // renderHitLayer: + // If true, renders a second layer for each Stencil, one + // acting as a 'hit' object for a wider mouse-click area. + // It also doubles as a hilight. If true, overrides + // renderHitLines setting. + renderHitLayer:true, + + // labelSameColor: + // If true, the label text color will be the same as the + // Stencil's line color. + labelSameColor:false, + + // + useSelectedStyle: true, + + norm:{ + // summary: + // Normal style of all shapes + // will get overridden by + // above andes styles + width:1, + color:"#000000", + style:"Solid", + cap:"round", // square, butt, round + fill:"#CCCCCC" + }, + + selected:{ + // summary: + // Selected style of all shapes + // styles not shown will used from + // norm + width:6, + color:"#00FF00" + }, + + highlighted:{ + // summary: + // Highlighted style of all shapes + // NOT CURRENTLY BEING USED + width:6, + color:"#FF00FF", + style:"Solid", + cap:"round", + fill:"#E11EBB" + }, + + disabled:{ + // summary: + // Disabled or "locked" or "fade" style of all shapes + width:1, + color:"#666666", + style:"solid", + cap:"round", + fill:"#cccccc" + }, + + // "hit" refers to the hidden object below the shape + // that is usually larger than the object to give a + // larger 'target' to click upon. These hit objects + // double as highlights. + // + hitNorm:{ + // summary: + // Normal style of a hit area + width:6, + color:{r:0, g:255, b:255, a:0}, + style:"Solid", + cap:"round", + fill:{r:255, g:255, b:255, a:0} + }, + hitSelected:{ + // summary: + // Selected style of a hit area + width:6, + color:"#FF9900", + style:"Solid", + cap:"round", + fill:{r:255, g:255, b:255, a:0} + }, + hitHighlighted:{ + // summary: + // Highlighted style of a hit area + width:6, + color:"#FFFF00", + style:"Solid", + cap:"round", + fill:{r:255, g:255, b:255, a:0} + }, + + + anchors:{ + // summary: + // Style for the anchor resize-points + size:10, + width:2, + color:"#999", + style:"solid", + fill:"#fff", + cap:"square", + minSize:10, + marginZero:5 // not really an anchor prop + }, + arrows:{ + // summary: + // Size of arrows on vectors. + // length is in pixels + // width is actually an angle + // but is close to pixels in size + length:30, + width:16 + }, + text:{ + // summary: + // Style of text + minWidth:100, + deleteEmptyCreate:true, + deleteEmptyModify:true, + pad:3, + size:"18px", + family:"sans-serif", + weight:"normal", + color:"#000000" + }, + textDisabled:{ + // summary: + // Style of disabled text + size:"18px", + family:"sans-serif", + weight:"normal", + color:"#cccccc" + }, + + textMode:{ + // summary: + // These styles apply to the containing + // text box (edit mode), and not the text itself + create:{ + width:2, + style:"dotted", + color:"#666666", + fill:null + }, + edit:{ + width:1, + style:"dashed", + color:"#666", + fill:null + } + + }, + + button:{ + norm:{ + "color": "#cccccc", + "fill": { + type:"linear", + x1:0, x2:0, y1:0, y2:100, + colors:[ + {offset:.5, color:"#ffffff"}, + {offset:1, color:"#e5e5e5"} + ] + } + }, + over:{ + "fill": { + type:"linear", + x1:0, x2:0, y1:0, y2:100, + colors:[{offset:.5, color:"#ffffff"}, {offset:1, color:"#e1eaf5"}] + }, + "color": "#92a0b3" + }, + down:{ + "fill": { + type:"linear", + x1:0, x2:0, y1:0, y2:100, + colors:[{offset:0, color:"#e1eaf5"}, {offset:1, color:"#ffffff"}] + }, + "color": "#92a0b3" + }, + selected:{ + "fill": { + type:"linear", + x1:0, x2:0, y1:0, y2:100, + colors:[{offset:0, color:"#97b4bf"}, {offset:1, color:"#c8dae1"}] + }, + "color": "#92a0b3" + }, + icon:{ + norm:{ + fill:null, + color:"#92a0b3" + }, + selected:{ + fill:"#ffffff", + color:"#92a0b3" + } + } + }, + + copy: function(){ + // summary + // Each shape gets its own copy + // of these styles so that instances + // do not change each other's styles + // + var cpy = function(obj){ + if(typeof(obj)!="object" || obj===null || obj===undefined){ + return obj; + } + var o; + if(obj.push){ + o = []; + for(var i=0; i<obj.length;i++){ + o.push(cpy(obj[i])) + } + return o; + } + o = {}; + for(var nm in obj){ + if(nm!="copy"){ + if(typeof(obj[nm])=="object"){ + o[nm] = cpy(obj[nm]); + }else{ + o[nm] = obj[nm] + } + } + } + return o; + } + var o = cpy(this); + o.current = o.norm; + o.currentHit = o.hitNorm; + o.currentText = o.text; + return o; + } + +}; + +}); diff --git a/js/dojo/dojox/drawing/library/greek.js b/js/dojo/dojox/drawing/library/greek.js new file mode 100644 index 0000000..aefde6c --- /dev/null +++ b/js/dojo/dojox/drawing/library/greek.js @@ -0,0 +1,70 @@ +//>>built +// wrapped by build app +define("dojox/drawing/library/greek", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.library.greek"); + +dojox.drawing.library.greek = { + // summary: + // Greek characters used by typesetter and greekPalette. + // description: + // These are used to convert between the character and + // the written version of greek letters. Any character + // can be included here and it will automatically be added + // to the palette and converted by typesetter + + "alpha": 945, //alpha, U+03B1 ISOgrk3 --> + "beta": 946, //beta, U+03B2 ISOgrk3 --> + "gamma": 947, //gamma, U+03B3 ISOgrk3 --> + "delta": 948, //delta, U+03B4 ISOgrk3 --> + "epsilon": 949, //epsilon, U+03B5 ISOgrk3 --> + "zeta": 950, //zeta, U+03B6 ISOgrk3 --> + "eta": 951, //eta, U+03B7 ISOgrk3 --> + "theta": 952, //theta, U+03B8 ISOgrk3 --> + "iota": 953, //iota, U+03B9 ISOgrk3 --> + "kappa": 954, //kappa, U+03BA ISOgrk3 --> + "lambda": 955, //lambda, U+03BB ISOgrk3 --> + "mu": 956, //mu, U+03BC ISOgrk3 --> + "nu": 957, //nu, U+03BD ISOgrk3 --> + "xi": 958, //xi, U+03BE ISOgrk3 --> + "omicron": 959, //omicron, U+03BF NEW --> + "pi": 960, //pi, U+03C0 ISOgrk3 --> + "rho": 961, //rho, U+03C1 ISOgrk3 --> + "sigmaf": 962, //final sigma, U+03C2 ISOgrk3 --> + "sigma": 963, //sigma, U+03C3 ISOgrk3 --> + "tau": 964, //tau, U+03C4 ISOgrk3 --> + "upsilon": 965, //upsilon, U+03C5 ISOgrk3 --> + "phi": 966, //phi, U+03C6 ISOgrk3 --> + "chi": 967, //chi, U+03C7 ISOgrk3 --> + "psi": 968, //psi, U+03C8 ISOgrk3 --> + "omega": 969, //omega, U+03C9 ISOgrk3 --> + "thetasym": 977, //theta symbol, U+03D1 NEW --> + "upsih": 978, // upsilon with hook symbol, U+03D2 NEW --> + "piv": 982, // greek pi symbol, U+03D6 ISOgrk3 --> + "Alpha": 913, // alpha, U+0391 --> + "Beta": 914, // beta, U+0392 --> + "Gamma": 915, //gamma, U+0393 ISOgrk3 --> + "Delta": 916, //delta, U+0394 ISOgrk3 --> + "Epsilon": 917, //epsilon, U+0395 --> + "Zeta": 918, //zeta, U+0396 --> + "Eta": 919, //eta, U+0397 --> + "Theta": 920, //theta, U+0398 ISOgrk3 --> + "Iota": 921, //iota, U+0399 --> + "Kappa": 922, //kappa, U+039A --> + "Lambda": 923, //lambda, U+039B ISOgrk3 --> + "Mu": 924, //mu, U+039C --> + "Nu": 925, //nu, U+039D --> + "Xi": 926, //xi, U+039E ISOgrk3 --> + "Omicron": 927, //omicron, U+039F --> + "Pi": 928, //pi, U+03A0 ISOgrk3 --> + "Rho": 929, //rho, U+03A1 --> + "Sigma": 931, //sigma, U+03A3 ISOgrk3 --> + "Tau": 932, //tau, U+03A4 --> + "Upsilon": 933, //upsilon, U+03A5 ISOgrk3 --> + "Phi": 934, //phi, U+03A6 ISOgrk3 --> + "Chi": 935, //chi, U+03A7 --> + "Psi": 936, //psi, U+03A8 ISOgrk3 --> + "Omega": 937 //omega, U+03A9 ISOgrk3 --> +}; + + +}); diff --git a/js/dojo/dojox/drawing/library/icons.js b/js/dojo/dojox/drawing/library/icons.js new file mode 100644 index 0000000..3fdad19 --- /dev/null +++ b/js/dojo/dojox/drawing/library/icons.js @@ -0,0 +1,165 @@ +//>>built +// wrapped by build app +define("dojox/drawing/library/icons", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.library.icons"); + +dojox.drawing.library.icons = { + // summary: + // A collection of icon Stencils for use with the buttons in the ui/Toolbar. + // description: + // Each icon name coresponds to a Stencil or a Plugin. One can be inserted + // into a button by adding the property: 'icon', which points to one of + // these Stencil objects. + line:{ + type:"line", + borderWidth:1, + x1:20, + y1:20, + x2:80, + y2:80 + }, + ellipse:{ + type:"ellipse", + borderWidth:1, + cx:50, + cy:50, + rx:35, + ry:20 + }, + rect:{ + type:"rect", + borderWidth:1, + x:10, + y:30, + width:80, + height:40 + }, + triangle:{ + type:"path", + borderWidth:1, + closePath:true, + points:[{x:50, y:10}, {x:10, y:90}, {x:90, y:90}] + }, + path:{ + type:"path", + borderWidth:0, + closePath:true, + points:[ { x:20, y:80 }, { x:26, y:20 }, { x:80, y:32 }, { x:32, y:50 }, { x:56, y:62 } ] + }, + arrow:{ + type:"path", + borderWidth:1, + closePath:false, + points:[ { x:20, y:20 }, { x:80, y:80 }, { t:'M', x:74, y:56 }, { x:80, y:80 }, { x:56, y:74 } ] + }, + textBlock:{ + type:"path", + borderWidth:0, + closePath:true, + points:[ { x:20, y:20 }, { x:80, y:20 }, { x:80, y:38 }, { x:80, y:38 }, { x:74, y:32 }, { x:68, y:26 }, { x:56, y:20 }, { x:56, y:68 }, { x:56, y:74 }, { x:62, y:80 }, { x:68, y:80 }, { x:68, y:80 }, { x:32, y:80 }, { x:32, y:80 }, { x:38, y:80 }, { x:44, y:74 }, { x:44, y:68 }, { x:44, y:20 }, { x:32, y:26 }, { x:26, y:32 }, { x:20, y:38 }, { x:20, y:38 } ] + }, + + equation:{ + type:"path", + borderWidth:2, + closePath:false, + points:[ {x:20, y:60 }, { x:80, y:60 }, {t:'M', x:20, y:40 }, { x:80, y:40 } ] + }, + + axes:{ + type:"path", + borderWidth:1, + closePath:false, + points:[ { x:20, y:32 }, { x:26, y:20 }, { x:32, y:32 }, { t:'M', x:26, y:20 }, { x:26, y:74 }, { x:80, y:74 }, { t:'M', x:68, y:68 }, { x:80, y:74 }, { x:68, y:80 } ] + }, + vector:{ + type:"path", + borderWidth:1, + closePath:false, + points:[ { x:20, y:80 }, { x:80, y:20 }, { t:'M', x:62, y:26 }, { x:80, y:20 }, { x:73, y:40 }, { t:'M', x:56, y:56 }, { x:62, y:68 }, { x:62, y:74 } ] + }, + pan:{ + type:"path", + borderWidth:1, + closePath:true, + points:[ { x:38, y:80 }, { x:26, y:68 }, { x:20, y:50 }, { x:26, y:44 }, { x:26, y:44 }, { x:38, y:56 }, { x:32, y:32 }, { x:32, y:26 }, { x:38, y:26 }, { x:44, y:44 }, { x:44, y:20 }, { x:50, y:20 }, { x:56, y:20 }, { x:56, y:44 }, { x:56, y:26 }, { x:62, y:26 }, { x:68, y:26 }, { x:68, y:50 }, { x:68, y:32 }, { x:74, y:32 }, { x:80, y:38 }, { x:80, y:50 }, { x:74, y:68 }, { x:68, y:80 } ] + }, + + plus:{ + type:"path", + borderWidth:3, + closePath:false, + points:[ { x:50, y:20 }, { x:50, y:80 }, { t:"M", x:20, y:50 }, { x:80, y:50 } ] + }, + zoomIn:{ + type:"path", + borderWidth:3, + closePath:false, + points:[ { x:50, y:20 }, { x:50, y:80 }, { t:"M", x:20, y:50 }, { x:80, y:50 } ] + }, + zoomOut:{ + type:"path", + borderWidth:3, + closePath:false, + points:[ {x:20, y:50 }, { x:80, y:50 } ] + }, + zoom100:{ + type:"text", + text:"100%" + }, + iconize:{ + type:"path", + borderWidth:0, + closePath:true, + points: + +[ +{ x:20, y:38 }, +{ x:32, y:26 }, +{ x:44, y:26 }, +{ x:56, y:38 }, +{ x:56, y:50 }, +{ x:44, y:62 }, +{ x:32, y:62 }, +{ x:20, y:56 }, +{ t:'Z' }, +{ t:'M', x:38, y:44 }, +{ x:68, y:44 }, +{ x:68, y:80 }, +{ x:38, y:80 }, +{ t:'Z' }, +{ t:'M', x:56, y:62 }, +{ x:68, y:20 }, +{ x:80, y:62 } +] + + }, + pencil:{ + type:"path", + borderWidth:0, + closePath:true, + points: + +[ +{ x:20, y:80 }, +{ x:26, y:68 }, +{ x:68, y:20 }, +{ x:80, y:20 }, +{ x:80, y:32 }, +{ x:38, y:74 }, +{ t:'Z' }, +{ t:'M', x:62, y:32 }, +{ x:68, y:26 }, +{ x:74, y:26 }, +{ x:74, y:32 }, +{ x:68, y:38 }, +{ x:68, y:32 }, +{ t:'Z' }, +{ t:'M', x:56, y:38 }, +{ x:62, y:38 }, +{ x:32, y:68 }, +{ x:32, y:68 } +] + } +}; +}); diff --git a/js/dojo/dojox/drawing/manager/Anchors.js b/js/dojo/dojox/drawing/manager/Anchors.js new file mode 100644 index 0000000..100d5bc --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Anchors.js @@ -0,0 +1,469 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Anchors", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Anchors"); + +dojox.drawing.manager.Anchors = dojox.drawing.util.oo.declare( + // summary: + // Creates and manages the anchor points that are attached to + // (usually) the corners of a Stencil. + // description: + // Used internally, but there are some things that should be known: + // Anchors attach to a Stencil's 'points' (See stencil.points) + // To not display an anchor on a certain point, add noAnchor:true + // to the point. + + function(/* dojox.__stencilArgs */options){ + // arguments: See stencil._Base + this.mouse = options.mouse; + this.undo = options.undo; + this.util = options.util; + this.drawing = options.drawing; + this.items = {}; + }, + { + onAddAnchor: function(/*Anchor*/anchor){ + // summary: + // Event fires when anchor is created + }, + + + onReset: function(/*Stencil*/stencil){ + // summary: + // Event fires when an anchor's reset method is called + // + // a desperate hack in order to get the anchor point to reset. + // FIXME: Is this still used? I think its item.deselect();item.select(); + var st = this.util.byId("drawing").stencils; + st.onDeselect(stencil); + st.onSelect(stencil); + }, + + onRenderStencil: function(){ + // summary: + // Event fires when an anchor calls a Stencil's render method + // + for(var nm in this.items){ + dojo.forEach(this.items[nm].anchors, function(a){ + a.shape.moveToFront(); + }); + } + }, + + onTransformPoint: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor drag + // + // If anchors are a "group", it's corresponding anchor + // is set. All anchors then moved to front. + var anchors = this.items[anchor.stencil.id].anchors; + var item = this.items[anchor.stencil.id].item; + var pts = []; + dojo.forEach(anchors, function(a, i){ + + + if(anchor.id == a.id || anchor.stencil.anchorType!="group"){ + // nothing + }else{ + if(anchor.org.y == a.org.y){ + a.setPoint({ + dx: 0, + dy: anchor.shape.getTransform().dy - a.shape.getTransform().dy + }); + }else if(anchor.org.x == a.org.x){ + a.setPoint({ + dx: anchor.shape.getTransform().dx - a.shape.getTransform().dx, + dy: 0 + }); + } + a.shape.moveToFront(); + } + + var mx = a.shape.getTransform(); + pts.push({x:mx.dx + a.org.x, y:mx.dy+ a.org.y}); + + if(a.point.t){ + pts[pts.length-1].t = a.point.t; + } + + }, this); + item.setPoints(pts); + item.onTransform(anchor); + this.onRenderStencil(); + }, + + onAnchorUp: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor mouseup + }, + + onAnchorDown: function(/*Anchor*/anchor){ + // summary: + // Event fired on anchor mousedown + }, + + onAnchorDrag: function(/*Anchor*/anchor){ + // summary: + // Event fired when anchor is moved + }, + + onChangeStyle: function(/*Object*/stencil){ + // summary: + // if the Stencil changes color while were's selected + // this moves the anchors to the back. Fix it. + + for(var nm in this.items){ + dojo.forEach(this.items[nm].anchors, function(a){ + a.shape.moveToFront(); + }); + } + }, + + add: function(/*Stencil*/item){ + // summary: + // Creates anchor points on a Stencil, based on the + // Stencil's points. + // + this.items[item.id] = { + item:item, + anchors:[] + }; + if(item.anchorType=="none"){ return; } + var pts = item.points; + dojo.forEach(pts, function(p, i){ + if(p.noAnchor){ return; } + if(i==0 || i == item.points.length-1){ + console.log("ITEM TYPE:", item.type, item.shortType); + } + var a = new dojox.drawing.manager.Anchor({stencil:item, point:p, pointIdx:i, mouse:this.mouse, util:this.util}); + this.items[item.id]._cons = [ + dojo.connect(a, "onRenderStencil", this, "onRenderStencil"), + dojo.connect(a, "reset", this, "onReset"), + dojo.connect(a, "onAnchorUp", this, "onAnchorUp"), + dojo.connect(a, "onAnchorDown", this, "onAnchorDown"), + dojo.connect(a, "onAnchorDrag", this, "onAnchorDrag"), + dojo.connect(a, "onTransformPoint", this, "onTransformPoint"), + // FIXME: this will fire for each anchor. yech. + dojo.connect(item, "onChangeStyle", this, "onChangeStyle") + ]; + + this.items[item.id].anchors.push(a); + this.onAddAnchor(a); + }, this); + + if(item.shortType=="path"){ + // check if we have a double-point of a closed-curve-path + var f = pts[0], l = pts[pts.length-1], a = this.items[item.id].anchors; + if(f.x ==l.x && f.y==l.y){ + console.warn("LINK ANVHROS", a[0], a[a.length-1]); + a[0].linkedAnchor = a[a.length-1]; + a[a.length-1].linkedAnchor = a[0]; + } + } + + if(item.anchorType=="group"){ + dojo.forEach(this.items[item.id].anchors, function(anchor){ + dojo.forEach(this.items[item.id].anchors, function(a){ + if(anchor.id != a.id){ + if(anchor.org.y == a.org.y){ + anchor.x_anchor = a; + }else if(anchor.org.x == a.org.x){ + anchor.y_anchor = a; + } + } + },this); + },this); + + } + }, + + remove: function(/*Stencil*/item){ + // summary: + // Destroys the anchor points for a Stencil. + // + if(!this.items[item.id]){ + return; + } + dojo.forEach(this.items[item.id].anchors, function(a){ + a.destroy(); + }); + dojo.forEach(this.items[item.id]._cons, dojo.disconnect, dojo); + this.items[item.id].anchors = null; + delete this.items[item.id]; + } + } +); + +dojox.drawing.manager.Anchor = dojox.drawing.util.oo.declare( + // summary: + // An anchor point that is attached to (usually) one of the + // corners of a Stencil. + // Used internally. + function(/* Object */options){ + // summary: + // constructor. + // arguments: + // dojox.__stencilArgs plus some additional + // data, like which point this is (pointIdx) + // + this.defaults = dojox.drawing.defaults.copy(); + this.mouse = options.mouse; + this.point = options.point; + this.pointIdx = options.pointIdx; + this.util = options.util; + this.id = options.id || this.util.uid("anchor"); + this.org = dojo.mixin({}, this.point); + this.stencil = options.stencil; + if(this.stencil.anchorPositionCheck){ + this.anchorPositionCheck = dojo.hitch(this.stencil, this.stencil.anchorPositionCheck); + } + if(this.stencil.anchorConstrain){ + this.anchorConstrain = dojo.hitch(this.stencil, this.stencil.anchorConstrain); + } + this._zCon = dojo.connect(this.mouse, "setZoom", this, "render"); + this.render(); + this.connectMouse(); + }, + { + y_anchor:null, + x_anchor:null, + render: function(){ + // summary: + // Creates the anchor point. Unlike most render methods + // in Drawing, this is only called once. + // + this.shape && this.shape.removeShape(); + var d = this.defaults.anchors, + z = this.mouse.zoom, + b = d.width * z, + s = d.size * z, + p = s/2, + line = { + width:b, + style:d.style, + color:d.color, + cap:d.cap + }; + + + var _r = { + x: this.point.x-p, + y: this.point.y-p, + width: s, + height: s + }; + this.shape = this.stencil.container.createRect(_r) + .setStroke(line) + .setFill(d.fill); + + this.shape.setTransform({dx:0, dy:0}); + this.util.attr(this, "drawingType", "anchor"); + this.util.attr(this, "id", this.id); + }, + onRenderStencil: function(/*Anchor*/anchor){ + // summary: + // Event fires when an anchor calls a Stencil's render method + }, + onTransformPoint: function(/*Anchor*/anchor){ + // summary: + // Event fires when an anchor changes the points of a Stencil + }, + onAnchorDown: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for mousedown on anchor + this.selected = obj.id == this.id; + }, + onAnchorUp: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for mouseup on anchor + this.selected = false; + this.stencil.onTransformEnd(this); + }, + + onAnchorDrag: function(/*Mouse.EventObject*/obj){ + // summary: + // Event fires for on dragging of an anchor + if(this.selected){ + // mx is the original transform from when the anchor + // was created. It does not change + var mx = this.shape.getTransform(); + + var pmx = this.shape.getParent().getParent().getTransform(); + + var marginZero = this.defaults.anchors.marginZero; + + var orgx = pmx.dx + this.org.x, + orgy = pmx.dy + this.org.y, + x = obj.x - orgx, + y = obj.y - orgy, + s = this.defaults.anchors.minSize; + + var conL, conR, conT, conB; + + var chk = this.anchorPositionCheck(x, y, this); + if(chk.x<0){ + console.warn("X<0 Shift"); + while(this.anchorPositionCheck(x, y, this).x<0){ + this.shape.getParent().getParent().applyTransform({dx:2, dy:0}); + } + } + if(chk.y<0){ + console.warn("Y<0 Shift"); + while(this.anchorPositionCheck(x, y, this).y<0){ + this.shape.getParent().getParent().applyTransform({dx:0, dy:2}); + } + } + + if(this.y_anchor){ + // prevent y overlap of opposite anchor + if(this.org.y > this.y_anchor.org.y){ + // bottom anchor + + conT = this.y_anchor.point.y + s - this.org.y; + conB = Infinity; + + if(y < conT){ + // overlapping other anchor + y = conT; + } + + + }else{ + // top anchor + + conT = -orgy + marginZero; + conB = this.y_anchor.point.y - s - this.org.y; + + if(y < conT){ + // less than zero + y = conT; + }else if(y > conB){ + // overlapping other anchor + y = conB; + } + } + }else{ + // Lines - check for zero + conT = -orgy + marginZero; + if(y < conT){ + // less than zero + y = conT; + } + } + + + + + if(this.x_anchor){ + // prevent x overlap of opposite anchor + + if(this.org.x>this.x_anchor.org.x){ + // right anchor + + conL = this.x_anchor.point.x + s - this.org.x; + conR = Infinity; + + if(x < conL){ + // overlapping other anchor + x = conL; + } + + }else{ + // left anchor + + conL = -orgx + marginZero; + conR = this.x_anchor.point.x - s - this.org.x; + + if(x < conL){ + x = conL; + }else if(x > conR){ + // overlapping other anchor + x = conR; + } + } + }else{ + // Lines check for zero + conL = -orgx + marginZero; + if(x < conL){ + x = conL; + } + } + //Constrains anchor point, returns null if not overwritten by stencil + var constrained = this.anchorConstrain(x, y); + if(constrained != null){ + x=constrained.x; + y=constrained.y; + } + + this.shape.setTransform({ + dx:x, + dy:y + }); + if(this.linkedAnchor){ + // first and last points of a closed-curve-path + this.linkedAnchor.shape.setTransform({ + dx:x, + dy:y + }); + } + this.onTransformPoint(this); + } + }, + + anchorConstrain: function(/* Number */x,/* Number */ y){ + // summary: + // To be over written by tool! + // Add an anchorConstrain method to the tool + // and it will automatically overwrite this stub. + // Should return a constrained x & y value. + return null; + }, + + anchorPositionCheck: function(/* Number */x,/* Number */ y, /* Anchor */anchor){ + // summary: + // To be over written by tool! + // Add a anchorPositionCheck method to the tool + // and it will automatically overwrite this stub. + // Should return x and y coords. Success is both + // being greater than zero, fail is if one or both + // are less than zero. + return {x:1, y:1}; + }, + + setPoint: function(mx){ + // summary: + // Internal. Sets the Stencil's point + this.shape.applyTransform(mx); + }, + + connectMouse: function(){ + // summary: + // Internal. Connects anchor to manager.mouse + this._mouseHandle = this.mouse.register(this); + }, + + disconnectMouse: function(){ + // summary: + // Internal. Disconnects anchor to manager.mouse + this.mouse.unregister(this._mouseHandle); + }, + + reset: function(stencil){ + // summary: + // Called (usually) from a Stencil when that Stencil + // needed to make modifications to the position of the + // point. Basically used when teh anchor causes a + // less than zero condition. + }, + + destroy: function(){ + // summary: + // Destroys anchor. + dojo.disconnect(this._zCon); + this.disconnectMouse(); + this.shape.removeShape(); + } + } +); + +}); diff --git a/js/dojo/dojox/drawing/manager/Canvas.js b/js/dojo/dojox/drawing/manager/Canvas.js new file mode 100644 index 0000000..5db1a25 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Canvas.js @@ -0,0 +1,165 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Canvas", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Canvas"); + +(function(){ + + dojox.drawing.manager.Canvas = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx.surface to be used for Drawing. Note that + // The 'surface' that Drawing uses is actually a dojox.gfx.group. + // This allows for more versatility. + // + // Called internally from a dojox.Drawing. + // + // Note: Surface creation is asynchrous. Connect to + // onSurfaceReady in Drawing. + // + function(/*Object*/options){ + dojo.mixin(this, options); + + var dim = dojo.contentBox(this.srcRefNode); + this.height = this.parentHeight = dim.h; + this.width = this.parentWidth = dim.w; + this.domNode = dojo.create("div", {id:"canvasNode"}, this.srcRefNode); + dojo.style(this.domNode, { + width:this.width, + height:"auto" + }); + + dojo.setSelectable(this.domNode, false); + + this.id = this.id || this.util.uid("surface"); + + console.info("create canvas"); + this.gfxSurface = dojox.gfx.createSurface(this.domNode, this.width, this.height); + this.gfxSurface.whenLoaded(this, function(){ + setTimeout(dojo.hitch(this, function(){ + this.surfaceReady = true; + if(dojo.isIE){ + //this.gfxSurface.rawNode.parentNode.id = this.id; + }else if(dojox.gfx.renderer == "silverlight"){ + this.id = this.domNode.firstChild.id + }else{ + //this.gfxSurface.rawNode.id = this.id; + } + + this.underlay = this.gfxSurface.createGroup(); + this.surface = this.gfxSurface.createGroup(); + this.overlay = this.gfxSurface.createGroup(); + this.surface.setTransform({dx:0, dy:0,xx:1,yy:1}); + + this.gfxSurface.getDimensions = dojo.hitch(this.gfxSurface, "getDimensions"); + if(options.callback){ + options.callback(this.domNode); + } + }),500); + }); + this._mouseHandle = this.mouse.register(this); + }, + { + // zoom: [readonly] Number + // The amount the canvas is zoomed + zoom:1, + + useScrollbars: true, + baseClass:"drawingCanvas", + + resize: function(width, height){ + // summary: + // Method used to change size of canvas. Potentially + // called from a container like ContentPane. May be + // called directly. + // + this.parentWidth = width; + this.parentHeight = height; + this.setDimensions(width, height); + }, + + setDimensions: function(width, height, scrollx, scrolly){ + // summary: + // Internal. Changes canvas size and sets scroll position. + // Do not call this, use resize(). + // + // changing the size of the surface and setting scroll + // if items are off screen + var sw = this.getScrollWidth(); //+ 10; + this.width = Math.max(width, this.parentWidth); + this.height = Math.max(height, this.parentHeight); + + if(this.height>this.parentHeight){ + this.width -= sw; + } + if(this.width>this.parentWidth){ + this.height -= sw; + } + + this.mouse.resize(this.width,this.height); + this.gfxSurface.setDimensions(this.width, this.height); + + + this.domNode.parentNode.scrollTop = scrolly || 0; + this.domNode.parentNode.scrollLeft = scrollx || 0; + + + if(this.useScrollbars){ + //console.info("Set Canvas Scroll", (this.height > this.parentHeight), this.height, this.parentHeight) + dojo.style(this.domNode.parentNode, { + overflowY: this.height > this.parentHeight ? "scroll" : "hidden", + overflowX: this.width > this.parentWidth ? "scroll" : "hidden" + }); + }else{ + dojo.style(this.domNode.parentNode, { + overflowY: "hidden", + overflowX: "hidden" + }); + } + }, + + + setZoom: function(zoom){ + // summary: + // Internal. Zooms canvas in and out. + this.zoom = zoom; + this.surface.setTransform({xx:zoom, yy:zoom}); + this.setDimensions(this.width*zoom, this.height*zoom) + }, + + onScroll: function(){ + // summary: + // Event fires on scroll.NOT IMPLEMENTED + }, + + getScrollOffset: function(){ + // summary: + // Get the scroll position of the canvas + return { + top:this.domNode.parentNode.scrollTop, + left:this.domNode.parentNode.scrollLeft + }; // Object + }, + + getScrollWidth: function(){ + // summary: + // Special method used to detect the width (and height) + // of the browser scrollbars. Becomes memoized. + // + var p = dojo.create('div'); + p.innerHTML = '<div style="width:50px;height:50px;overflow:hidden;position:absolute;top:0;left:-1000px;"><div style="height:100px;"></div>'; + var div = p.firstChild; + dojo.body().appendChild(div); + var noscroll = dojo.contentBox(div).h; + dojo.style(div, "overflow", "scroll"); + var scrollWidth = noscroll - dojo.contentBox(div).h; + dojo.destroy(div); + this.getScrollWidth = function(){ + return scrollWidth; + }; + return scrollWidth; // Object + } + } + ); + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/Mouse.js b/js/dojo/dojox/drawing/manager/Mouse.js new file mode 100644 index 0000000..ea8b105 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Mouse.js @@ -0,0 +1,519 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Mouse", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Mouse"); + +dojox.drawing.manager.Mouse = dojox.drawing.util.oo.declare( + // summary: + // Master object (instance) that tracks mouse + // events. A new instance is created for each + // Drawing object. + // description: + // You could connect to any method or event in this + // class, but it is designed to have the object + // 'registered'. All objects with the current event + // will be called directly. + // + // Custom events are used often. In addition to + // standard events onDown, onUp, onDrag, etc, if + // a certain object is clicked upon (or dragged, etc), + // that object's drawingType will create the custom event, + // such as onAnchorDown, or onStencilDown. + // + function(/* Object */options){ + this.util = options.util; + this.keys = options.keys; + this.id = options.id || this.util.uid("mouse"); + this.currentNodeId = ""; + this.registered = {}; + }, + + { + // doublClickSpeed: Number + // Milliseconds between clicks to + // register as for onDoubleClick + doublClickSpeed:400, + + // private properties + + _lastx:0, + _lasty:0, + __reg:0, + _downOnCanvas:false, + +/*===== +CustomEventMethod: function(){ + // summary: + // The custom event method that an Object that has + // registered with manager.Mouse can receive. + // Can contain any or all of the following methods + // and they will be called as mouse events. All events + // will be sent a EventObject event object. + // NOTE: + // Events happen anywhere in the document unless + // otherwise noted. + // + // onMove + // Fires on mousemove when mouse is up + // onDown + // Fires on mousedown *on the canvas* + // onDrag + // Fires on mousemove when mouse is down + // onUp + // Fires on mouseup, anywhere in the document + // onStencilDown + // Fired on mousedown on a Stencil + // onStencilDrag + // Fired when mouse moves and mose is down on a Stencil + // onStencilUp + // Fired on mouseup off of a Stencil + // on[Custom]Up|Down|Move + // Custom events can bet set and fired by setting a + // different drawingType on a Stencil, or by calling + // setEventMode(customEventName) +}, +EventObject: function(){ + // summary: + // The custom event object that is sent to registered objects + // and their respective methods. + // NOTE: Most event objects are the same with the exception + // of the onDown events, which have fewer. + // + // All event properties included onDown: + // + // id: String + // Id of the focused object + // pageX: Number + // The X coordinate of the mouse from the left side of + // the document. + // pageY: Number + // The Y coordinate of the mouse from the top of + // the document. + // x: Number + // The X coordinate of the mouse from the left side + // of the canvas + // y: Number + // The Y coordinate of the mouse from the top + // of the canvas + // + // These event properties are *not* in onDown: + // + // last: Object + // The x and y coordinates of the last mousemove + // relative to the canvas + // move: Object + // The x and y amounts the mouse moved since the last event + // orgX: Number + // The left side of the canvas from the side of the document + // orgY: Number + // The top of the canvas from the top of the document + // scroll: Object + // The 'top' and 'left' scroll amounts of the canvas. + // start: Object + // The x and y coordinates of the mousedown event + // withinCanvas: Boolean + // Whether the event happened within the Canvas or not + +}, +=====*/ + + init: function(/* HTMLNode*/node){ + // summary: + // Internal. Initializes mouse. + // + this.container = node; + this.setCanvas(); + var c; + var _isDown = false; + dojo.connect(this.container, "rightclick", this, function(evt){ + console.warn("RIGHTCLICK") + }); + + dojo.connect(document.body, "mousedown", this, function(evt){ + //evt.preventDefault(); + //dojo.stopEvent(evt); + }); + + dojo.connect(this.container, "mousedown", this, function(evt){ + this.down(evt); + // Right click shouldn't trigger drag + if(evt.button != dojo.mouseButtons.RIGHT){ + _isDown = true; + c = dojo.connect(document, "mousemove", this, "drag"); + } + }); + dojo.connect(document, "mouseup", this, function(evt){ + dojo.disconnect(c); + _isDown = false; + this.up(evt); + }); + dojo.connect(document, "mousemove", this, function(evt){ + if(!_isDown){ + this.move(evt); + } + }); + dojo.connect(this.keys, "onEsc", this, function(evt){ + this._dragged = false; + }); + }, + + setCanvas: function(){ + // summary: + // Internal. Sets canvas position + var pos = dojo.coords(this.container.parentNode); + this.origin = dojo.clone(pos); + }, + + scrollOffset: function(){ + // summary: + // Gets scroll offset of canvas + return { + top:this.container.parentNode.scrollTop, + left:this.container.parentNode.scrollLeft + }; // Object + }, + + resize: function(width,height){ + if(this.origin){ + this.origin.w=width; + this.origin.h=height; + } + }, + + register: function(/* Object*/scope){ + // summary: + // All objects (Stencils) should register here if they + // use mouse events. When registering, the object will + // be called if it has that method. + // argument: + // The object to be called + // Returns: handle + // Keep the handle to be used for disconnection. + // See: CustomEventMethod and EventObject + // + var handle = scope.id || "reg_"+(this.__reg++); + if(!this.registered[handle]){ this.registered[handle] = scope; } + return handle; // String + }, + unregister: function(handle){ + // summary: + // Disconnects object. Mouse events are no longer + // called for it. + if(!this.registered[handle]){ return; } + delete this.registered[handle]; + }, + + _broadcastEvent:function(strEvt, obj){ + // summary: + // Fire events to all registered objects. + // + //console.log("mouse.broadcast:", strEvt, obj) + for(var nm in this.registered){ + if(this.registered[nm][strEvt]) this.registered[nm][strEvt](obj); + } + }, + + onDown: function(obj){ + // summary: + // Create on[xx]Down event and send to broadcaster. + // Could be connected to. + //console.info("onDown:", this.eventName("down")) + this._broadcastEvent(this.eventName("down"), obj); + }, + + onDrag: function(obj){ + // summary: + // Create on[xx]Drag event and send to broadcaster. + // Could be connected to. + // + var nm = this.eventName("drag"); + if(this._selected && nm == "onDrag"){ + nm = "onStencilDrag" + } + this._broadcastEvent(nm, obj); + }, + + onMove: function(obj){ + // summary: + // Create onMove event and send to broadcaster. + // Could be connected to. + // Note: onMove never uses a custom event + // Note: onMove is currently not enabled in the app. + // + this._broadcastEvent("onMove", obj); + }, + + overName: function(obj,evt){ + var nm = obj.id.split("."); + evt = evt.charAt(0).toUpperCase() + evt.substring(1); + if(nm[0] == "dojox" && (dojox.drawing.defaults.clickable || !dojox.drawing.defaults.clickMode)){ + return "onStencil"+evt; + }else{ + return "on"+evt; + } + + }, + + onOver: function(obj){ + // summary: + // + this._broadcastEvent(this.overName(obj,"over"), obj); + }, + + onOut: function(obj){ + // summary: + // + this._broadcastEvent(this.overName(obj,"out"), obj); + }, + + onUp: function(obj){ + // summary: + // Create on[xx]Up event and send to broadcaster. + // Could be connected to. + // + // blocking first click-off (deselect), largely for TextBlock + // TODO: should have param to make this optional? + var nm = this.eventName("up"); + + if(nm == "onStencilUp"){ + this._selected = true; + }else if(this._selected && nm == "onUp"){ ////////////////////////////////////////// + nm = "onStencilUp"; + this._selected = false; + } + + console.info("Up Event:", this.id, nm, "id:", obj.id); + this._broadcastEvent(nm, obj); + + // Silverlight double-click handled in Silverlight class + if(dojox.gfx.renderer == "silverlight"){ return; } + + // Check Double Click + // If a double click is detected, the onDoubleClick event fires, + // but does not replace the normal event. They both fire. + this._clickTime = new Date().getTime(); + if(this._lastClickTime){ + if(this._clickTime-this._lastClickTime<this.doublClickSpeed){ + var dnm = this.eventName("doubleClick"); + console.warn("DOUBLE CLICK", dnm, obj); + this._broadcastEvent(dnm, obj); + }else{ + //console.log(" slow:", this._clickTime-this._lastClickTime) + } + } + this._lastClickTime = this._clickTime; + + }, + + zoom: 1, + setZoom: function(zoom){ + // summary: + // Internal. Sets the mouse zoom percentage to + // that of the canvas + this.zoom = 1/zoom; + }, + + setEventMode: function(mode){ + // summary: + // Sets the mouse mode s that custom events can be called. + // Also can 'disable' events by using a bogus mode: + // | mouse.setEventMode("DISABLED") + // (unless any object subscribes to this event, + // it is effectively disabled) + // + this.mode = mode ? "on" + mode.charAt(0).toUpperCase() + mode.substring(1) : ""; + }, + + eventName: function(name){ + // summary: + // Internal. Determine the event name + // + name = name.charAt(0).toUpperCase() + name.substring(1); + if(this.mode){ + if(this.mode == "onPathEdit"){ + return "on"+name; + } + if(this.mode == "onUI"){ + //return "on"+name; + } + return this.mode + name; + }else{ + //Allow a mode where stencils aren't clickable + if(!dojox.drawing.defaults.clickable && dojox.drawing.defaults.clickMode){return "on"+name;} + var dt = !this.drawingType || this.drawingType=="surface" || this.drawingType=="canvas" ? "" : this.drawingType; + var t = !dt ? "" : dt.charAt(0).toUpperCase() + dt.substring(1); + return "on"+t+name; + } + }, + + up: function(evt){ + // summary: + // Internal. Create onUp event + // + this.onUp(this.create(evt)); + }, + + down: function(evt){ + // summary: + // Internal. Create onDown event + // + this._downOnCanvas = true; + var sc = this.scrollOffset(); + var dim = this._getXY(evt); + this._lastpagex = dim.x; + this._lastpagey = dim.y; + var o = this.origin; + var x = dim.x - o.x + sc.left; + var y = dim.y - o.y + sc.top; + + var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; + x*= this.zoom; + y*= this.zoom; + + o.startx = x; + o.starty = y; + this._lastx = x; + this._lasty = y; + + this.drawingType = this.util.attr(evt, "drawingType") || ""; + var id = this._getId(evt); + //console.log("DOWN:", this.id, id, withinCanvas); + //console.log("this.drawingType:", this.drawingType); + + if(evt.button == dojo.mouseButtons.RIGHT && this.id == "mse"){ + //Allow right click events to bubble for context menus + }else{ + evt.preventDefault(); + dojo.stopEvent(evt); + } + this.onDown({ + mid:this.id, + x:x, + y:y, + pageX:dim.x, + pageY:dim.y, + withinCanvas:withinCanvas, + id:id + }); + + }, + over: function(obj){ + // summary: + // Internal. + // + this.onOver(obj); + }, + out: function(obj){ + // summary: + // Internal. + // + this.onOut(obj); + }, + move: function(evt){ + // summary: + // Internal. + // + var obj = this.create(evt); + if(this.id=="MUI"){ + //console.log("obj.id:", obj.id, "was:", this.currentNodeId) + } + if(obj.id != this.currentNodeId){ + // TODO: I wonder if an ID is good enough + // that would avoid the mixin + var outObj = {}; + for(var nm in obj){ + outObj[nm] = obj[nm]; + } + outObj.id = this.currentNodeId; + this.currentNodeId && this.out(outObj); + obj.id && this.over(obj); + this.currentNodeId = obj.id; + } + this.onMove(obj); + }, + drag: function(evt){ + // summary: + // Internal. Create onDrag event + this.onDrag(this.create(evt, true)); + }, + create: function(evt, squelchErrors){ + // summary: + // Internal. Create EventObject + // + var sc = this.scrollOffset(); + var dim = this._getXY(evt); + + var pagex = dim.x; + var pagey = dim.y; + + var o = this.origin; + var x = dim.x - o.x + sc.left; + var y = dim.y - o.y + sc.top; + + var withinCanvas = x>=0 && y>=0 && x<=o.w && y<=o.h; + x*= this.zoom; + y*= this.zoom; + + var id = withinCanvas ? this._getId(evt, squelchErrors) : ""; + var ret = { + mid:this.id, + x:x, + y:y, + pageX:dim.x, + pageY:dim.y, + page:{ + x:dim.x, + y:dim.y + }, + orgX:o.x, + orgY:o.y, + last:{ + x: this._lastx, + y: this._lasty + }, + start:{ + x: this.origin.startx, //+ sc.left, + y: this.origin.starty //+ sc.top + }, + move:{ + x:pagex - this._lastpagex, + y:pagey - this._lastpagey + }, + scroll:sc, + id:id, + withinCanvas:withinCanvas + }; + + //console.warn("MSE LAST:", x-this._lastx, y-this._lasty) + this._lastx = x; + this._lasty = y; + this._lastpagex = pagex; + this._lastpagey = pagey; + dojo.stopEvent(evt); + return ret; //Object + }, + _getId: function(evt, squelchErrors){ + // summary: + // Internal. Gets ID of focused node. + return this.util.attr(evt, "id", null, squelchErrors); // String + }, + _getXY: function(evt){ + // summary: + // Internal. Gets mouse coords to page. + return {x:evt.pageX, y:evt.pageY}; // Object + }, + + setCursor: function(cursor,/* HTMLNode*/node){ + // summary: + // Sets the cursor for a given node. If no + // node is specified the containing node is used. + if(!node){ + dojo.style(this.container, "cursor", cursor); + }else{ + dojo.style(node, "cursor", cursor); + } + } + } +); + +}); diff --git a/js/dojo/dojox/drawing/manager/Stencil.js b/js/dojo/dojox/drawing/manager/Stencil.js new file mode 100644 index 0000000..17e7a82 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Stencil.js @@ -0,0 +1,645 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Stencil", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Stencil"); + +(function(){ + var surface, surfaceNode; + dojox.drawing.manager.Stencil = dojox.drawing.util.oo.declare( + // summary: + // The main class for tracking Stencils that are cretaed, added, + // selected, or deleted. Also handles selections, multiple + // selections, adding and removing from selections, and dragging + // selections. It's this class that triggers the anchors to + // appear on a Stencil and whther there are anchor on a multiple + // select or not (currently not) + // + function(options){ + // + // TODO: mixin props + // + surface = options.surface; + this.canvas = options.canvas; + + this.defaults = dojox.drawing.defaults.copy(); + this.undo = options.undo; + this.mouse = options.mouse; + this.keys = options.keys; + this.anchors = options.anchors; + this.stencils = {}; + this.selectedStencils = {}; + this._mouseHandle = this.mouse.register(this); + + dojo.connect(this.keys, "onArrow", this, "onArrow"); + dojo.connect(this.keys, "onEsc", this, "deselect"); + dojo.connect(this.keys, "onDelete", this, "onDelete"); + + }, + { + _dragBegun: false, + _wasDragged:false, + _secondClick:false, + _isBusy:false, + + setRecentStencil: function(stencil){ + // summary: + // Keeps track of the most recent stencil interacted + // with, whether created or selected. + this.recent = stencil; + }, + + getRecentStencil: function(){ + // summary: + // Returns the stencil most recently interacted + // with whether it's last created or last selected + return this.recent; + }, + + register: function(/*Object*/stencil){ + // summary: + // Key method for adding Stencils. Stencils + // can be added to the canvas without adding + // them to this, but they won't have selection + // or drag ability. + // + console.log("Selection.register ::::::", stencil.id); + if(stencil.isText && !stencil.editMode && stencil.deleteEmptyCreate && !stencil.getText()){ + // created empty text field + // defaults say to delete + console.warn("EMPTY CREATE DELETE", stencil); + stencil.destroy(); + return false; + } + + this.stencils[stencil.id] = stencil; + this.setRecentStencil(stencil); + + if(stencil.execText){ + if(stencil._text && !stencil.editMode){ + console.log("select text"); + this.selectItem(stencil); + } + stencil.connect("execText", this, function(){ + if(stencil.isText && stencil.deleteEmptyModify && !stencil.getText()){ + console.warn("EMPTY MOD DELETE", stencil); + // text deleted + // defaults say to delete + this.deleteItem(stencil); + }else if(stencil.selectOnExec){ + this.selectItem(stencil); + } + }); + } + + stencil.connect("deselect", this, function(){ + if(!this._isBusy && this.isSelected(stencil)){ + // called from within stencil. do action. + this.deselectItem(stencil); + } + }); + + stencil.connect("select", this, function(){ + if(!this._isBusy && !this.isSelected(stencil)){ + // called from within stencil. do action. + this.selectItem(stencil); + } + }); + + return stencil; + }, + unregister: function(/*Object*/stencil){ + // summary: + // Method for removing Stencils from the manager. + // This doesn't delete them, only removes them from + // the list. + // + console.log("Selection.unregister ::::::", stencil.id, "sel:", stencil.selected); + if(stencil){ + stencil.selected && this.onDeselect(stencil); + delete this.stencils[stencil.id]; + } + }, + + onArrow: function(/*Key Event*/evt){ + // summary: + // Moves selection based on keyboard arrow keys + // + // FIXME: Check constraints + if(this.hasSelected()){ + this.saveThrottledState(); + this.group.applyTransform({dx:evt.x, dy: evt.y}); + } + }, + + _throttleVrl:null, + _throttle: false, + throttleTime:400, + _lastmxx:-1, + _lastmxy:-1, + saveMoveState: function(){ + // summary: + // Internal. Used for the prototype undo stack. + // Saves selection position. + // + var mx = this.group.getTransform(); + if(mx.dx == this._lastmxx && mx.dy == this._lastmxy){ return; } + this._lastmxx = mx.dx; + this._lastmxy = mx.dy; + //console.warn("SAVE MOVE!", mx.dx, mx.dy); + this.undo.add({ + before:dojo.hitch(this.group, "setTransform", mx) + }); + }, + + saveThrottledState: function(){ + // summary: + // Internal. Used for the prototype undo stack. + // Prevents an undo point on every mouse move. + // Only does a point when the mouse hesitates. + // + clearTimeout(this._throttleVrl); + clearInterval(this._throttleVrl); + this._throttleVrl = setTimeout(dojo.hitch(this, function(){ + this._throttle = false; + this.saveMoveState(); + }), this.throttleTime); + if(this._throttle){ return; } + this._throttle = true; + + this.saveMoveState(); + + }, + unDelete: function(/*Array*/stencils){ + // summary: + // Undeletes a stencil. Used in undo stack. + // + console.log("unDelete:", stencils); + for(var s in stencils){ + stencils[s].render(); + this.onSelect(stencils[s]); + } + }, + onDelete: function(/*Boolean*/noundo){ + // summary: + // Event fired on deletion of a stencil + // + console.log("Stencil onDelete", noundo); + if(noundo!==true){ + this.undo.add({ + before:dojo.hitch(this, "unDelete", this.selectedStencils), + after:dojo.hitch(this, "onDelete", true) + }); + } + this.withSelected(function(m){ + this.anchors.remove(m); + var id = m.id; + console.log("delete:", m); + m.destroy(); + delete this.stencils[id]; + }); + this.selectedStencils = {}; + }, + + deleteItem: function(/*Object*/stencil){ + // summary: + // Deletes a stencil. + // NOTE: supports limited undo. + // + // manipulating the selection to fire onDelete properly + if(this.hasSelected()){ + // there is a selection + var sids = []; + for(var m in this.selectedStencils){ + if(this.selectedStencils.id == stencil.id){ + if(this.hasSelected()==1){ + // the deleting stencil is the only one selected + this.onDelete(); + return; + } + }else{ + sids.push(this.selectedStencils.id); + } + } + // remove selection, delete, restore selection + this.deselect(); + this.selectItem(stencil); + this.onDelete(); + dojo.forEach(sids, function(id){ + this.selectItem(id); + }, this); + }else{ + // there is not a selection. select it, delete it + this.selectItem(stencil); + // now delete selection + this.onDelete(); + } + }, + + removeAll: function(){ + // summary: + // Deletes all Stencils on the canvas. + + this.selectAll(); + this._isBusy = true; + this.onDelete(); + this.stencils = {}; + this._isBusy = false; + }, + + setSelectionGroup: function(){ + // summary: + // Internal. Creates a new selection group + // used to hold selected stencils. + // + this.withSelected(function(m){ + this.onDeselect(m, true); + }); + + if(this.group){ + surface.remove(this.group); + this.group.removeShape(); + } + this.group = surface.createGroup(); + this.group.setTransform({dx:0, dy: 0}); + + this.withSelected(function(m){ + this.group.add(m.container); + m.select(); + }); + }, + + setConstraint: function(){ + // summary: + // Internal. Gets all selected stencils' coordinates + // and determines how far left and up the selection + // can go without going below zero + // + var t = Infinity, l = Infinity; + this.withSelected(function(m){ + var o = m.getBounds(); + t = Math.min(o.y1, t); + l = Math.min(o.x1, l); + }); + this.constrain = {l:-l, t:-t}; + }, + + + + onDeselect: function(stencil, keepObject){ + // summary: + // Event fired on deselection of a stencil + // + if(!keepObject){ + delete this.selectedStencils[stencil.id]; + } + //console.log('onDeselect, keep:', keepObject, "stencil:", stencil.type) + + this.anchors.remove(stencil); + + surface.add(stencil.container); + stencil.selected && stencil.deselect(); + stencil.applyTransform(this.group.getTransform()); + }, + + deselectItem: function(/*Object*/stencil){ + // summary: + // Deselect passed stencil + // + // note: just keeping with standardized methods + this.onDeselect(stencil); + }, + + deselect: function(){ // all stencils + // summary: + // Deselect all stencils + // + this.withSelected(function(m){ + this.onDeselect(m); + }); + this._dragBegun = false; + this._wasDragged = false; + }, + + onSelect: function(/*Object*/stencil){ + // summary: + // Event fired on selection of a stencil + // + //console.log("stencil.onSelect", stencil); + if(!stencil){ + console.error("null stencil is not selected:", this.stencils) + } + if(this.selectedStencils[stencil.id]){ return; } + this.selectedStencils[stencil.id] = stencil; + this.group.add(stencil.container); + stencil.select(); + if(this.hasSelected()==1){ + this.anchors.add(stencil, this.group); + } + }, + + selectAll: function(){ + // summary: + // Selects all items + this._isBusy = true; + for(var m in this.stencils){ + //if(!this.stencils[m].selected){ + this.selectItem(m); + //} + } + this._isBusy = false; + }, + + selectItem: function(/*String|Object*/ idOrItem){ + // summary: + // Method used to select a stencil. + // + var id = typeof(idOrItem)=="string" ? idOrItem : idOrItem.id; + var stencil = this.stencils[id]; + this.setSelectionGroup(); + this.onSelect(stencil); + this.group.moveToFront(); + this.setConstraint(); + }, + + onLabelDoubleClick: function(/*EventObject*/obj){ + // summary: + // Event to connect a textbox to + // for label edits + console.info("mgr.onLabelDoubleClick:", obj); + if(this.selectedStencils[obj.id]){ + this.deselect(); + } + }, + + onStencilDoubleClick: function(/*EventObject*/obj){ + // summary: + // Event fired on the double-click of a stencil + // + console.info("mgr.onStencilDoubleClick:", obj); + if(this.selectedStencils[obj.id]){ + if(this.selectedStencils[obj.id].edit){ + console.info("Mgr Stencil Edit -> ", this.selectedStencils[obj.id]); + var m = this.selectedStencils[obj.id]; + // deselect must happen first to set the transform + // then edit knows where to set the text box + m.editMode = true; + this.deselect(); + m.edit(); + } + } + + }, + + onAnchorUp: function(){ + // summary: + // Event fire on mouseup off of an anchor point + this.setConstraint(); + }, + + onStencilDown: function(/*EventObject*/obj, evt){ + // summary: + // Event fired on mousedown on a stencil + // + console.info(" >>> onStencilDown:", obj.id, this.keys.meta); + if(!this.stencils[obj.id]){ return; } + this.setRecentStencil(this.stencils[obj.id]); + this._isBusy = true; + + + if(this.selectedStencils[obj.id] && this.keys.meta){ + if(dojo.isMac && this.keys.cmmd){ + // block context menu + + } + console.log(" shift remove"); + this.onDeselect(this.selectedStencils[obj.id]); + if(this.hasSelected()==1){ + this.withSelected(function(m){ + this.anchors.add(m, this.group); + }); + } + this.group.moveToFront(); + this.setConstraint(); + return; + + }else if(this.selectedStencils[obj.id]){ + console.log(" clicked on selected"); + // clicking on same selected item(s) + // RESET OFFSETS + var mx = this.group.getTransform(); + this._offx = obj.x - mx.dx; + this._offy = obj.y - mx.dy; + return; + + }else if(!this.keys.meta){ + + console.log(" deselect all"); + this.deselect(); + + }else{ + // meta-key add + //console.log("reset sel and add stencil") + } + console.log(" add stencil to selection"); + // add a stencil + this.selectItem(obj.id); + + mx = this.group.getTransform(); + this._offx = obj.x - mx.dx; + this._offy = obj.y - mx.dx; + + this.orgx = obj.x; + this.orgy = obj.y; + + this._isBusy = false; + + // TODO: + // dojo.style(surfaceNode, "cursor", "pointer"); + + // TODO: + this.undo.add({ + before:function(){ + + }, + after: function(){ + + } + }); + }, + + onLabelDown: function(/*EventObject*/obj, evt){ + // summary: + // Event fired on mousedown of a stencil's label + // Because it's an annotation the id will be the + // master stencil. + //console.info("===============>>>Label click: ",obj, " evt: ",evt); + this.onStencilDown(obj,evt); + }, + + onStencilUp: function(/*EventObject*/obj){ + // summary: + // Event fired on mouseup off of a stencil + // + }, + + onLabelUp: function(/*EventObject*/obj){ + this.onStencilUp(obj); + }, + + onStencilDrag: function(/*EventObject*/obj){ + // summary: + // Event fired on every mousemove of a stencil drag + // + if(!this._dragBegun){ + // bug, in FF anyway - first mouse move shows x=0 + // the 'else' fixes it + this.onBeginDrag(obj); + this._dragBegun = true; + }else{ + this.saveThrottledState(); + + var x = obj.x - obj.last.x, + y = obj.y - obj.last.y, + c = this.constrain, + mz = this.defaults.anchors.marginZero; + + + x = obj.x - this._offx; + y = obj.y - this._offy; + + if(x < c.l + mz){ + x = c.l + mz; + } + if(y < c.t + mz){ + y = c.t + mz; + } + + this.group.setTransform({ + dx: x, + dy: y + }); + + + } + }, + + onLabelDrag: function(/*EventObject*/obj){ + this.onStencilDrag(obj); + }, + + onDragEnd: function(/*EventObject*/obj){ + // summary: + // Event fired at the end of a stencil drag + // + this._dragBegun = false; + }, + onBeginDrag: function(/*EventObject*/obj){ + // summary: + // Event fired at the beginning of a stencil drag + // + this._wasDragged = true; + }, + + onDown: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on the canvas + // + this.deselect(); + }, + + + onStencilOver: function(obj){ + // summary: + // This changes the cursor when hovering over + // a selectable stencil. + //console.log("OVER") + dojo.style(obj.id, "cursor", "move"); + }, + + onStencilOut: function(obj){ + // summary: + // This restores the cursor. + //console.log("OUT") + dojo.style(obj.id, "cursor", "crosshair"); + }, + + exporter: function(){ + // summary: + // Collects all Stencil data and returns an + // Array of objects. + var items = []; + for(var m in this.stencils){ + this.stencils[m].enabled && items.push(this.stencils[m].exporter()); + } + return items; // Array + }, + + listStencils: function(){ + return this.stencils; + }, + + toSelected: function(/*String*/func){ + // summary: + // Convenience function calls function *within* + // all selected stencils + var args = Array.prototype.slice.call(arguments).splice(1); + for(var m in this.selectedStencils){ + var item = this.selectedStencils[m]; + item[func].apply(item, args); + } + }, + + withSelected: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all selected stencils + var f = dojo.hitch(this, func); + for(var m in this.selectedStencils){ + f(this.selectedStencils[m]); + } + }, + + withUnselected: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all stencils that are not selected + var f = dojo.hitch(this, func); + for(var m in this.stencils){ + !this.stencils[m].selected && f(this.stencils[m]); + } + }, + + withStencils: function(/*Function*/func){ + // summary: + // Convenience function calls function on + // all stencils + var f = dojo.hitch(this, func); + for(var m in this.stencils){ + f(this.stencils[m]); + } + }, + + hasSelected: function(){ + // summary: + // Returns number of selected (generally used + // as truthy or falsey) + // + // FIXME: should be areSelected? + var ln = 0; + for(var m in this.selectedStencils){ ln++; } + return ln; // Number + }, + + isSelected: function(/*Object*/stencil){ + // summary: + // Returns if passed stencil is selected or not + // based on internal collection, not on stencil + // boolean + return !!this.selectedStencils[stencil.id]; // Boolean + } + } + + ); +})(); + +}); diff --git a/js/dojo/dojox/drawing/manager/StencilUI.js b/js/dojo/dojox/drawing/manager/StencilUI.js new file mode 100644 index 0000000..7fdaa14 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/StencilUI.js @@ -0,0 +1,69 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/StencilUI", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.StencilUI"); + +(function(){ + var surface, surfaceNode; + dojox.drawing.manager.StencilUI = dojox.drawing.util.oo.declare( + // summary: + // Used for handling Stencils as UI components. + // description: + // Replaces manager.Stencil. Handles basic UI mouse + // events like onmouseover. Does not handle selections + // or support delete, etc. + // + function(options){ + // + // TODO: mixin props + // + surface = options.surface; + this.canvas = options.canvas; + + this.defaults = dojox.drawing.defaults.copy(); + this.mouse = options.mouse; + this.keys = options.keys; + this._mouseHandle = this.mouse.register(this); + this.stencils = {}; + }, + { + register: function(/*Object*/stencil){ + this.stencils[stencil.id] = stencil; + return stencil; + }, + onUiDown: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onDown(obj); + }, + onUiUp: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onUp(obj); + }, + onOver: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onOver(obj); + }, + onOut: function(/*EventObject*/obj){ + // summary: + // Event fired on mousedown on a stencil + // + if(!this._isStencil(obj)){ return; } + this.stencils[obj.id].onOut(obj); + }, + _isStencil: function(/*EventObject*/obj){ + return !!obj.id && !!this.stencils[obj.id] && this.stencils[obj.id].type == "drawing.library.UI.Button"; + } + } + ); + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/Undo.js b/js/dojo/dojox/drawing/manager/Undo.js new file mode 100644 index 0000000..c904240 --- /dev/null +++ b/js/dojo/dojox/drawing/manager/Undo.js @@ -0,0 +1,61 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/Undo", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.Undo"); + +dojox.drawing.manager.Undo = dojox.drawing.util.oo.declare( + // summary + // Handles the Undo in drawing. + // NOTE: Only partially implemented!!! There is very + // little actual undo functionality! + // + function(options){ + this.keys = options.keys; + this.undostack = []; + this.redostack = []; + dojo.connect(this.keys, "onKeyDown", this, "onKeyDown"); + }, + { + onKeyDown: function(evt){ + if(!evt.cmmd){ return; } + + if(evt.keyCode==90 && !evt.shift){ + this.undo(); + }else if((evt.keyCode == 90 && evt.shift) || evt.keyCode==89){ + this.redo(); + } + + }, + add: function(stack){ + //console.log("undo add", stack) + stack.args = dojo.mixin({}, stack.args); + this.undostack.push(stack); + }, + apply: function(scope, method, args){ + dojo.hitch(scope, method)(args); + }, + undo: function(){ + + var o = this.undostack.pop(); + console.log("undo!", o); + if(!o){ return; } + + o.before(); + + this.redostack.push(o); + }, + redo: function(){ + console.log("redo!"); + var o = this.redostack.pop(); + if(!o){ return; } + if(o.after){ + o.after(); + }else{ + o.before(); ///?????? + } + + this.undostack.push(o); + } + } +); +}); diff --git a/js/dojo/dojox/drawing/manager/_registry.js b/js/dojo/dojox/drawing/manager/_registry.js new file mode 100644 index 0000000..53c5a7e --- /dev/null +++ b/js/dojo/dojox/drawing/manager/_registry.js @@ -0,0 +1,35 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/_registry", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager._registry"); + +(function(){ + + var _registered = { + tool:{}, + stencil:{}, + drawing:{}, + plugin:{}, + button:{} + }; + + dojox.drawing.register = function(item, type){ + if(type=="drawing"){ + _registered.drawing[item.id] = item; + }else if(type=="tool"){ + _registered.tool[item.name] = item; + }else if(type=="stencil"){ + _registered.stencil[item.name] = item; + }else if(type=="plugin"){ + _registered.plugin[item.name] = item; + }else if(type=="button"){ + _registered.button[item.toolType] = item; + } + }; + + dojox.drawing.getRegistered = function(type, id){ + return id ? _registered[type][id] : _registered[type]; + } + +})(); +}); diff --git a/js/dojo/dojox/drawing/manager/keys.js b/js/dojo/dojox/drawing/manager/keys.js new file mode 100644 index 0000000..1acb06b --- /dev/null +++ b/js/dojo/dojox/drawing/manager/keys.js @@ -0,0 +1,269 @@ +//>>built +// wrapped by build app +define("dojox/drawing/manager/keys", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.manager.keys"); + +(function(){ + + // Ref: isEdit allows events to happen in Drawing, like TextBlocks + var isEdit = false; + + // Ref: enabled = false allows inputs outside of drawing to function + var enabled = true; + + var alphabet = "abcdefghijklmnopqrstuvwxyz"; + + dojox.drawing.manager.keys = { + // summary: + // A singleton, master object that detects + // keyboard keys and events + // Connect to it like: + // dojo.connect(this.keys, "onEnter", ....); + // + // arrowIncrement:Number + // The amount, in pixels, a selected Stencil will + // move on an arrow key event + arrowIncrement:1, + // + // arrowShiftIncrement: Number + // The amount, in pixels, a selected Stencil will + // move on an arrow key + SHIFT event + arrowShiftIncrement:10, + // + // shift: [readonly] Boolean + // Indicates whether the Shift key is currently pressed + shift:false, + // + // ctrl: [readonly] Boolean + // Indicates whether the Control key is currently pressed + ctrl:false, + // + // alt: [readonly] Boolean + // Indicates whether the Alt or Option key is currently pressed + alt:false, + // + // cmmd: [readonly] Boolean + // Indicates whether the Apple Command key is currently pressed + cmmd:false, // apple key + // + // meta: [readonly] Boolean + // Indicates whether any 'meta' key is currently pressed: + // shift || ctrl || cmmd || alt + meta:false, // any meta key + + onDelete: function(/* Event */evt){ + // summary: + // Event fires when Delete key is released + }, + onEsc: function(/* Event */evt){ + // summary: + // Event fires when ESC key is released + }, + onEnter: function(/* Event */evt){ + // summary: + // Event fires when Enter key is released + }, + onArrow: function(/* Event */evt){ + // summary: + // Event fires when an Arrow key is released + // You will have to further check if evt.keyCode + // is 37,38,39, or 40 + }, + onKeyDown: function(/* Event */evt){ + // summary: + // Event fires when any key is pressed + }, + onKeyUp: function(/* Event */evt){ + // summary: + // Event fires when any key is released + }, + + listeners:[], + register: function(options){ + // summary: + // Register an object and callback to be notified + // of events. + // NOTE: Not really used in code, but should work. + // See manager.mouse for similar usage + // + var _handle = dojox.drawing.util.common.uid("listener"); + this.listeners.push({ + handle:_handle, + scope: options.scope || window, + callback:options.callback, + keyCode:options.keyCode + }); + }, + + _getLetter: function(evt){ + if(!evt.meta && evt.keyCode>=65 && evt.keyCode<=90){ + return alphabet.charAt(evt.keyCode-65); + } + return null; + }, + + _mixin: function(evt){ + // summary: + // Internal. Mixes in key events. + evt.meta = this.meta; + evt.shift = this.shift; + evt.alt = this.alt; + evt.cmmd = this.cmmd; + evt.letter = this._getLetter(evt); + return evt; + }, + + editMode: function(_isedit){ + // summary: + // Relinquishes control of events to another portion + // of Drawing; namely the TextBlock. + isEdit = _isedit; + }, + + enable: function(_enabled){ + // summary: + // Enables or disables key events, to relinquish + // control to something outside of Drawing; input + // fields for example. + // You may need to call this directly if you are + // using textareas or contenteditables. + // NOTE: See scanForFields + enabled = _enabled; + }, + + scanForFields: function(){ + // summary: + // Scans the document for inputs + // and calls this automatically. However you may need + // to call this if you create inputs after the fact. + // + if(this._fieldCons){ + dojo.forEach(this._fieldCons, dojo.disconnect, dojo); + } + this._fieldCons = []; + dojo.query("input").forEach(function(n){ + var a = dojo.connect(n, "focus", this, function(evt){ + this.enable(false); + }); + var b = dojo.connect(n, "blur", this, function(evt){ + this.enable(true); + }); + this._fieldCons.push(a); + this._fieldCons.push(b); + }, this); + + }, + + init: function(){ + // summary: + // Initialize the keys object + // + // a little extra time is needed in some browsers + setTimeout(dojo.hitch(this, "scanForFields"), 500); + + dojo.connect(document, "blur", this, function(evt){ + // when command tabbing to another application, the key "sticks" + // this clears any key used for such activity + this.meta = this.shift = this.ctrl = this.cmmd = this.alt = false; + }); + + dojo.connect(document, "keydown", this, function(evt){ + if(!enabled){ return; } + if(evt.keyCode==16){ + this.shift = true; + } + if(evt.keyCode==17){ + this.ctrl = true; + } + if(evt.keyCode==18){ + this.alt = true; + } + if(evt.keyCode==224){ + this.cmmd = true; + } + + this.meta = this.shift || this.ctrl || this.cmmd || this.alt; + + if(!isEdit){ + this.onKeyDown(this._mixin(evt)); + if(evt.keyCode==8 || evt.keyCode==46){ + dojo.stopEvent(evt); + } + } + }); + dojo.connect(document, "keyup", this, function(evt){ + if(!enabled){ return; } + //console.log("KEY UP:", evt.keyCode); + var _stop = false; + if(evt.keyCode==16){ + this.shift = false; + } + if(evt.keyCode==17){ + this.ctrl = false; + } + if(evt.keyCode==18){ + this.alt = false; + } + if(evt.keyCode==224){ + this.cmmd = false; + } + + this.meta = this.shift || this.ctrl || this.cmmd || this.alt; + + !isEdit && this.onKeyUp(this._mixin(evt)); + + if(evt.keyCode==13){ + console.warn("KEY ENTER"); + this.onEnter(evt); + _stop = true; + } + if(evt.keyCode==27){ + this.onEsc(evt); + _stop = true; + } + if(evt.keyCode==8 || evt.keyCode==46){ + this.onDelete(evt); + _stop = true; + } + + if(_stop && !isEdit){ + dojo.stopEvent(evt); + } + }); + + dojo.connect(document, "keypress", this, function(evt){ + if(!enabled){ return; } + var inc = this.shift ? this.arrowIncrement*this.arrowShiftIncrement : this.arrowIncrement; + + var x =0, y =0; + if(evt.keyCode==32 && !isEdit){ //space + dojo.stopEvent(evt); + } + if(evt.keyCode==37){ //left + x = -inc; + } + if(evt.keyCode==38){ //up + y = -inc; + } + if(evt.keyCode==39){ //right + x = inc; + } + if(evt.keyCode==40){ //down + y = inc; + } + if(x || y){ + evt.x = x; + evt.y = y; + evt.shift = this.shift; + if(!isEdit){ + this.onArrow(evt); + dojo.stopEvent(evt); + } + } + }); + } + }; + dojo.addOnLoad(dojox.drawing.manager.keys, "init"); +})(); + +}); diff --git a/js/dojo/dojox/drawing/plugins/_Plugin.js b/js/dojo/dojox/drawing/plugins/_Plugin.js new file mode 100644 index 0000000..44ebd51 --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/_Plugin.js @@ -0,0 +1,43 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/_Plugin", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins._Plugin"); + +dojox.drawing.plugins._Plugin = dojox.drawing.util.oo.declare( + // summary: + // Base class for plugins. + // description: + // When creating a plugin, use this class as the + // base to ensure full functionality. + function(options){ + this._cons = []; + dojo.mixin(this, options); + if(this.button && this.onClick){ + this.connect(this.button, "onClick", this, "onClick") + } + }, + { + util:null, + keys:null, + mouse:null, + drawing:null, + stencils:null, + anchors:null, + canvas:null, + node:null, + button:null,//gfx button + type:"dojox.drawing.plugins._Plugin", + connect: function(){ + this._cons.push(dojo.connect.apply(dojo, arguments)); + }, + disconnect: function(/*handle | Array*/handles){ + // summary: + // Removes connections based on passed + // handles arguments + if(!handles){ return }; + if(!dojo.isArray(handles)){ handles=[handles]; } + dojo.forEach(handles, dojo.disconnect, dojo); + } + } +); +}); diff --git a/js/dojo/dojox/drawing/plugins/drawing/GreekPalette.js b/js/dojo/dojox/drawing/plugins/drawing/GreekPalette.js new file mode 100644 index 0000000..66f21f8 --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/drawing/GreekPalette.js @@ -0,0 +1,340 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/drawing/GreekPalette", ["dijit","dojo","dojox","dojo/i18n!dojox/editor/plugins/nls/latinEntities","dojo/require!dojox/drawing/library/greek,dijit/focus,dijit/_Widget,dijit/_TemplatedMixin,dijit/_PaletteMixin,dojo/i18n"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.drawing.GreekPalette"); + +dojo.require("dojox.drawing.library.greek"); +dojo.require("dijit.focus"); +dojo.require("dijit._Widget"); +dojo.require("dijit._TemplatedMixin"); +dojo.require("dijit._PaletteMixin"); +dojo.require("dojo.i18n"); + +dojo.requireLocalization("dojox.editor.plugins", "latinEntities"); + +dojo.declare("dojox.drawing.plugins.drawing.GreekPalette", + [dijit._Widget, dijit._TemplatedMixin, dijit._PaletteMixin], + { + // summary: + // This plugin uses the palette dijit in order to give tips for + // non-english (mostly greek for now) letters. + // + // IMPORTANT! Because it is a full blown dijit it is NOT loaded + // like the other plugins. INSTEAD currently it is instantiated + // in markup. TextBlock LOOKS FOR IT by ID - "greekPalette" + // and if it finds it does the necessary initialization/connections. + // description: + // Grid showing all available entity options which the + // user can pick from. The library loaded for use by the picker + // is found in dojox.drawing.library.greek. Adding characters + // there will automatically add them to the palette. + // + // This works as a popup and as such its onChange and onCancel + // close it. TextBlock manages it, since it's what uses the assist + // so it calls show (all actual popup management happens here). + // In order to activate the plugin require it and then include the + // markup in the example: + // + // example: + // | <!--Because this is a widget it is included in markup and NOT like the other plugins--> + // | <div dojoType="dojox.drawing.plugins.drawing.GreekPalette" id="greekPalette"></div> + + postMixInProperties: function(){ + // Convert hash of entities into two-dimensional rows/columns table (array of arrays) + var choices = dojox.drawing.library.greek; + var numChoices = 0; + var entityKey; + for(entityKey in choices){numChoices++;} + var choicesPerRow = Math.floor(Math.sqrt(numChoices)); + var numRows = choicesPerRow; + var currChoiceIdx = 0; + var rows = []; + var row = []; + for(entityKey in choices){ + currChoiceIdx++; + row.push(entityKey); + if(currChoiceIdx % numRows === 0){ + rows.push(row); + row = []; + } + } + if(row.length > 0){ + rows.push(row); + } + this._palette = rows; + }, + + show: function(obj){ + dojo.mixin(obj, {popup: this}); + dijit.popup.open(obj); + }, + + onChange: function(val){ + var textBlock = this._textBlock; + dijit.popup.hide(this); + textBlock.insertText(this._pushChangeTo,val); + textBlock._dropMode = false; + }, + + onCancel: function(/*Boolean*/ closeAll){ + // summary: + // attach point for notification about when the user cancels the current menu + dijit.popup.hide(this); + this._textBlock._dropMode = false; + }, + + // templateString: String + // The template of this widget. Using dojoxEntityPalette classes + // in order to allow easy transfer of css + templateString: '<div class="dojoxEntityPalette">\n' + + ' <table>\n' + + ' <tbody>\n' + + ' <tr>\n' + + ' <td>\n' + + ' <table class="dijitPaletteTable">\n' + + ' <tbody dojoAttachPoint="gridNode"></tbody>\n' + + ' </table>\n' + + ' </td>\n' + + ' </tr>\n' + + ' <tr>\n' + + ' <td>\n'+ + ' <table dojoAttachPoint="previewPane" class="dojoxEntityPalettePreviewTable">\n' + + ' <tbody>\n' + + ' <tr>\n' + + ' <td class="dojoxEntityPalettePreviewDetailEntity">Type: <span class="dojoxEntityPalettePreviewDetail" dojoAttachPoint="previewNode"></span></td>\n' + + ' </tr>\n' + + ' </tbody>\n' + + ' </table>\n' + + ' </td>\n' + + ' </tr>\n' + + ' </tbody>\n' + + ' </table>\n' + + '</div>', + + + baseClass: "dojoxEntityPalette", + + // showPreview: [public] Boolean + // Whether the preview pane will be displayed, to show details about the selected entity. + showPreview: true, + + dyeClass: 'dojox.drawing.plugins.Greeks', + + // domNodeClass [protected] String + paletteClass: 'editorLatinEntityPalette', + + cellClass: "dojoxEntityPaletteCell", + + buildRendering: function(){ + this.inherited(arguments); + + var i18n = dojo.i18n.getLocalization("dojox.editor.plugins", "latinEntities"); + + this._preparePalette( + this._palette, + i18n + ); + + var cells = dojo.query(".dojoxEntityPaletteCell", this.gridNode); + dojo.forEach(cells, function(cellNode){ + this.connect(cellNode, "onmouseenter", "_onCellMouseEnter"); + }, this); + }, + + _onCellMouseEnter: function(e){ + // summary: + // Simple function to handle updating the display at the bottom of + // the palette. + // e: + // The event. + // tags: + // private + if(this.showPreview){ + this._displayDetails(e.target); + } + }, + + _onCellClick: function(/*Event*/ evt){ + // summary: + // Handler for click, enter key & space key. Selects the cell. + // evt: + // The event. + // tags: + // private + var target = evt.type == "click" ? evt.currentTarget : this._currentFocus, + value = this._getDye(target).getValue(); + + // First focus the clicked cell, and then send onChange() notification. + // onChange() (via _setValueAttr) must be after the focus call, because + // it may trigger a refocus to somewhere else (like the Editor content area), and that + // second focus should win. + // Use setTimeout because IE doesn't like changing focus inside of an event handler. + this._setCurrent(target); + setTimeout(dojo.hitch(this, function(){ + dijit.focus(target); + this._setValueAttr(value, true); + })); + + // workaround bug where hover class is not removed on popup because the popup is + // closed and then there's no onblur event on the cell + dojo.removeClass(target, "dijitPaletteCellHover"); + + dojo.stopEvent(evt); + }, + + postCreate: function(){ + this.inherited(arguments); + + if(!this.showPreview){ + dojo.style(this.previewNode,"display","none"); + } + dijit.popup.moveOffScreen(this); + }, + + _setCurrent: function(/*DOMNode*/ node){ + // summary: + // Sets which node is the focused cell. + // description: + // At any point in time there's exactly one + // cell with tabIndex != -1. If focus is inside the palette then + // focus is on that cell. + // + // After calling this method, arrow key handlers and mouse click handlers + // should focus the cell in a setTimeout(). + // tags: + // protected + if("_currentFocus" in this){ + // Remove tabIndex on old cell + dojo.attr(this._currentFocus, "tabIndex", "-1"); + dojo.removeClass(this._currentFocus,"dojoxEntityPaletteCellHover"); + } + + // Set tabIndex of new cell + this._currentFocus = node; + if(node){ + dojo.attr(node, "tabIndex", this.tabIndex); + dojo.addClass(this._currentFocus,"dojoxEntityPaletteCellHover"); + } + if(this.showPreview){ + this._displayDetails(node); + } + }, + + _displayDetails: function(/*DOMNode*/ cell){ + // summary: + // Display the details of the currently focused entity in the preview pane + var dye = this._getDye(cell); + if(dye){ + var ehtml = dye.getValue(); + var ename = dye._alias; + //console.warn("Greek help: ",dye._alias); + this.previewNode.innerHTML=ehtml; + }else{ + this.previewNode.innerHTML=""; + this.descNode.innerHTML=""; + } + }, + + _preparePalette: function(choices, titles) { + // summary: + // Subclass must call _preparePalette() from postCreate(), passing in the tooltip + // for each cell + // choices: String[][] + // id's for each cell of the palette, used to create Dye JS object for each cell + // titles: String[] + // Localized tooltip for each cell + + this._cells = []; + var url = this._blankGif; + + var dyeClassObj = dojo.getObject(this.dyeClass); + + for(var row=0; row < choices.length; row++){ + var rowNode = dojo.create("tr", {tabIndex: "-1"}, this.gridNode); + for(var col=0; col < choices[row].length; col++){ + var value = choices[row][col]; + if(value){ + var cellObject = new dyeClassObj(value); + + var cellNode = dojo.create("td", { + "class": this.cellClass, + tabIndex: "-1", + title: titles[value] + }); + + // prepare cell inner structure + cellObject.fillCell(cellNode, url); + + this.connect(cellNode, "ondijitclick", "_onCellClick"); + this._trackMouseState(cellNode, this.cellClass); + + dojo.place(cellNode, rowNode); + + cellNode.index = this._cells.length; + + // save cell info into _cells + this._cells.push({node:cellNode, dye:cellObject}); + } + } + } + this._xDim = choices[0].length; + this._yDim = choices.length; + + }, + + _navigateByArrow: function(evt){ + // summary: + // This is a departure from the dijit, the textBlock needs + // navigation without losing focus, this allows that + // increment: + // How much the key is navigated. + // tags: + // private + var keyIncrementMap = { + 38: -this._xDim, + // The down key the index is increase by the x dimension. + 40: this._xDim, + // Right and left move the index by 1. + 39: this.isLeftToRight() ? 1 : -1, + 37: this.isLeftToRight() ? -1 : 1 + }; + + var increment = keyIncrementMap[evt.keyCode]; + var newFocusIndex = this._currentFocus.index + increment; + if(newFocusIndex < this._cells.length && newFocusIndex > -1){ + var focusNode = this._cells[newFocusIndex].node; + this._setCurrent(focusNode); + } + } +}); + +dojo.declare("dojox.drawing.plugins.Greeks", + null, +{ + // summary: + // Represents a character. + // Initialized using an alias for the character (like cent) rather + // than with the character itself. + + constructor: function(/*String*/ alias){ + // summary: + // Construct JS object representing an entity (associated w/a cell + // in the palette) + // value: String + // alias name: 'cent', 'pound' .. + this._alias = alias; + }, + + getValue: function(){ + // summary: + // Returns HTML representing the character, like & + // + return this._alias; + }, + + fillCell: function(/*DOMNode*/ cell){ + // Deal with entities that have keys which are reserved words. + cell.innerHTML = "&"+this._alias+";"; + } +}); +}); diff --git a/js/dojo/dojox/drawing/plugins/drawing/Grid.js b/js/dojo/dojox/drawing/plugins/drawing/Grid.js new file mode 100644 index 0000000..5ee6109 --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/drawing/Grid.js @@ -0,0 +1,102 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/drawing/Grid", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.drawing.Grid"); +dojo.require("dojox.drawing.plugins._Plugin"); + +dojox.drawing.plugins.drawing.Grid = dojox.drawing.util.oo.declare( + // summary: + // Plugin that displays a grid on the Drawing canvas. + // example: + // | <div dojoType="dojox.drawing.Drawing" id="drawingNode" + // | plugins="[{'name':'dojox.drawing.plugins.drawing.Grid', 'options':{gap:50}}]"> + // | </div> + // + dojox.drawing.plugins._Plugin, + function(options){ + if(options.gap){ + this.major = options.gap; + } + this.majorColor = options.majorColor || this.majorColor; + this.minorColor = options.minorColor || this.minorColor; + + this.setGrid(); + dojo.connect(this.canvas, "setZoom", this, "setZoom"); + }, + { + type:"dojox.drawing.plugins.drawing.Grid", + // + // gap: Number + // How far apart to set the grid lines + gap:100, + major:100, + minor:0, + // + // majorColor: String + // Major lines color + majorColor: "#00ffff", + // + // minorColor: String + // Minor lines color + minorColor: "#d7ffff", + // + // zoom: [readonly] Number + // The current zoom of the grid + zoom:1, + + setZoom: function(zoom){ + // summary: + // Set's the zoom of the canvas + this.zoom = zoom; + this.setGrid(); + }, + setGrid: function(options){ + // summary: + // Renders grid + // + // TODO: major minor lines + // minors dont show on zoom out + // draw minors first + // + var mjr = Math.floor(this.major * this.zoom); + var mnr = this.minor ? Math.floor(this.minor * this.zoom) : mjr; + + this.grid && this.grid.removeShape(); + + var x1,x2,y1,y2,i,clr,len; + var s = this.canvas.underlay.createGroup(); + var w = 2000;//this.canvas.width; + var h = 1000;//this.canvas.height; + var b = 1; + var mj = this.majorColor; + var mn = this.minorColor; + + var createGridLine = function(x1,y1,x2,y2, c){ + s.createLine({x1: x1, y1: y1, x2: x2, y2: y2}).setStroke({style: "Solid", width: b, cap: "round", color:c}); + }; + + // horz + for(i=1,len = h/mnr; i<len; i++){ + x1 = 0, x2 = w; + y1 = mnr*i, y2 = y1; + + + clr = y1%mjr ? mn : mj; + createGridLine(x1,y1,x2,y2, clr); + } + // vert + for(i=1,len = w/mnr; i<len; i++){ + y1 = 0, y2 = h; + x1 = mnr*i, x2 = x1; + clr = x1%mjr ? mn : mj; + createGridLine(x1,y1,x2,y2, clr); + } + + s.moveToBack(); + this.grid = s; + this.util.attr(s, "id", "grid"); + return s; + } + } +); +}); diff --git a/js/dojo/dojox/drawing/plugins/drawing/Silverlight.js b/js/dojo/dojox/drawing/plugins/drawing/Silverlight.js new file mode 100644 index 0000000..50fbbea --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/drawing/Silverlight.js @@ -0,0 +1,197 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/drawing/Silverlight", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.drawing.Silverlight"); + +dojox.drawing.plugins.drawing.Silverlight = dojox.drawing.util.oo.declare( + // summary: + // "Plugin" to allow the Silverlight plugin to work + // with DojoX Drawing. + // + // WARNING: This is not completely implemented. For the most + // part, DojoX Drawing does not support Silverlight. This class + // was created in an attempt for support, but there were too many + // obstacles and not enough time. The basic functionality is here + // and there's a good head start if anyone elase wishes to pick up + // where I left off. + // + function(options){ + // summary: + // The constructor is the only method in this class. + // What is happening here is other methods in other + // classes are being overridden to adapt to Silverlight. + // + + if(dojox.gfx.renderer != "silverlight"){ return; } + this.mouse = options.mouse; + this.stencils = options.stencils; + this.anchors = options.anchors; + this.canvas = options.canvas; + this.util = options.util; + + + dojo.connect(this.stencils, "register", this, function(item){ + var c1, c2, c3, c4, c5, self = this; + var conMouse = function(){ + //console.info("------connect shape", item.id) + + // Connect to PARENT (SL Canvas) , not SHAPE + c1 = item.container.connect("onmousedown", function(evt){ + //console.info("----------------------------------SHAPE DOWN", item.container) + evt.superTarget = item; + self.mouse.down(evt); + }); + } + conMouse(); + + c2 = dojo.connect(item, "setTransform", this, function(){ + //dojo.disconnect(c1); + }); + + c3 = dojo.connect(item, "onBeforeRender", function(){ + //dojo.disconnect(c1); + }); + + + c4 = dojo.connect(item, "onRender", this, function(){ + //conMouse(); + }); + + c5 = dojo.connect(item, "destroy", this, function(){ + dojo.forEach([c1,c2,c3,c4,c5], dojo.disconnect, dojo); + }); + }); + + dojo.connect(this.anchors, "onAddAnchor", this, function(anchor){ + var c1 = anchor.shape.connect("onmousedown", this.mouse, function(evt){ + evt.superTarget = anchor; + this.down(evt) + }); + var c2 = dojo.connect(anchor, "disconnectMouse", this, function(){ + dojo.disconnect(c1); + dojo.disconnect(c2); + }); + + }); + + + this.mouse._down = function(evt){ + var dim = this._getXY(evt); + var x = dim.x - this.origin.x; + var y = dim.y - this.origin.y; + + x*= this.zoom; + y*= this.zoom; + + this.origin.startx = x; + this.origin.starty = y; + this._lastx = x; + this._lasty = y; + + this.drawingType = this.util.attr(evt, "drawingType") || ""; + var id = this._getId(evt); + var obj = {x:x,y:y, id:id}; + console.log(" > > > id:", id, "drawingType:", this.drawingType, "evt:", evt) + + this.onDown(obj); + + this._clickTime = new Date().getTime(); + if(this._lastClickTime){ + if(this._clickTime-this._lastClickTime<this.doublClickSpeed){ + var dnm = this.eventName("doubleClick"); + console.warn("DOUBLE CLICK", dnm, obj); + this._broadcastEvent(dnm, obj); + }else{ + //console.log(" slow:", this._clickTime-this._lastClickTime) + } + } + this._lastClickTime = this._clickTime; + + // throws errors in IE silverlight. Oddness. + //dojo.stopEvent(evt); + } + + this.mouse.down = function(evt){ + clearTimeout(this.__downInv); + if(this.util.attr(evt, "drawingType")=="surface"){ + this.__downInv = setTimeout(dojo.hitch(this, function(){ + this._down(evt); + }),500); + return; + } + this._down(evt); + + + } + + this.mouse._getXY = function(evt){ + + if(evt.pageX){ + return {x:evt.pageX, y:evt.pageY, cancelBubble:true}; + } + console.log("EVT", evt) + //console.log("EVT", evt.pageX) + for(var nm in evt){ + //console.log("..."+nm+"="+evt[nm]); + } + console.log("EVTX", evt.x) + if(evt.x !== undefined){ + return { + x:evt.x + this.origin.x, + y:evt.y + this.origin.y + }; + }else{ + return {x:evt.pageX, y:evt.pageY}; + } + } + + this.mouse._getId = function(evt){ + return this.util.attr(evt, "id"); + } + + this.util.attr = function(/* Object */ elem, /* property */ prop, /* ? value */ value, squelchErrors){ + if(!elem){ return false; } + try{ + + var t; + if(elem.superTarget){ + t = elem.superTarget; + }else if(elem.superClass){ + t = elem.superClass; + }else if(elem.target){ + t = elem.target; + }else{ + t = elem; + } + + if(value!==undefined){ + elem[prop] = value; + return value; + } + + if(t.tagName){ + if(prop=="drawingType" && t.tagName.toLowerCase()=="object"){ + return "surface"; + } + var r = dojo.attr(t, prop); + } + var r = t[prop]; + return r + + + }catch(e){ + if(!squelchErrors){ + // For debugging only. These errors actually cause more errors in IE's console + //console.error("BAD ATTR: prop:", prop, "el:", elem) + //console.error(e) + //console.trace(); + } + return false; + } + } + }, + { + + } +); +}); diff --git a/js/dojo/dojox/drawing/plugins/tools/Iconize.js b/js/dojo/dojox/drawing/plugins/tools/Iconize.js new file mode 100644 index 0000000..6d2437c --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/tools/Iconize.js @@ -0,0 +1,104 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/tools/Iconize", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.tools.Iconize"); +dojo.require("dojox.drawing.plugins._Plugin"); + +dojox.drawing.plugins.tools.Iconize = dojox.drawing.util.oo.declare( + // summary: + // Somewhat of internal use... + // Outputs a path to be used as an icon. Will end up being a + // sub-icon under Export options + + dojox.drawing.plugins._Plugin, + function(options){ + + }, + { + onClick: function(){ + var item; + for(var nm in this.stencils.stencils){ + console.log(" stanceil item:", this.stencils.stencils[nm].id, this.stencils.stencils[nm]) + if(this.stencils.stencils[nm].shortType=="path"){ + item = this.stencils.stencils[nm]; + break; + } + } + if(item){ + console.log("click Iconize plugin", item.points); + this.makeIcon(item.points); + } + }, + makeIcon: function(/*Array*/p){ + var rnd = function(n){ + return Number(n.toFixed(1)); + } + + var x = 10000; + var y = 10000; + p.forEach(function(pt){ + if(pt.x!==undefined && !isNaN(pt.x)){ + x = Math.min(x, pt.x); + y = Math.min(y, pt.y); + } + }); + + var xmax = 0; + var ymax = 0; + p.forEach(function(pt){ + if(pt.x!==undefined && !isNaN(pt.x)){ + pt.x = rnd(pt.x - x); + //console.log("Y:", pt.y, y, pt.y - y) + pt.y = rnd(pt.y - y); + xmax = Math.max(xmax, pt.x); + ymax = Math.max(ymax, pt.y); + } + }); + + console.log("xmax:", xmax, "ymax:", ymax) + + var s = 60 + var m = 20 + + p.forEach(function(pt){ + pt.x = rnd(pt.x / xmax) * s + m; + pt.y = rnd(pt.y / ymax) * s + m; + }); + + var txt = "[\n"; + dojo.forEach(p, function(pt, i){ + txt += "{\t" + if(pt.t){ + txt += "t:'"+pt.t+"'" + } + if(pt.x!==undefined && !isNaN(pt.x)){ + if(pt.t){ + txt += ", "; + } + txt += "x:"+pt.x+",\t\ty:"+pt.y; + } + txt += "\t}"; + if(i!=p.length-1){ + txt += "," + } + txt += "\n" + }); + txt+="]" + + console.log(txt) + var n = dojo.byId("data"); + if(n){ + n.value = txt; + } + } + } +); + +dojox.drawing.plugins.tools.Iconize.setup = { + name:"dojox.drawing.plugins.tools.Iconize", + tooltip:"Iconize Tool", + iconClass:"iconPan" +}; + +dojox.drawing.register(dojox.drawing.plugins.tools.Iconize.setup, "plugin"); +}); diff --git a/js/dojo/dojox/drawing/plugins/tools/Pan.js b/js/dojo/dojox/drawing/plugins/tools/Pan.js new file mode 100644 index 0000000..2cd3d68 --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/tools/Pan.js @@ -0,0 +1,246 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/tools/Pan", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.tools.Pan"); +dojo.require("dojox.drawing.plugins._Plugin"); + +dojox.drawing.plugins.tools.Pan = dojox.drawing.util.oo.declare( + // summary: + // A plugin that allows for a scrolling canvas. An action + // tool is added to the toolbar that allows for panning. Holding + // the space bar is a shortcut to that action. The canvas will + // only pan and scroll if there are objects out of the viewable + // area. + // example: + // | <div dojoType="dojox.drawing.Toolbar" drawingId="drawingNode" class="drawingToolbar vertical"> + // | <div tool="dojox.drawing.tools.Line" selected="true">Line</div> + // | <div plugin="dojox.drawing.plugins.tools.Pan" options="{}">Pan</div> + // | </div> + // + dojox.drawing.plugins._Plugin, + function(options){ + this.domNode = options.node; + var _scrollTimeout; + this.toolbar = options.scope; + this.connect(this.toolbar, "onToolClick", this, function(){ + this.onSetPan(false) + }); + this.connect(this.keys, "onKeyUp", this, "onKeyUp"); + this.connect(this.keys, "onKeyDown", this, "onKeyDown"); + this.connect(this.keys, "onArrow", this, "onArrow"); + this.connect(this.anchors, "onAnchorUp", this, "checkBounds"); + this.connect(this.stencils, "register", this, "checkBounds"); + this.connect(this.canvas, "resize", this, "checkBounds"); + this.connect(this.canvas, "setZoom", this, "checkBounds"); + this.connect(this.canvas, "onScroll", this, function(){ + if(this._blockScroll){ + this._blockScroll = false; + return; + } + _scrollTimeout && clearTimeout(_scrollTimeout); + _scrollTimeout = setTimeout(dojo.hitch(this, "checkBounds"), 200); + }); + this._mouseHandle = this.mouse.register(this); + // This HAS to be called after setting initial objects or things get screwy. + //this.checkBounds(); + + },{ + selected:false, + keyScroll:false, + type:"dojox.drawing.plugins.tools.Pan", + + onPanUp: function(obj){ + if(obj.id == this.button.id){ + this.onSetPan(false); + } + }, + + onKeyUp: function(evt){ + switch(evt.keyCode){ + case 32: + this.onSetPan(false); + break; + case 39: case 37: case 38: case 40: + clearInterval(this._timer); + break; + } + }, + + onKeyDown: function(evt){ + if(evt.keyCode == 32){ + this.onSetPan(true); + } + }, + + interval: 20, + + onArrow: function(evt){ + if(this._timer){ clearInterval(this._timer); } + this._timer = setInterval(dojo.hitch(this,function(evt){ + this.canvas.domNode.parentNode.scrollLeft += evt.x*10; + this.canvas.domNode.parentNode.scrollTop += evt.y*10; + },evt), this.interval); + }, + + onSetPan: function(/*Boolean | Event*/ bool){ + if(bool === true || bool === false){ + this.selected = !bool; + } + console.log('ON SET PAN:', this.selected) + if(this.selected){ + this.selected = false; + this.button.deselect(); + }else{ + this.selected = true; + this.button.select(); + } + this.mouse.setEventMode(this.selected ? "pan" : ""); + }, + + onPanDrag: function(obj){ + var x = obj.x - obj.last.x; + var y = obj.y - obj.last.y; + this.canvas.domNode.parentNode.scrollTop -= obj.move.y; + this.canvas.domNode.parentNode.scrollLeft -= obj.move.x; + this.canvas.onScroll(); + }, + + onUp: function(obj){ + if(obj.withinCanvas){ + this.keyScroll = true; + }else{ + this.keyScroll = false; + } + }, + + onStencilUp: function(obj){ + // this gets called even on click-off because of the + // issues with TextBlock deselection + this.checkBounds(); + }, + onStencilDrag: function(obj){ + // this gets called even on click-off because of the + // issues with TextBlock deselection + //this.checkBounds(); + }, + + checkBounds: function(){ + + //watch("CHECK BOUNDS DISABLED", true); return; + + + // summary: + // Scans all items on the canvas and checks if they are out of + // bounds. If so, a scroll bar (in Canvas) is shown. If the position + // is left or top, the canvas is scrolled all items are relocated + // the distance of the scroll. Ideally, it should look as if the + // items do not move. + + // logging stuff here so it can be turned on and off. This method is + // very high maintenance. + var log = function(){ + //console.log.apply(console, arguments); + } + var warn = function(){ + //console.warn.apply(console, arguments); + } + //console.clear(); + //console.time("check bounds"); + + // initialize a shot-tin of vars + var t=Infinity, r=-Infinity, b=-10000, l=10000, + sx=0, sy=0, dy=0, dx=0, + mx = this.stencils.group ? this.stencils.group.getTransform() : {dx:0, dy:0}, + sc = this.mouse.scrollOffset(), + // scY, scX: the scrollbar creates the need for extra dimension + scY = sc.left ? 10 : 0, + scX = sc.top ? 10 : 0, + // ch, cw: the current size of the canvas + ch = this.canvas.height, + cw = this.canvas.width, + z = this.canvas.zoom, + // pch, pcw: the normal size of the canvas (not scrolled) + // these could change if the container resizes. + pch = this.canvas.parentHeight, + pcw = this.canvas.parentWidth; + + + this.stencils.withSelected(function(m){ + var o = m.getBounds(); + warn("SEL BOUNDS:", o); + t = Math.min(o.y1 + mx.dy, t); + r = Math.max(o.x2 + mx.dx, r); + b = Math.max(o.y2 + mx.dy, b); + l = Math.min(o.x1 + mx.dx, l); + }); + + this.stencils.withUnselected(function(m){ + var o = m.getBounds(); + warn("UN BOUNDS:", o); + t = Math.min(o.y1, t); + r = Math.max(o.x2, r); + b = Math.max(o.y2, b); + l = Math.min(o.x1, l); + log("----------- B:", b, o.y2) + }); + + b *= z; + var xscroll = 0, yscroll = 0; + log("Bottom test", "b:", b, "z:", z, "ch:", ch, "pch:", pch, "top:", sc.top, "sy:", sy, "mx.dy:", mx.dy); + if(b > pch || sc.top ){ + log("*bottom scroll*"); + // item off bottom + ch = Math.max(b, pch + sc.top); + sy = sc.top; + xscroll += this.canvas.getScrollWidth(); + }else if(!sy && ch>pch){ + log("*bottom remove*"); + // item moved from bottom + ch = pch; + } + + r *= z; + if(r > pcw || sc.left){ + //log("*right scroll*"); + // item off right + cw = Math.max(r, pcw + sc.left); + sx = sc.left; + yscroll += this.canvas.getScrollWidth(); + }else if(!sx && cw>pcw){ + //log("*right remove*"); + // item moved from right + cw = pcw; + } + + // add extra space for scrollbars + // double it to give some breathing room + cw += xscroll*2; + ch += yscroll*2; + + this._blockScroll = true; + + // selected items are not transformed. The selection itself is + // and the items are on de-select + this.stencils.group && this.stencils.group.applyTransform({dx:dx, dy:dy}); + + // non-selected items are transformed + this.stencils.withUnselected(function(m){ + m.transformPoints({dx:dx, dy:dy}); + }); + + this.canvas.setDimensions(cw, ch, sx, sy); + + //console.timeEnd("check bounds"); + } + } +); + +dojox.drawing.plugins.tools.Pan.setup = { + name:"dojox.drawing.plugins.tools.Pan", + tooltip:"Pan Tool", + iconClass:"iconPan", + button:false +}; + +dojox.drawing.register(dojox.drawing.plugins.tools.Pan.setup, "plugin"); +}); diff --git a/js/dojo/dojox/drawing/plugins/tools/Zoom.js b/js/dojo/dojox/drawing/plugins/tools/Zoom.js new file mode 100644 index 0000000..a35073f --- /dev/null +++ b/js/dojo/dojox/drawing/plugins/tools/Zoom.js @@ -0,0 +1,129 @@ +//>>built +// wrapped by build app +define("dojox/drawing/plugins/tools/Zoom", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.plugins.tools.Zoom"); +dojo.require("dojox.drawing.plugins._Plugin"); + +(function(){ + // + // zoomInc: Float + // The amount of zoom that will occur upon each click. + var zoomInc = Math.pow(2.0,0.25), + // + // maxZoom: Number + // The maximum the canvas can be zoomed in. 10 = 1000% + maxZoom = 10, + // + // minZoom: Float + // The most the canvas can be zoomed out. .1 = 10% + minZoom = 0.1, + // + // zoomFactor: [readonly] Float + // The current zoom amount + zoomFactor = 1, + + dt = dojox.drawing.plugins.tools; + + dt.ZoomIn = dojox.drawing.util.oo.declare( + // summary: + // A plugin that allows for zooming the canvas in and out. An + // action-tool is added to the toolbar with plus, minus and 100% + // buttons. + // + function(options){ + // mix in private vars + + }, + {} + ); + + + dt.ZoomIn = dojox.drawing.util.oo.declare( + // summary: + dojox.drawing.plugins._Plugin, + function(options){ + + }, + { + type:"dojox.drawing.plugins.tools.ZoomIn", + onZoomIn: function(){ + // summary: + // Handles zoom in. + // + zoomFactor *= zoomInc; + zoomFactor = Math.min(zoomFactor, maxZoom); + this.canvas.setZoom(zoomFactor); + this.mouse.setZoom(zoomFactor); + }, + onClick: function(){ + this.onZoomIn(); + } + } + ); + + dt.Zoom100 = dojox.drawing.util.oo.declare( + // summary: + dojox.drawing.plugins._Plugin, + function(options){ + + }, + { + type:"dojox.drawing.plugins.tools.Zoom100", + onZoom100: function(){ + // summary: + // Zooms to 100% + // + zoomFactor = 1; + this.canvas.setZoom(zoomFactor); + this.mouse.setZoom(zoomFactor); + }, + onClick: function(){ + this.onZoom100(); + } + } + ); + + dt.ZoomOut = dojox.drawing.util.oo.declare( + // summary: + dojox.drawing.plugins._Plugin, + function(options){ + + }, + { + type:"dojox.drawing.plugins.tools.ZoomOut", + onZoomOut: function(){ + // summary: + // Handles zoom out. + // + zoomFactor /= zoomInc; + zoomFactor = Math.max(zoomFactor, minZoom); + this.canvas.setZoom(zoomFactor); + this.mouse.setZoom(zoomFactor); + }, + onClick: function(){ + this.onZoomOut(); + } + } + ); + + + dt.ZoomIn.setup = { + name:"dojox.drawing.plugins.tools.ZoomIn", + tooltip:"Zoom In" + }; + dojox.drawing.register(dt.ZoomIn.setup, "plugin"); + + dt.Zoom100.setup = { + name:"dojox.drawing.plugins.tools.Zoom100", + tooltip:"Zoom to 100%" + }; + dojox.drawing.register(dt.Zoom100.setup, "plugin"); + + dt.ZoomOut.setup = { + name:"dojox.drawing.plugins.tools.ZoomOut", + tooltip:"Zoom In" + }; + dojox.drawing.register(dt.ZoomOut.setup, "plugin"); + +})(); +}); diff --git a/js/dojo/dojox/drawing/resources/CrazyTruck.jpg b/js/dojo/dojox/drawing/resources/CrazyTruck.jpg Binary files differnew file mode 100644 index 0000000..bc270ec --- /dev/null +++ b/js/dojo/dojox/drawing/resources/CrazyTruck.jpg diff --git a/js/dojo/dojox/drawing/resources/GreekPalette.css b/js/dojo/dojox/drawing/resources/GreekPalette.css new file mode 100644 index 0000000..b1bb5e6 --- /dev/null +++ b/js/dojo/dojox/drawing/resources/GreekPalette.css @@ -0,0 +1,57 @@ +/* + This is directly from @import "../plugins/resources/css/InsertEntity.css"; + It's here because certain details may change over time. The classes + remain the same so applying the original will be easy in case of change. +*/ + +.dojoxEntityPalette { + /* outer node of the dropdown */ + border: 1px solid #999; + background: #fff; + -moz-border-radius: 3pt; +} + +.dojoxEntityPaletteCell { + /* individual cell of the drop down */ + border: 1px dotted gray; + width: 20px; /* todo: don't hardcode width/height; it's neither necessary nor a11y safe */ + line-height: 18px; + overflow: hidden; + z-index: 10; + text-align: center; +} + +.dojoxEntityPaletteCell:hover, .dojoxEntityPaletteCell:active, .dojoxEntityPaletteCell:focus { + width: 18px; + line-height: 16px; + overflow: hidden; + cursor: default; + border:1px dashed #000; + outline:1px dashed #dedede; +} + + +.dojoxEntityPalettePreviewTable { + table-layout: auto; + font-size: 1em; + width: 100%; +} + +.dojoxEntityPalettePreviewHeader { + font-size: .8em; + padding: 3px 3px 3px 3px; +} + +.dojoxEntityPalettePreviewDetailEntity { + font-size: .8em; + font-weight: bold; +} + +.dojoxEntityPalettePreviewDetail { + font-size: .8em; + padding: 3px 3px 3px 3px; +} + +.dijit_a11y .dojoxEntityPaletteCell { + background-color:transparent !important; +} diff --git a/js/dojo/dojox/drawing/resources/drawing.css b/js/dojo/dojox/drawing/resources/drawing.css new file mode 100644 index 0000000..9df91b4 --- /dev/null +++ b/js/dojo/dojox/drawing/resources/drawing.css @@ -0,0 +1,41 @@ +html, body{ + width:100%; + height:100%; + font-family:sans-serif; +} + +.textAnnotation{ + position:absolute; + border:1px solid #ccc; + background-color:#ffff00; + color:#0000ff; + padding:3px; + font-family:sans-serif; + font-size:12px; + margin-top:-10px; + z-index:501; +} + +#conEdit{ + border:0; + /*background:#ccc; + border:1px solid #ff0000; + outline:none;*/ + padding:3px; + margin:0; +} + +.drawingTooltip{ + font-family:sans-serif; + font-family:sans-serif; + font-weight:normal; + padding:5px; +} +.drawingTipTitle{ + font-size:12px; + font-weight:bold; +} +.drawingTipDesc{ + font-size:11px; + padding-left:10px; +}
\ No newline at end of file diff --git a/js/dojo/dojox/drawing/resources/images/tool_icons.png b/js/dojo/dojox/drawing/resources/images/tool_icons.png Binary files differnew file mode 100644 index 0000000..74eaee9 --- /dev/null +++ b/js/dojo/dojox/drawing/resources/images/tool_icons.png diff --git a/js/dojo/dojox/drawing/resources/images/tool_sprites.png b/js/dojo/dojox/drawing/resources/images/tool_sprites.png Binary files differnew file mode 100644 index 0000000..d80bd4d --- /dev/null +++ b/js/dojo/dojox/drawing/resources/images/tool_sprites.png diff --git a/js/dojo/dojox/drawing/resources/toolbar.css b/js/dojo/dojox/drawing/resources/toolbar.css new file mode 100644 index 0000000..c33a790 --- /dev/null +++ b/js/dojo/dojox/drawing/resources/toolbar.css @@ -0,0 +1,225 @@ +/* + FIXME: + IE refuses to honor the class targeting here, combining vertical and horizontal + currently vertical has precidence, horizontal will screw up +*/ +.horizontal.drawingToolbar{ + position:relative; + width:auto; + height:32px; + padding:5px; + left:5px; +} +.vertical.drawingToolbar{ + width:32px; + padding:5px; + position:absolute; + left:5px; + margin-top:5px; + background:#fff; +} + +.drawingToolbar .drawingButton{ + position:relative; + padding:0; + width:32px; + height:32px; + background-image:url("images/tool_sprites.png"); + background-position:0px 0px; + border:0; + cursor:pointer; +} + +.horizontal.drawingToolbar .drawingButton{ + float:left; + margin-right:5px; +} + +.dj_ie6 .drawingToolbar .drawingButton { + + filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale src='images/tool_sprites.png'); +} +.vertical.drawingToolbar .drawingButton{ + margin-bottom:5px; + margin-right:0px; +} + +.drawingToolbar .drawingButton.hover{ + background-position:-32px 0px; +} +.drawingToolbar .drawingButton.active{ + background-position:-64px 0px; +} +.drawingToolbar .drawingButton.selected{ + background-position:-96px 0px; +} +.drawingToolbar .drawingButton.selected:hover{} + +.drawingToolbar.vertical{} + + + +.drawingToolbar .drawingButton .icon{ + background-image:url("images/tool_icons.png"); + width:32px; + height:64px; + position:absolute; + left:0px; + top:0px; + margin:0; + padding:0; + clip: rect(0px 32px 32px 0px); +} + + +.drawingToolbar .drawingButton.selected .icon{ + /* + background-position-y is not supported in Firefox + The workaround is to shift the icon with 'top' + and hide a portion of it with 'clip' + */ + top:-32px; + clip: rect(32px 32px 64px 0px); +} + + +.drawingToolbar .drawingButton .icon.iconLine{ + background-position:-64px 0px; +} +.drawingToolbar .drawingButton .icon.iconRect{ + background-position: 0px 0px; +} +.drawingToolbar .drawingButton .icon.iconEllipse{ + background-position:-32px 0px; +} +.drawingToolbar .drawingButton .icon.iconText{ + background-position:-224px 0px; +} +.drawingToolbar .drawingButton .icon.iconArrow{ + background-position:-96px 0px; +} +.drawingToolbar .drawingButton .icon.iconVector{ + background-position:-128px 0px; +} +.drawingToolbar .drawingButton .icon.iconAxes{ + background-position:-160px 0px; +} +.drawingToolbar .drawingButton .icon.iconPan{ + background-position:-192px 0px; +} +.drawingToolbar .drawingButton .icon.iconEq{ + background-position:-351px 0px; +} + +.dj_ie6 .drawingToolbar .drawingButton .icon { + + filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, sizingMethod=scale src='images/tool_icons.png'); +} + + + +/* Combo button - a button that is connected (beg middle end) */ + +/* Vertical*/ +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboTop{ + background-position:-128px 0px; + margin:0; + margin-top:10px; + border-bottom:1px solid #d6d6d6; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboTop.hover{ + background-position:-160px 0px; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboTop.active{ + background-position:-192px 0px; +} + + +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboMid{ + background-position:-384px 0px; + border-style:solid; + border-width:1px; + border-color:#fff #cccccc #d6d6d6 #cccccc; + height:24px; + width:30px; + margin:0; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboMid.hover, +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboMid.hover{ + background-position:-416px 0px; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboMid.active, +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboMid.active{ + background-position:-448px 0px; +} + + +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboBot{ + background-position:-256px 0px; + border-top:1px solid #fff; + margin:0; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboBot.hover{ + background-position:-288px 0px; +} +.drawingToolbar.vertical .toolCombo .drawingButton.toolComboBot.active{ + background-position:-320px 0px; +} + + +/* Horizontal +FIXME: Horizontal alignment needs tweaking +*/ +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboTop{ + background-position:-512px 0px; + margin:0; + margin-left:10px; + border:0px; + border-right:1px solid #d6d6d6; + +} +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboTop.hover{ + background-position:-544px 0px; +} +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboTop.active{ + background-position:-576px 0px; +} + + +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboMid{ + background-position:-384px 0px; + border-style:solid; + border-width:1px; + border-color:#cccccc #d6d6d6 #cccccc #fff; + height:30px; + width:31px; + margin:0; +} + +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboBot{ + background-position:-640px 0px; + border-left:1px solid #fff; + margin:0; + margin-top:-1px; + margin-right:-1px; +} +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboBot.hover{ + background-position:-673px 0px; +} +.drawingToolbar.horizontal .toolCombo .drawingButton.toolComboBot.active{ + background-position:-704px 0px; +} + + +.drawingToolbar .drawingButton .icon.iconZoomIn{ + background-position:-256px 0px; +} +.drawingToolbar .drawingButton .icon.iconZoom100{ + background-position:-320px -4px; +} +.drawingToolbar .drawingButton .icon.iconZoomOut{ + background-position:-288px 0px; +} + + + diff --git a/js/dojo/dojox/drawing/stencil/Ellipse.js b/js/dojo/dojox/drawing/stencil/Ellipse.js new file mode 100644 index 0000000..66ecad7 --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Ellipse.js @@ -0,0 +1,104 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Ellipse", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Ellipse"); + +/*===== +__StencilData = { + // summary: + // the data used to create the dojox.gfx Shape + // + + // cx: Number + // Center point x + cx:0, + // cy: Number + // Center point y + cy:0, + // rx: Number + // Horizontal radius + rx:0, + // ry: Number + // Vertical radius + ry:0 +} +=====*/ + +dojox.drawing.stencil.Ellipse = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx Ellipse based on data or points provided. + // + dojox.drawing.stencil._Base, + function(options){ + // summary: + // constructor + }, + { + + type:"dojox.drawing.stencil.Ellipse", + anchorType: "group", + baseRender:true, + dataToPoints: function(/*Object*/o){ + //summary: + // Converts data to points. + o = o || this.data; + var x = o.cx - o.rx, + y = o.cy - o.ry, + w = o.rx*2, + h = o.ry*2 + this.points = [ + {x:x, y:y}, // TL + {x:x+w, y:y}, // TR + {x:x+w, y:y+h}, // BR + {x:x, y:y+h} // BL + ]; + return this.points; //Array + }, + + pointsToData: function(/*Array*/p){ + // summary: + // Converts points to data + p = p || this.points; + var s = p[0]; + var e = p[2]; + this.data = { + cx: s.x + (e.x - s.x)/2, + cy: s.y + (e.y - s.y)/2, + rx: (e.x - s.x)*.5, + ry: (e.y - s.y)*.5 + }; + return this.data; //Object + + }, + + _create: function(/*String*/shp, /*__StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + this[shp] = this.container.createEllipse(d) + .setStroke(sty) + .setFill(sty.fill); + this._setNodeAtts(this[shp]); + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). + // + this.onBeforeRender(this); + this.renderHit && this._create("hit", this.data, this.style.currentHit); + this._create("shape", this.data, this.style.current); + } + + } +); + +dojox.drawing.register({ + name:"dojox.drawing.stencil.Ellipse" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/Image.js b/js/dojo/dojox/drawing/stencil/Image.js new file mode 100644 index 0000000..6b23db7 --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Image.js @@ -0,0 +1,159 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Image", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Image"); + + +dojox.drawing.stencil.Image = dojox.drawing.util.oo.declare( + // summary: + // Creates an dojox.gfx Image based on the data + // provided. + // + dojox.drawing.stencil._Base, + function(options){ + // summary: + // constructor + }, + { + type:"dojox.drawing.stencil.Image", + anchorType: "group", + baseRender:true, + +/*===== +StencilData: { + // summary: + // The data used to create the dojox.gfx Shape + // x: Number + // Left point x + // y: Number + // Top point y + // width: ? Number + // Optional width of Image. If not provided, it is obtained + // height: ? Number + // Optional height of Image. If not provided, it is obtained + // src: String + // The location of the source image +}, + +StencilPoints: [ + // summary: + // An Array of dojox.__StencilPoint objects that describe the Stencil + // 0: Object + // Top left point + // 1: Object + // Top right point + // 2: Object + // Bottom right point + // 3: Object + // Bottom left point +], +=====*/ + + dataToPoints: function(/*Object*/o){ + //summary: + // Converts data to points. + o = o || this.data; + this.points = [ + {x:o.x, y:o.y}, // TL + {x:o.x + o.width, y:o.y}, // TR + {x:o.x + o.width, y:o.y + o.height}, // BR + {x:o.x, y:o.y + o.height} // BL + ]; + return this.points; + }, + + pointsToData: function(/*Array*/p){ + // summary: + // Converts points to data + p = p || this.points; + var s = p[0]; + var e = p[2]; + this.data = { + x: s.x, + y: s.y, + width: e.x-s.x, + height: e.y-s.y, + src: this.src || this.data.src + }; + return this.data; + + }, + + _createHilite: function(){ + // summary: + // Create the hit and highlight area + // for the Image. + this.remove(this.hit); + this.hit = this.container.createRect(this.data) + .setStroke(this.style.current) + .setFill(this.style.current.fill); + this._setNodeAtts(this.hit); + }, + _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + var s = this.container.getParent(); + this[shp] = s.createImage(d) + this.container.add(this[shp]); + this._setNodeAtts(this[shp]); + }, + + render: function(dbg){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). Image is slightly different than other + // implementations. Instead of calling render twice, it calls + // _createHilite for the 'hit' + // + if(this.data.width == "auto" || isNaN(this.data.width)){ + this.getImageSize(true); + console.warn("Image size not provided. Acquiring...") + return; + } + this.onBeforeRender(this); + this.renderHit && this._createHilite(); + this._create("shape", this.data, this.style.current); + }, + getImageSize: function(render){ + // summary: + // Internal. If no image size is passed in with the data + // create a dom node, insert and image, gets its dimensions + // record them - then destroy everything. + // + if(this._gettingSize){ return; } // IE gets it twice (will need to mod if src changes) + this._gettingSize = true; + var img = dojo.create("img", {src:this.data.src}, dojo.body()); + var err = dojo.connect(img, "error", this, function(){ + dojo.disconnect(c); + dojo.disconnect(err); + console.error("Error loading image:", this.data.src) + console.warn("Error image:", this.data) + + }); + var c = dojo.connect(img, "load", this, function(){ + var dim = dojo.marginBox(img); + this.setData({ + x:this.data.x, + y:this.data.y, + src:this.data.src, + width:dim.w, + height:dim.h + }); + dojo.disconnect(c); + dojo.destroy(img); + render && this.render(true); + }); + } + } +); + + +dojox.drawing.register({ + name:"dojox.drawing.stencil.Image" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/Line.js b/js/dojo/dojox/drawing/stencil/Line.js new file mode 100644 index 0000000..78bfbec --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Line.js @@ -0,0 +1,122 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Line", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Line"); + +dojox.drawing.stencil.Line = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx Line based on data or points provided. + // + dojox.drawing.stencil._Base, + function(options){ + // summary: + // constructor + }, + { + type:"dojox.drawing.stencil.Line", + anchorType: "single", + baseRender:true, + +/*===== +StencilData: { + // summary: + // The data used to create the dojox.gfx Shape + // x1: Number + // First point x + // y1: Number + // First point y + // x2: Number + // Second point x + // y2: Number + // Second point y + + // ALTERNATIVE: + + // x: Number + // First point x + // y: Number + // First point y + // angle: Number + // angle of line + // radius: Number + // length of line +}, + +StencilPoints: [ + // summary: + // An Array of dojox.__StencilPoint objects that describe the Stencil + // 0: Object + // First point + // 1: Object + // Second point +], +=====*/ + + dataToPoints: function(o){ + //summary: + // Converts data to points. + o = o || this.data; + if(o.radius || o.angle){ + // instead of using x1,x2,y1,y1, + // it's been set as x,y,angle,radius + + var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle); + //console.log(" ---- pts:", pt.x, pt.y); + this.data = o = { + x1:o.x, + y1:o.y, + x2:pt.x, + y2:pt.y + } + + } + this.points = [ + {x:o.x1, y:o.y1}, + {x:o.x2, y:o.y2} + ]; + return this.points; + }, + pointsToData: function(p){ + // summary: + // Converts points to data + p = p || this.points; + this.data = { + x1: p[0].x, + y1: p[0].y, + x2: p[1].x, + y2: p[1].y + }; + return this.data; + }, + + _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + this[shp] = this.container.createLine(d) + .setStroke(sty); + this._setNodeAtts(this[shp]); + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). + // + this.onBeforeRender(this); + this.renderHit && this._create("hit", this.data, this.style.currentHit); + this._create("shape", this.data, this.style.current); + + } + + } +); + +dojox.drawing.register({ + name:"dojox.drawing.stencil.Line" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/Path.js b/js/dojo/dojox/drawing/stencil/Path.js new file mode 100644 index 0000000..8bc3dde --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Path.js @@ -0,0 +1,173 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Path", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Path"); + + +dojox.drawing.stencil.Path = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx Path based on points provided. + // + dojox.drawing.stencil._Base, + function(options){ + dojo.disconnect(this._postRenderCon); + }, + { + type:"dojox.drawing.stencil.Path", + closePath: true, + baseRender:true, + closeRadius:10, + closeColor:{r:255,g:255,b:0,a:.5}, + +/*===== +StencilData: { + // NOT SUPPORTED FOR PATH +}, + +StencilPoints: [ + // summary: + // An Array of StencilPoint objects that describe the Stencil + // 0: Object + // First point + // [1, 2, 3...] more points +], +=====*/ + + _create: function(/*String*/shp, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + if(!this.points.length){ return; } + + if(dojox.gfx.renderer=="svg"){ + // NOTE: + // In order to avoid the Safari d="" errors, + // we'll need to build a string and set that. + var strAr = []; + dojo.forEach(this.points, function(o, i){ + if(!o.skip){ + if(i==0){ + strAr.push("M " + o.x +" "+ o.y); + }else{ + var cmd = (o.t || "") + " "; + if(o.x===undefined){// Z + undefined works here, but checking anyway + strAr.push(cmd); + }else{ + strAr.push(cmd + o.x +" "+ o.y); + } + } + } + }, this); + if(this.closePath){ + strAr.push("Z"); + } + + this.stringPath = strAr.join(" "); + + this[shp] = this.container.createPath(strAr.join(" ")).setStroke(sty); + this.closePath && this[shp].setFill(sty.fill); + + }else{ + // Leaving this code for VML. It seems slightly faster but times vary. + this[shp] = this.container.createPath({}).setStroke(sty); + + this.closePath && this[shp].setFill(sty.fill); + + dojo.forEach(this.points, function(o, i){ + if(!o.skip){ + if(i==0 || o.t=="M"){ + this[shp].moveTo(o.x, o.y); + }else if(o.t=="Z"){ + this.closePath && this[shp].closePath(); + }else{ + this[shp].lineTo(o.x, o.y); + } + } + }, this); + + this.closePath && this[shp].closePath(); + } + + this._setNodeAtts(this[shp]); + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). + // + this.onBeforeRender(this); + this.renderHit && this._create("hit", this.style.currentHit); + this._create("shape", this.style.current); + //console.log("path render") + + + //console.log("---------------------rend hit", this.renderHit, this.id) + }, + getBounds: function(/* ? Boolean*/absolute){ + // summary: + // Overwriting _Base.getBounds. Not sure how absolute should + // work for a path. + var minx = 10000, miny = 10000, maxx = 0, maxy = 0; + dojo.forEach(this.points, function(p){ + if(p.x!==undefined && !isNaN(p.x)){ + minx = Math.min(minx, p.x); + miny = Math.min(miny, p.y); + maxx = Math.max(maxx, p.x); + maxy = Math.max(maxy, p.y); + } + }); + + return { + x1:minx, + y1:miny, + x2:maxx, + y2:maxy, + x:minx, + y:miny, + w:maxx-minx, + h:maxy-miny + }; + }, + + checkClosePoint: function(/*Object*/firstPt, /*Object*/currPt, /*Boolean*/remove){ + // summary: + // Checks if points are close enough to indicate that + // path should be close. Provides a visual cue. + // description: + // Not actually used in stencil.path - this is used for + // drawable tools that extend it. Note that those tools + // need to remove the shape created: this.closeGuide, or + // add arg: remove + // + var dist = this.util.distance(firstPt.x, firstPt.y, currPt.x, currPt.y); + if(this.points.length>1){ + if(dist<this.closeRadius && !this.closeGuide && !remove){ + var c = { + cx:firstPt.x, + cy:firstPt.y, + rx:this.closeRadius, + ry:this.closeRadius + } + this.closeGuide = this.container.createEllipse(c) + .setFill(this.closeColor); + + }else if(remove || dist > this.closeRadius && this.closeGuide){ + this.remove(this.closeGuide); + this.closeGuide = null; + } + } + // return if we are within close distance + return dist < this.closeRadius; // Boolean + } + } +); + +dojox.drawing.register({ + name:"dojox.drawing.stencil.Path" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/Rect.js b/js/dojo/dojox/drawing/stencil/Rect.js new file mode 100644 index 0000000..454c7d8 --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Rect.js @@ -0,0 +1,86 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Rect", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Rect"); + + +dojox.drawing.stencil.Rect = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx rectangle based on data or points provided. + // + dojox.drawing.stencil._Base, + function(options){ + // summary: + // constructor + if(this.points.length){ + //this.render(); + } + }, + { + type:"dojox.drawing.stencil.Rect", + anchorType: "group", + baseRender:true, + + dataToPoints: function(/*Object*/d){ + //summary: + // Converts data to points. + d = d || this.data; + this.points = [ + {x:d.x, y:d.y}, // TL + {x:d.x + d.width, y:d.y}, // TR + {x:d.x + d.width, y:d.y + d.height}, // BR + {x:d.x, y:d.y + d.height} // BL + ]; + return this.points; + }, + + pointsToData: function(/*Array*/p){ + // summary: + // Converts points to data + p = p || this.points; + var s = p[0]; + var e = p[2]; + this.data = { + x: s.x, + y: s.y, + width: e.x-s.x, + height: e.y-s.y, + r:this.data.r || 0 + }; + return this.data; + + }, + + _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + //console.log("render rect", d) + //console.log("rect sty:", sty) + this.remove(this[shp]); + this[shp] = this.container.createRect(d) + .setStroke(sty) + .setFill(sty.fill); + + this._setNodeAtts(this[shp]); + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). + // + this.onBeforeRender(this); + this.renderHit && this._create("hit", this.data, this.style.currentHit); + this._create("shape", this.data, this.style.current); + } + } +); + +dojox.drawing.register({ + name:"dojox.drawing.stencil.Rect" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/Text.js b/js/dojo/dojox/drawing/stencil/Text.js new file mode 100644 index 0000000..a46e69f --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/Text.js @@ -0,0 +1,232 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/Text", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil.Text"); + +dojox.drawing.stencil.Text = dojox.drawing.util.oo.declare( + // summary: + // Creates a dojox.gfx Text (SVG or VML) based on data provided. + // description: + // There are two text classes. TextBlock extends this one and + // adds editable functionality, discovers text width etc. + // This class displays text only. There is no line wrapping. + // Multiple lines can be acheived by inserting \n linebreaks + // in the text. + // + dojox.drawing.stencil._Base, + function(options){ + // summary: + // constructor. + }, + { + type:"dojox.drawing.stencil.Text", + anchorType:"none", + baseRender:true, + + // align: String + // Text horizontal alignment. + // Options: start, middle, end + align:"start", + // + // valign:String + // Text vertical alignment + // Options: top, middle, bottom (FIXME: bottom not supported) + valign:"top", + // + // _lineHeight: [readonly] Number + // The height of each line of text. Based on style information + // and font size. + _lineHeight:1, + +/*===== +StencilData: { + // summary: + // The data used to create the dojox.gfx Text + // x: Number + // Left point x + // y: Number + // Top point y + // width: ? Number + // Optional width of Text. Not required but reccommended. + // for auto-sizing, use TextBlock + // height: ? Number + // Optional height of Text. If not provided, _lineHeight is used. + // text: String + // The string content. If not provided, may auto-delete depending on defaults. +}, + +StencilPoints: [ + // summary: + // An Array of dojox.__StencilPoint objects that describe the Stencil + // 0: Object + // Top left point + // 1: Object + // Top right point + // 2: Object + // Bottom right point + // 3: Object + // Bottom left point +], +=====*/ + + typesetter: function(text){ + // summary: + // Register raw text, returning typeset form. + // Uses function dojox.drawing.stencil.Text.typeset + // for typesetting, if it exists. + // + if(dojox.drawing.util.typeset){ + this._rawText = text; + return dojox.drawing.util.typeset.convertLaTeX(text); + } + return text; + }, + + setText: function(text){ + // summary: + // Setter for text. + // + // Only apply typesetting to objects that the user can modify. + // Else, it is assumed that typesetting is done elsewhere. + if(this.enabled){ + text = this.typesetter(text); + } + // This only has an effect if text is null or this.created is false. + this._text = text; + + this._textArray = []; + this.created && this.render(text); + }, + + getText: function(){ + // summary: + // Getter for text. + // + return this._rawText || this._text; + }, + + dataToPoints: function(/*Object*/o){ + //summary: + // Converts data to points. + o = o || this.data; + var w = o.width =="auto" ? 1 : o.width; + var h = o.height || this._lineHeight; + this.points = [ + {x:o.x, y:o.y}, // TL + {x:o.x + w, y:o.y}, // TR + {x:o.x + w, y:o.y + h}, // BR + {x:o.x, y:o.y + h} // BL + ]; + return this.points; + }, + pointsToData: function(/*Array*/p){ + // summary: + // Converts points to data + p = p || this.points; + var s = p[0]; + var e = p[2]; + this.data = { + x: s.x, + y: s.y, + width: e.x-s.x, + height: e.y-s.y + }; + return this.data; + }, + + render: function(/* String*/text){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). Text is slightly different than other + // implementations. Instead of calling render twice, it calls + // _createHilite for the 'hit' + // arguments: + // text String + // Changes text if sent. Be sure to use the setText and + // not to call this directly. + // + this.remove(this.shape, this.hit); + //console.log("text render, outline:", !this.annotation, this.renderHit, (!this.annotation && this.renderHit)) + !this.annotation && this.renderHit && this._renderOutline(); + if(text!=undefined){ + this._text = text; + this._textArray = this._text.split("\n"); + } + + var d = this.pointsToData(); + var h = this._lineHeight; + var x = d.x + this.style.text.pad*2; + var y = d.y + this._lineHeight - (this.textSize*.4); + if(this.valign=="middle"){ + y -= h/2; + } + this.shape = this.container.createGroup(); + + /*console.log(" render ", this.type, this.id) + console.log(" render Y:", d.y, "textSize:", this.textSize, "LH:", this._lineHeight) + console.log(" render text:", y, " ... ", this._text, "enabled:", this.enabled); + console.log(" render text:", this.style.currentText); + */ + dojo.forEach(this._textArray, function(txt, i){ + var tb = this.shape.createText({x: x, y: y+(h*i), text: unescape(txt), align: this.align}) + .setFont(this.style.currentText) + .setFill(this.style.currentText.color); + + this._setNodeAtts(tb); + + }, this); + + this._setNodeAtts(this.shape); + + }, + _renderOutline: function(){ + // summary: + // Create the hit and highlight area + // for the Text. + // + if(this.annotation){ return; } + var d = this.pointsToData(); + + if(this.align=="middle"){ + d.x -= d.width/2 - this.style.text.pad * 2; + }else if(this.align=="start"){ + d.x += this.style.text.pad; + }else if(this.align=="end"){ + d.x -= d.width - this.style.text.pad * 3; + } + + if(this.valign=="middle"){ + d.y -= (this._lineHeight )/2 - this.style.text.pad; + } + + this.hit = this.container.createRect(d) + .setStroke(this.style.currentHit) + .setFill(this.style.currentHit.fill); + //.setFill("#ffff00"); + + this._setNodeAtts(this.hit); + this.hit.moveToBack(); + }, + makeFit: function(text, w){ + var span = dojo.create('span', {innerHTML:text, id:"foo"}, document.body); + var sz = 1; + dojo.style(span, "fontSize", sz+"px"); + var cnt = 30; + while(dojo.marginBox(span).w<w){ + sz++; + dojo.style(span, "fontSize", sz+"px"); + if(cnt--<=0) break; + } + sz--; + var box = dojo.marginBox(span); + dojo.destroy(span); + + return {size:sz, box:box}; + } + } +); +dojox.drawing.register({ + name:"dojox.drawing.stencil.Text" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/stencil/_Base.js b/js/dojo/dojox/drawing/stencil/_Base.js new file mode 100644 index 0000000..3b77e52 --- /dev/null +++ b/js/dojo/dojox/drawing/stencil/_Base.js @@ -0,0 +1,1212 @@ +//>>built +// wrapped by build app +define("dojox/drawing/stencil/_Base", ["dijit","dojo","dojox","dojo/require!dojo/fx/easing"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.stencil._Base"); +dojo.require("dojo.fx.easing"); + +/*===== +StencilArgs = { +// container: [readonly] dojo.gfx.group +// The parent shape that contains all +// shapes used in a Stencil +container:null, +// +// anchorType: String +// Optionally blank or 'group'. 'group' tells +// an anchor point that it must constrain +// itself to other anchor points. +anchorType:"", +// +// isText: Boolean +// Whether this is a text object or not +// (either stencil.text or tools.TextBlock) +isText:false, +// +// shortType: String +// The type of stencil that corresponds with the types and +// constructors used in Drawing.registerTool +shortType:"", +// +// annotation: Boolean +// A Stencil used within a Stencil. An annotation +// is not selectable or clickable. A Label would +// be one example. +annotation:false, +// +// subShape: Boolean +// A Stencil used within a Stencil. A subShape +// is clickable. An arrow head would be an example. +subShape:false, +// +// style: Object +// An instance of the styles and defaults used within +// the Stencil. +style:null, +// +// util: Object +// Pointer to util.common +util:null, +// +// mouse: Object +// Pointer to the mouse instance +mouse:null, +// +// keys: Object +// Pointer to the keys class +keys:null, +// +// points: StencilPoints +// Points is an array of objects that make up the +// description of a Stencil. The points to a Rect +// that is 100x100 and at x:10 and y:10 would look like: +// [{x:10,y:10}, {x:110, y:10}, {x:110, y:110}, {x:10, y:110}] +// Points go clockwise from the top left. In the case of Paths, +// they would go in the order that the Stencil would be drawn. +// Always when the points Array is set, a data Object is created +// as well. So never set points directly, always use setPoints(). +// See: +// setPoints() +points:[], +// +// data: StencilData +// A data object typically (but not always) resembles the data +// that is used to create the dojox.gfx Shape. The same Rect +// example shown in points above would look like: +// {x:10, y:10, width:100, height:100} +// And an Ellipse with the same coordinates: +// {cx:55, cy:55, rx:50, ry:50} +// The only Stencil that does not support data (at this time) +// is the Path. While x1,x2,x3... culd be used in a data object +// it doesn't provide much benefit. +// Always when a data object is set, a set of points is created +// as well. So never set data directly, always use setData(). +// See: +// setData() +data:null, +// +// marginZero [readonly] Number +// How closely shape can get to y:0 or x:0. Less than zero has +// bugs in VML. This is set with defaults, and should be equal +// to half the size of an anchor point (5 px) +marginZero:0, +// +// created [readonly] Boolean +// Whether the Stencil has been rendered for the first time or +// not. +created: false, +// +// highlighted [readonly] Boolean +// Whether the Stencil is highlighted or not. +highlighted:false, +// +// selected [readonly] Boolean +// Whether the Stencil is selected or not. +selected:false, +// +// draws [readonly] Boolean +// Whether the Stencil can draw with a mouse drag or can just +// be created programmtically. If the Stencil comes from the +// stencil package, it should be draw:false. If it comes from +// the tools package it should be draw:true. +draws:false +} + +StencilPoint = { +// summary: +// One point Object in the points Array +// x: Number +// x position of point +// y: Number +// y position of point +} + +ToolsSetup = { +// summary: +// An object attached to a Tool's constructor +// used to inform the toolbar of its information +// and properties. +// description: +// This object is inserted into the *function* of +// a tool (not a stencil). Like: function.ToolsSetup +// It must be attached after constructr creation, so +// this object is found at the botton of the file. +// +// name:String +// Fully qualified name of constructor +// tooltip: Stirng +// Text to display on toolbar button hover +// iconClass: String +// CSS class with icon information to attach +// to toolbar button. +} +=====*/ + +dojox.drawing.stencil._Base = dojox.drawing.util.oo.declare( + // summary: + // The base class used for all Stencils. + // description: + // All stencils extend this base class. + // Most methods and events can be found here. + // + function(options){ + //console.log("______Base", this.type, options) + // clone style so changes are reflected in future shapes + dojo.mixin(this, options); + this.style = options.style || dojox.drawing.defaults.copy(); + if(options.stencil){ + this.stencil = options.stencil; + this.util = options.stencil.util; + this.mouse = options.stencil.mouse; + this.container = options.stencil.container; + this.style = options.stencil.style; + } + + // don't use the 'g' on these, it affects + // the global RegExp + var lineTypes = /Line|Vector|Axes|Arrow/; + var textTypes = /Text/; + + this.shortType = this.util.abbr(this.type); + this.isText = textTypes.test(this.type); + this.isLine = lineTypes.test(this.type); + + this.renderHit = this.style.renderHitLayer; + if(!this.renderHit && this.style.renderHitLines && this.isLine){ + this.renderHit = true; + } + if(!this.renderHit && this.style.useSelectedStyle){ + this.useSelectedStyle = true; + this.selCopy = dojo.clone(this.style.selected); + for(var nm in this.style.norm){ + if(this.style.selected[nm]===undefined){ + this.style.selected[nm] = this.style.norm[nm]; + } + } + this.textSelected = dojo.clone(this.style.text); + this.textSelected.color = this.style.selected.fill; + + } + + + this.angleSnap = this.style.angleSnap || 1; + + this.marginZero = options.marginZero || this.style.anchors.marginZero; + this.id = options.id || this.util.uid(this.type); + this._cons = []; + + if(!this.annotation && !this.subShape){ + this.util.attr(this.container, "id", this.id); + } + + this.connect(this, "onBeforeRender", "preventNegativePos"); + + this._offX = this.mouse.origin.x; + this._offY = this.mouse.origin.y; + + if(this.isText){ + this.align = options.align || this.align; + this.valign = options.valign || this.valign; + if(options.data && options.data.makeFit){ + var textObj = this.makeFit(options.data.text, options.data.width); + this.textSize = this.style.text.size = textObj.size; + this._lineHeight = textObj.box.h; + }else{ + this.textSize = parseInt(this.style.text.size, 10); + this._lineHeight = this.textSize * 1.4; + } + + + // TODO: thinner text selection + //this.style.hitSelected.width *= 0.5; + // + // ouch. how verbose. My mixin is weak.... + this.deleteEmptyCreate = options.deleteEmptyCreate!==undefined ? options.deleteEmptyCreate : this.style.text.deleteEmptyCreate; + this.deleteEmptyModify = options.deleteEmptyModify!==undefined ? options.deleteEmptyModify : this.style.text.deleteEmptyModify; + } + + //this.drawingType + + this.attr(options.data); + + // make truthy + // add to renders below + // this.baseRender && render() + //if(this.type == "dojox.drawing.tools.TextBlock"){ + if(this.noBaseRender){ + // TextBlock will handle rendering itself + return; + } + + //console.log("BASE OPTS:", options) + if(options.points){ + //console.log("__________Base.constr >>>> ", this.type, "points", options.points) + if(options.data && options.data.closePath===false){ + this.closePath = false; + } + this.setPoints(options.points); + this.connect(this, "render", this, "onRender", true); + this.baseRender && this.enabled && this.render(); + options.label && this.setLabel(options.label); + options.shadow && this.addShadow(options.shadow); + + }else if(options.data){ + //console.log("___________Base.constr", this.type, "options data", options.data) + options.data.width = options.data.width ? options.data.width : this.style.text.minWidth; + options.data.height = options.data.height ? options.data.height : this._lineHeight; + this.setData(options.data); + this.connect(this, "render", this, "onRender", true); + this.baseRender && this.enabled && this.render(options.data.text); + this.baseRender && options.label && this.setLabel(options.label); + this.baseRender && options.shadow && this.addShadow(options.shadow); + + }else if(this.draws){ + //console.log("_____________Base.constr", this.type, "draws") + this.points = []; + this.data = {}; + this.connectMouse(); + this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender"); + } + if(this.showAngle){ + this.angleLabel = new dojox.drawing.annotations.Angle({stencil:this}); + } + + if(!this.enabled){ + this.disable(); + this.moveToBack(); + // some things render some don't... + this.render(options.data.text); + } + + }, + { + + // type: String + // The type of Stencil this is. Should be overridden + // by extending classes. + // FIXME: should this be declaredClass? + type:"dojox.drawing.stencil", + // + // minimumSize: Number + // The minimum size allowed for a render. If the size + // is less, the shape is destroyed. + minimumSize:10, + // + // enabled [readonly] Boolean + // Whether the Stencil is enabled or not. + enabled:true, + + + drawingType:"stencil", + + //points:[], + + setData: function(/*StencilData*/data){ + // summary: + // Setter for Stencil data; also converts + // data to points. See individual Stencils + // for specific data properties. + this.data = data; + this.points = this.dataToPoints(); + }, + + setPoints: function(/*StencilPoints*/points){ + // summary: + // Setter for Stencil points; also converts + // points to data. See individual Stencils + // for specific points properties. + this.points = points; + // Path doesn't do data + if(this.pointsToData){ + this.data = this.pointsToData(); + } + }, + + onDelete: function(/* Stencil */ stencil){ + // summary: + // Stub - fires before this is destroyed + console.info("onDelete", this.id); + }, + + onBeforeRender: function(/*Object*/ stencil){ + // summary: + // Stub - Fires before render occurs. + }, + + onModify: function(/*Object*/stencil){ + // summary: + // Stub - fires on change of any property, + // including style properties + + }, + + onChangeData: function(/*Object*/ stencil){ + // summary: + // Stub - fires on change of dimensional + // properties or a text change + }, + + onChangeText: function(value){ // value or 'this' ? + // summary: + // Stub - fires on change of text in a + // TextBlock tool only + }, + + onRender: function(/*Object*/ stencil){ + // summary: + // Stub - Fires on creation. + // Drawing connects to this (once!) to be + // notified of drag completion. But only if it + // was registered as a Tool. Creating Stencil in and of + // itself does not register it. + // + // This should fire + // at the *end* of creation (not during drag) + // + // FIXME: + // This should probably be onCreate. It should + // only fire once. But the mechanism for determining + // this is more complicated than it sounds. + // + this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender"); + this.created = true; + this.disconnectMouse(); + + // for Silverlight + if(this.shape){ + this.shape.superClass = this; + }else{ + this.container.superClass = this; + } + this._setNodeAtts(this); + //console.warn("ONRENDER", this.id, this) + }, + + onChangeStyle: function(/*Object*/stencil){ + // summary: + // Fires when styles of shape has changed + // + this._isBeingModified = true; // need this to prevent onRender + if(!this.enabled){ + this.style.current = this.style.disabled; + this.style.currentText = this.style.textDisabled; + this.style.currentHit = this.style.hitNorm; + + }else{ + this.style.current = this.style.norm; + this.style.currentHit = this.style.hitNorm; + this.style.currentText = this.style.text; + } + + if(this.selected){ + if(this.useSelectedStyle){ + this.style.current = this.style.selected; + this.style.currentText = this.textSelected; + } + this.style.currentHit = this.style.hitSelected; + + }else if(this.highlighted){ + //this.style.current = this.style.highlighted; + this.style.currentHit = this.style.hitHighlighted; + //this.style.currentText = this.style.textHighlighted; + } + + // NOTE: Can't just change props like setStroke + // because Silverlight throws error + this.render(); + }, + + animate: function(options, create){ + console.warn("ANIMATE..........................") + var d = options.d || options.duration || 1000; + var ms = options.ms || 20; + var ease = options.ease || dojo.fx.easing.linear; + var steps = options.steps; + var ts = new Date().getTime(); + var w = 100; + var cnt = 0; + var isArray = true; + var sp, ep; + + if(dojo.isArray(options.start)){ + sp = options.start; + ep = options.end; + + }else if(dojo.isObject(options.start)){ + sp = options.start; + ep = options.end; + isArray = false; + }else{ + + console.warn("No data provided to animate") + } + + var v = setInterval(dojo.hitch(this, function(){ + var t = new Date().getTime() - ts; + var p = ease(1-t/d); + if(t > d || cnt++ > 100){ + clearInterval(v); + return; + } + + if(isArray){ + var pnts = []; + dojo.forEach(sp, function(pt, i){ + + var o = { + x: (ep[i].x-sp[i].x)*p + sp[i].x, + y: (ep[i].y-sp[i].y)*p + sp[i].y + }; + pnts.push(o); + }); + this.setPoints(pnts); + this.render(); + + }else{ + + var o = {}; + for(var nm in sp){ + o[nm] = (ep[nm] - sp[nm]) * p + sp[nm]; + } + + this.attr(o); + + } + //console.dir(pnts) + + + //this.attr("height", w); + ////console.log("W:", w) + //w += 5; + + }), ms); + }, + + attr: function(/*String | Object*/key, /* ? String | Number */value){ + // summary + // Changes properties in the style or disabled styles, + // depending on whether the object is enabled. + // Also can be used to change most position and size props. + + // NOTE: JUST A SETTTER!! TODO! + + // WARNING: + // Not doing any Stencil-type checking here. Setting a height + // on a line or an angle on a rectangle will just not render. + + // FIXME + // 'width' attr is used for line width. How to change the width of a stencil? + var n = this.enabled?this.style.norm:this.style.disabled; + var t = this.enabled?this.style.text:this.style.textDisabled; + var ts = this.textSelected || {}, + o, + nm, + width, + styleWas = dojo.toJson(n), + textWas = dojo.toJson(t); + + var coords = { + x:true, + y:true, + r:true, + height:true, + width:true, + radius:true, + angle:true + }; + var propChange = false; + if(typeof(key)!="object"){ + o = {}; + o[key] = value; + }else{ + // prevent changing actual data + o = dojo.clone(key); + } + + if(o.width){ + // using width for size, + // borderWidth should be used + // for line thickness + width = o.width; + delete o.width; + } + + for(nm in o){ + if(nm in n){ n[nm] = o[nm]; } + if(nm in t){ t[nm] = o[nm]; } + if(nm in ts){ ts[nm] = o[nm]; } + + if(nm in coords){ + coords[nm] = o[nm]; + propChange = true; + if(nm == "radius" && o.angle===undefined){ + o.angle = coords.angle = this.getAngle(); + }else if(nm == "angle" && o.radius===undefined){ + o.radius = coords.radius = this.getRadius(); + } + + } + if(nm == "text"){ + this.setText(o.text); + } + if(nm == "label"){ + this.setLabel(o.label); + } + } + if(o.borderWidth!==undefined){ + n.width = o.borderWidth; + } + + if(this.useSelectedStyle){ + // using the orginal selected style copy as + // a reference map of what props to copy + for(nm in this.style.norm){ + if(this.selCopy[nm]===undefined){ + this.style.selected[nm] = this.style.norm[nm]; + } + } + this.textSelected.color = this.style.selected.color; + + } + + if(!this.created){ + return; + } + + // basic transform + if(o.x!==undefined || o.y!==undefined){ + var box = this.getBounds(true); + var mx = { dx:0, dy:0 }; + for(nm in o){ + if(nm=="x" || nm =="y" || nm =="r"){ + mx["d"+nm] = o[nm] - box[nm]; + } + } + this.transformPoints(mx); + } + + + var p = this.points; + if(o.angle!==undefined){ + this.dataToPoints({ + x:this.data.x1, + y:this.data.y1, + angle:o.angle, + radius:o.radius + }); + + } else if(width!==undefined){ + p[1].x = p[2].x = p[0].x + width; + this.pointsToData(p); + } + + if(o.height!==undefined && o.angle===undefined){ + console.log("Doing P2D-2"); + p[2].y = p[3].y = p[0].y + o.height; + this.pointsToData(p); + } + + if(o.r!==undefined){ + this.data.r = Math.max(0, o.r); + } + + //console.dir(this.data); + if(propChange || textWas!=dojo.toJson(t) || styleWas != dojo.toJson(n)){ + // to trigger the render + // other events will be called post render + this.onChangeStyle(this); + } + o.width = width; + + if(o.cosphi!=undefined){ + !this.data? this.data = {cosphi:o.cosphi} : this.data.cosphi = o.cosphi; + this.style.zAxis = o.cosphi!=0 ? true : false; + } + }, + + exporter: function(){ + // summary: + // Exports Stencil data + // + var type = this.type.substring(this.type.lastIndexOf(".")+1).charAt(0).toLowerCase() + + this.type.substring(this.type.lastIndexOf(".")+2); + var o = dojo.clone(this.style.norm); + o.borderWidth = o.width; + delete o.width; + if(type=="path"){ + o.points = this.points; + }else{ + o = dojo.mixin(o, this.data); + } + o.type = type; + if(this.isText){ + o.text = this.getText(); + o = dojo.mixin(o, this.style.text); + delete o.minWidth; + delete o.deleteEmptyCreate; + delete o.deleteEmptyModify; + } + var lbl = this.getLabel(); + if(lbl){ + o.label = lbl; + } + return o; + }, + + + // TODO: + // Makes these all called by att() + // Should points and data be? + // + disable: function(){ + // summary: + // Disables Stencil so it is not selectable. + // Changes the color to the disabled style. + this.enabled = false; + this.renderHit = false; + this.onChangeStyle(this); + }, + + enable: function(){ + // summary: + // Enables Stencil so it is not selectable (if + // it was selectable to begin with). Changes the + // color to the current style. + this.enabled = true; + this.renderHit = true; + this.onChangeStyle(this); + }, + + select: function(){ + // summary: + // Called when the Stencil is selected. + // NOTE: Calling this will not select the Stencil + // calling this just sets the style to the 'selected' + // theme. 'manager.Stencil' should be used for selecting + // Stencils. + // + this.selected = true; + this.onChangeStyle(this); + }, + + deselect: function(/*Boolean*/useDelay){ + // summary: + // Called when the Stencil is deselected. + // NOTE: Calling this will not deselect the Stencil + // calling this just sets the style to the current + // theme. 'manager.Stencil' should be used for selecting + // and deselecting Stencils. + // + // arguments: + // useDelay: Boolean + // Adds slight delay before the style is set. + // + // should not have to render here because the deselection + // re-renders after the transform + // but... oh well. + if(useDelay){ + setTimeout(dojo.hitch(this, function(){ + this.selected = false; + this.onChangeStyle(this); + }),200); + }else{ + this.selected = false; + this.onChangeStyle(this); + } + }, + _toggleSelected: function(){ + if(!this.selected){ return; } + this.deselect(); + setTimeout(dojo.hitch(this, "select"), 0); + }, + + highlight: function(){ + // summary: + // Changes style to the highlight theme. + this.highlighted = true; + this.onChangeStyle(this); + }, + + unhighlight: function(){ + // summary: + // Changes style to the current theme. + this.highlighted = false; + this.onChangeStyle(this); + }, + + moveToFront: function(){ + // summary: + // Moves Stencil to the front of all other items + // on the canvas. + this.container && this.container.moveToFront(); + }, + + moveToBack: function(){ + // summary: + // Moves Stencil to the back of all other items + // on the canvas. + this.container && this.container.moveToBack(); + }, + + onTransformBegin: function(/* ? manager.Anchor */anchor){ + // summary: + // Fired at the start of a transform. This would be + // an anchor drag or a selection. + // + this._isBeingModified = true; + }, + + onTransformEnd: function(/* manager.Anchor */anchor){ + // summary: + // Called from anchor point up mouse up + this._isBeingModified = false; + this.onModify(this); + }, + + onTransform: function(/* ? manager.Anchor */anchor){ + // summary: + // Called from anchor point mouse drag + // also called from plugins.Pan.checkBounds + if(!this._isBeingModified){ + this.onTransformBegin(); + } + // this is not needed for anchor moves, but it + // is for stencil move: + this.setPoints(this.points); + this.render(); + }, + + transformPoints: function(mx){ + // summary: + // Moves object to a new X Y location + // mx is additive. So mx.dx=1 will move the stencil + // 1 pixel to the right from wherever it was. + // + // An attempt is made to prevent < 0 errors, but + // this won't work on all shapes (like Axes) + // + if(!mx.dx && !mx.dy){ + // no change + return; + } + var backup = dojo.clone(this.points), abort = false; + dojo.forEach(this.points, function(o){ + o.x += mx.dx; + o.y += mx.dy; + if(o.x<this.marginZero || o.y<this.marginZero){ + abort = true; + } + }); + if(abort){ + this.points = backup; + console.error("Attempt to set object '"+this.id+"' to less than zero."); + return; + } + this.onTransform(); + this.onTransformEnd(); + }, + + applyTransform: function(mx){ + // summary: + // Applies the transform to the stencil + // NOTE: PARTIALLY IMPLEMENTED + // Only applies x y coords + this.transformPoints(mx); + }, + + setTransform: function(/*Object*/mx){ + // summary: + // Sets the transform to the stencil + // NOTE: PARTIALLY IMPLEMENTED + // Only applies x y coords + this.attr({ + x:mx.dx, + y:mx.dy + }); + }, + + getTransform: function(){ + // summary: + // Returns the current transform (position) of the Stencil's + // container + return this.selected ? this.container.getParent().getTransform() : {dx:0, dy:0}; // Object + }, + + addShadow: function(/*Object*/args){ + args = args===true ? {} : args; + args.stencil = this; + this.shadow = new dojox.drawing.annotations.BoxShadow(args); + }, + + removeShadow: function(){ + this.shadow.destroy(); + }, + + setLabel: function(/*String*/text){ + // summary: + // Creates and sets a label annotation for the Stencil. + // If Stencil contains a labelPosition method, that will + // be used for positioning. Otherwise + // dojox.drawing.util.positioning.label is used. + // arguments: + // text: String + // The text to set as the label. + // + if(!this._label){ + this._label = new dojox.drawing.annotations.Label({ + text:text, + util:this.util, + mouse:this.mouse, + stencil:this, + annotation:true, + container:this.container, + labelPosition:this.labelPosition + }); + }else if(text!=undefined){ + this._label.setLabel(text); + } + }, + + getLabel: function(){ + // summary: + // Get the text of the label. + // + if(this._label){ + return this._label.getText(); // String + } + return null; //null + }, + + getAngle: function(){ + // summary: + // Gets angle of Stencil + // NOTE: Only works for Lines, Arrows, Vectors and Axes + // (works on points, not transforms) + var d = this.pointsToData(); + var obj = { + start:{ + x:d.x1, + y:d.y1 + }, + x:d.x2, + y:d.y2 + }; + var angle = this.util.angle(obj, this.angleSnap); + // converting the angle for display: -180 -> 180, -90 -> 270 + angle<0 ? angle = 360 + angle : angle; + return angle; + }, + getRadius: function(){ + // summary: + // Gets radius (length) of Stencil + // NOTE: Only works for Lines, Arrows and Vectors + // (not for Ellipse, Axes has its own version) + // + var box = this.getBounds(true); + var line = {start:{x:box.x1, y:box.y1}, x:box.x2, y:box.y2}; + return this.util.length(line); + }, + getBounds: function(/* ? Boolean*/absolute){ + // summary: + // Returns the coordinates of the Stencil. This is often + // different than the data or the points. + // arguments: + // absolute: Boolean + // Keeps lines from flipping (see note). + // + // NOTE: Won't work for paths or annotations (labels, Axes, arrow tips) + // They should overwrite. + // NOTE: Primarily used for checking for if shape is off + // canvas. Therefore Lines could get flipped. Use absolute + // to prevent this. + // + var p = this.points, x1, y1, x2, y2; + if(p.length==2){ + if(absolute){ + x1 = p[0].x; + y1 = p[0].y; + x2 = p[1].x; + y2 = p[1].y + }else{ + x1 = p[0].x < p[1].x ? p[0].x : p[1].x; + y1 = p[0].y < p[1].y ? p[0].y : p[1].y; + x2 = p[0].x < p[1].x ? p[1].x : p[0].x; + y2 = p[0].y < p[1].y ? p[1].y : p[0].y; + } + return { + x1:x1, + y1:y1, + x2:x2, + y2:y2, + x:x1, + y:y1, + w:x2-x1, + h:y2-y1 + }; // Object + }else{ + return { + x1:p[0].x, + y1:p[0].y, + x2:p[2].x, + y2:p[2].y, + x:p[0].x, + y:p[0].y, + w:p[2].x - p[0].x, + h:p[2].y - p[0].y + }; // Object + } + }, + + + preventNegativePos: function(){ + // summary: + // Internal. Prevent item from being drawn/rendered less + // than zero on the X or Y. + // + // if being modified anchors will prevent less than zero. + if(this._isBeingModified){ return; } + // FIXME: why is this sometimes empty? + if(!this.points || !this.points.length){ return; } + + if(this.type=="dojox.drawing.tools.custom.Axes"){ + // this scenario moves all points if < 0 + var minY = this.marginZero, minX = this.marginZero; + dojo.forEach(this.points, function(p){ minY = Math.min(p.y, minY); }); + dojo.forEach(this.points, function(p){ minX = Math.min(p.x, minX); }); + + if(minY<this.marginZero){ + dojo.forEach(this.points, function(p, i){ + p.y = p.y + (this.marginZero-minY) + }, this); + } + if(minX<this.marginZero){ + dojo.forEach(this.points, function(p){ + p.x += (this.marginZero-minX) + }, this); + } + + }else{ + // this scenario moves just the one point that is < 0 + dojo.forEach(this.points, function(p){ + p.x = p.x < 0 ? this.marginZero : p.x; + p.y = p.y < 0 ? this.marginZero : p.y; + }); + } + this.setPoints(this.points); + }, + + _onPostRender: function(/*Object*/data){ + // summary: + // Drag-create or programmatic create calls onRender + // and afterwards, _onPostRender is called and + // manages further events. + // + // TODO: can this be onModify? Is that more clear? + // + //console.info("...........post render....."); + + if(this._isBeingModified){ + this.onModify(this); + this._isBeingModified = false; + }else if(!this.created){ + //this.onCreate(this); + //this.onRender(this); + } + + if(!this.editMode && !this.selected && this._prevData && dojo.toJson(this._prevData) != dojo.toJson(this.data)){ + //console.info("_Base data changed ----> : this.editMode:", this.editMode) + this.onChangeData(this); + this._prevData = dojo.clone(this.data); + + }else if(!this._prevData && (!this.isText || this.getText())){ + //console.info("_Base no prevData.........................."); + this._prevData = dojo.clone(this.data); + + } + + }, + + _setNodeAtts: function(shape){ + // summary: + // Internal. Sets the rawNode attribute. (Or in Silverlight + // an "object attribute". "stencil" is + // used by the application to determine if + // something is selectable or not. This also + // sets the mouse custom events like: + // "onStencilUp". To disable the selectability, + // make the att "", which causes a standard + // mouse event. + // Labels are special and used to select master stencils. + var att = this.enabled && (!this.annotation || this.drawingType=="label") ? this.drawingType : ""; + this.util.attr(shape, "drawingType", att); + }, + + + destroy: function(){ + // summary: + // Destroys this Stencil + // Note: + // Can connect to this, but it's better to + // connect to onDelete + // + // prevent loops: + if(this.destroyed){ return; } + if(this.data || this.points && this.points.length){ + this.onDelete(this); + } + + this.disconnectMouse(); + this.disconnect(this._cons); + dojo.disconnect(this._postRenderCon); + this.remove(this.shape, this.hit); + this.destroyed = true; + }, + + remove: function(/*Shape...*/){ + // summary: + // Removes shape(s), typically before a re-render + // No args defaults to this.shape + // Pass in multiple args to remove multiple shapes + // + // FIXME: Use an Array of all shapes + // + var a = arguments; + if(!a.length){ + if(!this.shape){ return; } + a = [this.shape]; + } + for(var i=0;i<a.length;i++){ + if(a[i]){ + a[i].removeShape(); + } + } + }, + + connectMult: function(/*dojo.connect args */){ + // summary: + // Convenience method for batches of quick connects + // Handles are not returned and therefore cannot be + // disconnected until Shape destroy time + // + if(arguments.length>1){ + // arguments are the connect params + this._cons.push(this.connect.apply(this, arguments)); + }else if(dojo.isArray(arguments[0][0])){ + // an array of arrays of params + dojo.forEach(arguments[0], function(ar){ + this._cons.push(this.connect.apply(this, ar)); + }, this); + }else{ + //one array of params + this._cons.push(this.connect.apply(this, arguments[0])); + } + + }, + + // TODO: connect to a Shape event from outside class + connect: function(o, e, s, m, /* Boolean*/once){ + // summary: + // Convenience method for quick connects + // See comments below for possiblities + // functions can be strings + // once: + // If true, the connection happens only + // once then disconnects. Five args are required + // for this functionality. + // + var c; + if(typeof(o)!="object"){ + if(s){ + // ** function object function ** + m = s; s = e; e=o; o = this; + }else{ + // ** function function ** + m = e; e = o; o = s = this; + } + }else if(!m){ + // ** object function function ** + m = s; s = this; + }else if(once){ + // ** object function object function Boolean ** + c = dojo.connect(o, e, function(evt){ + dojo.hitch(s, m)(evt); + dojo.disconnect(c); + }); + this._cons.push(c); + return c; + }else{ + // ** object function object function ** + } + c = dojo.connect(o, e, s, m); + this._cons.push(c); + return c; + }, + + disconnect: function(/*handle | Array*/handles){ + // summary: + // Removes connections based on passed + // handles arguments + if(!handles){ return } + if(!dojo.isArray(handles)){ handles=[handles]; } + dojo.forEach(handles, dojo.disconnect, dojo); + }, + + connectMouse: function(){ + // summary: + // Internal. Registers this Stencil to receive + // mouse events. + this._mouseHandle = this.mouse.register(this); + }, + disconnectMouse: function(){ + // summary: + // Internal. Unregisters this Stencil from receiving + // mouse events. + this.mouse.unregister(this._mouseHandle); + }, + + // Should be overwritten by sub class: + render: function(){ + // summary: + // This Stencil's render description. Often + // calls 'sub render' methods. + }, + //renderOutline: function(){}, + dataToPoints: function(/*Object*/data){ + // summary: + // Converts data to points. + }, + pointsToData: function(/*Array*/points){ + // summary: + // Converts points to data + }, + onDown: function(/*EventObject*/obj){ + // summary: + // Mouse event, fired on mousedown on canvas + // + // by default, object is ready to accept data + // turn this off for dragging or onRender will + // keep firing and register the shape + // NOTE: Not needed for all stencils. Axes needs it. + this._downOnCanvas = true; + dojo.disconnect(this._postRenderCon); + this._postRenderCon = null; + }, + onMove: function(/*EventObject*/obj){ + // summary: + // Mouse event, fired on mousemove while mouse + // is not down. + // NOTE: Not currently implemented + }, + onDrag: function(/*EventObject*/obj){ + // summary: + // Mouse event, fired on mousemove while mouse + // is down on canvas + }, + onUp: function(/*EventObject*/obj){ + // summary: + // Mouse event, fired on mouseup + } + } +); + + +}); diff --git a/js/dojo/dojox/drawing/tools/Arrow.js b/js/dojo/dojox/drawing/tools/Arrow.js new file mode 100644 index 0000000..d87396d --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Arrow.js @@ -0,0 +1,87 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Arrow", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Arrow"); + +dojox.drawing.tools.Arrow = dojox.drawing.util.oo.declare( + // summary: + // Extends stencil.Line and adds an arrow head + // to the end and or start. + // + dojox.drawing.tools.Line, + function(options){ + // summary: constructor + if(this.arrowStart){ + this.begArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:0, idx2:1}); + } + if(this.arrowEnd){ + this.endArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:1, idx2:0}); + } + if(this.points.length){ + // This is protecting against cases when there are no points + // not sure how that would ever happen + // Render & label here instead of in base because of Arrow annotation + this.render(); + options.label && this.setLabel(options.label); + } + }, + { + draws:true, + type:"dojox.drawing.tools.Arrow", + baseRender:false, + + // arrowStart: Boolean + // Whether or not to place an arrow on start. + arrowStart:false, + // + // arrowEnd: Boolean + // Whether or not to place an arrow on end. + arrowEnd:true, + + labelPosition: function(){ + // summary: + // The custom position used for the label + // + var d = this.data; + var pt = dojox.drawing.util.positioning.label({x:d.x1,y:d.y1},{x:d.x2,y:d.y2}); + return { + x:pt.x, + y:pt.y + } + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this.shape){ return; } + + // if too small, need to reset + var p = this.points; + var len = this.util.distance(p[0].x,p[0].y,p[1].x,p[1].y); + if(len<this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + + var pt = this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + + this.renderedOnce = true; + this.onRender(this); + } + } +); + +dojox.drawing.tools.Arrow.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Arrow", + tooltip:"Arrow Tool", + iconClass:"iconArrow" +}; + +dojox.drawing.register(dojox.drawing.tools.Arrow.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Ellipse.js b/js/dojo/dojox/drawing/tools/Ellipse.js new file mode 100644 index 0000000..927c0de --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Ellipse.js @@ -0,0 +1,83 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Ellipse", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Ellipse"); + +dojox.drawing.tools.Ellipse = dojox.drawing.util.oo.declare( + // summary: + // A drawable Ellipse. + // + dojox.drawing.stencil.Ellipse, + function(){ + // summary: constructor + }, + { + draws:true, + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var s = obj.start, e = obj; + var x = s.x < e.x ? s.x : e.x, + y = s.y < e.y ? s.y : e.y, + w = s.x < e.x ? e.x-s.x : s.x-e.x, + h = s.y < e.y ? e.y-s.y : s.y-e.y; + + if(this.keys.shift){ w = h = Math.max(w,h); } + if(!this.keys.alt){ // ellipse is normally on center + x+=w/2; y+=h/2; w/=2; h/=2; + } else{ + if(y - h < 0){ h = y; } + if(x - w < 0){ w = x; } + } + + this.points = [ + {x:x-w, y:y-h}, // TL + {x:x+w, y:y-h}, // TR + {x:x+w, y:y+h}, // BR + {x:x-w, y:y+h} // BL + ]; + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default shape on single click + if(!this.shape){ + var s = obj.start, e = this.minimumSize*2; + this.data = { + cx: s.x+e, + cy: s.y+e, + rx: e, + ry: e + }; + this.dataToPoints(); + this.render(); + }else{ + // if too small, need to reset + var o = this.pointsToData(); + console.log("Create a default shape here, pt to data: ",o); + if(o.rx*2<this.minimumSize && o.ry*2 < this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Ellipse.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Ellipse", + tooltip:"Ellipse Tool", + iconClass:"iconEllipse" +}; + +dojox.drawing.register(dojox.drawing.tools.Ellipse.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Line.js b/js/dojo/dojox/drawing/tools/Line.js new file mode 100644 index 0000000..f8d3cde --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Line.js @@ -0,0 +1,122 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Line", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Line"); + +dojox.drawing.tools.Line = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable Line + dojox.drawing.stencil.Line, + function(){ + // summary: constructor + }, + { + draws:true, + showAngle:true, + onTransformEnd: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformEnd + // + this._toggleSelected(); + if(this.getRadius()<this.minimumSize){ + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:p[0].x, y:p[0].y} + ]); + }else{ + var d = this.data; + var obj = {start:{x:d.x1,y:d.y1},x:d.x2,y:d.y2}; + var pt = this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:d.x1, y:d.y1}, + {x:pt.x, y:pt.y} + ]); + + this._isBeingModified = false; + this.onModify(this); + } + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(this.created){ return; } + var x1 = obj.start.x, + y1 = obj.start.y, + x2 = obj.x, + y2 = obj.y; + + if(this.keys.shift){ + var pt = this.util.snapAngle(obj, 45/180); + x2 = pt.x; + y2 = pt.y; + } + + if(this.keys.alt){ + // FIXME: + // should double the length of the line + // FIXME: + // if alt dragging past ZERO it seems to work + // but select/deselect shows bugs + var dx = x2>x1 ? ((x2-x1)/2) : ((x1-x2)/-2); + var dy = y2>y1 ? ((y2-y1)/2) : ((y1-y2)/-2); + x1 -= dx; + x2 -= dx; + y1 -= dy; + y2 -= dy; + } + + this.setPoints([ + {x:x1, y:y1}, + {x:x2, y:y2} + ]); + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default shape on single click + if(!this.shape){ + var s = obj.start, e = this.minimumSize*4; + this.setPoints([ + {x:s.x, y:s.y+e}, + {x:s.x, y:s.y} + ]); + this.render(); + + }else{ + // if too small, need to reset + + if(this.getRadius()<this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + + var pt = this.util.snapAngle(obj, this.angleSnap/180); + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + + this.renderedOnce = true; + this.onRender(this); + } + } +); + +dojox.drawing.tools.Line.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Line", + tooltip:"Line Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Line.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Path.js b/js/dojo/dojox/drawing/tools/Path.js new file mode 100644 index 0000000..c9a01af --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Path.js @@ -0,0 +1,207 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Path", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Path"); + +dojox.drawing.tools.Path = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable Path + // + dojox.drawing.stencil.Path, + function(){ + // summary: constructor + + this.pathMode = ""; + this.currentPathMode = ""; + this._started = false; + this.oddEvenClicks = 0; + + }, + { + draws:true, + onDown: function(obj){ + if(!this._started){ + this.onStartPath(obj); + } + + }, + + makeSubPath: function(_closePath){ + if(_closePath){ + if(this.currentPathMode=="Q"){ + this.points.push({ + x:this.points[0].x, + y:this.points[0].y + }); + } + this.points.push({t:"Z"}); + this.render(); + } + this.currentPathMode = ""; + this.pathMode = "M"; + }, + + onStartPath: function(obj){ + this._started = true; + this.revertRenderHit = this.renderHit; + this.renderHit = false; + this.closePath = false; + + + this.mouse.setEventMode("PathEdit"); + + this.closePoint = {x:obj.x, y:obj.y}; + + this._kc1 = this.connect(this.keys, "onEsc", this, function(){ + this.onCompletePath(false); + }); + + this._kc2 = this.connect(this.keys, "onKeyUp", this, function(evt){ + + switch(evt.letter){ + case "c": + this.onCompletePath(true); break; + case "l": this.pathMode = "L"; break; + case "m": this.makeSubPath(false); break; + case "q": this.pathMode = "Q"; break; + case "s": this.pathMode = "S"; break; + case "z": this.makeSubPath(true); break; + } + + //console.log("KEY:", evt.letter); + }); + }, + + onCompletePath:function(_closePath){ + this.remove(this.closeGuide, this.guide); + var box = this.getBounds(); + if(box.w<this.minimumSize && box.h<this.minimumSize){ + this.remove(this.hit, this.shape, this.closeGuide); + this._started = false; + this.mouse.setEventMode(""); + this.setPoints([]); + return; + } + + + if(_closePath){ + if(this.currentPathMode=="Q"){ + this.points.push({ + x:this.points[0].x, + y:this.points[0].y + }); + } + this.closePath = true; + } + + + this.renderHit = this.revertRenderHit; + this.renderedOnce = true; + this.onRender(this); + this.disconnect([this._kc1, this._kc2]); + this.mouse.setEventMode(""); + this.render(); + //console.log(this.stringPath); + }, + + onUp: function(/*EventObject*/obj){ + //console.log(" Path UP", obj.mid, "within:", obj.withinCanvas) + + + if(!this._started || !obj.withinCanvas){ return; } + + + if(this.points.length>2 && this.closeRadius>this.util.distance(obj.x, obj.y, this.closePoint.x, this.closePoint.y)){ + this.onCompletePath(true); + + }else { + var p = { + x:obj.x, + y:obj.y + }; + this.oddEvenClicks++; + if(this.currentPathMode != this.pathMode){ + if(this.pathMode=="Q"){ + p.t = "Q"; + this.oddEvenClicks = 0; + }else if(this.pathMode=="L"){ + p.t = "L"; + }else if(this.pathMode=="M"){ + p.t = "M"; + this.closePoint = {x:obj.x, y:obj.y}; + } + this.currentPathMode = this.pathMode; + } + + + this.points.push(p); + if(this.points.length>1){ + this.remove(this.guide); + this.render(); + } + + } + + //console.log(this.stringPath); + }, + createGuide: function(obj){ + if(!this.points.length){ return; } + var realPoints = [].concat(this.points); + + var pt = { + x:obj.x, + y:obj.y + }; + if(this.currentPathMode=="Q" && this.oddEvenClicks % 2){ + // On a Q curve, every other click needs to be a + // straight line - the inbetween Q coords don't render + pt.t = "L"; // this is not permanent + } + + this.points.push(pt); + + this.render(); + this.points = realPoints; + + + var dist = this.util.distance(obj.x, obj.y, this.closePoint.x, this.closePoint.y); + if(this.points.length>1){ + if(dist<this.closeRadius && !this.closeGuide){ + var c = { + cx:this.closePoint.x, + cy:this.closePoint.y, + rx:this.closeRadius, + ry:this.closeRadius + } + this.closeGuide = this.container.createEllipse(c) + .setFill(this.closeColor); + + }else if(dist>this.closeRadius && this.closeGuide){ + this.remove(this.closeGuide); + this.closeGuide = null; + } + } + + }, + + onMove: function(obj){ + if(!this._started){ return; } + this.createGuide(obj); + }, + onDrag: function(obj){ + if(!this._started){ return; } + this.createGuide(obj); + } + } +); + +dojox.drawing.tools.Path.setup = { + // summary: See Base ToolsSetup + // + name:"dojox.drawing.tools.Path", + tooltip:"Path Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Path.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Pencil.js b/js/dojo/dojox/drawing/tools/Pencil.js new file mode 100644 index 0000000..a0b82a1 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Pencil.js @@ -0,0 +1,87 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Pencil", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Pencil"); + +dojox.drawing.tools.Pencil = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable, continous Path + // + dojox.drawing.stencil.Path, + function(){ + // summary: constructor + this._started = false; + }, + { + draws:true, + // minDist: Number + // The distance the mouse must travel before rendering + // a path segment. Lower number is a higher definition + // path but more points. + minDist: 15, // how to make this more dynamic? Settable? + + onDown: function(obj){ + this._started = true; + var p = { + x:obj.x, + y:obj.y + }; + this.points = [p]; + this.lastPoint = p; + this.revertRenderHit = this.renderHit; + this.renderHit = false; + this.closePath = false; + }, + + onDrag: function(obj){ + if( + !this._started + || this.minDist > this.util.distance(obj.x, obj.y, this.lastPoint.x, this.lastPoint.y) + ){ return; } + + var p = { + x:obj.x, + y:obj.y + }; + this.points.push(p); + this.render(); + this.checkClosePoint(this.points[0], obj); + this.lastPoint = p; + }, + + onUp: function(obj){ + if(!this._started){ return; } + if(!this.points || this.points.length<2){ + this._started = false; + this.points = []; + return; + } + var box = this.getBounds(); + if(box.w<this.minimumSize && box.h<this.minimumSize){ + this.remove(this.hit, this.shape, this.closeGuide); + this._started = false; + this.setPoints([]); + return; + } + if(this.checkClosePoint(this.points[0], obj, true)){ + this.closePath = true; + } + this.renderHit = this.revertRenderHit; + this.renderedOnce = true; + this.render(); + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Pencil.setup = { + // summary: See Base ToolsSetup + // + name:"dojox.drawing.tools.Pencil", + tooltip:"Pencil Tool", + iconClass:"iconLine" +}; + +dojox.drawing.register(dojox.drawing.tools.Pencil.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/Rect.js b/js/dojo/dojox/drawing/tools/Rect.js new file mode 100644 index 0000000..75ab4aa --- /dev/null +++ b/js/dojo/dojox/drawing/tools/Rect.js @@ -0,0 +1,83 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/Rect", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.Rect"); + +dojox.drawing.tools.Rect = dojox.drawing.util.oo.declare( + // summary: + // Class for a drawable rectangle + // + dojox.drawing.stencil.Rect, + function(){ + // summary: constructor + }, + { + draws:true, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var s = obj.start, e = obj; + var x = s.x < e.x ? s.x : e.x, + y = s.y < e.y ? s.y : e.y, + w = s.x < e.x ? e.x-s.x : s.x-e.x, + h = s.y < e.y ? e.y-s.y : s.y-e.y; + + if(this.keys.shift){ w = h = Math.max(w,h); } + + if(this.keys.alt){ + x-=w; y-=h; w*=2; h*=2; + x = Math.max(x, 0); + y = Math.max(y, 0); + } + this.setPoints ([ + {x:x, y:y}, // TL + {x:x+w, y:y}, // TR + {x:x+w, y:y+h}, // BR + {x:x, y:y+h} // BL + ]); + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + + //Default shape on single click + if(!this.shape){ + var s = obj.start; + var e = this.minimumSize*4; + this.setPoints([ + {x:s.x, y:s.y}, + {x:s.x+e, y:s.y}, + {x:s.x+e, y:s.y+e}, + {x:s.x, y:s.y+e} + ]); + this.render(); + }else{ + + // if too small, need to reset + var o = this.data; + if(o.width<this.minimumSize && o.height < this.minimumSize){ + this.remove(this.shape, this.hit); + return; + } + } + this.onRender(this); + + } + } +); + +dojox.drawing.tools.Rect.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.Rect", + tooltip:'<span class="drawingTipTitle">Rectangle Tool</span><br/>' + + '<span class="drawingTipDesc">SHIFT - constrain to square</span>', + iconClass:"iconRect" +}; +dojox.drawing.register(dojox.drawing.tools.Rect.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/TextBlock.js b/js/dojo/dojox/drawing/tools/TextBlock.js new file mode 100644 index 0000000..4584e5e --- /dev/null +++ b/js/dojo/dojox/drawing/tools/TextBlock.js @@ -0,0 +1,808 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/TextBlock", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Text"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.TextBlock"); +dojo.require("dojox.drawing.stencil.Text"); + +(function(){ + var conEdit; + dojo.addOnLoad(function(){ + // In order to use VML in IE, it's necessary to remove the + // DOCTYPE. But this has the side effect that causes a bug + // where contenteditable divs cannot be made dynamically. + // The solution is to include one in the main document + // that can be appended and removed as necessary: + // <div id="conEdit" contenteditable="true"></div> + // + // console.log("Removing conedit"); + conEdit = dojo.byId("conEdit"); + if(!conEdit){ + console.error("A contenteditable div is missing from the main document. See 'dojox.drawing.tools.TextBlock'") + }else{ + conEdit.parentNode.removeChild(conEdit); + } + }); + + dojox.drawing.tools.TextBlock = dojox.drawing.util.oo.declare( + // summary: + // A tool to create text fields on a canvas. + // description: + // Extends stencil.Text by adding an HTML layer that + // can be dragged out to a certain size, and accept + // a text entry. Will wrap text to the width of the + // html field. + // When created programmtically, use 'auto' to shrink + // the width to the size of the text. Use line breaks + // ( \n ) to create new lines. + // + // TODO - disable zoom while showing? + // + // FIXME: + // Handles width: auto, align:middle, etc. but for + // display only, edit is out of whack + // + dojox.drawing.stencil.Text, + function(options){ + // summary: constructor + // + if(options.data){ + var d = options.data; + var text = d.text ? this.typesetter(d.text) : d.text; + var w = !d.width ? this.style.text.minWidth : d.width=="auto" ? "auto" : Math.max(d.width, this.style.text.minWidth); + var h = this._lineHeight; + + if(text && w=="auto"){ + var o = this.measureText(this.cleanText(text, false), w); + w = o.w; + h = o.h; + }else{ + // w = this.style.text.minWidth; + this._text = ""; + } + + this.points = [ + {x:d.x, y:d.y}, + {x:d.x+w, y:d.y}, + {x:d.x+w, y:d.y+h}, + {x:d.x, y:d.y+h} + ]; + + if(d.showEmpty || text){ + this.editMode = true; + + + dojo.disconnect(this._postRenderCon); + this._postRenderCon = null; + this.connect(this, "render", this, "onRender", true); + + if(d.showEmpty){ + this._text = text || ""; + this.edit(); + }else if(text && d.editMode){ + this._text = ""; + this.edit(); + }else if(text){ + this.render(text); + } + setTimeout(dojo.hitch(this, function(){ + this.editMode = false; + }),100) + + }else{ + // Why make it if it won't render... + this.render(); + } + + }else{ + this.connectMouse(); + this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender"); + } + //console.log("TextBlock:", this.id) + }, + { + draws:true, + baseRender:false, + type:"dojox.drawing.tools.TextBlock", + _caretStart: 0, + _caretEnd: 0, + _blockExec: false, + +/*===== +StencilData: { + // summary: + // The data used to create the dojox.gfx Text + // x: Number + // Left point x + // y: Number + // Top point y + // width: ? Number|String + // Optional width of Text. Not required but reccommended. + // for auto-sizing, use 'auto' + // height: ? Number + // Optional height of Text. If not provided, _lineHeight is used. + // text: String + // The string content. If not provided, may auto-delete depending on defaults. +}, +=====*/ + + // selectOnExec: Boolean + // Whether the Stencil is selected when the text field + // is executed or not + selectOnExec:true, + // + // showEmpty: Boolean + // If true and there is no text in the data, the TextBlock + // Is displayed and focused and awaits input. + showEmpty: false, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(!this.parentNode){ + this.showParent(obj); + } + var s = this._startdrag, e = obj.page; + this._box.left = (s.x < e.x ? s.x : e.x); + this._box.top = s.y; + this._box.width = (s.x < e.x ? e.x-s.x : s.x-e.x) + this.style.text.pad; + + dojo.style(this.parentNode, this._box.toPx()); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + + if(!this._downOnCanvas){ return; } + this._downOnCanvas = false; + + var c = dojo.connect(this, "render", this, function(){ + dojo.disconnect(c); + this.onRender(this); + + }); + this.editMode = true; + this.showParent(obj); + this.created = true; + this.createTextField(); + this.connectTextField(); + }, + + showParent: function(/*EventObject*/obj){ + // summary: + // Internal. Builds the parent node for the + // contenteditable HTML node. + // + if(this.parentNode){ return; } + var x = obj.pageX || 10; + var y = obj.pageY || 10; + this.parentNode = dojo.doc.createElement("div"); + this.parentNode.id = this.id; + var d = this.style.textMode.create; + this._box = { + left:x, + top:y, + width:obj.width || 1, + height:obj.height && obj.height>8 ? obj.height : this._lineHeight, + border:d.width+"px "+d.style+" "+d.color, + position:"absolute", + zIndex:500, + toPx: function(){ + var o = {}; + for(var nm in this){ + o[nm] = typeof(this[nm])=="number" && nm!="zIndex" ? this[nm] + "px" : this[nm]; + } + return o; + } + }; + + dojo.style(this.parentNode, this._box); + + document.body.appendChild(this.parentNode); + }, + createTextField: function(/*String*/txt){ + // summary: + // Internal. Inserts the contenteditable HTML node + // into its parent node, and styles it. + // + // style parent + var d = this.style.textMode.edit; + this._box.border = d.width+"px "+d.style+" "+d.color; + this._box.height = "auto"; + this._box.width = Math.max(this._box.width, this.style.text.minWidth*this.mouse.zoom); + dojo.style(this.parentNode, this._box.toPx()); + // style input + this.parentNode.appendChild(conEdit); + dojo.style(conEdit, { + height: txt ? "auto" : this._lineHeight+"px", + fontSize:(this.textSize/this.mouse.zoom)+"px", + fontFamily:this.style.text.family + }); + // FIXME: + // In Safari, if the txt ends with '&' it gets stripped + conEdit.innerHTML = txt || ""; + + return conEdit; //HTMLNode + }, + connectTextField: function(){ + // summary: + // Internal. Creates the connections to the + // contenteditable HTML node. + // + if(this._textConnected){ return; } // good ol' IE and its double events + // FIXME: + // Ouch-getting greekPalette by id. At the minimum this should + // be from the plugin manager + var greekPalette = dijit.byId("greekPalette"); + var greekHelp = greekPalette==undefined ? false : true; + if(greekHelp){ + //set it up + dojo.mixin(greekPalette,{ + _pushChangeTo: conEdit, + _textBlock: this + }); + }; + + this._textConnected = true; + this._dropMode = false; + this.mouse.setEventMode("TEXT"); + this.keys.editMode(true); + var kc1, kc2, kc3, kc4, self = this, _autoSet = false, + exec = function(){ + if(self._dropMode){ return; } + dojo.forEach([kc1,kc2,kc3,kc4], function(c){ + dojo.disconnect(c) + }); + self._textConnected = false; + self.keys.editMode(false); + self.mouse.setEventMode(); + self.execText(); + }; + + kc1 = dojo.connect(conEdit, "keyup", this, function(evt){ + // if text is empty, we need a height so the field's height + // doesn't collapse + if(dojo.trim(conEdit.innerHTML) && !_autoSet){ + dojo.style(conEdit, "height", "auto"); _autoSet = true; + }else if(dojo.trim(conEdit.innerHTML).length<2 && _autoSet){ + dojo.style(conEdit, "height", this._lineHeight+"px"); _autoSet = false; + } + + if(!this._blockExec){ + if(evt.keyCode==13 || evt.keyCode==27){ + dojo.stopEvent(evt); + exec(); + } + } else { + if(evt.keyCode==dojo.keys.SPACE){ + dojo.stopEvent(evt); + greekHelp && greekPalette.onCancel(); + } + } + }); + kc2 = dojo.connect(conEdit, "keydown", this, function(evt){ + if(evt.keyCode==13 || evt.keyCode==27){ // TODO: make escape an option + dojo.stopEvent(evt); + } + // if backslash, user is inputting a special character + // This gives popup help. + if(evt.keyCode==220){ + if(!greekHelp){ + console.info("For greek letter assistance instantiate: dojox.drawing.plugins.drawing.GreekPalette"); + return; + } + dojo.stopEvent(evt); + this.getSelection(conEdit); + // Differences in how browsers handle events made it necessary + // to stop the evt and add the backslash here. + this.insertText(conEdit,"\\"); + this._dropMode = true; + this._blockExec = true; + greekPalette.show({ + around:this.parentNode, + orient:{'BL':'TL'} + }); + } + if(!this._dropMode){ + this._blockExec = false; + } else { + // Controls for when we have a character helper and it's active + switch(evt.keyCode){ + case dojo.keys.UP_ARROW: + case dojo.keys.DOWN_ARROW: + case dojo.keys.LEFT_ARROW: + case dojo.keys.RIGHT_ARROW: + dojo.stopEvent(evt); + greekPalette._navigateByArrow(evt); + break; + case dojo.keys.ENTER: + dojo.stopEvent(evt); + greekPalette._onCellClick(evt); + break; + case dojo.keys.BACKSPACE: + case dojo.keys.DELETE: + dojo.stopEvent(evt); + greekPalette.onCancel(); + break; + } + } + + }); + + kc3 = dojo.connect(document, "mouseup", this, function(evt){ + // note: _onAnchor means an anchor has been clicked upon + if(!this._onAnchor && evt.target.id != "conEdit"){ + dojo.stopEvent(evt); + exec(); + }else if(evt.target.id == "conEdit" && conEdit.innerHTML == ""){ + // wonky stuff happens when you click on the + // field when its empty. + conEdit.blur(); + setTimeout(function(){ + conEdit.focus(); + },200) + } + }); + + this.createAnchors(); + + kc4 = dojo.connect(this.mouse, "setZoom", this, function(evt){ + exec(); + }); + + + conEdit.focus(); + + this.onDown = function(){}; + this.onDrag = function(){}; + + setTimeout(dojo.hitch(this, function(){ + // once again for Silverlight: + conEdit.focus(); + + // this is a pretty odd chunk of code here. + // specifcally need to overwrite old onUp + // however, this still gets called. its + // not disconnecting. + this.onUp = function(){ + if(!self._onAnchor && this.parentNode){ + self.disconnectMouse(); + exec(); + self.onUp = function(){} + } + } + }), 500); + }, + + + execText: function(){ + // summary: + // Internal. Method fired when text is executed, + // via mouse-click-off, ESC key or Enter key. + // + var d = dojo.marginBox(this.parentNode); + var w = Math.max(d.w, this.style.text.minWidth); + + var txt = this.cleanText(conEdit.innerHTML, true); + conEdit.innerHTML = ""; + conEdit.blur(); + this.destroyAnchors(); + + // need to convert characters before measuring width. + txt = this.typesetter(txt); + + var o = this.measureText(txt, w); + var sc = this.mouse.scrollOffset(); + var org = this.mouse.origin; + + var x = this._box.left + sc.left - org.x; + var y = this._box.top + sc.top - org.y; + + x *= this.mouse.zoom; + y *= this.mouse.zoom; + w *= this.mouse.zoom; + o.h *= this.mouse.zoom; + + + this.points = [ + {x:x, y:y}, + {x:x+w, y:y}, + {x:x+w, y:y+o.h}, + {x:x, y:y+o.h} + ]; + this.editMode = false; + + + console.log("EXEC TEXT::::", this._postRenderCon); + + if(!o.text){ + this._text = ""; + this._textArray = []; + } + // Only for Combo objects (vectors, rectangle, or ellipse). + this.render(o.text); + this.onChangeText(this.getText()); + }, + + edit: function(){ + // summary: + // Internal? + // Method used to instantiate the contenteditable HTML node. + // + this.editMode = true; + var text = this.getText() || ""; + console.log("EDIT TEXT:",text, " ",text.replace("/n", " ")); + // NOTE: no mouse obj + if(this.parentNode || !this.points){ return; } + var d = this.pointsToData(); + + var sc = this.mouse.scrollOffset(); + var org = this.mouse.origin; + + var obj = { + pageX: (d.x ) / this.mouse.zoom - sc.left + org.x, + pageY: (d.y ) / this.mouse.zoom- sc.top + org.y, + width:d.width / this.mouse.zoom, + height:d.height / this.mouse.zoom + }; + + this.remove(this.shape, this.hit); + this.showParent(obj); + this.createTextField(text.replace("/n", " ")); + this.connectTextField(); + if(text){ + //setTimeout(dojo.hitch(this, function(){ + this.setSelection(conEdit, "end"); + //}), 500) + } + }, + cleanText: function(/*String*/txt, /*Boolean*/removeBreaks){ + // summary: + // Cleans text. Strings HTML chars and double spaces + // and optionally removes line breaks. + var replaceHtmlCodes = function(str){ + var chars = { + "<":"<", + ">":">", + "&":"&" + }; + for(var nm in chars){ + str = str.replace(new RegExp(nm, "gi"), chars[nm]) + } + return str + }; + + if(removeBreaks){ + dojo.forEach(['<br>', '<br/>', '<br />', '\\n', '\\r'], function(br){ + txt = txt.replace(new RegExp(br, 'gi'), " "); + }); + } + txt = txt.replace(/ /g, " "); + txt = replaceHtmlCodes(txt); + txt = dojo.trim(txt); + // remove double spaces, since SVG doesn't show them anyway + txt = txt.replace(/\s{2,}/g, " "); + return txt; //String + }, + + measureText: function(/* String */ str, /* ? Number */width){ + // summary: + // Mechanism for measuring text. + // SVG nor VML have a way of determining the width or + // height of a block of text. This method creates an + // HTML text block and those measurements are used for + // displaying the SVG/VML text. + // arguments: + // str: String + // The text to display and measure. + // width: [optional] Number + // If the width is not provided, it will be assumed + // that the text is one line and the width will be + // measured and the _lineHeight used for th height. + // If width is provided, word-wrap is assumed, and + // line breaks will be inserted into the text at each + // point where a word wraps in the HTML. The height is + // then measured. + // + var r = "(<br\\s*/*>)|(\\n)|(\\r)"; + this.showParent({width:width || "auto", height:"auto"}); + this.createTextField(str); + var txt = ""; + var el = conEdit; + el.innerHTML = "X"; + var h = dojo.marginBox(el).h; + + el.innerHTML = str; + + if(!width || new RegExp(r, "gi").test(str)){ + // has line breaks in text + txt = str.replace(new RegExp(r, "gi"), "\n"); + el.innerHTML = str.replace(new RegExp(r, "gi"), "<br/>"); + + }else if(dojo.marginBox(el).h == h){ + // one line + txt = str; + + }else{ + // text wraps + var ar = str.split(" "); + var strAr = [[]]; + var line = 0; + el.innerHTML = ""; + while(ar.length){ + var word = ar.shift(); + el.innerHTML += word+" "; //urk, always an extra space + if(dojo.marginBox(el).h > h){ + line++; + strAr[line] = []; + el.innerHTML = word+" "; + } + strAr[line].push(word) + } + + dojo.forEach(strAr, function(ar, i){ + strAr[i] = ar.join(" "); + }); + txt = strAr.join("\n"); + + // get the resultant height + el.innerHTML = txt.replace("\n", "<br/>"); + + } + + var dim = dojo.marginBox(el); + + conEdit.parentNode.removeChild(conEdit); + dojo.destroy(this.parentNode); + this.parentNode = null; + + return {h:dim.h, w:dim.w, text:txt}; //Object + }, + + _downOnCanvas:false, + onDown: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDown + // + this._startdrag = { + x: obj.pageX, + y: obj.pageY + }; + dojo.disconnect(this._postRenderCon); + this._postRenderCon = null; + this._downOnCanvas = true; + }, + + createAnchors: function(){ + // summary: + // Internal. Creates HTML nodes at each corner + // of the contenteditable div. These nodes are + // draggable and will resize the div horizontally. + // + this._anchors = {}; + var self = this; + var d = this.style.anchors, + b = d.width, + w = d.size-b*2, + h = d.size-b*2, + p = (d.size)/2*-1 + "px"; + + var s = { + position:"absolute", + width:w+"px", + height:h+"px", + backgroundColor:d.fill, + border:b+"px " + d.style + " "+d.color + }; + if(dojo.isIE){ + s.paddingLeft = w + "px"; + s.fontSize = w + "px" + } + var ss = [ + {top: p, left:p}, + {top:p, right:p}, + {bottom:p, right:p}, + {bottom:p,left:p} + ]; + for(var i=0;i<4;i++){ + var isLeft = (i==0) || (i==3); + var id = this.util.uid(isLeft ? "left_anchor" : "right_anchor"); + + var a = dojo.create("div", {id:id}, this.parentNode); + dojo.style(a, dojo.mixin(dojo.clone(s), ss[i])); + + var md, mm, mu; + var md = dojo.connect(a, "mousedown", this, function(evt){ + isLeft = evt.target.id.indexOf("left")>-1; + self._onAnchor = true; + var orgX = evt.pageX; + var orgW = this._box.width; + dojo.stopEvent(evt); + + + mm = dojo.connect(document, "mousemove", this, function(evt){ + var x = evt.pageX; + if(isLeft){ + this._box.left = x; + this._box.width = orgW + orgX - x; + }else{ + this._box.width = x + orgW - orgX; + } + dojo.style(this.parentNode, this._box.toPx()); + }); + + mu = dojo.connect(document, "mouseup", this, function(evt){ + orgX = this._box.left; + orgW = this._box.width; + dojo.disconnect(mm); + dojo.disconnect(mu); + self._onAnchor = false; + conEdit.focus(); + dojo.stopEvent(evt); + }); + }); + + this._anchors[id] = { + a:a, + cons:[md] + } + } + }, + + destroyAnchors: function(){ + // summary: + // Internal. Destroys HTML anchors. + for(var n in this._anchors){ + dojo.forEach(this._anchors[n].con, dojo.disconnect, dojo); + dojo.destroy(this._anchors[n].a); + }; + }, + + setSavedCaret: function(val){ + // summary: + // Internal, called when caret needs to + // be moved into position after text is added + this._caretStart = this._caretEnd = val; + }, + + getSavedCaret: function(){ + return {start: this._caretStart, end: this._caretEnd} + }, + + insertText: function(node,val){ + // summary: + // Uses saved caret position to insert text + // into position and place caret at the end of + // insertion + // + var t, text = node.innerHTML; + var caret = this.getSavedCaret(); + + text = text.replace(/ /g, " "); + t = text.substr(0,caret.start) + val + text.substr(caret.end); + t = this.cleanText(t,true); + this.setSavedCaret(Math.min(t.length,(caret.end + val.length))); + node.innerHTML = t; + this.setSelection(node,"stored"); + }, + + getSelection: function(node){ + // summary: + // This gets and stores the caret position + // in the contentEditable div (conEdit). + // NOTE: Doesn't work with html nodes inside + // the div. + // + var start, end; + if(dojo.doc.selection){ + //debugger; + var r = dojo.doc.selection.createRange(); + var rs = dojo.body().createTextRange(); + rs.moveToElementText(node); + var re = rs.duplicate(); + rs.moveToBookmark(r.getBookmark()); + re.setEndPoint('EndToStart', rs); + start = this._caretStart = re.text.length; + end = this._caretEnd = re.text.length+r.text.length; + console.warn("Caret start: ",start," end: ",end," length: ",re.text.length," text: ",re.text); + } else { + this._caretStart = dojo.global.getSelection().getRangeAt(node).startOffset; + this._caretEnd = dojo.global.getSelection().getRangeAt(node).endOffset; + console.log("Caret start: ", this._caretStart," end: ", this._caretEnd); + } + }, + + setSelection: function(node, what){ + // summary: + // Used for placing the cursor during edits and character help. + // Takes the values: end, beg, start, all or any numerical value + // (in which case the number will constitute the caret position) + // + console.warn("setSelection:"); + if(dojo.doc.selection){ // IE + //debugger; + var rs = dojo.body().createTextRange(); + rs.moveToElementText(node); + + switch(what){ + case "end": + rs.collapse(false); + break; + case "beg" || "start": + rs.collapse(); + break; + case "all": + rs.collapse(); + rs.moveStart("character", 0); + rs.moveEnd("character",node.text.length); + break; + case "stored": + rs.collapse(); + var dif = this._caretStart-this._caretEnd; + //console.log("start: ",this._caretStart, " end: ",this._caretEnd," dif: ",dif); + rs.moveStart("character",this._caretStart); + rs.moveEnd("character",dif); + break; + }; + rs.select(); + + }else{ + var getAllChildren = function(node, children){ + children = children || []; + for(var i=0;i<node.childNodes.length; i++){ + var n = node.childNodes[i]; + if(n.nodeType==3){ + children.push(n); + }else if(n.tagName && n.tagName.toLowerCase()=="img"){ + children.push(n); + }; + + if(n.childNodes && n.childNodes.length){ + getAllChildren(n, children); + }; + } + return children; + }; + console.log("ff node:", node) + node.focus(); + var selection = dojo.global.getSelection(); + selection.removeAllRanges(); + var r = dojo.doc.createRange(); + var nodes = getAllChildren(node); + switch(what){ + case "end": + console.log("len:", nodes[nodes.length - 1].textContent.length); + r.setStart(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + break; + case "beg" || "start": + r.setStart(nodes[0], 0); + r.setEnd(nodes[0], 0); + break; + case "all": + r.setStart(nodes[0], 0); + r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length); + break; + case "stored": + console.log("Caret start: ",this._caretStart," caret end: ",this._caretEnd); + r.setStart(nodes[0], this._caretStart); + r.setEnd(nodes[0], this._caretEnd); + } + selection.addRange(r); + console.log("sel ", what, " on ", node); + } + } + } + ); + + dojox.drawing.tools.TextBlock.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.TextBlock", + tooltip:"Text Tool", + iconClass:"iconText" + }; + dojox.drawing.register(dojox.drawing.tools.TextBlock.setup, "tool"); + +})(); + +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Axes.js b/js/dojo/dojox/drawing/tools/custom/Axes.js new file mode 100644 index 0000000..a30bc11 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Axes.js @@ -0,0 +1,577 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Axes", ["dijit","dojo","dojox","dojo/require!dojox/drawing/stencil/Path"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Axes"); +dojo.require("dojox.drawing.stencil.Path"); + + +dojox.drawing.tools.custom.Axes = dojox.drawing.util.oo.declare( + // summary: + // Draws a right-angle Axes (shaped like an L, not a +) + // description: + // This Stencil is created with a Path so that the L shape + // is one continuous piece. Arrow heads are placed at the end + // of each axis. The Axes can be rotated. There are custom + // label methods. + // + dojox.drawing.stencil.Path, + function(options){ + this.closePath = false; + + this.xArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:0, idx2:1}); + this.yArrow = new dojox.drawing.annotations.Arrow({stencil:this, idx1:2, idx2:1}); + if(options.data){ + //Allows import of z-axis in non-enabled canvas and xy-axis in + //enabled canvas + this.style.zAxisEnabled = options.data.cosphi == 1 ? true : false; + this.setData(options.data); + } + if(this.style.zAxisEnabled){ + // If the z-axis is enabled, all axes will be created with a z-axis on the canvas. + // there is no switching back and forth for the axis, only for vectors. + this.data.cosphi = 1; + var ops = {}; + dojo.mixin(ops,options); + dojo.mixin(ops,{ + container:this.container.createGroup(), + style: this.style, + showAngle: false, + label: null + }); + if(options.data && (!ops.data.radius || !ops.data.angle)){ + ops.data.x2 = ops.data.x4; + ops.data.y2 = ops.data.y4; + } + ops.style.zAxis = true; + this.zAxis = new dojox.drawing.tools.custom.Vector(ops); + this.zAxis.minimumSize = 5; + //console.log("-----constructing axes: ",this.zAxis); + this.connectMult([ + [this, "onChangeStyle", this.zAxis, "onChangeStyle"], + [this, "select", this.zAxis, "select"], + [this, "deselect", this.zAxis, "deselect"], + [this, "onDelete", this.zAxis, "destroy"], + [this, "onDrag", this, "zSet"], + [this, "onTransform", this, "zSet"], + [this.zAxis, "onBeforeRender", this, "zSet"], + [this, "_onPostRender", this.zAxis, "render"] + ]); + + } + + if(this.points && this.points.length){ + this.setPoints = this._postSetPoints; + // render isn't called yet because baseRender is false + // instead it is called here + this.render(); + options.label && this.setLabel(options.label); + options.shadow && this.addShadow(options.shadow); + } + }, + { + draws:true, + type:"dojox.drawing.tools.custom.Axes", + minimumSize:30, + showAngle:true, + closePath:false, + baseRender:false, + zScale:.5, + + zPoint: function(obj){ + // summary: + // Finds the point for the z axis. + obj.radius = this.util.length(obj); + var pt = this.util.pointOnCircle(obj.start.x, obj.start.y, obj.radius*this.zScale, this.style.zAngle); + return {x:pt.x, y:pt.y, skip:true, noAnchor:true}; + }, + + zSet: function(){ + if(!this.zAxis){ return; }; + var c = this.points[1]; + var z = this.points[3]; + var p = [ + {x:c.x, y:c.y}, + {x:z.x, y:z.y} + ]; + var len = this.util.length({start:{x:c.x, y:c.y}, x:z.x, y:z.y}); + len > this.zAxis.minimumSize ? this.zAxis.setPoints(p) : false; + this.zAxis.cosphi = 1; + }, + + createLabels: function(){ + // summary: + // Creates the label for each axis. + // + // NOTE: Not passing style into text because it's changing it + var props = {align:"middle", valign:"middle", util:this.util, annotation:true, container:this.container, mouse:this.mouse, stencil:this}; + this.labelX = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelX + })); + this.labelY = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelY + })); + if(this.style.zAxisEnabled){ + this.labelZ = new dojox.drawing.annotations.Label(dojo.mixin(props,{ + labelPosition:this.setLabelZ + })); + } + + }, + + setLabelX: function(){ + // summary: + // Custom placement for x-axis label + // + var ax = this.points[0]; + var c = this.points[1]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + + pt = this.util.lineSub(c.x, c.y, ax.x, ax.y, dist); + px = pt.x + (pt.y -ax.y); + py = pt.y + (ax.x - pt.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + + return { + x: pt2.x, + y: pt2.y, + width:20 + }; + }, + setLabelY: function(){ + // summary: + // Custom placement for y-axis label + // + var c = this.points[1]; + var ay = this.points[2]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + pt = this.util.lineSub(c.x, c.y, ay.x, ay.y, dist); + px = pt.x + (ay.y - pt.y); + py = pt.y + (pt.x - ay.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + return { + x: pt2.x, + y: pt2.y, + width:20 + }; + }, + setLabelZ: function(){ + // summary: + // Custom placement for z-axis label + // + var c = this.points[1]; + var z = this.points[3]; + + var dist = 40; + var offdist = 20; + var pt, px, py, pt2; + pt = this.util.lineSub(c.x, c.y, z.x, z.y, dist); + px = pt.x + (pt.y - z.y); + py = pt.y + (z.x - pt.x); + pt2 = this.util.lineSub(pt.x, pt.y, px, py, (dist-offdist)); + + return { + x:pt2.x, + y:pt2.y, + width:20 + } + }, + setLabel: function(/* ? String*/value){ + // summary: + // Set the text of the labels. The text would be + // broken up into the two labels. + // arguments: + // value: [optional] String + // If no argument is passed, defaults to two labels + // 'x' and 'y'. If an argument is passed, that + // text will be split on the word 'and' to determine + // the two labels. + // + if(this._labelsCreated){ return; } + !this.labelX && this.createLabels(); + var x = "x"; + var y = "y"; + var z = "z"; + if(value){ + // match first "and" or "&" and trim whitespace. + // Non-greedy matches are not supported in older + // browsers such as Netscape Navigator 4 or + // Microsoft Internet Explorer 5.0. + if(this.labelZ){ + var lbls = value.match(/(.*?)(and|&)(.*?)(and|&)(.*)/i); + if(lbls.length>4){ + x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,""); + y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,""); + z = lbls[5].replace(/^\s+/,"").replace(/\s+$/,""); + } + }else{ + var lbls = value.match(/(.*?)(and|&)(.*)/i); + if(lbls.length>2){ + x = lbls[1].replace(/^\s+/,"").replace(/\s+$/,""); + y = lbls[3].replace(/^\s+/,"").replace(/\s+$/,""); + } + } + } + this.labelX.setLabel(x); + this.labelY.setLabel(y); + if(this.labelZ){ + this.labelZ.setLabel(z); + } + this._labelsCreated = true; + }, + getLabel: function(){ + // summary: + // Getter for the labels. returns an object. + // + if(!this.labelX){ return null; } + return { + x:this.labelX.getText(), + y:this.labelY.getText(), + z:this.labelZ?this.labelZ.getText():null + }; // Object + }, + + anchorPositionCheck: function(/*Number*/x, /*Number*/y, /*manager.Anchor*/anchor){ + // summary: + // Gets called from anchor to check if its current + // position is ok. If not, its x or y transform will + // be changed until this passes. + // + var pm = this.container.getParent().getTransform(); + var am = anchor.shape.getTransform(); + + // the xaxis point has changed and is not yet set as a point + // - but the center should be good (except for the transform). + // Now check the yaxis point. + + var p = this.points; + var o = {x:am.dx+anchor.org.x+pm.dx, y:am.dy+anchor.org.y+pm.dy}; + var c = {x:p[1].x+pm.dx, y:p[1].y+pm.dy}; + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + return {x:ox, y:oy}; + + }, + + onTransformBegin: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformBegin + // + // called from anchor point up mouse down + this._isBeingModified = true; + }, + + onTransformEnd: function(/*manager.Anchor*/anchor){ + // summary: + // Overwrites _Base.onTransformEnd + // + // Gets called on anchor mouseup + // also gets called by checkBounds - we don't want that. + if(!anchor){ return; } + + // tell anchor to go to prev point if wrong + // called from anchor point up mouse up + + this._isBeingModified = false; + //this.deselect(); + this._toggleSelected(); + console.log("before:", Math.ceil(this.points[1].x), " x ", Math.ceil(this.points[1].y)) + + var o = this.points[0]; + var c = this.points[1]; + var obj = {start:{x:c.x,y:c.y},x:o.x, y:o.y}; + var pt = this.util.constrainAngle(obj, 0, 89); + var zpt = this.style.zAxisEnabled ? this.zPoint(obj) : null; + + if(pt.x==o.x && pt.y == o.y){ + // we're within the constraint, so now we snap + pt = this.util.snapAngle(obj, this.angleSnap/180); + + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + console.warn("AXES ERROR LESS THAN ZERO - ABORT"); + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + this.points.push({x:ox, y:oy, noAnchor:true}); + if(zpt){ this.points.push(zpt);} + this.setPoints(this.points); + + //this.select(); + this.onModify(this); + return; + } + + // we're outside of the constraint. Set to the low or high. + this.points[0].x = pt.x + this.points[0].y = pt.y; + o = this.points[0]; + + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + this.points[2] = {x:ox, y:oy, noAnchor:true}; + if(zpt){ this.points.push(zpt); } + this.setPoints(this.points); + + // reset handles render + //anchor.reset(this); + + this.labelX.setLabel(); + this.labelY.setLabel(); + if(this.labelZ){ + this.labelZ.setLabel(); + } + + //this.select(); + this.onModify(this); + + }, + + getBounds: function(/*Boolean*/absolute){ + // summary: + // Custom getBounds overwrites _Base.getBounds + // + var px = this.points[0], + pc = this.points[1], + py = this.points[2]; + if(this.style.zAxisEnabled){ var pz = this.points[3]; } + + if(absolute){ + var bounds = { + x:pc.x, + y:pc.y, + x1:pc.x, + y1:pc.y, + x2:px.x, + y2:px.y, + x3:py.x, + y3:py.y + }; + if(this.style.zAxisEnabled){ + bounds.x4 = pz.x; + bounds.y4 = pz.y; + } + return bounds; + } + + var x1 = this.style.zAxisEnabled ? (py.x < pz.x ? py.x : pz.x) : py.x; + y1 = py.y < px.y ? py.y : px.y, + x2 = px.x, + y2 = this.style.zAxisEnabled ? pz.y : pc.y; + + return { + x1:x1, + y1:y1, + x2:x2, + y2:y2, + x:x1, + y:y1, + w:x2-x1, + h:y2-y1 + }; + }, + + _postSetPoints: function(/*Array*/pts){ + // summary: + // Because Axes only has one anchor, + // we substitute a special setPoints method + // + this.points[0] = pts[0]; + if(this.pointsToData){ + this.data = this.pointsToData(); + } + }, + + onTransform: function(/*Number*/anchor){ + // summary: + // Overwrites _Base.onTransform + // + // the xaxis point has changed - the center will not. + // need to find the yaxis point. + var o = this.points[0]; + var c = this.points[1]; + var ox = c.x - (c.y - o.y); + var oy = c.y - (o.x - c.x); + + // 'noAnchor' on a point indicates an anchor should + // not be rendered. This is the Y point being set. + this.points[2] = {x:ox, y:oy, noAnchor:true}; + if(this.style.zAxisEnabled){ + this.points[3] = this.zPoint({start:{x:c.x, y:c.y}, x:o.x, y:o.y}); + } + this.setPoints(this.points); + if(!this._isBeingModified){ + this.onTransformBegin(); + } + this.render(); + }, + + pointsToData: function(){ + //summary: + // Converts points to data. + var p = this.points; + var d = { + x1:p[1].x, + y1:p[1].y, + x2:p[0].x, + y2:p[0].y, + x3:p[2].x, + y3:p[2].y + } + if(this.style.zAxisEnabled){ + d.x4 = p[3].x; + d.y4 = p[3].y; + d.cosphi = 1; + } + return d; + + }, + + getRadius: function(){ + //summary: + // Possibility of z-axis makes bounds unreliable. + // Use these points instead. + var p = this.points; + var line = {start:{x:p[1].x, y:p[1].y}, x:p[0].x, y:p[0].y}; + return this.util.length(line); + }, + + dataToPoints: function(/* ? Object*/o){ + //summary: + // Converts data to points. + o = o || this.data; + if(o.radius || o.angle){ + // instead of using x1,x2,y1,y1, + // it's been set as x,y,angle,radius + var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle), zpt; + var ox = o.x - (o.y - pt.y); + var oy = o.y - (pt.x - o.x); + if((o.cosphi && o.cosphi==1) || this.style.zAxisEnabled){ + this.style.zAxisEnabled = true; + zpt = this.util.pointOnCircle(o.x, o.y, o.radius*this.zScale, this.style.zAngle); + } + this.data = o = { + x1:o.x, + y1:o.y, + x2:pt.x, + y2:pt.y, + x3:ox, + y3:oy + } + if(this.style.zAxisEnabled){ + this.data.x4 = o.x4 = zpt.x; + this.data.y4 = o.y4 = zpt.y; + this.data.cosphi = 1; + } + + } + this.points = [ + {x:o.x2, y:o.y2}, + {x:o.x1, y:o.y1, noAnchor:true}, + {x:o.x3, y:o.y3, noAnchor:true} + ]; + if(this.style.zAxisEnabled){ this.points.push({x:o.x4, y:o.y4, skip:true, noAnchor:true}); } + return this.points; + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + var pt = this.util.constrainAngle(obj, 0, 89); + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + + this.points.push({x:ox, y:oy, noAnchor:true}); + if(this.style.zAxisEnabled){ + var zpt = this.zPoint(obj); + this.points.push(zpt); + } + this.render(); + }, + + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(!this._downOnCanvas){ return; } + this._downOnCanvas = false; + var p = this.points; + if(!p.length){ + var s = obj.start, d = 100; + this.points = [ + {x:s.x+d, y:s.y+d}, + {x:s.x, y:s.y+d, noAnchor:true}, + {x:s.x, y:s.y, noAnchor:true} + ]; + if(this.style.zAxisEnabled){ + var zpt = this.zPoint({start:{x:s.x, y:s.y+d}, x:s.x+d, y:s.y+d}); + this.points.push(zpt); + } + this.setPoints = this._postSetPoints; + this.pointsToData(); + this.render(); + this.onRender(this); + return; + } + + var len = this.util.distance(p[1].x ,p[1].y ,p[0].x ,p[0].y ); + if(!p || !p.length){ + return; + }else if(len < this.minimumSize){ + this.remove(this.shape, this.hit); + this.xArrow.remove(this.xArrow.shape, this.xArrow.hit); + this.yArrow.remove(this.yArrow.shape, this.yArrow.hit); + if(this.zArrow){ + this.zArrow.remove(this.zArrow.shape, this.zArrow.hit); + } + return; + } + + var o = p[0]; + var c = p[1]; + obj = {start:{x:c.x,y:c.y},x:o.x,y:o.y}; + var pt = this.util.snapAngle(obj, this.angleSnap/180); + obj.x = pt.x; + obj.y = pt.y; + var ox = obj.start.x - (obj.start.y - obj.y); + var oy = obj.start.y - (obj.x - obj.start.x); + + if(ox<0 || oy<0){ + return; + } + this.points = [{x:obj.x, y:obj.y}, {x:obj.start.x, y:obj.start.y, noAnchor:true}]; + + this.points.push({x:ox, y:oy, noAnchor:true}); + if(this.style.zAxisEnabled){ this.points.push(this.zPoint(obj)); } + this.onRender(this); + this.setPoints = this._postSetPoints; + } + } +); + +dojox.drawing.tools.custom.Axes.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Axes", + tooltip:"Axes Tool", + iconClass:"iconAxes" +}; +dojox.drawing.register(dojox.drawing.tools.custom.Axes.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Equation.js b/js/dojo/dojox/drawing/tools/custom/Equation.js new file mode 100644 index 0000000..b9b5f40 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Equation.js @@ -0,0 +1,30 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Equation", ["dijit","dojo","dojox","dojo/require!dojox/drawing/tools/TextBlock"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Equation"); +dojo.require("dojox.drawing.tools.TextBlock"); + +dojox.drawing.tools.custom.Equation = dojox.drawing.util.oo.declare( + // summary: + // Essentially the same as the TextBlock tool, but + // allows for a different icon and tooltip title. + // + dojox.drawing.tools.TextBlock, + function(options){ + + }, + { + customType:"equation" + } + +); + +dojox.drawing.tools.custom.Equation.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Equation", + tooltip:"Equation Tool", + iconClass:"iconEq" +}; +dojox.drawing.register(dojox.drawing.tools.custom.Equation.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/tools/custom/Vector.js b/js/dojo/dojox/drawing/tools/custom/Vector.js new file mode 100644 index 0000000..748af50 --- /dev/null +++ b/js/dojo/dojox/drawing/tools/custom/Vector.js @@ -0,0 +1,392 @@ +//>>built +// wrapped by build app +define("dojox/drawing/tools/custom/Vector", ["dijit","dojo","dojox","dojo/require!dojox/drawing/tools/Arrow,dojox/drawing/util/positioning"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.tools.custom.Vector"); +dojo.require("dojox.drawing.tools.Arrow"); +dojo.require("dojox.drawing.util.positioning"); + +dojox.drawing.tools.custom.Vector = dojox.drawing.util.oo.declare( + // summary: + // Creates a Vector Stencil. + // description: + // Generally the same as an arrow, except that the arrow + // head is only at the end. There is additionaly functionality + // to allow for a 'zero vector' - one with no length. + // + // + dojox.drawing.tools.Arrow, + function(options){ + this.minimumSize = this.style.arrows.length; + this.addShadow({size:3, mult:2}); + }, + { + draws:true, + type:"dojox.drawing.tools.custom.Vector", + minimumSize:30, + showAngle:true, + + + + changeAxis: function(cosphi){ + // summary: + // Converts a vector to and from the z axis. + // If passed a cosphi value that is used to set + // the axis, otherwise it is the opp of what it is. + cosphi = cosphi!==undefined?cosphi:this.style.zAxis? 0 : 1; + if(cosphi == 0){ + this.style.zAxis = false; + this.data.cosphi = 0; + }else{ + this.style.zAxis = true; + var p = this.points; + var pt = this.zPoint(); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + } + this.render(); + }, + + _createZeroVector: function(shp, d, sty){ + // summary: + // Special creation function for the zero-vector shape + // + var s = shp=="hit" ? this.minimumSize : this.minimumSize/6; + var f = shp=="hit" ? sty.fill : null; + d = { + cx:this.data.x1, + cy:this.data.y1, + rx:s, + ry:s + }; + + this.remove(this[shp]); + this[shp] = this.container.createEllipse(d) + .setStroke(sty) + .setFill(f); + this.util.attr(this[shp], "drawingType", "stencil"); + }, + + _create: function(/*String*/shp, /*StencilData*/d, /*Object*/sty){ + // summary: + // Creates a dojox.gfx.shape based on passed arguments. + // Can be called many times by implementation to create + // multiple shapes in one stencil. + // + this.remove(this[shp]); + this[shp] = this.container.createLine(d) + .setStroke(sty); + this._setNodeAtts(this[shp]); + }, + + onDrag: function(/*EventObject*/obj){ + // summary: See stencil._Base.onDrag + // + if(this.created){ return; } + + var x1 = obj.start.x, + y1 = obj.start.y, + x2 = obj.x, + y2 = obj.y; + + if(this.keys.shift && !this.style.zAxis){ + var pt = this.util.snapAngle(obj, 45/180); + x2 = pt.x; + y2 = pt.y; + } + + if(this.keys.alt){ + // FIXME: + // should double the length of the line + // FIXME: + // if alt dragging past ZERO it seems to work + // but select/deselect shows bugs + var dx = x2>x1 ? ((x2-x1)/2) : ((x1-x2)/-2); + var dy = y2>y1 ? ((y2-y1)/2) : ((y1-y2)/-2); + x1 -= dx; + x2 -= dx; + y1 -= dy; + y2 -= dy; + } + + if(this.style.zAxis){ + var pts = this.zPoint(obj); + x2 = pts.x; + y2 = pts.y; + } + + this.setPoints([ + {x:x1, y:y1}, + {x:x2, y:y2} + ]); + this.render(); + }, + + onTransform: function(/* ? manager.Anchor */anchor){ + // summary: + // Called from anchor point mouse drag + // also called from plugins.Pan.checkBounds + if(!this._isBeingModified){ + this.onTransformBegin(); + } + // this is not needed for anchor moves, but it + // is for stencil move: + + this.setPoints(this.points); + this.render(); + }, + + anchorConstrain: function(x, y){ + // summary: + // Called from anchor point mouse drag + if(!this.style.zAxis){ return null; } + var radians = this.style.zAngle*Math.PI/180; + //Constrain to angle + var test = x<0 ? x>-y : x<-y; + var dx = test ? x : -y/Math.tan(radians); + var dy = !test ? y : -Math.tan(radians)*x; + return {x:dx, y:dy} + }, + + zPoint: function(obj){ + // summary: + // Takes any point and converts it to + // be on the z-axis. + if(obj===undefined){ + if(!this.points[0]){ return null; }; + var d = this.pointsToData(); + obj = { + start:{ + x:d.x1, + y:d.y1 + }, + x:d.x2, + y:d.y2 + }; + } + var radius = this.util.length(obj); + var angle = this.util.angle(obj); + angle<0 ? angle = 360 + angle : angle; + + angle = angle > 135 && angle < 315 ? this.style.zAngle : this.util.oppAngle(this.style.zAngle); + + return this.util.pointOnCircle(obj.start.x, obj.start.y, radius, angle); + }, + + pointsToData: function(p){ + // summary: + // Converts points to data + p = p || this.points; + var cosphi = 0; + var obj = {start:{x:p[0].x, y:p[0].y}, x:p[1].x, y:p[1].y}; + if(this.style.zAxis && (this.util.length(obj)>this.minimumSize)){ + + var angle = this.util.angle(obj); + angle<0 ? angle = 360 + angle : angle; + cosphi = angle > 135 && angle < 315 ? 1 : -1; + } + this.data = { + x1: p[0].x, + y1: p[0].y, + x2: p[1].x, + y2: p[1].y, + cosphi: cosphi + }; + return this.data; + }, + + dataToPoints: function(o){ + //summary: + // Converts data to points. + o = o || this.data; + if(o.radius || o.angle){ + // instead of using x1,x2,y1,y1, + // it's been set as x,y,angle,radius + var cosphi = 0; + var pt = this.util.pointOnCircle(o.x,o.y,o.radius,o.angle); + if(this.style.zAxis || (o.cosphi && o.cosphi!=0)){ + this.style.zAxis = true; + cosphi = o.angle > 135 && o.angle < 315 ? 1 : -1; + } + //console.log(" ---- pts:", pt.x, pt.y); + this.data = o = { + x1:o.x, + y1:o.y, + x2:pt.x, + y2:pt.y, + cosphi:cosphi + } + + } + this.points = [ + {x:o.x1, y:o.y1}, + {x:o.x2, y:o.y2} + ]; + return this.points; + }, + + render: function(){ + // summary: + // Renders the 'hit' object (the shape used for an expanded + // hit area and for highlighting) and the'shape' (the actual + // display object). Additionally checks if Vector should be + // drawn as an arrow or a circle (zero-length) + // + this.onBeforeRender(this); + if(this.getRadius() >= this.minimumSize){ + this._create("hit", this.data, this.style.currentHit); + this._create("shape", this.data, this.style.current); + + }else{ + this.data.cosphi = 0; + this._createZeroVector("hit", this.data, this.style.currentHit); + this._createZeroVector("shape", this.data, this.style.current); + } + }, + onUp: function(/*EventObject*/obj){ + // summary: See stencil._Base.onUp + // + if(this.created || !this._downOnCanvas){ return; } + this._downOnCanvas = false; + //Default vector for single click + if(!this.shape){ + var d = 100; + obj.start.x = this.style.zAxis ? obj.start.x + d : obj.start.x; + obj.y = obj.y+d; + this.setPoints([ + {x:obj.start.x, y:obj.start.y}, + {x:obj.x, y:obj.y} + ]); + this.render(); + } + + // When within minimum size this sets zero vector length to zero + if(this.getRadius()<this.minimumSize){ + var p = this.points; + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:p[0].x, y:p[0].y} + ]); + }else{ + //SnapAngle fails for the zero length vector + var p = this.points; + var pt = this.style.zAxis ? this.zPoint(obj) : this.util.snapAngle(obj, this.angleSnap/180); + this.setPoints([ + {x:p[0].x, y:p[0].y}, + {x:pt.x, y:pt.y} + ]); + } + this.renderedOnce = true; + this.onRender(this); + } + } + +); + +dojox.drawing.tools.custom.Vector.setup = { + // summary: See stencil._Base ToolsSetup + // + name:"dojox.drawing.tools.custom.Vector", + tooltip:"Vector Tool", + iconClass:"iconVector" +}; + +if(dojox.drawing.defaults.zAxisEnabled){ + dojox.drawing.tools.custom.Vector.setup.secondary = { + // summary: + // Creates a secondary tool for the Vector Stencil. + // description: + // See Toolbar.js makeButtons function. The toolbar + // checks Vector.setup for a secondary tool and requires + // name, label, and funct. Currently it doesn't accept icon + // and only uses text from label for the button. Funct is the + // function that fires when the button is clicked. + // + // Setup and postSetup are optional + // and allow tool specific functions to be added to the + // Toolbar object as if they were written there. + name: "vectorSecondary", + label: "z-axis", + funct: function(button){ + button.selected ? this.zDeselect(button) : this.zSelect(button); + + var stencils = this.drawing.stencils.selectedStencils; + for(var nm in stencils){ + if(stencils[nm].shortType == "vector" && (stencils[nm].style.zAxis != dojox.drawing.defaults.zAxis)){ + var s = stencils[nm]; + s.changeAxis(); + //Reset anchors + if(s.style.zAxis){ s.deselect(); s.select(); } + } + } + + }, + setup: function(){ + // summary: + // All functions, variables and connections defined here + // are treated as if they were added directly to toolbar. + // They are included with the tool because secondary buttons + // are tool specific. + var zAxis = dojox.drawing.defaults.zAxis; + this.zSelect = function(button){ + if(!button.enabled){ return; } + zAxis = true; + dojox.drawing.defaults.zAxis = true; + button.select(); + this.vectorTest(); + this.zSelected = button; + }; + this.zDeselect = function(button){ + if(!button.enabled){ return; } + zAxis = false; + dojox.drawing.defaults.zAxis = false; + button.deselect(); + this.vectorTest(); + this.zSelected = null; + }; + this.vectorTest = function(){ + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vector" && b.selected){ + this.drawing.currentStencil.style.zAxis = zAxis; + } + },this); + }; + dojo.connect(this, "onRenderStencil", this, function(){ if(this.zSelected){ this.zDeselect(this.zSelected)}}); + var c = dojo.connect(this.drawing, "onSurfaceReady", this, function(){ + dojo.disconnect(c); + dojo.connect(this.drawing.stencils, "onSelect", this, function(stencil){ + if(stencil.shortType == "vector"){ + if(stencil.style.zAxis){ + //If stencil is on the z-axis, update button to reflect that + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vectorSecondary"){ + this.zSelect(b); + } + },this); + + }else{ + //Update button to not be z-axis + dojo.forEach(this.buttons, function(b){ + if(b.toolType=="vectorSecondary"){ + this.zDeselect(b); + } + },this); + } + }; + }); + }); + }, + postSetup: function(btn){ + // summary: + // Depending on the secondary tool, it may need + // extra functionality for some of the basic functions. + // Post is passed the button so those connections can + // be made. + dojo.connect(btn, "enable", function(){ dojox.drawing.defaults.zAxisEnabled = true; }); + dojo.connect(btn, "disable", function(){ dojox.drawing.defaults.zAxisEnabled = false; }); + } + }; +} +dojox.drawing.register(dojox.drawing.tools.custom.Vector.setup, "tool"); +}); diff --git a/js/dojo/dojox/drawing/ui/Button.js b/js/dojo/dojox/drawing/ui/Button.js new file mode 100644 index 0000000..7407610 --- /dev/null +++ b/js/dojo/dojox/drawing/ui/Button.js @@ -0,0 +1,230 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/Button", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.Button"); + +dojox.drawing.ui.Button = dojox.drawing.util.oo.declare( + // summary: + // Creates a clickable button in "UI" mode of the drawing. + // description: + // Creates a 4-state button: normal, hover, active, selected. + // Optionally may include button text or an icon. + function(options){ + options.subShape = true; + dojo.mixin(this, options); + //console.log(" button:", this); + this.width = options.data.width || options.data.rx*2; + this.height = options.data.height || options.data.ry*2; + this.y = options.data.y || options.data.cy - options.data.ry; + // + this.id = this.id || this.util.uid(this.type); + this.util.attr(this.container, "id", this.id); + if(this.callback){ + this.hitched = dojo.hitch(this.scope || window, this.callback, this); + } + + // Rectangle itself must be "ui" for radio buttons to work. + // This is a work-around for messy code associated with drawingType; + // see http://www.andestutor.org/bugzilla/show_bug.cgi?id=1745 + options.drawingType="ui"; + // Choose between rectangle and ellipse based on options + if(options.data.width && options.data.height){ + this.shape = new dojox.drawing.stencil.Rect(options); + }else{ + this.shape = new dojox.drawing.stencil.Ellipse(options); + } + + var setGrad = function(s, p, v){ + dojo.forEach(["norm", "over", "down", "selected"], function(nm){ + s[nm].fill[p] = v; + }); + }; + // for button backs, not for icons + setGrad(this.style.button, "y2", this.height + this.y); + setGrad(this.style.button, "y1", this.y); + + if(options.icon && !options.icon.text){ + var constr = this.drawing.getConstructor(options.icon.type); + var o = this.makeOptions(options.icon); + o.data = dojo.mixin(o.data, this.style.button.icon.norm); + + if(o.data && o.data.borderWidth===0){ + o.data.fill = this.style.button.icon.norm.fill = o.data.color; + }else if(options.icon.type=="line" || (options.icon.type=="path" && !options.icon.closePath)){ + this.style.button.icon.selected.color = this.style.button.icon.selected.fill; + }else{ + //o.data.fill = this.style.button.icon.norm.fill = o.data.color; + } + this.icon = new constr(o); + //console.log(" button:", this.toolType, this.style.button.icon) + }else if(options.text || (options.icon && options.icon.text)){ + //console.warn("button text:", options.text || options.icon.text) + o = this.makeOptions(options.text || options.icon.text); + o.data.color = this.style.button.icon.norm.color; //= o.data.fill; + this.style.button.icon.selected.color = this.style.button.icon.selected.fill; + this.icon = new dojox.drawing.stencil.Text(o); + this.icon.attr({ + height: this.icon._lineHeight, + y:((this.height-this.icon._lineHeight)/2)+this.y + }); + } + + var c = this.drawing.getConstructor(this.toolType); + if(c){ + this.drawing.addUI("tooltip", {data:{text:c.setup.tooltip}, button:this}); + } + + this.onOut(); + + },{ + + callback:null, + scope:null, + hitched:null, + toolType:"", + + onClick: function(/*Object*/button){ + // summary: + // Stub to connect. Event is 'this' + // Alternatively can pass in a scope and a callback + // on creation. + }, + + makeOptions: function(/*Object*/d, /*Float*/s){ + + s = s || 1; + d = dojo.clone(d); + var o = { + util: this.util, + mouse: this.mouse, + container: this.container, + subShape:true + }; + + if(typeof(d)=="string"){ + + o.data = { + x:this.data.x - 5, + y: this.data.y + 2, + width:this.data.width, + height:this.data.height, + text:d, + makeFit:true + }; + + }else if(d.points){ + //console.warn("points") + dojo.forEach(d.points, function(pt){ + pt.x = pt.x * this.data.width * .01 * s + this.data.x; + pt.y = pt.y * this.data.height * .01 * s + this.data.y; + }, this); + o.data = {}; + for(var n in d){ + if(n!="points"){ + o.data[n] = d[n]; + } + } + o.points = d.points; + + }else{ + //console.warn("data") + for(n in d){ + if(/x|width/.test(n)){ + d[n] = d[n] * this.data.width * .01 * s; + }else if(/y|height/.test(n)){ + d[n] = d[n] * this.data.height * .01 * s; + } + if(/x/.test(n) && !/r/.test(n)){ + d[n] += this.data.x; + }else if(/y/.test(n) && !/r/.test(n)){ + d[n] += this.data.y; + } + } + delete d.type; + o.data = d; + + } + o.drawingType = "ui"; + return o; + + // do style + if(d.borderWidth!==undefined){ + //console.log(" -------- bw data:", o.data) + o.data.borderWidth = d.borderWidth; + } + + return o; + }, + + enabled:true, + selected:false, + type:"drawing.library.UI.Button", + + // note: + // need to move the Stencil's shape to front, not + // its container. Therefore we can't use the Stencil's + // moveTo.. methods. + select: function(){ + this.selected = true; + if(this.icon){this.icon.attr(this.style.button.icon.selected);} + this._change(this.style.button.selected); + this.shape.shadow && this.shape.shadow.hide(); + }, + deselect: function(){ + this.selected = false; + if(this.icon){this.icon.attr(this.style.button.icon.norm);} + this.shape.shadow && this.shape.shadow.show(); + this._change(this.style.button.norm); + + }, + disable: function(){ + if(!this.enabled){ return; } + this.enabled = false; + this._change(this.style.button.disabled); + this.icon.attr({color:this.style.button.norm.color}); + }, + enable: function(){ + if(this.enabled){ return; } + this.enabled = true; + this._change(this.style.button.norm); + this.icon.attr({color:this.style.button.icon.norm.color}); + }, + + _change: function(/*Object*/sty){ + this.shape.attr(sty); + this.shape.shadow && this.shape.shadow.container.moveToBack(); + if(this.icon){this.icon.shape.moveToFront();}; + }, + onOver: function(){ + //console.log("BUTTON OVER") + if(this.selected || !this.enabled){ return; } + this._change(this.style.button.over); + }, + onOut: function(){ + if(this.selected){ return; } + this._change(this.style.button.norm); + }, + onDown: function(){ + if(this.selected || !this.enabled){ return; } + this._change(this.style.button.selected); + }, + onUp: function(){ + //console.log("BUTTON UP") + if(!this.enabled){ return; } + this._change(this.style.button.over); + if(this.hitched){ + this.hitched(); + } + this.onClick(this); + }, + attr: function(options){ + if(this.icon){this.icon.attr(options);} + } + } + +); + +dojox.drawing.register({ + name:"dojox.drawing.ui.Button" +}, "stencil"); +}); diff --git a/js/dojo/dojox/drawing/ui/Toolbar.js b/js/dojo/dojox/drawing/ui/Toolbar.js new file mode 100644 index 0000000..20bc781 --- /dev/null +++ b/js/dojo/dojox/drawing/ui/Toolbar.js @@ -0,0 +1,284 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/Toolbar", ["dijit","dojo","dojox","dojo/require!dojox/drawing/library/icons"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.Toolbar"); +dojo.require("dojox.drawing.library.icons"); + +dojo.declare("dojox.drawing.ui.Toolbar", [], { + // summary: + // A Toolbar used for holding buttons; typically representing the Stencils + // used for a DojoX Drawing. + // description: + // Creates a GFX-based toobar that holds GFX-based buttons. Can be either created + // within the actual drawing or within a seperate DOM element. When within the + // drawing, the toolbar will cover a portion of the drawing; hence the option. + // + // A Toolbar can be created programmtically or in markup. Currently markup is as + // a separate DOM element and programmtic is within the drawing. + // examples: + // | dojo.connect(myDrawing, "onSurfaceReady", function(){ + // | new dojox.drawing.ui.Toolbar({ + // | drawing:myDrawing, + // | tools:"all", + // | plugs:"all", + // | selected:"ellipse" + // | }); + // | }); + // + // | <div dojoType="dojox.drawing.ui.Toolbar" id="gfxToolbarNode" drawingId="drawingNode" + // | class="gfxToolbar" tools="all" plugs="all" selected="ellipse" orient="H"></div> + // + // + constructor: function(props, node){ + //console.warn("GFX Toolbar:", props, node) + this.util = dojox.drawing.util.common; + + // no mixin. painful. + if(props.drawing){ + // programmatic + this.toolDrawing = props.drawing; + this.drawing = this.toolDrawing; + this.width = this.toolDrawing.width; + this.height = this.toolDrawing.height; + this.strSelected = props.selected; + this.strTools = props.tools; + this.strPlugs = props.plugs; + this._mixprops(["padding", "margin", "size", "radius"], props); + this.addBack(); + this.orient = props.orient ? props.orient : false; + }else{ + // markup + var box = dojo.marginBox(node); + this.width = box.w; + this.height = box.h; + this.strSelected = dojo.attr(node, "selected"); + this.strTools = dojo.attr(node, "tools"); + this.strPlugs = dojo.attr(node, "plugs"); + this._mixprops(["padding", "margin", "size", "radius"], node); + this.toolDrawing = new dojox.drawing.Drawing({mode:"ui"}, node); + this.orient = dojo.attr(node, "orient"); + } + + this.horizontal = this.orient ? this.orient == "H" : this.width > this.height; + console.log("this.hor: ",this.horizontal," orient: ",this.orient); + if(this.toolDrawing.ready){ + this.makeButtons(); + if(!this.strSelected && this.drawing.defaults.clickMode){ this.drawing.mouse.setCursor('default'); }; + }else{ + var c = dojo.connect(this.toolDrawing, "onSurfaceReady", this, function(){ + //console.log("TB built") + dojo.disconnect(c); + this.drawing = dojox.drawing.getRegistered("drawing", dojo.attr(node, "drawingId")); // + this.makeButtons(); + if(!this.strSelected && this.drawing.defaults.clickMode){ + var c = dojo.connect(this.drawing, "onSurfaceReady", this, function(){ + dojo.disconnect(c); + this.drawing.mouse.setCursor('default'); + }); + } + }); + } + + }, + + // padding:Number + // The amount of spce between the top and left of the toolbar and the buttons. + padding:10, + // margin: Number + // The space between each button. + margin:5, + // size: Number + // The width and height of the button + size:30, + // radius: Number + // The size of the button's rounded corner + radius:3, + // + // toolPlugGap: number + // The distnce between the tool buttons and plug buttons + toolPlugGap:20, + + // strSlelected | selected: String + // The button that should be selected at startup. + strSelected:"", + // strTools | tools: String + // A comma delineated list of the Stencil-tools to include in the Toolbar. + // If "all" is used, all registered tools are included. + strTools:"", + // strPlugs | plugs: String + // A comma delineated list of the plugins to include in the Toolbar. + // If "all" is used, all registered plugins are included. + strPlugs:"", + + makeButtons: function(){ + // summary: + // Internal. create buttons. + this.buttons = []; + this.plugins = []; + + var x = this.padding, y = this.padding, w = this.size, h = this.size, r = this.radius, g = this.margin, + sym = dojox.drawing.library.icons, + s = {place:"BR", size:2, mult:4}; + + if(this.strTools){ + var toolAr = []; + var tools = dojox.drawing.getRegistered("tool"); + var toolMap = {}; + for(var nm in tools){ + var tool = this.util.abbr(nm); + toolMap[tool] = tools[nm]; + if(this.strTools=="all"){ + toolAr.push(tool); + var details = dojox.drawing.getRegistered("tool",nm); + if(details.secondary){ + toolAr.push(details.secondary.name); + } + } + } + if(this.strTools!="all"){ + var toolTmp = this.strTools.split(","); + dojo.forEach(toolTmp, function(tool){ + tool = dojo.trim(tool); + toolAr.push(tool); + var details = dojox.drawing.getRegistered("tool",toolMap[tool].name); + if(details.secondary){ + toolAr.push(details.secondary.name); + } + }, this); + //dojo.map(toolAr, function(t){ return dojo.trim(t); }); + } + + dojo.forEach(toolAr, function(t){ + t = dojo.trim(t); + var secondary = false; + if(t.indexOf("Secondary")>-1){ + var prim = t.substring(0,t.indexOf("Secondary")); + var sec = dojox.drawing.getRegistered("tool",toolMap[prim].name).secondary; + var label = sec.label; + this[t] = sec.funct; + if(sec.setup){ dojo.hitch(this, sec.setup)(); }; + var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h/2, r:r}, toolType:t, secondary:true, text:label, shadow:s, scope:this, callback:this[t]}); + if(sec.postSetup){ dojo.hitch(this, sec.postSetup, btn)(); }; + secondary = true; + } else { + var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onToolClick"}); + } + dojox.drawing.register(btn, "button"); + this.buttons.push(btn); + if(this.strSelected==t){ + btn.select(); + this.selected = btn; + this.drawing.setTool(btn.toolType); + } + if(this.horizontal){ + x += h + g; + }else{ + var space = secondary ? h/2 + g : h + g; + y += space; + } + }, this); + } + + if(this.horizontal){ + x += this.toolPlugGap; + }else{ + y += this.toolPlugGap; + } + + if(this.strPlugs){ + var plugAr = []; + var plugs = dojox.drawing.getRegistered("plugin"); + var plugMap = {}; + for(var nm in plugs){ + var abbr = this.util.abbr(nm); + plugMap[abbr] = plugs[nm]; + if(this.strPlugs=="all"){ plugAr.push(abbr); } + } + if(this.strPlugs!="all"){ + plugAr = this.strPlugs.split(","); + dojo.map(plugAr, function(p){ return dojo.trim(p); }); + } + + dojo.forEach(plugAr, function(p){ + var t = dojo.trim(p); + //console.log(" plugin:", p); + if(plugMap[p].button != false){ + var btn = this.toolDrawing.addUI("button", {data:{x:x, y:y, width:w, height:h, r:r}, toolType:t, icon:sym[t], shadow:s, scope:this, callback:"onPlugClick"}); + dojox.drawing.register(btn, "button"); + this.plugins.push(btn); + + if(this.horizontal){ + x += h + g; + }else{ + y += h + g; + } + } + + var addPlug = {} + plugMap[p].button == false ? addPlug = {name:this.drawing.stencilTypeMap[p]} : addPlug = {name:this.drawing.stencilTypeMap[p], options:{button:btn}}; + this.drawing.addPlugin(addPlug); + }, this); + } + + dojo.connect(this.drawing, "onRenderStencil", this, "onRenderStencil"); + }, + + onRenderStencil: function(/* Object */stencil){ + // summary: + // Stencil render event. + if(this.drawing.defaults.clickMode){ + this.drawing.mouse.setCursor("default"); + this.selected && this.selected.deselect(); + this.selected = null; + } + + }, + + addTool: function(){ + // TODO: add button here + }, + + addPlugin: function(){ + // TODO: add button here + }, + + addBack: function(){ + // summary: + // Internal. Adds the back, behind the toolbar. + this.toolDrawing.addUI("rect", {data:{x:0, y:0, width:this.width, height:this.size + (this.padding*2), fill:"#ffffff", borderWidth:0}}); + }, + + onToolClick: function(/*Object*/button){ + // summary: + // Tool click event. May be connected to. + // + if(this.drawing.defaults.clickMode){ this.drawing.mouse.setCursor("crosshair"); } + dojo.forEach(this.buttons, function(b){ + if(b.id==button.id){ + b.select(); + this.selected = b; + this.drawing.setTool(button.toolType) + }else{ + if(!b.secondary){ b.deselect(); } + } + },this) + }, + + onPlugClick: function(/*Object*/button){ + // summary: + // Plugin click event. May be connected to. + }, + + _mixprops: function(/*Array*/props, /*Object | Node*/objNode){ + // summary: + // Internally used for mixing in props from an object or + // from a dom node. + dojo.forEach(props, function(p){ + this[p] = objNode.tagName + ? dojo.attr(objNode, p)===null ? this[p] : dojo.attr(objNode, p) + : objNode[p]===undefined ? this[p] : objNode[p]; + }, this); + } + +}); +}); diff --git a/js/dojo/dojox/drawing/ui/Tooltip.js b/js/dojo/dojox/drawing/ui/Tooltip.js new file mode 100644 index 0000000..8a43a78 --- /dev/null +++ b/js/dojo/dojox/drawing/ui/Tooltip.js @@ -0,0 +1,119 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/Tooltip", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.Tooltip"); +dojo.require("dojox.drawing.plugins._Plugin"); + + +(function(){ + + // summary: + // Used for UI tooltips. Buttons in the toolbar. + // This file is not complete. + // + var master = null; + var MasterC = dojox.drawing.util.oo.declare( + + dojox.drawing.plugins._Plugin, + function(options){ + this.createDom(); + }, + { + show: function(button, text){ + this.domNode.innerHTML = text; + + var dx = 30; + var px = button.data.x + button.data.width; + var py = button.data.y + button.data.height; + var x = px + this.mouse.origin.x + dx; + var y = py + this.mouse.origin.y + dx; + + dojo.style(this.domNode, { + display: "inline", + left:x +"px", + top:y+"px" + }); + + var box = dojo.marginBox(this.domNode); + + this.createShape(x-this.mouse.origin.x, y-this.mouse.origin.y, box.w, box.h); + }, + + + createShape: function(x,y,w,h){ + this.balloon && this.balloon.destroy(); + var r = 5, x2 = x+w, y2 = y+h, points = []; + var add = function(){ + for(var i=0;i<arguments.length;i++){ + points.push(arguments[i]); + } + }; + + add({x:x,y:y+5}, + {t:"Q", x:x,y:y}, + {x:x+r,y:y}); + + add({t:"L", x:x2-r,y:y}); + + add({t:"Q", x:x2,y:y}, + {x:x2,y:y+r}); + + add({t:"L", x:x2,y:y2-r}); + + add({t:"Q", x:x2,y:y2}, + {x:x2-r,y:y2}); + + add({t:"L", x:x+r,y:y2}); + + add({t:"Q", x:x,y:y2}, + {x:x,y:y2-r}); + + add({t:"L", x:x,y:y+r}); + + this.balloon = this.drawing.addUI("path", {points:points}); + }, + + createDom: function(){ + this.domNode = dojo.create('span', {"class":"drawingTooltip"}, document.body); + dojo.style(this.domNode, { + display: "none", + position:"absolute" + }); + } + } + ); + + dojox.drawing.ui.Tooltip = dojox.drawing.util.oo.declare( + + dojox.drawing.plugins._Plugin, + function(options){ + if(!master){ + master = new MasterC(options); + } + if(options.stencil){ + //todo + }else if(this.button){ + this.connect(this.button, "onOver", this, "onOver"); + this.connect(this.button, "onOut", this, "onOut"); + } + + }, + { + width:300, + height:200, + onOver: function(){ + //console.log(" tooltip over", this.data.text) + master.show(this.button, this.data.text); + }, + + onOut: function(){ + //console.log(" tooltip out") + } + } + ); + + dojox.drawing.register({ + name:"dojox.drawing.ui.Tooltip" + }, "stencil"); +})(); +}); diff --git a/js/dojo/dojox/drawing/ui/dom/Pan.js b/js/dojo/dojox/drawing/ui/dom/Pan.js new file mode 100644 index 0000000..5911abc --- /dev/null +++ b/js/dojo/dojox/drawing/ui/dom/Pan.js @@ -0,0 +1,213 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/dom/Pan", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.dom.Pan"); +dojo.require("dojox.drawing.plugins._Plugin"); +dojo.deprecated("dojox.drawing.ui.dom.Pan", "It may not even make it to the 1.4 release.", 1.4); + +dojox.drawing.ui.dom.Pan = dojox.drawing.util.oo.declare( + // NOTE: + // dojox.drawing.ui.dom.Pan is DEPRECATED. + // This was a temporary DOM solution. Use the non-dom + // tools for Toobar and Plugins. + // + // summary: + // A plugin that allows for a scrolling canvas. An action + // tool is added to the toolbar that allows for panning. Holding + // the space bar is a shortcut to that action. The canvas will + // only pan and scroll if there are objects out of the viewable + // area. + // example: + // | <div dojoType="dojox.drawing.Toolbar" drawingId="drawingNode" class="drawingToolbar vertical"> + // | <div tool="dojox.drawing.tools.Line" selected="true">Line</div> + // | <div plugin="dojox.drawing.ui.dom.Pan" options="{}">Pan</div> + // | </div> + // + dojox.drawing.plugins._Plugin, + function(options){ + + this.domNode = options.node; + var _scrollTimeout; + dojo.connect(this.domNode, "click", this, "onSetPan"); + dojo.connect(this.keys, "onKeyUp", this, "onKeyUp"); + dojo.connect(this.keys, "onKeyDown", this, "onKeyDown"); + dojo.connect(this.anchors, "onAnchorUp", this, "checkBounds"); + dojo.connect(this.stencils, "register", this, "checkBounds"); + dojo.connect(this.canvas, "resize", this, "checkBounds"); + dojo.connect(this.canvas, "setZoom", this, "checkBounds"); + dojo.connect(this.canvas, "onScroll", this, function(){ + if(this._blockScroll){ + this._blockScroll = false; + return; + } + _scrollTimeout && clearTimeout(_scrollTimeout); + _scrollTimeout = setTimeout(dojo.hitch(this, "checkBounds"), 200); + }); + this._mouseHandle = this.mouse.register(this); + // This HAS to be called after setting initial objects or things get screwy. + //this.checkBounds(); + },{ + selected:false, + type:"dojox.drawing.ui.dom.Pan", + + onKeyUp: function(evt){ + if(evt.keyCode == 32){ + this.onSetPan(false); + } + }, + + onKeyDown: function(evt){ + if(evt.keyCode == 32){ + this.onSetPan(true); + } + }, + + onSetPan: function(/*Boolean | Event*/ bool){ + if(bool === true || bool === false){ + this.selected = !bool; + } + if(this.selected){ + this.selected = false; + dojo.removeClass(this.domNode, "selected"); + }else{ + this.selected = true; + dojo.addClass(this.domNode, "selected"); + } + this.mouse.setEventMode(this.selected ? "pan" : ""); + }, + + onPanDrag: function(obj){ + var x = obj.x - obj.last.x; + var y = obj.y - obj.last.y; + this.canvas.domNode.parentNode.scrollTop -= obj.move.y; + this.canvas.domNode.parentNode.scrollLeft -= obj.move.x; + this.canvas.onScroll(); + }, + + onStencilUp: function(obj){ + // this gets called even on click-off because of the + // issues with TextBlock deselection + this.checkBounds(); + }, + onStencilDrag: function(obj){ + // this gets called even on click-off because of the + // issues with TextBlock deselection + //this.checkBounds(); + }, + + checkBounds: function(){ + + //watch("CHECK BOUNDS DISABLED", true); return; + + + // summary: + // Scans all items on the canvas and checks if they are out of + // bounds. If so, a scroll bar (in Canvas) is shown. If the position + // is left or top, the canvas is scrolled all items are relocated + // the distance of the scroll. Ideally, it should look as if the + // items do not move. + + // logging stuff here so it can be turned on and off. This method is + // very high maintenance. + var log = function(){ + ///console.log.apply(console, arguments); + }; + var warn = function(){ + //console.warn.apply(console, arguments); + }; + //console.clear(); + //console.time("check bounds"); + var t=Infinity, r=-Infinity, b=-Infinity, l=Infinity, + sx=0, sy=0, dy=0, dx=0, + mx = this.stencils.group ? this.stencils.group.getTransform() : {dx:0, dy:0}, + sc = this.mouse.scrollOffset(), + // scY, scX: the scrollbar creates the need for extra dimension + scY = sc.left ? 10 : 0, + scX = sc.top ? 10 : 0, + // ch, cw: the current size of the canvas + ch = this.canvas.height, + cw = this.canvas.width, + z = this.canvas.zoom, + // pch, pcw: the normal size of the canvas (not scrolled) + // these could change if the container resizes. + pch = this.canvas.parentHeight, + pcw = this.canvas.parentWidth; + + + this.stencils.withSelected(function(m){ + var o = m.getBounds(); + warn("SEL BOUNDS:", o); + t = Math.min(o.y1 + mx.dy, t); + r = Math.max(o.x2 + mx.dx, r); + b = Math.max(o.y2 + mx.dy, b); + l = Math.min(o.x1 + mx.dx, l); + }); + + this.stencils.withUnselected(function(m){ + var o = m.getBounds(); + warn("UN BOUNDS:", o); + t = Math.min(o.y1, t); + r = Math.max(o.x2, r); + b = Math.max(o.y2, b); + l = Math.min(o.x1, l); + }); + + b *= z; + var xscroll = 0, yscroll = 0; + log("Bottom test", "b:", b, "z:", z, "ch:", ch, "pch:", pch, "top:", sc.top, "sy:", sy); + if(b > pch || sc.top ){ + log("*bottom scroll*"); + // item off bottom + ch = Math.max(b, pch + sc.top); + sy = sc.top; + xscroll += this.canvas.getScrollWidth(); + }else if(!sy && ch>pch){ + log("*bottom remove*"); + // item moved from bottom + ch = pch; + } + + r *= z; + if(r > pcw || sc.left){ + //log("*right scroll*"); + // item off right + cw = Math.max(r, pcw + sc.left); + sx = sc.left; + yscroll += this.canvas.getScrollWidth(); + }else if(!sx && cw>pcw){ + //log("*right remove*"); + // item moved from right + cw = pcw; + } + + // add extra space for scrollbars + // double it to give some breathing room + cw += xscroll*2; + ch += yscroll*2; + + this._blockScroll = true; + + // selected items are not transformed. The selection itself is + // and the items are on de-select + this.stencils.group && this.stencils.group.applyTransform({dx:dx, dy:dy}); + + // non-selected items are transformed + this.stencils.withUnselected(function(m){ + m.transformPoints({dx:dx, dy:dy}); + }); + + this.canvas.setDimensions(cw, ch, sx, sy); + + //console.timeEnd("check bounds"); + } + } +); + +dojox.drawing.ui.dom.Pan.setup = { + name:"dojox.drawing.ui.dom.Pan", + tooltip:"Pan Tool", + iconClass:"iconPan" +}; + +dojox.drawing.register(dojox.drawing.ui.dom.Pan.setup, "plugin"); +}); diff --git a/js/dojo/dojox/drawing/ui/dom/Toolbar.js b/js/dojo/dojox/drawing/ui/dom/Toolbar.js new file mode 100644 index 0000000..d7b0a8c --- /dev/null +++ b/js/dojo/dojox/drawing/ui/dom/Toolbar.js @@ -0,0 +1,211 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/dom/Toolbar", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.dom.Toolbar"); +dojo.deprecated("dojox.drawing.ui.dom.Toolbar", "It may not even make it to the 1.4 release.", 1.4); + +(function(){ + + dojo.declare("dojox.drawing.ui.dom.Toolbar", [], { + // NOTE: + // dojox.drawing.Toolbar is DEPRECATED. + // The intention never was to use HTML as buttons for a Drawing. + // This was implemented in order to finish the project for which + // Drawing was developed. + // Instead use: drawing/ui/Toolbar.js + // + // summary: + // Creates a Toolbar to be used with a DojoX Drawing. + // description: + // Currently works in markup only. A class is required with + // either horizontal or vertical as a class (IE prevented using + // either as a default). Assign an attribute of 'drawingId' with + // the id of the DojoX Drawing to which this is assigned. + // The node children will be assigned as the Tools in the toolbar. + // Plugins can also be assigned. + // The Toolbar is largely self contained and has no real public + // methods or events. the Drawing object should be used. + // + // 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="true"> Rect</div> + // | <div plugin="dojox.drawing.plugins.tools.Zoom" options="{zoomInc:.1,minZoom:.5,maxZoom:2}">Zoom</div> + // | </div> + // + // TODO: Toolbar works in markup only. Need programmatic. + // NOTE: There are plans to make the toolbar out of dojox.gfx vectors. + // This may change the APIs in the future. + // + // baseClass:String + // The CSS style to apply to the toolbar node + baseClass:"drawingToolbar", + // buttonClass:String + // The CSS style to apply to each button node + buttonClass:"drawingButton", + // iconClass:String + // The CSS style to apply to each button icon node + iconClass:"icon", + // + constructor: function(props, node){ + // props is null from markup + dojo.addOnLoad(this, function(){ + this.domNode = dojo.byId(node); + dojo.addClass(this.domNode, this.baseClass); + this.parse(); + }); + }, + + createIcon: function(/*HTMLNode*/node, /* ? Function*/constr){ + // summary: + // Internal. Creates an icon node for each button. + // arguments: + // node: HTMLNode + // The button node. + // constr: [optional] Function + // Optional. If not supplied, an icon is not created. + // Information for each icon is derived from + // the ToolsSetup object defined at the end + // of each tool. See: stencil._Base + // + var setup = constr && constr.setup ? constr.setup : {}; + if(setup.iconClass){ + var icon = setup.iconClass ? setup.iconClass : "iconNone"; + var tip = setup.tooltip ? setup.tooltip : "Tool"; + + var iNode = dojo.create("div", {title:tip}, node); + dojo.addClass(iNode, this.iconClass); + dojo.addClass(iNode, icon); + + dojo.connect(node, "mouseup", function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "active"); + }); + dojo.connect(node, "mouseover", function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "hover"); + }); + dojo.connect(node, "mousedown", this, function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "active"); + }); + + dojo.connect(node, "mouseout", this, function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "hover"); + }); + } + }, + + createTool: function(/*HTMLNode*/node){ + // summary: + // Creates a button on the Toolbar that is + // a Tool, not a Plugin. Tools draw Stencils, + // Plugins do actions. + // arguments: + // node: HTMLNode + // The button node. + // + node.innerHTML = ""; + var type = dojo.attr(node, "tool"); + this.toolNodes[type] = node; + dojo.attr(node, "tabIndex", 1); + var constr = dojo.getObject(type); + + this.createIcon(node, constr); + + this.drawing.registerTool(type, constr); + dojo.connect(node, "mouseup", this, function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "active"); + this.onClick(type); + }); + dojo.connect(node, "mouseover", function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "hover"); + }); + dojo.connect(node, "mousedown", this, function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "active"); + }); + + dojo.connect(node, "mouseout", this, function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "hover"); + }); + }, + + parse: function(){ + // summary: + // Initializing method that reads the dom node and its + // children for tools and plugins. + // + var drawingId = dojo.attr(this.domNode, "drawingId"); + this.drawing = dojox.drawing.util.common.byId(drawingId); + !this.drawing && console.error("Drawing not found based on 'drawingId' in Toolbar. "); + this.toolNodes = {}; + var _sel; + dojo.query(">", this.domNode).forEach(function(node, i){ + node.className = this.buttonClass; + var tool = dojo.attr(node, "tool"); + var action = dojo.attr(node, "action"); + var plugin = dojo.attr(node, "plugin"); + if(tool){ + if(i==0 || dojo.attr(node, "selected")=="true"){ + _sel = tool; + } + this.createTool(node); + + }else if(plugin){ + + + + + var p = {name:plugin, options:{}}, + opt = dojo.attr(node, "options"); + if(opt){ + p.options = eval("("+opt+")"); + } + p.options.node = node; + node.innerHTML = ""; + this.drawing.addPlugin(p); + + + + + + this.createIcon(node, dojo.getObject(dojo.attr(node, "plugin"))); + } + + }, this); + this.drawing.initPlugins(); + dojo.connect(this.drawing, "setTool", this, "onSetTool"); + this.drawing.setTool(_sel); + }, + onClick: function(/*String*/type){ + // summary: + // Event fired from clicking a Tool, not a PLugin. + // Plugin clicks are handled within the plugin's class. + // arguments: + // type: Fully qualified name of class. ex: + // dojox.drawing.tools.Ellipse + // + this.drawing.setTool(type); + }, + onSetTool: function(/*String*/type){ + // summary: + // handles buttons clicks and selects or deselects + for(var n in this.toolNodes){ + if(n == type){ + dojo.addClass(this.toolNodes[type], "selected"); + this.toolNodes[type].blur(); + }else{ + dojo.removeClass(this.toolNodes[n], "selected"); + } + + } + } + }); + +})(); +}); diff --git a/js/dojo/dojox/drawing/ui/dom/Zoom.js b/js/dojo/dojox/drawing/ui/dom/Zoom.js new file mode 100644 index 0000000..39cdab6 --- /dev/null +++ b/js/dojo/dojox/drawing/ui/dom/Zoom.js @@ -0,0 +1,134 @@ +//>>built +// wrapped by build app +define("dojox/drawing/ui/dom/Zoom", ["dijit","dojo","dojox","dojo/require!dojox/drawing/plugins/_Plugin"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.ui.dom.Zoom"); +dojo.require("dojox.drawing.plugins._Plugin"); + +dojox.drawing.ui.dom.Zoom = dojox.drawing.util.oo.declare( + // NOTE: + // dojox.drawing.ui.dom.Zoom is DEPRECATED. + // This was a temporary DOM solution. Use the non-dom + // tools for Toobar and Plugins. + // + // summary: + // A plugin that allows for zooming the canvas in and out. An + // action-tool is added to the toolbar with plus, minus and 100% + // buttons. + // example: + // | <div dojoType="dojox.drawing.Toolbar" drawingId="drawingNode" class="drawingToolbar vertical"> + // | <div tool="dojox.drawing.tools.Line" selected="true">Line</div> + // | <div plugin="dojox.drawing.ui.dom.Zoom" options="{zoomInc:.1,minZoom:.5,maxZoom:2}">Zoom</div> + // | </div> + // + dojox.drawing.plugins._Plugin, + function(options){ + var cls = options.node.className; + var txt = options.node.innerHTML; + this.domNode = dojo.create("div", {id:"btnZoom", "class":"toolCombo"}, options.node, "replace"); + + this.makeButton("ZoomIn", this.topClass); + this.makeButton("Zoom100", this.midClass); + this.makeButton("ZoomOut", this.botClass); + + }, + { + type:"dojox.drawing.ui.dom.Zoom", + // + // zoomInc: Float + // The amount of zoom that will occur upon each click. + zoomInc:.1, + // + // maxZoom: Number + // The maximum the canvas can be zoomed in. 10 = 1000% + maxZoom:10, + // + // minZoom: Float + // The most the canvas can be zoomed out. .1 = 10% + minZoom:.1, + // + // zoomFactor: [readonly] Float + // The current zoom amount + zoomFactor:1, + // + // baseClass: String + // The CSS class added to the Toolbar buttons + baseClass:"drawingButton", + // + // topClass: String + // The CSS class added to the top (or left) Toolbar button + topClass:"toolComboTop", + // + // midClass: String + // The CSS class added to the middle Toolbar button + midClass:"toolComboMid", + // + // botClass: String + // The CSS class added to the bottom (or right) Toolbar button + botClass:"toolComboBot", + // + makeButton: function(name, cls){ + // summary: + // Internal. Creates one of the buttons in the zoom-button set. + // + var node = dojo.create("div", {id:"btn"+name, "class":this.baseClass+" "+cls, + innerHTML:'<div title="Zoom In" class="icon icon'+name+'"></div>'}, this.domNode); + + dojo.connect(document, "mouseup", function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "active"); + }); + dojo.connect(node, "mouseup", this, function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "active"); + this["on"+name](); // this is what calls the methods below + }); + dojo.connect(node, "mouseover", function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "hover"); + }); + dojo.connect(node, "mousedown", this, function(evt){ + dojo.stopEvent(evt); + dojo.addClass(node, "active"); + }); + + dojo.connect(node, "mouseout", this, function(evt){ + dojo.stopEvent(evt); + dojo.removeClass(node, "hover"); + }); + + }, + + onZoomIn: function(/*Mouse Event*/evt){ + // summary: + // Handles zoom in. + // + this.zoomFactor += this.zoomInc; + this.zoomFactor = Math.min(this.zoomFactor, this.maxZoom); + this.canvas.setZoom(this.zoomFactor); + this.mouse.setZoom(this.zoomFactor); + }, + onZoom100: function(/*Mouse Event*/evt){ + // summary: + // Zooms to 100% + // + this.zoomFactor = 1; + this.canvas.setZoom(this.zoomFactor); + this.mouse.setZoom(this.zoomFactor); + }, + onZoomOut: function(/*Mouse Event*/evt){ + // summary: + // Handles zoom out. + // + this.zoomFactor -= this.zoomInc; + this.zoomFactor = Math.max(this.zoomFactor, this.minZoom); + this.canvas.setZoom(this.zoomFactor); + this.mouse.setZoom(this.zoomFactor); + } + } +); + + +//dojox.drawing.register(dojox.drawing.plugins.tools.Pan, "plugin"); + + +}); diff --git a/js/dojo/dojox/drawing/util/common.js b/js/dojo/dojox/drawing/util/common.js new file mode 100644 index 0000000..af740a0 --- /dev/null +++ b/js/dojo/dojox/drawing/util/common.js @@ -0,0 +1,284 @@ +//>>built +// wrapped by build app +define("dojox/drawing/util/common", ["dijit","dojo","dojox","dojo/require!dojox/math/round"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.util.common"); +dojo.require("dojox.math.round"); + +(function(){ + + var uidMap = {}; + var start = 0; + dojox.drawing.util.common = { + // summary: + // A collection of common methods used for DojoX Drawing. + // This singleton is accessible in most Drawing classes + // as this.util + // + // NOTE: + // A lot of functions use a EventObject + // as an argument. An attempt was made to accept + // either that object or a list of numbers. That wasn't + // finished (it didn't work well in all cases) but is + // likely to happen in the future. + // In cases where you are not sending a Mouse object, + // form your argument like so: + // var obj = { + // start:{ + // x:Number, // start x + // y:Number // start y + // }, + // x: Number, // end x + // y:Number // end y + // } + // + // + radToDeg: function(/*Numer*/n){ + // summary: + // Convert the passed number to degrees. + return (n*180)/Math.PI; // Number + }, + + degToRad: function(/*Numer*/n){ + // summary: + // Convert the passed number to radians. + return (n*Math.PI)/180; // Number + }, + + angle: function(/*EventObject*/obj, /* ? Float */snap){ + // summary: + // Return angle based on mouse object + // arguments: + // obj: EventObject + // Manager.Mouse event. + // snap: Float + // Returns nearest angle within snap limits + // + //obj = this.argsToObj.apply(this, arguments); + if(snap){ + snap = snap/180; + var radians = this.radians(obj), + seg = Math.PI * snap, + rnd = dojox.math.round(radians/seg), + new_radian = rnd*seg; + return dojox.math.round(this.radToDeg(new_radian)); // Whole Number + + }else{ + return this.radToDeg(this.radians(obj)); // Float + } + }, + + oppAngle: function(/*Angle*/ang){ + (ang+=180) > 360 ? ang = ang - 360 : ang; + return ang; + }, + + radians: function(/*EventObject*/o){ + // summary: + // Return the radians derived from the coordinates + // in the Mouse object. + // + //var o = this.argsToObj.apply(this, arguments); + return Math.atan2(o.start.y-o.y,o.x-o.start.x); + }, + + length: function(/*EventObject*/o){ + // summary: + // Return the length derived from the coordinates + // in the Mouse object. + // + return Math.sqrt(Math.pow(o.start.x-o.x, 2)+Math.pow(o.start.y-o.y, 2)); + }, + + lineSub: function(/*Number*/x1, /*Number*/y1, /*Number*/x2, /*Number*/y2, /*Number*/amt){ + // summary: + // Subtract an amount from a line + // description: + // x1,y1,x2,y2 represents the Line. 'amt' represents the amount + // to subtract from it. + // + var len = this.distance(this.argsToObj.apply(this, arguments)); + len = len < amt ? amt : len; + var pc = (len-amt)/len; + var x = x1 - (x1-x2) * pc; + var y = y1 - (y1-y2) * pc; + return {x:x, y:y}; // Object + }, + + argsToObj: function(){ + // summary: + // Attempts to determine in a Mouse Object + // was passed or indiviual numbers. Returns + // an object. + // + var a = arguments; + if(a.length < 4){ return a[0]; } + return { + start:{ + x:a[0], + y:a[1] + }, + x:a[2], + y:a[3]//, + //snap:a[4] + }; // Object + }, + + distance: function(/*EventObject or x1,y1,x2,y2*/){ + // summary: + // Return the length derived from the coordinates + // in the Mouse object. Different from util.length + // in that this always returns an absolute value. + // + var o = this.argsToObj.apply(this, arguments); + return Math.abs(Math.sqrt(Math.pow(o.start.x-o.x, 2)+Math.pow(o.start.y-o.y, 2))); // Number + }, + + slope:function(/*Object*/p1, /*Object*/p2){ + // summary: + // Given two poits of a line, returns the slope. + if(!(p1.x-p2.x)){ return 0; } + return ((p1.y-p2.y)/(p1.x-p2.x)); // Number + }, + + pointOnCircle: function(/*Number*/cx, /*Number*/cy, /*Number*/radius, /*Number*/angle){ + // summary: + // A *very* helpful method. If you know the center + // (or starting) point, length and angle, find the + // x,y point at the end of that line. + // + var radians = angle * Math.PI / 180.0; + var x = radius * Math.cos(radians); + var y = radius * Math.sin(radians); + return { + x:cx+x, + y:cy-y + }; // Object + }, + + constrainAngle: function(/*EventObject*/obj, /*Number*/min, /*Number*/max){ + // summary: + // Ensures the angle in the Mouse Object is within the + // min and max limits. If not one of those limits is used. + // Returns an x,y point for the angle used. + // + var angle = this.angle(obj); + if(angle >= min && angle <= max){ + return obj; // Object + } + var radius = this.length(obj); + var new_angle = angle > max ? max : min - angle < 100 ? min : max; + return this.pointOnCircle(obj.start.x,obj.start.y,radius, new_angle); // Object + }, + + snapAngle: function(/*EventObject*/obj, /*Float*/ca){ + // summary: + // Snaps a line to the nearest angle + // obj: Mouse object (see dojox.drawing.Mouse) + // ca: Fractional amount to snap to + // A decimal number fraction of a half circle + // .5 would snap to 90 degrees + // .25 would snap to 45 degrees + // .125 would snap to 22.5 degrees, etc. + // + var radians = this.radians(obj), + radius = this.length(obj), + seg = Math.PI * ca, + rnd = Math.round(radians/seg), + new_radian = rnd*seg, + new_angle = this.radToDeg(new_radian), + pt = this.pointOnCircle(obj.start.x,obj.start.y,radius,new_angle); + return pt; // Object + }, + + // helpers + idSetStart: function(num){ + start=num; + }, + + uid: function(/* ? String */str){ + // summary: + // Creates a unique ID. + // arguments: + // str: String + // If provided, kept in a map, incremented + // and used in the id. Otherwise 'shape' is used. + // + str = str || "shape"; + uidMap[str] = uidMap[str]===undefined ? start : uidMap[str] + 1; + return str + uidMap[str]; // String + }, + + abbr: function(type){ + // summary: + // Converts a namespace (typically a tool or a stencil) into + // an abbreviation + return type.substring(type.lastIndexOf(".")+1).charAt(0).toLowerCase() + + type.substring(type.lastIndexOf(".")+2); + }, + mixin: function(o1, o2){ + // TODO: make faster + //return dojo.mixin(dojo.clone(o1), dojo.clone(o2)); + }, + + objects:{}, //private? + register: function(/*Object*/obj){ + // summary: + // Since util is the only Singleton in Drawing (besides + // keys) it is used to help connect the Drawing object + // the Toolbar. Since multiple drawings can be on one + // page, this function serves a little more use than + // on first apearance. + this.objects[obj.id] = obj; + }, + byId: function(/*String*/id){ + // summary: + // Get an object that was registered with util.register + // + return this.objects[id]; + }, + attr: function(/* Object */ elem, /* property */ prop, /* ? value */ value, squelchErrors){ + // summary: + // Helper function to attach attributes to SVG and VML raw nodes. + // + + if(!elem){ return false; } + try{ + + // util is a crappy check, but we need to tell the diff + // between a Drawing shape and a GFX shape + if(elem.shape && elem.util){ + elem = elem.shape; + } + + if(!value && prop=="id" && elem.target){ + + var n = elem.target; + while(!dojo.attr(n, "id")){ + n = n.parentNode; + } + return dojo.attr(n, "id"); + } + + if(elem.rawNode || elem.target){ + var args = Array.prototype.slice.call(arguments); + args[0] = elem.rawNode || elem.target; + return dojo.attr.apply(dojo, args); + } + return dojo.attr(elem, "id"); + + + + }catch(e){ + if(!squelchErrors){ + // For debugging only. These errors actually cause errors in IE's console + //console.error("BAD ATTR: prop:", prop, "el:", elem) + //console.error(e) + //console.trace(); + } + return false; + } + } + }; + +})(); +}); diff --git a/js/dojo/dojox/drawing/util/oo.js b/js/dojo/dojox/drawing/util/oo.js new file mode 100644 index 0000000..33048f9 --- /dev/null +++ b/js/dojo/dojox/drawing/util/oo.js @@ -0,0 +1,126 @@ +//>>built +// wrapped by build app +define("dojox/drawing/util/oo", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.util.oo"); + +// TODO: +// allow a declare without a mixin + +dojox.drawing.util.oo = { + // summary: + // Inheritance utilities used in DojoX Drawing + // description: + // Inheritance utilities used in DojoX Drawing. + // There were designed in a effort to make Drawing as + // fast as possible - especially in a case where thousands + // of objects are being loaded. Drawing declare performs + // about 3 times faster than Dojo declare and 2 times + // faster than Dojox declare. This is not to say Drawing + // declare is wthout limitations. It doesn't have the same + // syntatic sugar and extensibility of the other two. You + // can't inhert methods. It won't work with Dijit. But it + // is simple and effective. + // + declare: function(){ + // summary: + // Creates a constructor Function from a + // Function, and collection of methods, and + // more Functions that are extended. + // description: + // Similar in look and feel to Dojo declare as + // far as order and number of arguments, although + // constructed a little closer to prototypical + // inheritance. All arguments passed into the + // constructor are passed into all sub constructors. + // arguments: + // Function, [Object|Function....] + // The first argument is always the base + // constructor. The last argument is always + // an object of methods (or empty object) to + // be mixed in (in the future would like to + // make that object optional). Remaining + // arguments are other constructors mixed in + // using extend() (See below). + // example: + // | MyFunction = dojox.drawing.util.oo.declare( + // | MyOtherFunction, + // | YetAnotherFunction, + // | function(options){ + // | // This is my constructor. It will fire last. + // | // The other constructors will fire before this. + // | }, + // | { + // | customType:"equation", // mixed in property + // | doThing: function(){ // mixed in method + // | + // | } + // | } + // | ); + // | + // | var f = new MyFunction(); + // + var f, o, ext=0, a = arguments; + + if(a.length<2){ console.error("drawing.util.oo.declare; not enough arguments")} + if(a.length==2){ + f = a[0]; o = a[1]; + }else{ + a = Array.prototype.slice.call(arguments); + o = a.pop(); + f = a.pop(); + ext = 1; + } + for(var n in o){ + f.prototype[n] = o[n]; + } + if(ext){ + a.unshift(f); + f = this.extend.apply(this, a); + } + return f; // Function + }, + extend: function(){ + // summary: + // Extends constructors to inherit from other + // constructors . + // description: + // Typically not used by itself - it's used as + // part of declare(). Could be used by itself + // however, to mix together two or more + // constructors. + // arguments: + // Function, [ Function...] + // Any number of arguments, all must be + // function constructors. The first is + // considered the base object and its + // constructor will fire first. + // example: + // | var A = function(){}; + // | var B = function(){}; + // | var C = function(){}; + // | var D = dojox.drawing.util.oo.extend(A, B, C); + // | var e = new D(); + // + var a = arguments, sub = a[0]; + if(a.length<2){ console.error("drawing.util.oo.extend; not enough arguments")} + var f = function (){ + for(var i=1;i<a.length;i++){ + a[i].prototype.constructor.apply(this, arguments); + } + // sub should fire last + sub.prototype.constructor.apply(this, arguments); + + } + for(var i=1;i<a.length;i++){ + for(var n in a[i].prototype){ + f.prototype[n] = a[i].prototype[n]; + } + } + + for(n in sub.prototype){ + f.prototype[n] = sub.prototype[n]; + } + return f; // Function + } +}; +}); diff --git a/js/dojo/dojox/drawing/util/positioning.js b/js/dojo/dojox/drawing/util/positioning.js new file mode 100644 index 0000000..13afcad --- /dev/null +++ b/js/dojo/dojox/drawing/util/positioning.js @@ -0,0 +1,69 @@ +//>>built +// wrapped by build app +define("dojox/drawing/util/positioning", ["dijit","dojo","dojox"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.util.positioning"); + +(function(){ + + var textOffset = 4; // distance from line to text box + var textYOffset = 20; // height of text box + + + dojox.drawing.util.positioning.label = function(/*Object*/start, /*Object*/end){ + // summary: + // Returns the optimal text positions for annotations.Label. + + // label at middle of vector + var x = 0.5*(start.x+end.x); + var y = 0.5*(start.y+end.y); + + // move label a set distance from the line + var slope = dojox.drawing.util.common.slope(start, end); + var deltay = textOffset/Math.sqrt(1.0+slope*slope); + + if(end.y>start.y && end.x>start.x || end.y<start.y && end.x<start.x){ + // Position depending on quadrant. Y offset + // positions box aligned vertically from top + deltay = -deltay; + y -= textYOffset; + } + x += -deltay*slope; + y += deltay; + + // want text to be away from start of vector + // This will make force diagrams less crowded + var align = end.x<start.x ? "end" : "start"; + + return { x:x, y:y, foo:"bar", align:align}; // Object + }; + + dojox.drawing.util.positioning.angle = function(/*Object*/start, /*Object*/end){ + // summary: + // Returns the optimal position for annotations.Angle. + // + // angle at first third of vector + var x = 0.7*start.x+0.3*end.x; + var y = 0.7*start.y+0.3*end.y; + // move label a set distance from the line + var slope = dojox.drawing.util.common.slope(start, end); + var deltay = textOffset/Math.sqrt(1.0+slope*slope); + + if(end.x<start.x){deltay = -deltay;} + x += -deltay * slope; + y += deltay; + + // want text to be clockwise from vector + // to match angle measurement from x-axis + var align = end.y>start.y ? "end" : "start"; + // box vertical aligned from middle + y += end.x > start.x ? 0.5*textYOffset : -0.5*textYOffset; + + return { x:x, y:y, align:align}; // Object + } + +})(); + + + + +}); diff --git a/js/dojo/dojox/drawing/util/typeset.js b/js/dojo/dojox/drawing/util/typeset.js new file mode 100644 index 0000000..c9c81c1 --- /dev/null +++ b/js/dojo/dojox/drawing/util/typeset.js @@ -0,0 +1,72 @@ +//>>built +// wrapped by build app +define("dojox/drawing/util/typeset", ["dijit","dojo","dojox","dojo/require!dojox/drawing/library/greek"], function(dijit,dojo,dojox){ +dojo.provide("dojox.drawing.util.typeset"); +dojo.require("dojox.drawing.library.greek"); +// Summary: +// Singleton used for converting characters and typsetting. Required by _base. +// +// Description: +// Eventually, this is supposed to turn input strings of mathematical +// expressions into typeset expressions that can be displayed on the +// canvas. For now, we just generate Greek letters based on LaTeX style +// entity codes. + +(function(){ + + var greeks = dojox.drawing.library.greek; + + dojox.drawing.util.typeset = { + + convertHTML: function(inText){ + if(inText){ + return inText.replace(/&([^;]+);/g,function(match,code){ + if(code.charAt(0)=='#'){ + //coerce remainder of string to int + var number=+code.substr(1); + if(!isNaN(number)){ + return String.fromCharCode(number); + } + }else if(greeks[code]){ + return String.fromCharCode(greeks[code]); + } + // This is generally for server code, so there + // is no point bothering the user in the case of an error. + console.warn("no HTML conversion for ",match); + return match; + }); + } + return inText; + }, + + convertLaTeX: function(inText){ + // console.log("***** convertLaTeX for ",inText); + if(inText){ + return inText.replace(/\\([a-zA-Z]+)/g,function(match,word){ + if(greeks[word]){ + return String.fromCharCode(greeks[word]); + }else if(word.substr(0,2)=="mu"){ + // special handling for \mu since it is + // a unit prefix for micro. + return String.fromCharCode(greeks["mu"])+word.substr(2); + }else if(word.substr(0,5)=="theta"){ + // special handling for \theta since it is + // a standard prefix for angle associated with a vector. + return String.fromCharCode(greeks["theta"])+word.substr(5); + }else if(word.substr(0,3)=="phi"){ + // special handling for \phi since it is + // a standard prefix for angle associated with a z-axis vector. + return String.fromCharCode(greeks["phi"])+word.substr(3); + } + console.log("no match for ",match," in ",inText); + console.log("Need user-friendly error handling here!"); + }).replace(/\\\\/g,'\\'); + } + return inText; + } + + }; + +})(); + +}); |
