TextBlock.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  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.tools.TextBlock"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.drawing.tools.TextBlock"] = true;
  8. dojo.provide("dojox.drawing.tools.TextBlock");
  9. dojo.require("dojox.drawing.stencil.Text");
  10. (function(){
  11. var conEdit;
  12. dojo.addOnLoad(function(){
  13. // In order to use VML in IE, it's necessary to remove the
  14. // DOCTYPE. But this has the side effect that causes a bug
  15. // where contenteditable divs cannot be made dynamically.
  16. // The solution is to include one in the main document
  17. // that can be appended and removed as necessary:
  18. // <div id="conEdit" contenteditable="true"></div>
  19. //
  20. // console.log("Removing conedit");
  21. conEdit = dojo.byId("conEdit");
  22. if(!conEdit){
  23. console.error("A contenteditable div is missing from the main document. See 'dojox.drawing.tools.TextBlock'")
  24. }else{
  25. conEdit.parentNode.removeChild(conEdit);
  26. }
  27. });
  28. dojox.drawing.tools.TextBlock = dojox.drawing.util.oo.declare(
  29. // summary:
  30. // A tool to create text fields on a canvas.
  31. // description:
  32. // Extends stencil.Text by adding an HTML layer that
  33. // can be dragged out to a certain size, and accept
  34. // a text entry. Will wrap text to the width of the
  35. // html field.
  36. // When created programmtically, use 'auto' to shrink
  37. // the width to the size of the text. Use line breaks
  38. // ( \n ) to create new lines.
  39. //
  40. // TODO - disable zoom while showing?
  41. //
  42. // FIXME:
  43. // Handles width: auto, align:middle, etc. but for
  44. // display only, edit is out of whack
  45. //
  46. dojox.drawing.stencil.Text,
  47. function(options){
  48. // summary: constructor
  49. //
  50. if(options.data){
  51. var d = options.data;
  52. var text = d.text ? this.typesetter(d.text) : d.text;
  53. var w = !d.width ? this.style.text.minWidth : d.width=="auto" ? "auto" : Math.max(d.width, this.style.text.minWidth);
  54. var h = this._lineHeight;
  55. if(text && w=="auto"){
  56. var o = this.measureText(this.cleanText(text, false), w);
  57. w = o.w;
  58. h = o.h;
  59. }else{
  60. // w = this.style.text.minWidth;
  61. this._text = "";
  62. }
  63. this.points = [
  64. {x:d.x, y:d.y},
  65. {x:d.x+w, y:d.y},
  66. {x:d.x+w, y:d.y+h},
  67. {x:d.x, y:d.y+h}
  68. ];
  69. if(d.showEmpty || text){
  70. this.editMode = true;
  71. dojo.disconnect(this._postRenderCon);
  72. this._postRenderCon = null;
  73. this.connect(this, "render", this, "onRender", true);
  74. if(d.showEmpty){
  75. this._text = text || "";
  76. this.edit();
  77. }else if(text && d.editMode){
  78. this._text = "";
  79. this.edit();
  80. }else if(text){
  81. this.render(text);
  82. }
  83. setTimeout(dojo.hitch(this, function(){
  84. this.editMode = false;
  85. }),100)
  86. }else{
  87. // Why make it if it won't render...
  88. this.render();
  89. }
  90. }else{
  91. this.connectMouse();
  92. this._postRenderCon = dojo.connect(this, "render", this, "_onPostRender");
  93. }
  94. //console.log("TextBlock:", this.id)
  95. },
  96. {
  97. draws:true,
  98. baseRender:false,
  99. type:"dojox.drawing.tools.TextBlock",
  100. _caretStart: 0,
  101. _caretEnd: 0,
  102. _blockExec: false,
  103. /*=====
  104. StencilData: {
  105. // summary:
  106. // The data used to create the dojox.gfx Text
  107. // x: Number
  108. // Left point x
  109. // y: Number
  110. // Top point y
  111. // width: ? Number|String
  112. // Optional width of Text. Not required but reccommended.
  113. // for auto-sizing, use 'auto'
  114. // height: ? Number
  115. // Optional height of Text. If not provided, _lineHeight is used.
  116. // text: String
  117. // The string content. If not provided, may auto-delete depending on defaults.
  118. },
  119. =====*/
  120. // selectOnExec: Boolean
  121. // Whether the Stencil is selected when the text field
  122. // is executed or not
  123. selectOnExec:true,
  124. //
  125. // showEmpty: Boolean
  126. // If true and there is no text in the data, the TextBlock
  127. // Is displayed and focused and awaits input.
  128. showEmpty: false,
  129. onDrag: function(/*EventObject*/obj){
  130. // summary: See stencil._Base.onDrag
  131. //
  132. if(!this.parentNode){
  133. this.showParent(obj);
  134. }
  135. var s = this._startdrag, e = obj.page;
  136. this._box.left = (s.x < e.x ? s.x : e.x);
  137. this._box.top = s.y;
  138. this._box.width = (s.x < e.x ? e.x-s.x : s.x-e.x) + this.style.text.pad;
  139. dojo.style(this.parentNode, this._box.toPx());
  140. },
  141. onUp: function(/*EventObject*/obj){
  142. // summary: See stencil._Base.onUp
  143. //
  144. if(!this._downOnCanvas){ return; }
  145. this._downOnCanvas = false;
  146. var c = dojo.connect(this, "render", this, function(){
  147. dojo.disconnect(c);
  148. this.onRender(this);
  149. });
  150. this.editMode = true;
  151. this.showParent(obj);
  152. this.created = true;
  153. this.createTextField();
  154. this.connectTextField();
  155. },
  156. showParent: function(/*EventObject*/obj){
  157. // summary:
  158. // Internal. Builds the parent node for the
  159. // contenteditable HTML node.
  160. //
  161. if(this.parentNode){ return; }
  162. var x = obj.pageX || 10;
  163. var y = obj.pageY || 10;
  164. this.parentNode = dojo.doc.createElement("div");
  165. this.parentNode.id = this.id;
  166. var d = this.style.textMode.create;
  167. this._box = {
  168. left:x,
  169. top:y,
  170. width:obj.width || 1,
  171. height:obj.height && obj.height>8 ? obj.height : this._lineHeight,
  172. border:d.width+"px "+d.style+" "+d.color,
  173. position:"absolute",
  174. zIndex:500,
  175. toPx: function(){
  176. var o = {};
  177. for(var nm in this){
  178. o[nm] = typeof(this[nm])=="number" && nm!="zIndex" ? this[nm] + "px" : this[nm];
  179. }
  180. return o;
  181. }
  182. };
  183. dojo.style(this.parentNode, this._box);
  184. document.body.appendChild(this.parentNode);
  185. },
  186. createTextField: function(/*String*/txt){
  187. // summary:
  188. // Internal. Inserts the contenteditable HTML node
  189. // into its parent node, and styles it.
  190. //
  191. // style parent
  192. var d = this.style.textMode.edit;
  193. this._box.border = d.width+"px "+d.style+" "+d.color;
  194. this._box.height = "auto";
  195. this._box.width = Math.max(this._box.width, this.style.text.minWidth*this.mouse.zoom);
  196. dojo.style(this.parentNode, this._box.toPx());
  197. // style input
  198. this.parentNode.appendChild(conEdit);
  199. dojo.style(conEdit, {
  200. height: txt ? "auto" : this._lineHeight+"px",
  201. fontSize:(this.textSize/this.mouse.zoom)+"px",
  202. fontFamily:this.style.text.family
  203. });
  204. // FIXME:
  205. // In Safari, if the txt ends with '&' it gets stripped
  206. conEdit.innerHTML = txt || "";
  207. return conEdit; //HTMLNode
  208. },
  209. connectTextField: function(){
  210. // summary:
  211. // Internal. Creates the connections to the
  212. // contenteditable HTML node.
  213. //
  214. if(this._textConnected){ return; } // good ol' IE and its double events
  215. // FIXME:
  216. // Ouch-getting dropdown by id. At the minimum this should
  217. // be from the plugin manager
  218. var dropdown = dijit.byId("dropdown");
  219. this._textConnected = true;
  220. this._dropMode = false;
  221. this.mouse.setEventMode("TEXT");
  222. this.keys.editMode(true);
  223. var kc1, kc2, kc3, kc4, self = this, _autoSet = false,
  224. exec = function(){
  225. if(self._dropMode){ return; }
  226. dojo.forEach([kc1,kc2,kc3,kc4], function(c){
  227. dojo.disconnect(c)
  228. });
  229. self._textConnected = false;
  230. self.keys.editMode(false);
  231. self.mouse.setEventMode();
  232. self.execText();
  233. };
  234. kc1 = dojo.connect(conEdit, "keyup", this, function(evt){
  235. // if text is empty, we need a height so the field's height
  236. // doesn't collapse
  237. if(dojo.trim(conEdit.innerHTML) && !_autoSet){
  238. dojo.style(conEdit, "height", "auto"); _autoSet = true;
  239. }else if(dojo.trim(conEdit.innerHTML).length<2 && _autoSet){
  240. dojo.style(conEdit, "height", this._lineHeight+"px"); _autoSet = false;
  241. }
  242. if(!this._blockExec){
  243. if(evt.keyCode==13 || evt.keyCode==27){
  244. dojo.stopEvent(evt);
  245. exec();
  246. }
  247. } else {
  248. if(evt.keyCode==dojo.keys.SPACE){
  249. dojo.stopEvent(evt);
  250. dropdown.onCancel();
  251. }
  252. }
  253. });
  254. kc2 = dojo.connect(conEdit, "keydown", this, function(evt){
  255. if(evt.keyCode==13 || evt.keyCode==27){ // TODO: make escape an option
  256. dojo.stopEvent(evt);
  257. }
  258. // if backslash, user is inputting a special character
  259. // This gives popup help.
  260. if(evt.keyCode==220){
  261. if(dropdown==undefined){
  262. console.warn("Dropdown not found");
  263. return;
  264. }
  265. dojo.stopEvent(evt);
  266. this.getSelection(conEdit);
  267. // Differences in how browsers handle events made it necessary
  268. // to stop the evt and add the backslash here.
  269. this.insertText(conEdit,"\\");
  270. this._dropMode = true;
  271. this._blockExec = true;
  272. dropdown._pushChangeTo = conEdit;
  273. dropdown._textBlock = this;
  274. dijit.popup.open({
  275. parent:this.parentNode,
  276. popup:dropdown,
  277. around:this.parentNode,
  278. orient:{'BL':'TL'}
  279. });
  280. }
  281. if(!this._dropMode){
  282. this._blockExec = false;
  283. } else {
  284. switch(evt.keyCode){
  285. case dojo.keys.UP_ARROW:
  286. case dojo.keys.DOWN_ARROW:
  287. case dojo.keys.LEFT_ARROW:
  288. case dojo.keys.RIGHT_ARROW:
  289. dojo.stopEvent(evt);
  290. dropdown._navigateByArrow(evt);
  291. break;
  292. case dojo.keys.ENTER:
  293. dojo.stopEvent(evt);
  294. dropdown._onCellClick(evt);
  295. break;
  296. case dojo.keys.BACKSPACE:
  297. case dojo.keys.DELETE:
  298. dojo.stopEvent(evt);
  299. dropdown.onCancel();
  300. break;
  301. }
  302. }
  303. });
  304. kc3 = dojo.connect(document, "mouseup", this, function(evt){
  305. // note: _onAnchor means an anchor has been clicked upon
  306. if(!this._onAnchor && evt.target.id != "conEdit"){
  307. dojo.stopEvent(evt);
  308. exec();
  309. }else if(evt.target.id == "conEdit" && conEdit.innerHTML == ""){
  310. // wonky stuff happens when you click on the
  311. // field when its empty.
  312. conEdit.blur();
  313. setTimeout(function(){
  314. conEdit.focus();
  315. },200)
  316. }
  317. });
  318. this.createAnchors();
  319. kc4 = dojo.connect(this.mouse, "setZoom", this, function(evt){
  320. exec();
  321. });
  322. conEdit.focus();
  323. this.onDown = function(){};
  324. this.onDrag = function(){};
  325. setTimeout(dojo.hitch(this, function(){
  326. // once again for Silverlight:
  327. conEdit.focus();
  328. // this is a pretty odd chunk of code here.
  329. // specifcally need to overwrite old onUp
  330. // however, this still gets called. its
  331. // not disconnecting.
  332. this.onUp = function(){
  333. if(!self._onAnchor && this.parentNode){
  334. self.disconnectMouse();
  335. exec();
  336. self.onUp = function(){}
  337. }
  338. }
  339. }), 500);
  340. },
  341. execText: function(){
  342. // summary:
  343. // Internal. Method fired when text is executed,
  344. // via mouse-click-off, ESC key or Enter key.
  345. //
  346. var d = dojo.marginBox(this.parentNode);
  347. var w = Math.max(d.w, this.style.text.minWidth);
  348. var txt = this.cleanText(conEdit.innerHTML, true);
  349. conEdit.innerHTML = "";
  350. conEdit.blur();
  351. this.destroyAnchors();
  352. // need to convert characters before measuring width.
  353. txt = this.typesetter(txt);
  354. var o = this.measureText(txt, w);
  355. var sc = this.mouse.scrollOffset();
  356. var org = this.mouse.origin;
  357. var x = this._box.left + sc.left - org.x;
  358. var y = this._box.top + sc.top - org.y;
  359. x *= this.mouse.zoom;
  360. y *= this.mouse.zoom;
  361. w *= this.mouse.zoom;
  362. o.h *= this.mouse.zoom;
  363. this.points = [
  364. {x:x, y:y},
  365. {x:x+w, y:y},
  366. {x:x+w, y:y+o.h},
  367. {x:x, y:y+o.h}
  368. ];
  369. this.editMode = false;
  370. console.log("EXEC TEXT::::", this._postRenderCon);
  371. if(!o.text){
  372. this._text = "";
  373. this._textArray = [];
  374. }
  375. // Only for Combo objects (vectors, rectangle, or ellipse).
  376. this.render(o.text);
  377. this.onChangeText(this.getText());
  378. },
  379. edit: function(){
  380. // summary:
  381. // Internal?
  382. // Method used to instantiate the contenteditable HTML node.
  383. //
  384. this.editMode = true;
  385. var text = this.getText() || "";
  386. console.log("EDIT TEXT:",text, " ",text.replace("/n", " "));
  387. // NOTE: no mouse obj
  388. if(this.parentNode || !this.points){ return; }
  389. var d = this.pointsToData();
  390. var sc = this.mouse.scrollOffset();
  391. var org = this.mouse.origin;
  392. var obj = {
  393. pageX: (d.x ) / this.mouse.zoom - sc.left + org.x,
  394. pageY: (d.y ) / this.mouse.zoom- sc.top + org.y,
  395. width:d.width / this.mouse.zoom,
  396. height:d.height / this.mouse.zoom
  397. };
  398. this.remove(this.shape, this.hit);
  399. this.showParent(obj);
  400. this.createTextField(text.replace("/n", " "));
  401. this.connectTextField();
  402. if(text){
  403. //setTimeout(dojo.hitch(this, function(){
  404. this.setSelection(conEdit, "end");
  405. //}), 500)
  406. }
  407. },
  408. cleanText: function(/*String*/txt, /*Boolean*/removeBreaks){
  409. // summary:
  410. // Cleans text. Strings HTML chars and double spaces
  411. // and optionally removes line breaks.
  412. var replaceHtmlCodes = function(str){
  413. var chars = {
  414. "&lt;":"<",
  415. "&gt;":">",
  416. "&amp;":"&"
  417. };
  418. for(var nm in chars){
  419. str = str.replace(new RegExp(nm, "gi"), chars[nm])
  420. }
  421. return str
  422. };
  423. if(removeBreaks){
  424. dojo.forEach(['<br>', '<br/>', '<br />', '\\n', '\\r'], function(br){
  425. txt = txt.replace(new RegExp(br, 'gi'), " ");
  426. });
  427. }
  428. txt = txt.replace(/&nbsp;/g, " ");
  429. txt = replaceHtmlCodes(txt);
  430. txt = dojo.trim(txt);
  431. // remove double spaces, since SVG doesn't show them anyway
  432. txt = txt.replace(/\s{2,}/g, " ");
  433. return txt; //String
  434. },
  435. measureText: function(/* String */ str, /* ? Number */width){
  436. // summary:
  437. // Mechanism for measuring text.
  438. // SVG nor VML have a way of determining the width or
  439. // height of a block of text. This method creates an
  440. // HTML text block and those measurements are used for
  441. // displaying the SVG/VML text.
  442. // arguments:
  443. // str: String
  444. // The text to display and measure.
  445. // width: [optional] Number
  446. // If the width is not provided, it will be assumed
  447. // that the text is one line and the width will be
  448. // measured and the _lineHeight used for th height.
  449. // If width is provided, word-wrap is assumed, and
  450. // line breaks will be inserted into the text at each
  451. // point where a word wraps in the HTML. The height is
  452. // then measured.
  453. //
  454. var r = "(<br\\s*/*>)|(\\n)|(\\r)";
  455. this.showParent({width:width || "auto", height:"auto"});
  456. this.createTextField(str);
  457. var txt = "";
  458. var el = conEdit;
  459. el.innerHTML = "X";
  460. var h = dojo.marginBox(el).h;
  461. el.innerHTML = str;
  462. if(!width || new RegExp(r, "gi").test(str)){
  463. // has line breaks in text
  464. txt = str.replace(new RegExp(r, "gi"), "\n");
  465. el.innerHTML = str.replace(new RegExp(r, "gi"), "<br/>");
  466. }else if(dojo.marginBox(el).h == h){
  467. // one line
  468. txt = str;
  469. }else{
  470. // text wraps
  471. var ar = str.split(" ");
  472. var strAr = [[]];
  473. var line = 0;
  474. el.innerHTML = "";
  475. while(ar.length){
  476. var word = ar.shift();
  477. el.innerHTML += word+" "; //urk, always an extra space
  478. if(dojo.marginBox(el).h > h){
  479. line++;
  480. strAr[line] = [];
  481. el.innerHTML = word+" ";
  482. }
  483. strAr[line].push(word)
  484. }
  485. dojo.forEach(strAr, function(ar, i){
  486. strAr[i] = ar.join(" ");
  487. });
  488. txt = strAr.join("\n");
  489. // get the resultant height
  490. el.innerHTML = txt.replace("\n", "<br/>");
  491. }
  492. var dim = dojo.marginBox(el);
  493. conEdit.parentNode.removeChild(conEdit);
  494. dojo.destroy(this.parentNode);
  495. this.parentNode = null;
  496. return {h:dim.h, w:dim.w, text:txt}; //Object
  497. },
  498. _downOnCanvas:false,
  499. onDown: function(/*EventObject*/obj){
  500. // summary: See stencil._Base.onDown
  501. //
  502. this._startdrag = {
  503. x: obj.pageX,
  504. y: obj.pageY
  505. };
  506. dojo.disconnect(this._postRenderCon);
  507. this._postRenderCon = null;
  508. this._downOnCanvas = true;
  509. },
  510. createAnchors: function(){
  511. // summary:
  512. // Internal. Creates HTML nodes at each corner
  513. // of the contenteditable div. These nodes are
  514. // draggable and will resize the div horizontally.
  515. //
  516. this._anchors = {};
  517. var self = this;
  518. var d = this.style.anchors,
  519. b = d.width,
  520. w = d.size-b*2,
  521. h = d.size-b*2,
  522. p = (d.size)/2*-1 + "px";
  523. var s = {
  524. position:"absolute",
  525. width:w+"px",
  526. height:h+"px",
  527. backgroundColor:d.fill,
  528. border:b+"px " + d.style + " "+d.color
  529. };
  530. if(dojo.isIE){
  531. s.paddingLeft = w + "px";
  532. s.fontSize = w + "px"
  533. }
  534. var ss = [
  535. {top: p, left:p},
  536. {top:p, right:p},
  537. {bottom:p, right:p},
  538. {bottom:p,left:p}
  539. ];
  540. for(var i=0;i<4;i++){
  541. var isLeft = (i==0) || (i==3);
  542. var id = this.util.uid(isLeft ? "left_anchor" : "right_anchor");
  543. var a = dojo.create("div", {id:id}, this.parentNode);
  544. dojo.style(a, dojo.mixin(dojo.clone(s), ss[i]));
  545. var md, mm, mu;
  546. var md = dojo.connect(a, "mousedown", this, function(evt){
  547. isLeft = evt.target.id.indexOf("left")>-1;
  548. self._onAnchor = true;
  549. var orgX = evt.pageX;
  550. var orgW = this._box.width;
  551. dojo.stopEvent(evt);
  552. mm = dojo.connect(document, "mousemove", this, function(evt){
  553. var x = evt.pageX;
  554. if(isLeft){
  555. this._box.left = x;
  556. this._box.width = orgW + orgX - x;
  557. }else{
  558. this._box.width = x + orgW - orgX;
  559. }
  560. dojo.style(this.parentNode, this._box.toPx());
  561. });
  562. mu = dojo.connect(document, "mouseup", this, function(evt){
  563. orgX = this._box.left;
  564. orgW = this._box.width;
  565. dojo.disconnect(mm);
  566. dojo.disconnect(mu);
  567. self._onAnchor = false;
  568. conEdit.focus();
  569. dojo.stopEvent(evt);
  570. });
  571. });
  572. this._anchors[id] = {
  573. a:a,
  574. cons:[md]
  575. }
  576. }
  577. },
  578. destroyAnchors: function(){
  579. // summary:
  580. // Internal. Destroys HTML anchors.
  581. for(var n in this._anchors){
  582. dojo.forEach(this._anchors[n].con, dojo.disconnect, dojo);
  583. dojo.destroy(this._anchors[n].a);
  584. };
  585. },
  586. setSavedCaret: function(val){
  587. // summary:
  588. // Internal, called when caret needs to
  589. // be moved into position after text is added
  590. this._caretStart = this._caretEnd = val;
  591. },
  592. getSavedCaret: function(){
  593. return {start: this._caretStart, end: this._caretEnd}
  594. },
  595. insertText: function(node,val){
  596. // summary:
  597. // Uses saved caret position to insert text
  598. // into position and place caret at the end of
  599. // insertion
  600. //
  601. var t, text = node.innerHTML;
  602. var caret = this.getSavedCaret();
  603. text = text.replace(/&nbsp;/g, " ");
  604. t = text.substr(0,caret.start) + val + text.substr(caret.end);
  605. t = this.cleanText(t,true);
  606. this.setSavedCaret(Math.min(t.length,(caret.end + val.length)));
  607. node.innerHTML = t;
  608. this.setSelection(node,"stored");
  609. },
  610. getSelection: function(node){
  611. // summary:
  612. // This gets and stores the caret position
  613. // in the contentEditable div (conEdit).
  614. // NOTE: Doesn't work with html nodes inside
  615. // the div.
  616. //
  617. var start, end;
  618. if(dojo.doc.selection){
  619. //debugger;
  620. var r = dojo.doc.selection.createRange();
  621. var rs = dojo.body().createTextRange();
  622. rs.moveToElementText(node);
  623. var re = rs.duplicate();
  624. rs.moveToBookmark(r.getBookmark());
  625. re.setEndPoint('EndToStart', rs);
  626. start = this._caretStart = re.text.length;
  627. end = this._caretEnd = re.text.length+r.text.length;
  628. console.warn("Caret start: ",start," end: ",end," length: ",re.text.length," text: ",re.text);
  629. } else {
  630. this._caretStart = dojo.global.getSelection().getRangeAt(node).startOffset;
  631. this._caretEnd = dojo.global.getSelection().getRangeAt(node).endOffset;
  632. console.log("Caret start: ", this._caretStart," end: ", this._caretEnd);
  633. }
  634. },
  635. setSelection: function(node, what){
  636. // summary:
  637. // Used for placing the cursor during edits and character help.
  638. // Takes the values: end, beg, start, all or any numerical value
  639. // (in which case the number will constitute the caret position)
  640. //
  641. console.warn("setSelection:");
  642. if(dojo.doc.selection){ // IE
  643. //debugger;
  644. var rs = dojo.body().createTextRange();
  645. rs.moveToElementText(node);
  646. switch(what){
  647. case "end":
  648. rs.collapse(false);
  649. break;
  650. case "beg" || "start":
  651. rs.collapse();
  652. break;
  653. case "all":
  654. rs.collapse();
  655. rs.moveStart("character", 0);
  656. rs.moveEnd("character",node.text.length);
  657. break;
  658. case "stored":
  659. rs.collapse();
  660. var dif = this._caretStart-this._caretEnd;
  661. //console.log("start: ",this._caretStart, " end: ",this._caretEnd," dif: ",dif);
  662. rs.moveStart("character",this._caretStart);
  663. rs.moveEnd("character",dif);
  664. break;
  665. };
  666. rs.select();
  667. }else{
  668. var getAllChildren = function(node, children){
  669. children = children || [];
  670. for(var i=0;i<node.childNodes.length; i++){
  671. var n = node.childNodes[i];
  672. if(n.nodeType==3){
  673. children.push(n);
  674. }else if(n.tagName && n.tagName.toLowerCase()=="img"){
  675. children.push(n);
  676. };
  677. if(n.childNodes && n.childNodes.length){
  678. getAllChildren(n, children);
  679. };
  680. }
  681. return children;
  682. };
  683. console.log("ff node:", node)
  684. node.focus();
  685. var selection = dojo.global.getSelection();
  686. selection.removeAllRanges();
  687. var r = dojo.doc.createRange();
  688. var nodes = getAllChildren(node);
  689. switch(what){
  690. case "end":
  691. console.log("len:", nodes[nodes.length - 1].textContent.length);
  692. r.setStart(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length);
  693. r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length);
  694. break;
  695. case "beg" || "start":
  696. r.setStart(nodes[0], 0);
  697. r.setEnd(nodes[0], 0);
  698. break;
  699. case "all":
  700. r.setStart(nodes[0], 0);
  701. r.setEnd(nodes[nodes.length - 1], nodes[nodes.length - 1].textContent.length);
  702. break;
  703. case "stored":
  704. console.log("Caret start: ",this._caretStart," caret end: ",this._caretEnd);
  705. r.setStart(nodes[0], this._caretStart);
  706. r.setEnd(nodes[0], this._caretEnd);
  707. }
  708. selection.addRange(r);
  709. console.log("sel ", what, " on ", node);
  710. }
  711. }
  712. }
  713. );
  714. dojox.drawing.tools.TextBlock.setup = {
  715. // summary: See stencil._Base ToolsSetup
  716. //
  717. name:"dojox.drawing.tools.TextBlock",
  718. tooltip:"Text Tool",
  719. iconClass:"iconText"
  720. };
  721. dojox.drawing.register(dojox.drawing.tools.TextBlock.setup, "tool");
  722. })();
  723. }