##// END OF EJS Templates
Fixed: cursor not positioned correctly when using wiki toolbar buttons under IE (Balazs Dan)....
Jean-Philippe Lang -
r648:f601d5fb9e02
parent child
Show More
@@ -1,464 +1,468
1 1 /* ***** BEGIN LICENSE BLOCK *****
2 2 * This file is part of DotClear.
3 3 * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All
4 4 * rights reserved.
5 5 *
6 6 * DotClear is free software; you can redistribute it and/or modify
7 7 * it under the terms of the GNU General Public License as published by
8 8 * the Free Software Foundation; either version 2 of the License, or
9 9 * (at your option) any later version.
10 10 *
11 11 * DotClear is distributed in the hope that it will be useful,
12 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 * GNU General Public License for more details.
15 15 *
16 16 * You should have received a copy of the GNU General Public License
17 17 * along with DotClear; if not, write to the Free Software
18 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 19 *
20 20 * ***** END LICENSE BLOCK *****
21 21 */
22 22
23 23 /* Modified by JP LANG for textile formatting */
24 24
25 25 function jsToolBar(textarea) {
26 26 if (!document.createElement) { return; }
27 27
28 28 if (!textarea) { return; }
29 29
30 30 if ((typeof(document["selection"]) == "undefined")
31 31 && (typeof(textarea["setSelectionRange"]) == "undefined")) {
32 32 return;
33 33 }
34 34
35 35 this.textarea = textarea;
36 36
37 37 this.editor = document.createElement('div');
38 38 this.editor.className = 'jstEditor';
39 39
40 40 this.textarea.parentNode.insertBefore(this.editor,this.textarea);
41 41 this.editor.appendChild(this.textarea);
42 42
43 43 this.toolbar = document.createElement("div");
44 44 this.toolbar.className = 'jstElements';
45 45 this.editor.parentNode.insertBefore(this.toolbar,this.editor);
46 46
47 47 // Dragable resizing (only for gecko)
48 48 if (this.editor.addEventListener)
49 49 {
50 50 this.handle = document.createElement('div');
51 51 this.handle.className = 'jstHandle';
52 52 var dragStart = this.resizeDragStart;
53 53 var This = this;
54 54 this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
55 55 // fix memory leak in Firefox (bug #241518)
56 56 window.addEventListener('unload',function() {
57 57 var del = This.handle.parentNode.removeChild(This.handle);
58 58 delete(This.handle);
59 59 },false);
60 60
61 61 this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
62 62 }
63 63
64 64 this.context = null;
65 65 this.toolNodes = {}; // lorsque la toolbar est dessinΓ©e , cet objet est garni
66 66 // de raccourcis vers les Γ©lΓ©ments DOM correspondants aux outils.
67 67 }
68 68
69 69 function jsButton(title, fn, scope, className) {
70 70 this.title = title || null;
71 71 this.fn = fn || function(){};
72 72 this.scope = scope || null;
73 73 this.className = className || null;
74 74 }
75 75 jsButton.prototype.draw = function() {
76 76 if (!this.scope) return null;
77 77
78 78 var button = document.createElement('button');
79 79 button.setAttribute('type','button');
80 80 if (this.className) button.className = this.className;
81 81 button.title = this.title;
82 82 var span = document.createElement('span');
83 83 span.appendChild(document.createTextNode(this.title));
84 84 button.appendChild(span);
85 85
86 86 if (this.icon != undefined) {
87 87 button.style.backgroundImage = 'url('+this.icon+')';
88 88 }
89 89 if (typeof(this.fn) == 'function') {
90 90 var This = this;
91 91 button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
92 92 }
93 93 return button;
94 94 }
95 95
96 96 function jsSpace(id) {
97 97 this.id = id || null;
98 98 this.width = null;
99 99 }
100 100 jsSpace.prototype.draw = function() {
101 101 var span = document.createElement('span');
102 102 if (this.id) span.id = this.id;
103 103 span.appendChild(document.createTextNode(String.fromCharCode(160)));
104 104 span.className = 'jstSpacer';
105 105 if (this.width) span.style.marginRight = this.width+'px';
106 106
107 107 return span;
108 108 }
109 109
110 110 function jsCombo(title, options, scope, fn, className) {
111 111 this.title = title || null;
112 112 this.options = options || null;
113 113 this.scope = scope || null;
114 114 this.fn = fn || function(){};
115 115 this.className = className || null;
116 116 }
117 117 jsCombo.prototype.draw = function() {
118 118 if (!this.scope || !this.options) return null;
119 119
120 120 var select = document.createElement('select');
121 121 if (this.className) select.className = className;
122 122 select.title = this.title;
123 123
124 124 for (var o in this.options) {
125 125 //var opt = this.options[o];
126 126 var option = document.createElement('option');
127 127 option.value = o;
128 128 option.appendChild(document.createTextNode(this.options[o]));
129 129 select.appendChild(option);
130 130 }
131 131
132 132 var This = this;
133 133 select.onchange = function() {
134 134 try {
135 135 This.fn.call(This.scope, this.value);
136 136 } catch (e) { alert(e); }
137 137
138 138 return false;
139 139 }
140 140
141 141 return select;
142 142 }
143 143
144 144
145 145 jsToolBar.prototype = {
146 146 base_url: '',
147 147 mode: 'wiki',
148 148 elements: {},
149 149
150 150 getMode: function() {
151 151 return this.mode;
152 152 },
153 153
154 154 setMode: function(mode) {
155 155 this.mode = mode || 'wiki';
156 156 },
157 157
158 158 switchMode: function(mode) {
159 159 mode = mode || 'wiki';
160 160 this.draw(mode);
161 161 },
162 162
163 163 button: function(toolName) {
164 164 var tool = this.elements[toolName];
165 165 if (typeof tool.fn[this.mode] != 'function') return null;
166 166 var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName);
167 167 if (tool.icon != undefined) b.icon = tool.icon;
168 168 return b;
169 169 },
170 170 space: function(toolName) {
171 171 var tool = new jsSpace(toolName)
172 172 if (this.elements[toolName].width !== undefined)
173 173 tool.width = this.elements[toolName].width;
174 174 return tool;
175 175 },
176 176 combo: function(toolName) {
177 177 var tool = this.elements[toolName];
178 178 var length = tool[this.mode].list.length;
179 179
180 180 if (typeof tool[this.mode].fn != 'function' || length == 0) {
181 181 return null;
182 182 } else {
183 183 var options = {};
184 184 for (var i=0; i < length; i++) {
185 185 var opt = tool[this.mode].list[i];
186 186 options[opt] = tool.options[opt];
187 187 }
188 188 return new jsCombo(tool.title, options, this, tool[this.mode].fn);
189 189 }
190 190 },
191 191 draw: function(mode) {
192 192 this.setMode(mode);
193 193
194 194 // Empty toolbar
195 195 while (this.toolbar.hasChildNodes()) {
196 196 this.toolbar.removeChild(this.toolbar.firstChild)
197 197 }
198 198 this.toolNodes = {}; // vide les raccourcis DOM/**/
199 199
200 200 // Draw toolbar elements
201 201 var b, tool, newTool;
202 202
203 203 for (var i in this.elements) {
204 204 b = this.elements[i];
205 205
206 206 var disabled =
207 207 b.type == undefined || b.type == ''
208 208 || (b.disabled != undefined && b.disabled)
209 209 || (b.context != undefined && b.context != null && b.context != this.context);
210 210
211 211 if (!disabled && typeof this[b.type] == 'function') {
212 212 tool = this[b.type](i);
213 213 if (tool) newTool = tool.draw();
214 214 if (newTool) {
215 215 this.toolNodes[i] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
216 216 this.toolbar.appendChild(newTool);
217 217 }
218 218 }
219 219 }
220 220 },
221 221
222 222 singleTag: function(stag,etag) {
223 223 stag = stag || null;
224 224 etag = etag || stag;
225 225
226 226 if (!stag || !etag) { return; }
227 227
228 228 this.encloseSelection(stag,etag);
229 229 },
230 230
231 231 encloseSelection: function(prefix, suffix, fn) {
232 232 this.textarea.focus();
233 233
234 234 prefix = prefix || '';
235 235 suffix = suffix || '';
236 236
237 237 var start, end, sel, scrollPos, subst, res;
238 238
239 239 if (typeof(document["selection"]) != "undefined") {
240 240 sel = document.selection.createRange().text;
241 241 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
242 242 start = this.textarea.selectionStart;
243 243 end = this.textarea.selectionEnd;
244 244 scrollPos = this.textarea.scrollTop;
245 245 sel = this.textarea.value.substring(start, end);
246 246 }
247 247
248 248 if (sel.match(/ $/)) { // exclude ending space char, if any
249 249 sel = sel.substring(0, sel.length - 1);
250 250 suffix = suffix + " ";
251 251 }
252 252
253 253 if (typeof(fn) == 'function') {
254 254 res = (sel) ? fn.call(this,sel) : fn('');
255 255 } else {
256 256 res = (sel) ? sel : '';
257 257 }
258 258
259 259 subst = prefix + res + suffix;
260 260
261 261 if (typeof(document["selection"]) != "undefined") {
262 var range = document.selection.createRange().text = subst;
263 this.textarea.caretPos -= suffix.length;
262 document.selection.createRange().text = subst;
263 var range = this.textarea.createTextRange();
264 range.collapse(false);
265 range.move('character', -suffix.length);
266 range.select();
267 // this.textarea.caretPos -= suffix.length;
264 268 } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
265 269 this.textarea.value = this.textarea.value.substring(0, start) + subst +
266 270 this.textarea.value.substring(end);
267 271 if (sel) {
268 272 this.textarea.setSelectionRange(start + subst.length, start + subst.length);
269 273 } else {
270 274 this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
271 275 }
272 276 this.textarea.scrollTop = scrollPos;
273 277 }
274 278 },
275 279
276 280 stripBaseURL: function(url) {
277 281 if (this.base_url != '') {
278 282 var pos = url.indexOf(this.base_url);
279 283 if (pos == 0) {
280 284 url = url.substr(this.base_url.length);
281 285 }
282 286 }
283 287
284 288 return url;
285 289 }
286 290 };
287 291
288 292 /** Resizer
289 293 -------------------------------------------------------- */
290 294 jsToolBar.prototype.resizeSetStartH = function() {
291 295 this.dragStartH = this.textarea.offsetHeight + 0;
292 296 };
293 297 jsToolBar.prototype.resizeDragStart = function(event) {
294 298 var This = this;
295 299 this.dragStartY = event.clientY;
296 300 this.resizeSetStartH();
297 301 document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
298 302 document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
299 303 };
300 304
301 305 jsToolBar.prototype.resizeDragMove = function(event) {
302 306 this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
303 307 };
304 308
305 309 jsToolBar.prototype.resizeDragStop = function(event) {
306 310 document.removeEventListener('mousemove', this.dragMoveHdlr, false);
307 311 document.removeEventListener('mouseup', this.dragStopHdlr, false);
308 312 };
309 313
310 314 // Elements definition ------------------------------------
311 315
312 316 // strong
313 317 jsToolBar.prototype.elements.strong = {
314 318 type: 'button',
315 319 title: 'Strong emphasis',
316 320 fn: {
317 321 wiki: function() { this.singleTag('*') }
318 322 }
319 323 }
320 324
321 325 // em
322 326 jsToolBar.prototype.elements.em = {
323 327 type: 'button',
324 328 title: 'Emphasis',
325 329 fn: {
326 330 wiki: function() { this.singleTag("_") }
327 331 }
328 332 }
329 333
330 334 // ins
331 335 jsToolBar.prototype.elements.ins = {
332 336 type: 'button',
333 337 title: 'Inserted',
334 338 fn: {
335 339 wiki: function() { this.singleTag('+') }
336 340 }
337 341 }
338 342
339 343 // del
340 344 jsToolBar.prototype.elements.del = {
341 345 type: 'button',
342 346 title: 'Deleted',
343 347 fn: {
344 348 wiki: function() { this.singleTag('-') }
345 349 }
346 350 }
347 351
348 352 // quote
349 353 jsToolBar.prototype.elements.quote = {
350 354 type: 'button',
351 355 title: 'Inline quote',
352 356 fn: {
353 357 wiki: function() { this.singleTag('??') }
354 358 }
355 359 }
356 360
357 361 // code
358 362 jsToolBar.prototype.elements.code = {
359 363 type: 'button',
360 364 title: 'Code',
361 365 fn: {
362 366 wiki: function() { this.singleTag('@') }
363 367 }
364 368 }
365 369
366 370 // spacer
367 371 jsToolBar.prototype.elements.space1 = {type: 'space'}
368 372
369 373 // heading
370 374 jsToolBar.prototype.elements.heading = {
371 375 type: 'button',
372 376 title: 'Heading',
373 377 fn: {
374 378 wiki: function() {
375 379 this.encloseSelection('','',function(str) {
376 380 str = str.replace(/\r/g,'');
377 381 return 'h2. '+str.replace(/\n/g,"\n* ");
378 382 });
379 383 }
380 384 }
381 385 }
382 386
383 387 // br
384 388 //jsToolBar.prototype.elements.br = {
385 389 // type: 'button',
386 390 // title: 'Line break',
387 391 // fn: {
388 392 // wiki: function() { this.encloseSelection("%%%\n",'') }
389 393 // }
390 394 //}
391 395
392 396 // spacer
393 397 jsToolBar.prototype.elements.space2 = {type: 'space'}
394 398
395 399 // ul
396 400 jsToolBar.prototype.elements.ul = {
397 401 type: 'button',
398 402 title: 'Unordered list',
399 403 fn: {
400 404 wiki: function() {
401 405 this.encloseSelection('','',function(str) {
402 406 str = str.replace(/\r/g,'');
403 407 return '* '+str.replace(/\n/g,"\n* ");
404 408 });
405 409 }
406 410 }
407 411 }
408 412
409 413 // ol
410 414 jsToolBar.prototype.elements.ol = {
411 415 type: 'button',
412 416 title: 'Ordered list',
413 417 fn: {
414 418 wiki: function() {
415 419 this.encloseSelection('','',function(str) {
416 420 str = str.replace(/\r/g,'');
417 421 return '# '+str.replace(/\n/g,"\n# ");
418 422 });
419 423 }
420 424 }
421 425 }
422 426
423 427 // spacer
424 428 jsToolBar.prototype.elements.space3 = {type: 'space'}
425 429
426 430 // link
427 431 /*
428 432 jsToolBar.prototype.elements.link = {
429 433 type: 'button',
430 434 title: 'Link',
431 435 fn: {},
432 436 href_prompt: 'Please give page URL:',
433 437 hreflang_prompt: 'Language of this page:',
434 438 default_hreflang: '',
435 439 prompt: function(href,hreflang) {
436 440 href = href || '';
437 441 hreflang = hreflang || this.elements.link.default_hreflang;
438 442
439 443 href = window.prompt(this.elements.link.href_prompt,href);
440 444 if (!href) { return false; }
441 445
442 446 hreflang = ""
443 447
444 448 return { href: this.stripBaseURL(href), hreflang: hreflang };
445 449 }
446 450 }
447 451
448 452 jsToolBar.prototype.elements.link.fn.wiki = function() {
449 453 var link = this.elements.link.prompt.call(this);
450 454 if (link) {
451 455 var stag = '"';
452 456 var etag = '":'+link.href;
453 457 this.encloseSelection(stag,etag);
454 458 }
455 459 };
456 460 */
457 461 // link or wiki page
458 462 jsToolBar.prototype.elements.link = {
459 463 type: 'button',
460 464 title: 'Link',
461 465 fn: {
462 466 wiki: function() { this.encloseSelection("[[", "]]") }
463 467 }
464 468 }
General Comments 0
You need to be logged in to leave comments. Login now