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