Dotclear

source: plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.js @ 3642:7b5fba5faea7

Revision 3642:7b5fba5faea7, 15.6 KB checked in by franck <carnet.franck.paul@…>, 8 years ago (diff)

Set caret et the end of the inserted thing (img, url, blockquote, …) in Legacy editor if current selection is empty

Line 
1/* ***** BEGIN LICENSE BLOCK *****
2 * This file is part of DotClear.
3 * Copyright (c) 2005 Nicolas Martin & Olivier Meunier and contributors. All
4 * rights reserved.
5 *
6 * DotClear is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * DotClear is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with DotClear; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19 *
20 * ***** END LICENSE BLOCK *****
21*/
22
23function jsToolBar(textarea) {
24     if (!document.createElement) { return; }
25
26     if (!textarea) { return; }
27
28     if ((typeof(document["selection"]) == "undefined")
29     && (typeof(textarea["setSelectionRange"]) == "undefined")) {
30          return;
31     }
32
33     this.textarea = textarea;
34
35     this.editor = document.createElement('div');
36     this.editor.className = 'jstEditor';
37
38     this.textarea.parentNode.insertBefore(this.editor,this.textarea);
39     this.editor.appendChild(this.textarea);
40
41     this.toolbar = document.createElement("div");
42     this.toolbar.className = 'jstElements';
43
44     if (this.toolbar_bottom) {
45          this.editor.parentNode.insertBefore(this.toolbar,this.editor.nextSibling);
46     } else {
47          this.editor.parentNode.insertBefore(this.toolbar,this.editor);
48     }
49
50     // Dragable resizing (only for gecko)
51     if (navigator.appName == 'Microsoft Internet Explorer')
52     {
53          if (this.editor.addEventListener)
54          {
55               this.handle = document.createElement('div');
56               this.handle.className = 'jstHandle';
57               var dragStart = this.resizeDragStart;
58               var This = this;
59               this.handle.addEventListener('mousedown',function(event) { dragStart.call(This,event); },false);
60               this.editor.parentNode.insertBefore(this.handle,this.editor.nextSibling);
61          }
62     }
63
64     this.context = null;
65     this.toolNodes = {}; // lorsque la toolbar est dessinée , cet objet est garni
66                         // de raccourcis vers les éléments DOM correspondants aux outils.
67};
68
69function jsButton(title, fn, scope, className, accesskey) {
70     this.title = title || null;
71     this.fn = fn || function(){};
72     this.scope = scope || null;
73     this.className = className || null;
74     this.accesskey = accesskey || null;
75};
76jsButton.prototype.draw = function() {
77     if (!this.scope) return null;
78
79     var button = document.createElement('button');
80     button.setAttribute('type','button');
81     if (this.className) button.className = this.className;
82     button.title = this.title;
83     if (this.accesskey) button.accessKey = this.accesskey;
84     var span = document.createElement('span');
85     span.appendChild(document.createTextNode(this.title));
86     button.appendChild(span);
87
88     if (this.icon != undefined) {
89          button.style.backgroundImage = 'url('+this.icon+')';
90     }
91     if (typeof(this.fn) == 'function') {
92          var This = this;
93          button.onclick = function() { try { This.fn.apply(This.scope, arguments) } catch (e) {} return false; };
94     }
95     return button;
96};
97
98function jsSpace(id) {
99     this.id = id || null;
100     this.width = null;
101};
102jsSpace.prototype.draw = function() {
103     var span = document.createElement('span');
104     if (this.id) span.id = this.id;
105     span.appendChild(document.createTextNode(String.fromCharCode(160)));
106     span.className = 'jstSpacer';
107     if (this.width) span.style.marginRight = this.width+'px';
108
109     return span;
110};
111
112function jsCombo(title, options, scope, fn, className) {
113     this.title = title || null;
114     this.options = options || null;
115     this.scope = scope || null;
116     this.fn = fn || function(){};
117     this.className = className || null;
118};
119jsCombo.prototype.draw = function() {
120     if (!this.scope || !this.options) return null;
121
122     var select = document.createElement('select');
123     if (this.className) select.className = className;
124     select.title = this.title;
125
126     for (var o in this.options) {
127          //var opt = this.options[o];
128          var option = document.createElement('option');
129          option.value = o;
130          option.appendChild(document.createTextNode(this.options[o]));
131          select.appendChild(option);
132     }
133
134     var This = this;
135     select.onchange = function() {
136          try {
137               This.fn.call(This.scope, this.value);
138          } catch (e) { alert(e); }
139
140          return false;
141     };
142
143     return select;
144};
145
146
147jsToolBar.prototype = {
148     base_url: '',
149     mode: 'xhtml',
150     elements: {},
151     toolbar_bottom: false,
152
153     getMode: function() {
154          return this.mode;
155     },
156
157     setMode: function(mode) {
158          this.mode = mode || 'xhtml';
159     },
160
161     switchMode: function(mode) {
162          mode = mode || 'xhtml';
163          this.draw(mode);
164     },
165
166     button: function(toolName) {
167          var tool = this.elements[toolName];
168          if (typeof tool.fn[this.mode] != 'function') return null;
169          var b = new jsButton(tool.title, tool.fn[this.mode], this, 'jstb_'+toolName, tool.accesskey);
170          if (tool.icon != undefined) {
171               b.icon = tool.icon;
172          }
173          return b;
174     },
175     space: function(toolName) {
176          var tool = new jsSpace(toolName);
177          if (this.elements[toolName].format != undefined && !this.elements[toolName].format[this.mode]) return null;
178          if (this.elements[toolName].width !== undefined) {
179               tool.width = this.elements[toolName].width;
180          }
181          return tool;
182     },
183     combo: function(toolName) {
184          var tool = this.elements[toolName];
185
186          if( tool[this.mode] != undefined) {
187
188               var length = tool[this.mode].list.length;
189
190               if (typeof tool[this.mode].fn != 'function' || length == 0) {
191                    return null;
192               } else {
193                    var options = {};
194                    for (var i=0; i < length; i++) {
195                         var opt = tool[this.mode].list[i];
196                         options[opt] = tool.options[opt];
197                    }
198                    return new jsCombo(tool.title, options, this, tool[this.mode].fn);
199               }
200
201          }
202
203     },
204     draw: function(mode) {
205          this.setMode(mode);
206
207          // Empty toolbar
208          while (this.toolbar.hasChildNodes()) {
209               this.toolbar.removeChild(this.toolbar.firstChild)
210          }
211          this.toolNodes = {}; // vide les raccourcis DOM/**/
212
213          // Draw toolbar elements
214          var b, tool, newTool;
215
216          for (var i in this.elements) {
217               b = this.elements[i];
218
219               var disabled =
220               b.type == undefined || b.type == ''
221               || (b.disabled != undefined && b.disabled)
222               || (b.context != undefined && b.context != null && b.context != this.context);
223
224               if (!disabled && typeof this[b.type] == 'function') {
225                    tool = this[b.type](i);
226                    if (tool) newTool = tool.draw();
227                    if (newTool) {
228                         this.toolNodes[i] = newTool; //mémorise l'accès DOM pour usage éventuel ultérieur
229                         this.toolbar.appendChild(newTool);
230                    }
231               }
232          }
233     },
234
235     singleTag: function(stag,etag) {
236          stag = stag || null;
237          etag = etag || stag;
238
239          if (!stag || !etag) { return; }
240
241          this.encloseSelection(stag,etag);
242     },
243
244     encloseSelection: function(prefix, suffix, fn) {
245          this.textarea.focus();
246
247          prefix = prefix || '';
248          suffix = suffix || '';
249
250          var start, end, sel, scrollPos, subst, res;
251
252          if (typeof(document["selection"]) != "undefined") {
253               sel = document.selection.createRange().text;
254          } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
255               start = this.textarea.selectionStart;
256               end = this.textarea.selectionEnd;
257               scrollPos = this.textarea.scrollTop;
258               sel = this.textarea.value.substring(start, end);
259          }
260
261          if (sel.match(/ $/)) { // exclude ending space char, if any
262               sel = sel.substring(0, sel.length - 1);
263               suffix = suffix + " ";
264          }
265
266          if (typeof(fn) == 'function') {
267               res = (sel) ? fn.call(this,sel) : fn('');
268          } else {
269               res = (sel) ? sel : '';
270          }
271
272          subst = prefix + res + suffix;
273
274          if (typeof(document["selection"]) != "undefined") {
275               var range = document.selection.createRange().text = subst;
276               this.textarea.caretPos -= suffix.length;
277          } else if (typeof(this.textarea["setSelectionRange"]) != "undefined") {
278               this.textarea.value = this.textarea.value.substring(0, start) + subst +
279               this.textarea.value.substring(end);
280               if (sel) {
281                    this.textarea.setSelectionRange(start + subst.length, start + subst.length);
282               } else {
283                    if (typeof(fn) == 'function') {
284                         this.textarea.caretPos -= start + subst.length;
285                    } else {
286                         this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
287                    }
288               }
289               this.textarea.scrollTop = scrollPos;
290          }
291     },
292
293     stripBaseURL: function(url) {
294          if (this.base_url != '') {
295               var pos = url.indexOf(this.base_url);
296               if (pos == 0) {
297                    url = url.substr(this.base_url.length);
298               }
299          }
300
301          return url;
302     }
303};
304
305/** Resizer
306-------------------------------------------------------- */
307jsToolBar.prototype.resizeSetStartH = function() {
308     this.dragStartH = this.textarea.offsetHeight + 0;
309};
310jsToolBar.prototype.resizeDragStart = function(event) {
311     var This = this;
312     this.dragStartY = event.clientY;
313     this.resizeSetStartH();
314     document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
315     document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
316};
317
318jsToolBar.prototype.resizeDragMove = function(event) {
319     this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
320};
321
322jsToolBar.prototype.resizeDragStop = function(event) {
323     document.removeEventListener('mousemove', this.dragMoveHdlr, false);
324     document.removeEventListener('mouseup', this.dragStopHdlr, false);
325};
326
327// Elements definition ------------------------------------
328// block format (paragraph, headers)
329jsToolBar.prototype.elements.blocks = {
330     type: 'combo',
331     title: 'block format',
332     options: {
333          none: '-- none --', // only for wysiwyg mode
334          nonebis: '- block format -', // only for xhtml mode
335          p: 'Paragraph',
336          h1: 'Header 1',
337          h2: 'Header 2',
338          h3: 'Header 3',
339          h4: 'Header 4',
340          h5: 'Header 5',
341          h6: 'Header 6'
342     },
343     xhtml: {
344          list: ['nonebis','p','h1','h2','h3','h4','h5','h6'],
345          fn: function(opt) {
346               if (opt=='nonebis')
347                    this.textarea.focus();
348               else
349                    try {this.singleTag('<'+opt+'>','</'+opt+'>');} catch(e){};
350               this.toolNodes.blocks.value = 'nonebis';
351          }
352     },
353     wiki: {
354          list: ['nonebis','h3','h4','h5'],
355          fn: function(opt) {
356               switch (opt) {
357                    case 'nonebis': this.textarea.focus(); break;
358                    case 'h3': this.encloseSelection('!!!'); break;
359                    case 'h4': this.encloseSelection('!!'); break;
360                    case 'h5': this.encloseSelection('!'); break;
361               }
362               this.toolNodes.blocks.value = 'nonebis';
363          }
364     }
365};
366
367// spacer
368jsToolBar.prototype.elements.space0 = {
369     type:'space',
370     format:{
371          wysiwyg:true,
372          wiki:true,
373          xhtml:true
374     }
375};
376
377// strong
378jsToolBar.prototype.elements.strong = {
379     type: 'button',
380     title: 'Strong emphasis',
381     fn: {
382          wiki: function() { this.singleTag('__') },
383          xhtml: function() { this.singleTag('<strong>','</strong>') }
384     }
385};
386
387// em
388jsToolBar.prototype.elements.em = {
389     type: 'button',
390     title: 'Emphasis',
391     fn: {
392          wiki: function() { this.singleTag("''") },
393          xhtml: function() { this.singleTag('<em>','</em>') }
394     }
395};
396
397// ins
398jsToolBar.prototype.elements.ins = {
399     type: 'button',
400     title: 'Inserted',
401     fn: {
402          wiki: function() { this.singleTag('++') },
403          xhtml: function() { this.singleTag('<ins>','</ins>') }
404     }
405};
406
407// del
408jsToolBar.prototype.elements.del = {
409     type: 'button',
410     title: 'Deleted',
411     fn: {
412          wiki: function() { this.singleTag('--') },
413          xhtml: function() { this.singleTag('<del>','</del>') }
414     }
415};
416
417// quote
418jsToolBar.prototype.elements.quote = {
419     type: 'button',
420     title: 'Inline quote',
421     fn: {
422          wiki: function() { this.singleTag('{{','}}') },
423          xhtml: function() { this.singleTag('<q>','</q>') }
424     }
425};
426
427// code
428jsToolBar.prototype.elements.code = {
429     type: 'button',
430     title: 'Code',
431     fn: {
432          wiki: function() { this.singleTag('@@') },
433          xhtml: function() { this.singleTag('<code>','</code>')}
434     }
435};
436
437// code
438jsToolBar.prototype.elements.mark = {
439     type: 'button',
440     title: 'Mark',
441     fn: {
442          wiki: function() { this.singleTag('""') },
443          xhtml: function() { this.singleTag('<mark>','</mark>')}
444     }
445};
446
447// spacer
448jsToolBar.prototype.elements.space1 = {
449     type:'space',
450     format:{
451          wysiwyg:true,
452          wiki:true,
453          xhtml:true
454     }
455};
456
457// br
458jsToolBar.prototype.elements.br = {
459     type: 'button',
460     title: 'Line break',
461     fn: {
462          wiki: function() { this.encloseSelection("%%%\n",'') },
463          xhtml: function() { this.encloseSelection("<br />\n",'')}
464     }
465};
466
467// spacer
468jsToolBar.prototype.elements.space2 = {
469     type:'space',
470     format:{
471          wysiwyg:true,
472          wiki:true,
473          xhtml:true
474     }
475};
476
477// blockquote
478jsToolBar.prototype.elements.blockquote = {
479     type: 'button',
480     title: 'Blockquote',
481     fn: {
482          xhtml: function() { this.singleTag('<blockquote>','</blockquote>') },
483          wiki: function() {
484               this.encloseSelection("\n",'',
485               function(str) {
486                    str = str.replace(/\r/g,'');
487                    return '> '+str.replace(/\n/g,"\n> ");
488               });
489          }
490     }
491};
492
493// pre
494jsToolBar.prototype.elements.pre = {
495     type: 'button',
496     title: 'Preformated text',
497     fn: {
498          wiki: function() { this.singleTag("///\n","\n///") },
499          xhtml: function() { this.singleTag('<pre>','</pre>') }
500     }
501};
502
503// ul
504jsToolBar.prototype.elements.ul = {
505     type: 'button',
506     title: 'Unordered list',
507     fn: {
508          wiki: function() {
509               this.encloseSelection('','',function(str) {
510                    str = str.replace(/\r/g,'');
511                    return '* '+str.replace(/\n/g,"\n* ");
512               });
513          },
514          xhtml: function() {
515               this.encloseSelection('','',function(str) {
516                    str = str.replace(/\r/g,'');
517                    str = str.replace(/\n/g,"</li>\n <li>");
518                    return "<ul>\n <li>"+str+"</li>\n</ul>";
519               });
520          }
521     }
522};
523
524// ol
525jsToolBar.prototype.elements.ol = {
526     type: 'button',
527     title: 'Ordered list',
528     fn: {
529          wiki: function() {
530               this.encloseSelection('','',function(str) {
531                    str = str.replace(/\r/g,'');
532                    return '# '+str.replace(/\n/g,"\n# ");
533               });
534          },
535          xhtml: function() {
536               this.encloseSelection('','',function(str) {
537                    str = str.replace(/\r/g,'');
538                    str = str.replace(/\n/g,"</li>\n <li>");
539                    return "<ol>\n <li>"+str+"</li>\n</ol>";
540               });
541          }
542     }
543};
544
545// spacer
546jsToolBar.prototype.elements.space3 = {
547     type:'space',
548     format:{
549          wysiwyg:true,
550          wiki:true,
551          xhtml:true
552     }
553};
554
555// link
556jsToolBar.prototype.elements.link = {
557     type: 'button',
558     title: 'Link',
559     fn: {},
560     accesskey: 'l',
561     href_prompt: 'Please give page URL:',
562     hreflang_prompt: 'Language of this page:',
563     default_hreflang: '',
564     prompt: function(href,hreflang) {
565          href = href || '';
566          hreflang = hreflang || this.elements.link.default_hreflang;
567
568          href = window.prompt(this.elements.link.href_prompt,href);
569          if (!href) { return false; }
570
571          hreflang = window.prompt(this.elements.link.hreflang_prompt,
572          hreflang);
573
574          return { href: this.stripBaseURL(href), hreflang: hreflang };
575     }
576};
577
578jsToolBar.prototype.elements.link.fn.xhtml = function() {
579     var link = this.elements.link.prompt.call(this);
580     if (link) {
581          var stag = '<a href="'+link.href+'"';
582          if (link.hreflang) { stag = stag+' hreflang="'+link.hreflang+'"'; }
583          stag = stag+'>';
584          var etag = '</a>';
585
586          this.encloseSelection(stag,etag);
587     }
588};
589jsToolBar.prototype.elements.link.fn.wiki = function() {
590     var link = this.elements.link.prompt.call(this);
591     if (link) {
592          var stag = '[';
593          var etag = '|'+link.href;
594          if (link.hreflang) { etag = etag+'|'+link.hreflang; }
595          etag = etag+']';
596
597          this.encloseSelection(stag,etag);
598     }
599};
600
601// img
602jsToolBar.prototype.elements.img = {
603          type: 'button',
604          title: 'External image',
605          src_prompt: 'Please give image URL:',
606          fn: {},
607          prompt: function(src) {
608               src = src || '';
609               return this.stripBaseURL(window.prompt(this.elements.img.src_prompt,src));
610          }
611};
612jsToolBar.prototype.elements.img.fn.xhtml = function() {
613     var src = this.elements.img.prompt.call(this);
614     if (src) {
615          this.encloseSelection('','',function(str) {
616               if (str) {
617                    return '<img src="'+src+'" alt="'+str+'" />';
618               } else {
619                    return '<img src="'+src+'" alt="" />';
620               }
621          });
622     }
623};
624jsToolBar.prototype.elements.img.fn.wiki = function() {
625     var src = this.elements.img.prompt.call(this);
626     if (src) {
627          this.encloseSelection('','',function(str) {
628               if (str) {
629                    return '(('+src+'|'+str+'))';
630               } else {
631                    return '(('+src+'))';
632               }
633          });
634     }
635};
Note: See TracBrowser for help on using the repository browser.

Sites map