diff options
Diffstat (limited to 'js/dojo/dojox/drawing/stencil/_Base.js')
| -rw-r--r-- | js/dojo/dojox/drawing/stencil/_Base.js | 1212 |
1 files changed, 1212 insertions, 0 deletions
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 + } + } +); + + +}); |
