/*
Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved.
Available via Academic Free License >= 2.1 OR the modified BSD license.
see: http://dojotoolkit.org/license for details
*/
if(!dojo._hasResource["dojox.drawing.stencil._Base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojox.drawing.stencil._Base"] = true;
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
}
}
);
}