_Base.js 32 KB

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