_Base.js 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211
  1. // wrapped by build app
  2. define("dojox/drawing/stencil/_Base", ["dijit","dojo","dojox","dojo/require!dojo/fx/easing"], function(dijit,dojo,dojox){
  3. dojo.provide("dojox.drawing.stencil._Base");
  4. dojo.require("dojo.fx.easing");
  5. /*=====
  6. StencilArgs = {
  7. // container: [readonly] dojo.gfx.group
  8. // The parent shape that contains all
  9. // shapes used in a Stencil
  10. container:null,
  11. //
  12. // anchorType: String
  13. // Optionally blank or 'group'. 'group' tells
  14. // an anchor point that it must constrain
  15. // itself to other anchor points.
  16. anchorType:"",
  17. //
  18. // isText: Boolean
  19. // Whether this is a text object or not
  20. // (either stencil.text or tools.TextBlock)
  21. isText:false,
  22. //
  23. // shortType: String
  24. // The type of stencil that corresponds with the types and
  25. // constructors used in Drawing.registerTool
  26. shortType:"",
  27. //
  28. // annotation: Boolean
  29. // A Stencil used within a Stencil. An annotation
  30. // is not selectable or clickable. A Label would
  31. // be one example.
  32. annotation:false,
  33. //
  34. // subShape: Boolean
  35. // A Stencil used within a Stencil. A subShape
  36. // is clickable. An arrow head would be an example.
  37. subShape:false,
  38. //
  39. // style: Object
  40. // An instance of the styles and defaults used within
  41. // the Stencil.
  42. style:null,
  43. //
  44. // util: Object
  45. // Pointer to util.common
  46. util:null,
  47. //
  48. // mouse: Object
  49. // Pointer to the mouse instance
  50. mouse:null,
  51. //
  52. // keys: Object
  53. // Pointer to the keys class
  54. keys:null,
  55. //
  56. // points: StencilPoints
  57. // Points is an array of objects that make up the
  58. // description of a Stencil. The points to a Rect
  59. // that is 100x100 and at x:10 and y:10 would look like:
  60. // [{x:10,y:10}, {x:110, y:10}, {x:110, y:110}, {x:10, y:110}]
  61. // Points go clockwise from the top left. In the case of Paths,
  62. // they would go in the order that the Stencil would be drawn.
  63. // Always when the points Array is set, a data Object is created
  64. // as well. So never set points directly, always use setPoints().
  65. // See:
  66. // setPoints()
  67. points:[],
  68. //
  69. // data: StencilData
  70. // A data object typically (but not always) resembles the data
  71. // that is used to create the dojox.gfx Shape. The same Rect
  72. // example shown in points above would look like:
  73. // {x:10, y:10, width:100, height:100}
  74. // And an Ellipse with the same coordinates:
  75. // {cx:55, cy:55, rx:50, ry:50}
  76. // The only Stencil that does not support data (at this time)
  77. // is the Path. While x1,x2,x3... culd be used in a data object
  78. // it doesn't provide much benefit.
  79. // Always when a data object is set, a set of points is created
  80. // as well. So never set data directly, always use setData().
  81. // See:
  82. // setData()
  83. data:null,
  84. //
  85. // marginZero [readonly] Number
  86. // How closely shape can get to y:0 or x:0. Less than zero has
  87. // bugs in VML. This is set with defaults, and should be equal
  88. // to half the size of an anchor point (5 px)
  89. marginZero:0,
  90. //
  91. // created [readonly] Boolean
  92. // Whether the Stencil has been rendered for the first time or
  93. // not.
  94. created: false,
  95. //
  96. // highlighted [readonly] Boolean
  97. // Whether the Stencil is highlighted or not.
  98. highlighted:false,
  99. //
  100. // selected [readonly] Boolean
  101. // Whether the Stencil is selected or not.
  102. selected:false,
  103. //
  104. // draws [readonly] Boolean
  105. // Whether the Stencil can draw with a mouse drag or can just
  106. // be created programmtically. If the Stencil comes from the
  107. // stencil package, it should be draw:false. If it comes from
  108. // the tools package it should be draw:true.
  109. draws:false
  110. }
  111. StencilPoint = {
  112. // summary:
  113. // One point Object in the points Array
  114. // x: Number
  115. // x position of point
  116. // y: Number
  117. // y position of point
  118. }
  119. ToolsSetup = {
  120. // summary:
  121. // An object attached to a Tool's constructor
  122. // used to inform the toolbar of its information
  123. // and properties.
  124. // description:
  125. // This object is inserted into the *function* of
  126. // a tool (not a stencil). Like: function.ToolsSetup
  127. // It must be attached after constructr creation, so
  128. // this object is found at the botton of the file.
  129. //
  130. // name:String
  131. // Fully qualified name of constructor
  132. // tooltip: Stirng
  133. // Text to display on toolbar button hover
  134. // iconClass: String
  135. // CSS class with icon information to attach
  136. // to toolbar button.
  137. }
  138. =====*/
  139. dojox.drawing.stencil._Base = dojox.drawing.util.oo.declare(
  140. // summary:
  141. // The base class used for all Stencils.
  142. // description:
  143. // All stencils extend this base class.
  144. // Most methods and events can be found here.
  145. //
  146. function(options){
  147. //console.log("______Base", this.type, options)
  148. // clone style so changes are reflected in future shapes
  149. dojo.mixin(this, options);
  150. this.style = options.style || dojox.drawing.defaults.copy();
  151. if(options.stencil){
  152. this.stencil = options.stencil;
  153. this.util = options.stencil.util;
  154. this.mouse = options.stencil.mouse;
  155. this.container = options.stencil.container;
  156. this.style = options.stencil.style;
  157. }
  158. // don't use the 'g' on these, it affects
  159. // the global RegExp
  160. var lineTypes = /Line|Vector|Axes|Arrow/;
  161. var textTypes = /Text/;
  162. this.shortType = this.util.abbr(this.type);
  163. this.isText = textTypes.test(this.type);
  164. this.isLine = lineTypes.test(this.type);
  165. this.renderHit = this.style.renderHitLayer;
  166. if(!this.renderHit && this.style.renderHitLines && this.isLine){
  167. this.renderHit = true;
  168. }
  169. if(!this.renderHit && this.style.useSelectedStyle){
  170. this.useSelectedStyle = true;
  171. this.selCopy = dojo.clone(this.style.selected);
  172. for(var nm in this.style.norm){
  173. if(this.style.selected[nm]===undefined){
  174. this.style.selected[nm] = this.style.norm[nm];
  175. }
  176. }
  177. this.textSelected = dojo.clone(this.style.text);
  178. this.textSelected.color = this.style.selected.fill;
  179. }
  180. this.angleSnap = this.style.angleSnap || 1;
  181. this.marginZero = options.marginZero || this.style.anchors.marginZero;
  182. this.id = options.id || this.util.uid(this.type);
  183. this._cons = [];
  184. if(!this.annotation && !this.subShape){
  185. this.util.attr(this.container, "id", this.id);
  186. }
  187. this.connect(this, "onBeforeRender", "preventNegativePos");
  188. this._offX = this.mouse.origin.x;
  189. this._offY = this.mouse.origin.y;
  190. if(this.isText){
  191. this.align = options.align || this.align;
  192. this.valign = options.valign || this.valign;
  193. if(options.data && options.data.makeFit){
  194. var textObj = this.makeFit(options.data.text, options.data.width);
  195. this.textSize = this.style.text.size = textObj.size;
  196. this._lineHeight = textObj.box.h;
  197. }else{
  198. this.textSize = parseInt(this.style.text.size, 10);
  199. this._lineHeight = this.textSize * 1.4;
  200. }
  201. // TODO: thinner text selection
  202. //this.style.hitSelected.width *= 0.5;
  203. //
  204. // ouch. how verbose. My mixin is weak....
  205. this.deleteEmptyCreate = options.deleteEmptyCreate!==undefined ? options.deleteEmptyCreate : this.style.text.deleteEmptyCreate;
  206. this.deleteEmptyModify = options.deleteEmptyModify!==undefined ? options.deleteEmptyModify : this.style.text.deleteEmptyModify;
  207. }
  208. //this.drawingType
  209. this.attr(options.data);
  210. // make truthy
  211. // add to renders below
  212. // this.baseRender && render()
  213. //if(this.type == "dojox.drawing.tools.TextBlock"){
  214. if(this.noBaseRender){
  215. // TextBlock will handle rendering itself
  216. return;
  217. }
  218. //console.log("BASE OPTS:", options)
  219. if(options.points){
  220. //console.log("__________Base.constr >>>> ", this.type, "points", options.points)
  221. if(options.data && options.data.closePath===false){
  222. this.closePath = false;
  223. }
  224. this.setPoints(options.points);
  225. this.connect(this, "render", this, "onRender", true);
  226. this.baseRender && this.enabled && this.render();
  227. options.label && this.setLabel(options.label);
  228. options.shadow && this.addShadow(options.shadow);
  229. }else if(options.data){
  230. //console.log("___________Base.constr", this.type, "options data", options.data)
  231. options.data.width = options.data.width ? options.data.width : this.style.text.minWidth;
  232. options.data.height = options.data.height ? options.data.height : this._lineHeight;
  233. this.setData(options.data);
  234. this.connect(this, "render", this, "onRender", true);
  235. this.baseRender && this.enabled && this.render(options.data.text);
  236. this.baseRender && options.label && this.setLabel(options.label);
  237. this.baseRender && options.shadow && this.addShadow(options.shadow);
  238. }else if(this.draws){
  239. //console.log("_____________Base.constr", this.type, "draws")
  240. this.points = [];
  241. this.data = {};
  242. this.connectMouse();
  243. this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender");
  244. }
  245. if(this.showAngle){
  246. this.angleLabel = new dojox.drawing.annotations.Angle({stencil:this});
  247. }
  248. if(!this.enabled){
  249. this.disable();
  250. this.moveToBack();
  251. // some things render some don't...
  252. this.render(options.data.text);
  253. }
  254. },
  255. {
  256. // type: String
  257. // The type of Stencil this is. Should be overridden
  258. // by extending classes.
  259. // FIXME: should this be declaredClass?
  260. type:"dojox.drawing.stencil",
  261. //
  262. // minimumSize: Number
  263. // The minimum size allowed for a render. If the size
  264. // is less, the shape is destroyed.
  265. minimumSize:10,
  266. //
  267. // enabled [readonly] Boolean
  268. // Whether the Stencil is enabled or not.
  269. enabled:true,
  270. drawingType:"stencil",
  271. //points:[],
  272. setData: function(/*StencilData*/data){
  273. // summary:
  274. // Setter for Stencil data; also converts
  275. // data to points. See individual Stencils
  276. // for specific data properties.
  277. this.data = data;
  278. this.points = this.dataToPoints();
  279. },
  280. setPoints: function(/*StencilPoints*/points){
  281. // summary:
  282. // Setter for Stencil points; also converts
  283. // points to data. See individual Stencils
  284. // for specific points properties.
  285. this.points = points;
  286. // Path doesn't do data
  287. if(this.pointsToData){
  288. this.data = this.pointsToData();
  289. }
  290. },
  291. onDelete: function(/* Stencil */ stencil){
  292. // summary:
  293. // Stub - fires before this is destroyed
  294. console.info("onDelete", this.id);
  295. },
  296. onBeforeRender: function(/*Object*/ stencil){
  297. // summary:
  298. // Stub - Fires before render occurs.
  299. },
  300. onModify: function(/*Object*/stencil){
  301. // summary:
  302. // Stub - fires on change of any property,
  303. // including style properties
  304. },
  305. onChangeData: function(/*Object*/ stencil){
  306. // summary:
  307. // Stub - fires on change of dimensional
  308. // properties or a text change
  309. },
  310. onChangeText: function(value){ // value or 'this' ?
  311. // summary:
  312. // Stub - fires on change of text in a
  313. // TextBlock tool only
  314. },
  315. onRender: function(/*Object*/ stencil){
  316. // summary:
  317. // Stub - Fires on creation.
  318. // Drawing connects to this (once!) to be
  319. // notified of drag completion. But only if it
  320. // was registered as a Tool. Creating Stencil in and of
  321. // itself does not register it.
  322. //
  323. // This should fire
  324. // at the *end* of creation (not during drag)
  325. //
  326. // FIXME:
  327. // This should probably be onCreate. It should
  328. // only fire once. But the mechanism for determining
  329. // this is more complicated than it sounds.
  330. //
  331. this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender");
  332. this.created = true;
  333. this.disconnectMouse();
  334. // for Silverlight
  335. if(this.shape){
  336. this.shape.superClass = this;
  337. }else{
  338. this.container.superClass = this;
  339. }
  340. this._setNodeAtts(this);
  341. //console.warn("ONRENDER", this.id, this)
  342. },
  343. onChangeStyle: function(/*Object*/stencil){
  344. // summary:
  345. // Fires when styles of shape has changed
  346. //
  347. this._isBeingModified = true; // need this to prevent onRender
  348. if(!this.enabled){
  349. this.style.current = this.style.disabled;
  350. this.style.currentText = this.style.textDisabled;
  351. this.style.currentHit = this.style.hitNorm;
  352. }else{
  353. this.style.current = this.style.norm;
  354. this.style.currentHit = this.style.hitNorm;
  355. this.style.currentText = this.style.text;
  356. }
  357. if(this.selected){
  358. if(this.useSelectedStyle){
  359. this.style.current = this.style.selected;
  360. this.style.currentText = this.textSelected;
  361. }
  362. this.style.currentHit = this.style.hitSelected;
  363. }else if(this.highlighted){
  364. //this.style.current = this.style.highlighted;
  365. this.style.currentHit = this.style.hitHighlighted;
  366. //this.style.currentText = this.style.textHighlighted;
  367. }
  368. // NOTE: Can't just change props like setStroke
  369. // because Silverlight throws error
  370. this.render();
  371. },
  372. animate: function(options, create){
  373. console.warn("ANIMATE..........................")
  374. var d = options.d || options.duration || 1000;
  375. var ms = options.ms || 20;
  376. var ease = options.ease || dojo.fx.easing.linear;
  377. var steps = options.steps;
  378. var ts = new Date().getTime();
  379. var w = 100;
  380. var cnt = 0;
  381. var isArray = true;
  382. var sp, ep;
  383. if(dojo.isArray(options.start)){
  384. sp = options.start;
  385. ep = options.end;
  386. }else if(dojo.isObject(options.start)){
  387. sp = options.start;
  388. ep = options.end;
  389. isArray = false;
  390. }else{
  391. console.warn("No data provided to animate")
  392. }
  393. var v = setInterval(dojo.hitch(this, function(){
  394. var t = new Date().getTime() - ts;
  395. var p = ease(1-t/d);
  396. if(t > d || cnt++ > 100){
  397. clearInterval(v);
  398. return;
  399. }
  400. if(isArray){
  401. var pnts = [];
  402. dojo.forEach(sp, function(pt, i){
  403. var o = {
  404. x: (ep[i].x-sp[i].x)*p + sp[i].x,
  405. y: (ep[i].y-sp[i].y)*p + sp[i].y
  406. };
  407. pnts.push(o);
  408. });
  409. this.setPoints(pnts);
  410. this.render();
  411. }else{
  412. var o = {};
  413. for(var nm in sp){
  414. o[nm] = (ep[nm] - sp[nm]) * p + sp[nm];
  415. }
  416. this.attr(o);
  417. }
  418. //console.dir(pnts)
  419. //this.attr("height", w);
  420. ////console.log("W:", w)
  421. //w += 5;
  422. }), ms);
  423. },
  424. attr: function(/*String | Object*/key, /* ? String | Number */value){
  425. // summary
  426. // Changes properties in the style or disabled styles,
  427. // depending on whether the object is enabled.
  428. // Also can be used to change most position and size props.
  429. // NOTE: JUST A SETTTER!! TODO!
  430. // WARNING:
  431. // Not doing any Stencil-type checking here. Setting a height
  432. // on a line or an angle on a rectangle will just not render.
  433. // FIXME
  434. // 'width' attr is used for line width. How to change the width of a stencil?
  435. var n = this.enabled?this.style.norm:this.style.disabled;
  436. var t = this.enabled?this.style.text:this.style.textDisabled;
  437. var ts = this.textSelected || {},
  438. o,
  439. nm,
  440. width,
  441. styleWas = dojo.toJson(n),
  442. textWas = dojo.toJson(t);
  443. var coords = {
  444. x:true,
  445. y:true,
  446. r:true,
  447. height:true,
  448. width:true,
  449. radius:true,
  450. angle:true
  451. };
  452. var propChange = false;
  453. if(typeof(key)!="object"){
  454. o = {};
  455. o[key] = value;
  456. }else{
  457. // prevent changing actual data
  458. o = dojo.clone(key);
  459. }
  460. if(o.width){
  461. // using width for size,
  462. // borderWidth should be used
  463. // for line thickness
  464. width = o.width;
  465. delete o.width;
  466. }
  467. for(nm in o){
  468. if(nm in n){ n[nm] = o[nm]; }
  469. if(nm in t){ t[nm] = o[nm]; }
  470. if(nm in ts){ ts[nm] = o[nm]; }
  471. if(nm in coords){
  472. coords[nm] = o[nm];
  473. propChange = true;
  474. if(nm == "radius" && o.angle===undefined){
  475. o.angle = coords.angle = this.getAngle();
  476. }else if(nm == "angle" && o.radius===undefined){
  477. o.radius = coords.radius = this.getRadius();
  478. }
  479. }
  480. if(nm == "text"){
  481. this.setText(o.text);
  482. }
  483. if(nm == "label"){
  484. this.setLabel(o.label);
  485. }
  486. }
  487. if(o.borderWidth!==undefined){
  488. n.width = o.borderWidth;
  489. }
  490. if(this.useSelectedStyle){
  491. // using the orginal selected style copy as
  492. // a reference map of what props to copy
  493. for(nm in this.style.norm){
  494. if(this.selCopy[nm]===undefined){
  495. this.style.selected[nm] = this.style.norm[nm];
  496. }
  497. }
  498. this.textSelected.color = this.style.selected.color;
  499. }
  500. if(!this.created){
  501. return;
  502. }
  503. // basic transform
  504. if(o.x!==undefined || o.y!==undefined){
  505. var box = this.getBounds(true);
  506. var mx = { dx:0, dy:0 };
  507. for(nm in o){
  508. if(nm=="x" || nm =="y" || nm =="r"){
  509. mx["d"+nm] = o[nm] - box[nm];
  510. }
  511. }
  512. this.transformPoints(mx);
  513. }
  514. var p = this.points;
  515. if(o.angle!==undefined){
  516. this.dataToPoints({
  517. x:this.data.x1,
  518. y:this.data.y1,
  519. angle:o.angle,
  520. radius:o.radius
  521. });
  522. } else if(width!==undefined){
  523. p[1].x = p[2].x = p[0].x + width;
  524. this.pointsToData(p);
  525. }
  526. if(o.height!==undefined && o.angle===undefined){
  527. console.log("Doing P2D-2");
  528. p[2].y = p[3].y = p[0].y + o.height;
  529. this.pointsToData(p);
  530. }
  531. if(o.r!==undefined){
  532. this.data.r = Math.max(0, o.r);
  533. }
  534. //console.dir(this.data);
  535. if(propChange || textWas!=dojo.toJson(t) || styleWas != dojo.toJson(n)){
  536. // to trigger the render
  537. // other events will be called post render
  538. this.onChangeStyle(this);
  539. }
  540. o.width = width;
  541. if(o.cosphi!=undefined){
  542. !this.data? this.data = {cosphi:o.cosphi} : this.data.cosphi = o.cosphi;
  543. this.style.zAxis = o.cosphi!=0 ? true : false;
  544. }
  545. },
  546. exporter: function(){
  547. // summary:
  548. // Exports Stencil data
  549. //
  550. var type = this.type.substring(this.type.lastIndexOf(".")+1).charAt(0).toLowerCase()
  551. + this.type.substring(this.type.lastIndexOf(".")+2);
  552. var o = dojo.clone(this.style.norm);
  553. o.borderWidth = o.width;
  554. delete o.width;
  555. if(type=="path"){
  556. o.points = this.points;
  557. }else{
  558. o = dojo.mixin(o, this.data);
  559. }
  560. o.type = type;
  561. if(this.isText){
  562. o.text = this.getText();
  563. o = dojo.mixin(o, this.style.text);
  564. delete o.minWidth;
  565. delete o.deleteEmptyCreate;
  566. delete o.deleteEmptyModify;
  567. }
  568. var lbl = this.getLabel();
  569. if(lbl){
  570. o.label = lbl;
  571. }
  572. return o;
  573. },
  574. // TODO:
  575. // Makes these all called by att()
  576. // Should points and data be?
  577. //
  578. disable: function(){
  579. // summary:
  580. // Disables Stencil so it is not selectable.
  581. // Changes the color to the disabled style.
  582. this.enabled = false;
  583. this.renderHit = false;
  584. this.onChangeStyle(this);
  585. },
  586. enable: function(){
  587. // summary:
  588. // Enables Stencil so it is not selectable (if
  589. // it was selectable to begin with). Changes the
  590. // color to the current style.
  591. this.enabled = true;
  592. this.renderHit = true;
  593. this.onChangeStyle(this);
  594. },
  595. select: function(){
  596. // summary:
  597. // Called when the Stencil is selected.
  598. // NOTE: Calling this will not select the Stencil
  599. // calling this just sets the style to the 'selected'
  600. // theme. 'manager.Stencil' should be used for selecting
  601. // Stencils.
  602. //
  603. this.selected = true;
  604. this.onChangeStyle(this);
  605. },
  606. deselect: function(/*Boolean*/useDelay){
  607. // summary:
  608. // Called when the Stencil is deselected.
  609. // NOTE: Calling this will not deselect the Stencil
  610. // calling this just sets the style to the current
  611. // theme. 'manager.Stencil' should be used for selecting
  612. // and deselecting Stencils.
  613. //
  614. // arguments:
  615. // useDelay: Boolean
  616. // Adds slight delay before the style is set.
  617. //
  618. // should not have to render here because the deselection
  619. // re-renders after the transform
  620. // but... oh well.
  621. if(useDelay){
  622. setTimeout(dojo.hitch(this, function(){
  623. this.selected = false;
  624. this.onChangeStyle(this);
  625. }),200);
  626. }else{
  627. this.selected = false;
  628. this.onChangeStyle(this);
  629. }
  630. },
  631. _toggleSelected: function(){
  632. if(!this.selected){ return; }
  633. this.deselect();
  634. setTimeout(dojo.hitch(this, "select"), 0);
  635. },
  636. highlight: function(){
  637. // summary:
  638. // Changes style to the highlight theme.
  639. this.highlighted = true;
  640. this.onChangeStyle(this);
  641. },
  642. unhighlight: function(){
  643. // summary:
  644. // Changes style to the current theme.
  645. this.highlighted = false;
  646. this.onChangeStyle(this);
  647. },
  648. moveToFront: function(){
  649. // summary:
  650. // Moves Stencil to the front of all other items
  651. // on the canvas.
  652. this.container && this.container.moveToFront();
  653. },
  654. moveToBack: function(){
  655. // summary:
  656. // Moves Stencil to the back of all other items
  657. // on the canvas.
  658. this.container && this.container.moveToBack();
  659. },
  660. onTransformBegin: function(/* ? manager.Anchor */anchor){
  661. // summary:
  662. // Fired at the start of a transform. This would be
  663. // an anchor drag or a selection.
  664. //
  665. this._isBeingModified = true;
  666. },
  667. onTransformEnd: function(/* manager.Anchor */anchor){
  668. // summary:
  669. // Called from anchor point up mouse up
  670. this._isBeingModified = false;
  671. this.onModify(this);
  672. },
  673. onTransform: function(/* ? manager.Anchor */anchor){
  674. // summary:
  675. // Called from anchor point mouse drag
  676. // also called from plugins.Pan.checkBounds
  677. if(!this._isBeingModified){
  678. this.onTransformBegin();
  679. }
  680. // this is not needed for anchor moves, but it
  681. // is for stencil move:
  682. this.setPoints(this.points);
  683. this.render();
  684. },
  685. transformPoints: function(mx){
  686. // summary:
  687. // Moves object to a new X Y location
  688. // mx is additive. So mx.dx=1 will move the stencil
  689. // 1 pixel to the right from wherever it was.
  690. //
  691. // An attempt is made to prevent < 0 errors, but
  692. // this won't work on all shapes (like Axes)
  693. //
  694. if(!mx.dx && !mx.dy){
  695. // no change
  696. return;
  697. }
  698. var backup = dojo.clone(this.points), abort = false;
  699. dojo.forEach(this.points, function(o){
  700. o.x += mx.dx;
  701. o.y += mx.dy;
  702. if(o.x<this.marginZero || o.y<this.marginZero){
  703. abort = true;
  704. }
  705. });
  706. if(abort){
  707. this.points = backup;
  708. console.error("Attempt to set object '"+this.id+"' to less than zero.");
  709. return;
  710. }
  711. this.onTransform();
  712. this.onTransformEnd();
  713. },
  714. applyTransform: function(mx){
  715. // summary:
  716. // Applies the transform to the stencil
  717. // NOTE: PARTIALLY IMPLEMENTED
  718. // Only applies x y coords
  719. this.transformPoints(mx);
  720. },
  721. setTransform: function(/*Object*/mx){
  722. // summary:
  723. // Sets the transform to the stencil
  724. // NOTE: PARTIALLY IMPLEMENTED
  725. // Only applies x y coords
  726. this.attr({
  727. x:mx.dx,
  728. y:mx.dy
  729. });
  730. },
  731. getTransform: function(){
  732. // summary:
  733. // Returns the current transform (position) of the Stencil's
  734. // container
  735. return this.selected ? this.container.getParent().getTransform() : {dx:0, dy:0}; // Object
  736. },
  737. addShadow: function(/*Object*/args){
  738. args = args===true ? {} : args;
  739. args.stencil = this;
  740. this.shadow = new dojox.drawing.annotations.BoxShadow(args);
  741. },
  742. removeShadow: function(){
  743. this.shadow.destroy();
  744. },
  745. setLabel: function(/*String*/text){
  746. // summary:
  747. // Creates and sets a label annotation for the Stencil.
  748. // If Stencil contains a labelPosition method, that will
  749. // be used for positioning. Otherwise
  750. // dojox.drawing.util.positioning.label is used.
  751. // arguments:
  752. // text: String
  753. // The text to set as the label.
  754. //
  755. if(!this._label){
  756. this._label = new dojox.drawing.annotations.Label({
  757. text:text,
  758. util:this.util,
  759. mouse:this.mouse,
  760. stencil:this,
  761. annotation:true,
  762. container:this.container,
  763. labelPosition:this.labelPosition
  764. });
  765. }else if(text!=undefined){
  766. this._label.setLabel(text);
  767. }
  768. },
  769. getLabel: function(){
  770. // summary:
  771. // Get the text of the label.
  772. //
  773. if(this._label){
  774. return this._label.getText(); // String
  775. }
  776. return null; //null
  777. },
  778. getAngle: function(){
  779. // summary:
  780. // Gets angle of Stencil
  781. // NOTE: Only works for Lines, Arrows, Vectors and Axes
  782. // (works on points, not transforms)
  783. var d = this.pointsToData();
  784. var obj = {
  785. start:{
  786. x:d.x1,
  787. y:d.y1
  788. },
  789. x:d.x2,
  790. y:d.y2
  791. };
  792. var angle = this.util.angle(obj, this.angleSnap);
  793. // converting the angle for display: -180 -> 180, -90 -> 270
  794. angle<0 ? angle = 360 + angle : angle;
  795. return angle;
  796. },
  797. getRadius: function(){
  798. // summary:
  799. // Gets radius (length) of Stencil
  800. // NOTE: Only works for Lines, Arrows and Vectors
  801. // (not for Ellipse, Axes has its own version)
  802. //
  803. var box = this.getBounds(true);
  804. var line = {start:{x:box.x1, y:box.y1}, x:box.x2, y:box.y2};
  805. return this.util.length(line);
  806. },
  807. getBounds: function(/* ? Boolean*/absolute){
  808. // summary:
  809. // Returns the coordinates of the Stencil. This is often
  810. // different than the data or the points.
  811. // arguments:
  812. // absolute: Boolean
  813. // Keeps lines from flipping (see note).
  814. //
  815. // NOTE: Won't work for paths or annotations (labels, Axes, arrow tips)
  816. // They should overwrite.
  817. // NOTE: Primarily used for checking for if shape is off
  818. // canvas. Therefore Lines could get flipped. Use absolute
  819. // to prevent this.
  820. //
  821. var p = this.points, x1, y1, x2, y2;
  822. if(p.length==2){
  823. if(absolute){
  824. x1 = p[0].x;
  825. y1 = p[0].y;
  826. x2 = p[1].x;
  827. y2 = p[1].y
  828. }else{
  829. x1 = p[0].x < p[1].x ? p[0].x : p[1].x;
  830. y1 = p[0].y < p[1].y ? p[0].y : p[1].y;
  831. x2 = p[0].x < p[1].x ? p[1].x : p[0].x;
  832. y2 = p[0].y < p[1].y ? p[1].y : p[0].y;
  833. }
  834. return {
  835. x1:x1,
  836. y1:y1,
  837. x2:x2,
  838. y2:y2,
  839. x:x1,
  840. y:y1,
  841. w:x2-x1,
  842. h:y2-y1
  843. }; // Object
  844. }else{
  845. return {
  846. x1:p[0].x,
  847. y1:p[0].y,
  848. x2:p[2].x,
  849. y2:p[2].y,
  850. x:p[0].x,
  851. y:p[0].y,
  852. w:p[2].x - p[0].x,
  853. h:p[2].y - p[0].y
  854. }; // Object
  855. }
  856. },
  857. preventNegativePos: function(){
  858. // summary:
  859. // Internal. Prevent item from being drawn/rendered less
  860. // than zero on the X or Y.
  861. //
  862. // if being modified anchors will prevent less than zero.
  863. if(this._isBeingModified){ return; }
  864. // FIXME: why is this sometimes empty?
  865. if(!this.points || !this.points.length){ return; }
  866. if(this.type=="dojox.drawing.tools.custom.Axes"){
  867. // this scenario moves all points if < 0
  868. var minY = this.marginZero, minX = this.marginZero;
  869. dojo.forEach(this.points, function(p){ minY = Math.min(p.y, minY); });
  870. dojo.forEach(this.points, function(p){ minX = Math.min(p.x, minX); });
  871. if(minY<this.marginZero){
  872. dojo.forEach(this.points, function(p, i){
  873. p.y = p.y + (this.marginZero-minY)
  874. }, this);
  875. }
  876. if(minX<this.marginZero){
  877. dojo.forEach(this.points, function(p){
  878. p.x += (this.marginZero-minX)
  879. }, this);
  880. }
  881. }else{
  882. // this scenario moves just the one point that is < 0
  883. dojo.forEach(this.points, function(p){
  884. p.x = p.x < 0 ? this.marginZero : p.x;
  885. p.y = p.y < 0 ? this.marginZero : p.y;
  886. });
  887. }
  888. this.setPoints(this.points);
  889. },
  890. _onPostRender: function(/*Object*/data){
  891. // summary:
  892. // Drag-create or programmatic create calls onRender
  893. // and afterwards, _onPostRender is called and
  894. // manages further events.
  895. //
  896. // TODO: can this be onModify? Is that more clear?
  897. //
  898. //console.info("...........post render.....");
  899. if(this._isBeingModified){
  900. this.onModify(this);
  901. this._isBeingModified = false;
  902. }else if(!this.created){
  903. //this.onCreate(this);
  904. //this.onRender(this);
  905. }
  906. if(!this.editMode && !this.selected && this._prevData && dojo.toJson(this._prevData) != dojo.toJson(this.data)){
  907. //console.info("_Base data changed ----> : this.editMode:", this.editMode)
  908. this.onChangeData(this);
  909. this._prevData = dojo.clone(this.data);
  910. }else if(!this._prevData && (!this.isText || this.getText())){
  911. //console.info("_Base no prevData..........................");
  912. this._prevData = dojo.clone(this.data);
  913. }
  914. },
  915. _setNodeAtts: function(shape){
  916. // summary:
  917. // Internal. Sets the rawNode attribute. (Or in Silverlight
  918. // an "object attribute". "stencil" is
  919. // used by the application to determine if
  920. // something is selectable or not. This also
  921. // sets the mouse custom events like:
  922. // "onStencilUp". To disable the selectability,
  923. // make the att "", which causes a standard
  924. // mouse event.
  925. // Labels are special and used to select master stencils.
  926. var att = this.enabled && (!this.annotation || this.drawingType=="label") ? this.drawingType : "";
  927. this.util.attr(shape, "drawingType", att);
  928. },
  929. destroy: function(){
  930. // summary:
  931. // Destroys this Stencil
  932. // Note:
  933. // Can connect to this, but it's better to
  934. // connect to onDelete
  935. //
  936. // prevent loops:
  937. if(this.destroyed){ return; }
  938. if(this.data || this.points && this.points.length){
  939. this.onDelete(this);
  940. }
  941. this.disconnectMouse();
  942. this.disconnect(this._cons);
  943. dojo.disconnect(this._postRenderCon);
  944. this.remove(this.shape, this.hit);
  945. this.destroyed = true;
  946. },
  947. remove: function(/*Shape...*/){
  948. // summary:
  949. // Removes shape(s), typically before a re-render
  950. // No args defaults to this.shape
  951. // Pass in multiple args to remove multiple shapes
  952. //
  953. // FIXME: Use an Array of all shapes
  954. //
  955. var a = arguments;
  956. if(!a.length){
  957. if(!this.shape){ return; }
  958. a = [this.shape];
  959. }
  960. for(var i=0;i<a.length;i++){
  961. if(a[i]){
  962. a[i].removeShape();
  963. }
  964. }
  965. },
  966. connectMult: function(/*dojo.connect args */){
  967. // summary:
  968. // Convenience method for batches of quick connects
  969. // Handles are not returned and therefore cannot be
  970. // disconnected until Shape destroy time
  971. //
  972. if(arguments.length>1){
  973. // arguments are the connect params
  974. this._cons.push(this.connect.apply(this, arguments));
  975. }else if(dojo.isArray(arguments[0][0])){
  976. // an array of arrays of params
  977. dojo.forEach(arguments[0], function(ar){
  978. this._cons.push(this.connect.apply(this, ar));
  979. }, this);
  980. }else{
  981. //one array of params
  982. this._cons.push(this.connect.apply(this, arguments[0]));
  983. }
  984. },
  985. // TODO: connect to a Shape event from outside class
  986. connect: function(o, e, s, m, /* Boolean*/once){
  987. // summary:
  988. // Convenience method for quick connects
  989. // See comments below for possiblities
  990. // functions can be strings
  991. // once:
  992. // If true, the connection happens only
  993. // once then disconnects. Five args are required
  994. // for this functionality.
  995. //
  996. var c;
  997. if(typeof(o)!="object"){
  998. if(s){
  999. // ** function object function **
  1000. m = s; s = e; e=o; o = this;
  1001. }else{
  1002. // ** function function **
  1003. m = e; e = o; o = s = this;
  1004. }
  1005. }else if(!m){
  1006. // ** object function function **
  1007. m = s; s = this;
  1008. }else if(once){
  1009. // ** object function object function Boolean **
  1010. c = dojo.connect(o, e, function(evt){
  1011. dojo.hitch(s, m)(evt);
  1012. dojo.disconnect(c);
  1013. });
  1014. this._cons.push(c);
  1015. return c;
  1016. }else{
  1017. // ** object function object function **
  1018. }
  1019. c = dojo.connect(o, e, s, m);
  1020. this._cons.push(c);
  1021. return c;
  1022. },
  1023. disconnect: function(/*handle | Array*/handles){
  1024. // summary:
  1025. // Removes connections based on passed
  1026. // handles arguments
  1027. if(!handles){ return }
  1028. if(!dojo.isArray(handles)){ handles=[handles]; }
  1029. dojo.forEach(handles, dojo.disconnect, dojo);
  1030. },
  1031. connectMouse: function(){
  1032. // summary:
  1033. // Internal. Registers this Stencil to receive
  1034. // mouse events.
  1035. this._mouseHandle = this.mouse.register(this);
  1036. },
  1037. disconnectMouse: function(){
  1038. // summary:
  1039. // Internal. Unregisters this Stencil from receiving
  1040. // mouse events.
  1041. this.mouse.unregister(this._mouseHandle);
  1042. },
  1043. // Should be overwritten by sub class:
  1044. render: function(){
  1045. // summary:
  1046. // This Stencil's render description. Often
  1047. // calls 'sub render' methods.
  1048. },
  1049. //renderOutline: function(){},
  1050. dataToPoints: function(/*Object*/data){
  1051. // summary:
  1052. // Converts data to points.
  1053. },
  1054. pointsToData: function(/*Array*/points){
  1055. // summary:
  1056. // Converts points to data
  1057. },
  1058. onDown: function(/*EventObject*/obj){
  1059. // summary:
  1060. // Mouse event, fired on mousedown on canvas
  1061. //
  1062. // by default, object is ready to accept data
  1063. // turn this off for dragging or onRender will
  1064. // keep firing and register the shape
  1065. // NOTE: Not needed for all stencils. Axes needs it.
  1066. this._downOnCanvas = true;
  1067. dojo.disconnect(this._postRenderCon);
  1068. this._postRenderCon = null;
  1069. },
  1070. onMove: function(/*EventObject*/obj){
  1071. // summary:
  1072. // Mouse event, fired on mousemove while mouse
  1073. // is not down.
  1074. // NOTE: Not currently implemented
  1075. },
  1076. onDrag: function(/*EventObject*/obj){
  1077. // summary:
  1078. // Mouse event, fired on mousemove while mouse
  1079. // is down on canvas
  1080. },
  1081. onUp: function(/*EventObject*/obj){
  1082. // summary:
  1083. // Mouse event, fired on mouseup
  1084. }
  1085. }
  1086. );
  1087. });