Dotclear

source: plugins/dcLegacyEditor/js/jsToolBar/jsToolBar.js @ 3361:23cff72066bd

Revision 3361:23cff72066bd, 15.5 KB checked in by franck <carnet.franck.paul@…>, 9 years ago (diff)

Add 'l' and 'm' accesskey for legacy editor toolbar, respectively for 'insert link' and 'select media' buttons

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                    this.textarea.setSelectionRange(start + prefix.length, start + prefix.length);
284               }
285               this.textarea.scrollTop = scrollPos;
286          }
287     },
288
289     stripBaseURL: function(url) {
290          if (this.base_url != '') {
291               var pos = url.indexOf(this.base_url);
292               if (pos == 0) {
293                    url = url.substr(this.base_url.length);
294               }
295          }
296
297          return url;
298     }
299};
300
301/** Resizer
302-------------------------------------------------------- */
303jsToolBar.prototype.resizeSetStartH = function() {
304     this.dragStartH = this.textarea.offsetHeight + 0;
305};
306jsToolBar.prototype.resizeDragStart = function(event) {
307     var This = this;
308     this.dragStartY = event.clientY;
309     this.resizeSetStartH();
310     document.addEventListener('mousemove', this.dragMoveHdlr=function(event){This.resizeDragMove(event);}, false);
311     document.addEventListener('mouseup', this.dragStopHdlr=function(event){This.resizeDragStop(event);}, false);
312};
313
314jsToolBar.prototype.resizeDragMove = function(event) {
315     this.textarea.style.height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
316};
317
318jsToolBar.prototype.resizeDragStop = function(event) {
319     document.removeEventListener('mousemove', this.dragMoveHdlr, false);
320     document.removeEventListener('mouseup', this.dragStopHdlr, false);
321};
322
323// Elements definition ------------------------------------
324// block format (paragraph, headers)
325jsToolBar.prototype.elements.blocks = {
326     type: 'combo',
327     title: 'block format',
328     options: {
329          none: '-- none --', // only for wysiwyg mode
330          nonebis: '- block format -', // only for xhtml mode
331          p: 'Paragraph',
332          h1: 'Header 1',
333          h2: 'Header 2',
334          h3: 'Header 3',
335          h4: 'Header 4',
336          h5: 'Header 5',
337          h6: 'Header 6'
338     },
339     xhtml: {
340          list: ['nonebis','p','h1','h2','h3','h4','h5','h6'],
341          fn: function(opt) {
342               if (opt=='nonebis')
343                    this.textarea.focus();
344               else
345                    try {this.singleTag('<'+opt+'>','</'+opt+'>');} catch(e){};
346               this.toolNodes.blocks.value = 'nonebis';
347          }
348     },
349     wiki: {
350          list: ['nonebis','h3','h4','h5'],
351          fn: function(opt) {
352               switch (opt) {
353                    case 'nonebis': this.textarea.focus(); break;
354                    case 'h3': this.encloseSelection('!!!'); break;
355                    case 'h4': this.encloseSelection('!!'); break;
356                    case 'h5': this.encloseSelection('!'); break;
357               }
358               this.toolNodes.blocks.value = 'nonebis';
359          }
360     }
361};
362
363// spacer
364jsToolBar.prototype.elements.space0 = {
365     type:'space',
366     format:{
367          wysiwyg:true,
368          wiki:true,
369          xhtml:true
370     }
371};
372
373// strong
374jsToolBar.prototype.elements.strong = {
375     type: 'button',
376     title: 'Strong emphasis',
377     fn: {
378          wiki: function() { this.singleTag('__') },
379          xhtml: function() { this.singleTag('<strong>','</strong>') }
380     }
381};
382
383// em
384jsToolBar.prototype.elements.em = {
385     type: 'button',
386     title: 'Emphasis',
387     fn: {
388          wiki: function() { this.singleTag("''") },
389          xhtml: function() { this.singleTag('<em>','</em>') }
390     }
391};
392
393// ins
394jsToolBar.prototype.elements.ins = {
395     type: 'button',
396     title: 'Inserted',
397     fn: {
398          wiki: function() { this.singleTag('++') },
399          xhtml: function() { this.singleTag('<ins>','</ins>') }
400     }
401};
402
403// del
404jsToolBar.prototype.elements.del = {
405     type: 'button',
406     title: 'Deleted',
407     fn: {
408          wiki: function() { this.singleTag('--') },
409          xhtml: function() { this.singleTag('<del>','</del>') }
410     }
411};
412
413// quote
414jsToolBar.prototype.elements.quote = {
415     type: 'button',
416     title: 'Inline quote',
417     fn: {
418          wiki: function() { this.singleTag('{{','}}') },
419          xhtml: function() { this.singleTag('<q>','</q>') }
420     }
421};
422
423// code
424jsToolBar.prototype.elements.code = {
425     type: 'button',
426     title: 'Code',
427     fn: {
428          wiki: function() { this.singleTag('@@') },
429          xhtml: function() { this.singleTag('<code>','</code>')}
430     }
431};
432
433// code
434jsToolBar.prototype.elements.mark = {
435     type: 'button',
436     title: 'Mark',
437     fn: {
438          wiki: function() { this.singleTag('""') },
439          xhtml: function() { this.singleTag('<mark>','</mark>')}
440     }
441};
442
443// spacer
444jsToolBar.prototype.elements.space1 = {
445     type:'space',
446     format:{
447          wysiwyg:true,
448          wiki:true,
449          xhtml:true
450     }
451};
452
453// br
454jsToolBar.prototype.elements.br = {
455     type: 'button',
456     title: 'Line break',
457     fn: {
458          wiki: function() { this.encloseSelection("%%%\n",'') },
459          xhtml: function() { this.encloseSelection("<br />\n",'')}
460     }
461};
462
463// spacer
464jsToolBar.prototype.elements.space2 = {
465     type:'space',
466     format:{
467          wysiwyg:true,
468          wiki:true,
469          xhtml:true
470     }
471};
472
473// blockquote
474jsToolBar.prototype.elements.blockquote = {
475     type: 'button',
476     title: 'Blockquote',
477     fn: {
478          xhtml: function() { this.singleTag('<blockquote>','</blockquote>') },
479          wiki: function() {
480               this.encloseSelection("\n",'',
481               function(str) {
482                    str = str.replace(/\r/g,'');
483                    return '> '+str.replace(/\n/g,"\n> ");
484               });
485          }
486     }
487};
488
489// pre
490jsToolBar.prototype.elements.pre = {
491     type: 'button',
492     title: 'Preformated text',
493     fn: {
494          wiki: function() { this.singleTag("///\n","\n///") },
495          xhtml: function() { this.singleTag('<pre>','</pre>') }
496     }
497};
498
499// ul
500jsToolBar.prototype.elements.ul = {
501     type: 'button',
502     title: 'Unordered list',
503     fn: {
504          wiki: function() {
505               this.encloseSelection('','',function(str) {
506                    str = str.replace(/\r/g,'');
507                    return '* '+str.replace(/\n/g,"\n* ");
508               });
509          },
510          xhtml: function() {
511               this.encloseSelection('','',function(str) {
512                    str = str.replace(/\r/g,'');
513                    str = str.replace(/\n/g,"</li>\n <li>");
514                    return "<ul>\n <li>"+str+"</li>\n</ul>";
515               });
516          }
517     }
518};
519
520// ol
521jsToolBar.prototype.elements.ol = {
522     type: 'button',
523     title: 'Ordered list',
524     fn: {
525          wiki: function() {
526               this.encloseSelection('','',function(str) {
527                    str = str.replace(/\r/g,'');
528                    return '# '+str.replace(/\n/g,"\n# ");
529               });
530          },
531          xhtml: function() {
532               this.encloseSelection('','',function(str) {
533                    str = str.replace(/\r/g,'');
534                    str = str.replace(/\n/g,"</li>\n <li>");
535                    return "<ol>\n <li>"+str+"</li>\n</ol>";
536               });
537          }
538     }
539};
540
541// spacer
542jsToolBar.prototype.elements.space3 = {
543     type:'space',
544     format:{
545          wysiwyg:true,
546          wiki:true,
547          xhtml:true
548     }
549};
550
551// link
552jsToolBar.prototype.elements.link = {
553     type: 'button',
554     title: 'Link',
555     fn: {},
556     accesskey: 'l',
557     href_prompt: 'Please give page URL:',
558     hreflang_prompt: 'Language of this page:',
559     default_hreflang: '',
560     prompt: function(href,hreflang) {
561          href = href || '';
562          hreflang = hreflang || this.elements.link.default_hreflang;
563
564          href = window.prompt(this.elements.link.href_prompt,href);
565          if (!href) { return false; }
566
567          hreflang = window.prompt(this.elements.link.hreflang_prompt,
568          hreflang);
569
570          return { href: this.stripBaseURL(href), hreflang: hreflang };
571     }
572};
573
574jsToolBar.prototype.elements.link.fn.xhtml = function() {
575     var link = this.elements.link.prompt.call(this);
576     if (link) {
577          var stag = '<a href="'+link.href+'"';
578          if (link.hreflang) { stag = stag+' hreflang="'+link.hreflang+'"'; }
579          stag = stag+'>';
580          var etag = '</a>';
581
582          this.encloseSelection(stag,etag);
583     }
584};
585jsToolBar.prototype.elements.link.fn.wiki = function() {
586     var link = this.elements.link.prompt.call(this);
587     if (link) {
588          var stag = '[';
589          var etag = '|'+link.href;
590          if (link.hreflang) { etag = etag+'|'+link.hreflang; }
591          etag = etag+']';
592
593          this.encloseSelection(stag,etag);
594     }
595};
596
597// img
598jsToolBar.prototype.elements.img = {
599          type: 'button',
600          title: 'External image',
601          src_prompt: 'Please give image URL:',
602          fn: {},
603          prompt: function(src) {
604               src = src || '';
605               return this.stripBaseURL(window.prompt(this.elements.img.src_prompt,src));
606          }
607};
608jsToolBar.prototype.elements.img.fn.xhtml = function() {
609     var src = this.elements.img.prompt.call(this);
610     if (src) {
611          this.encloseSelection('','',function(str) {
612               if (str) {
613                    return '<img src="'+src+'" alt="'+str+'" />';
614               } else {
615                    return '<img src="'+src+'" alt="" />';
616               }
617          });
618     }
619};
620jsToolBar.prototype.elements.img.fn.wiki = function() {
621     var src = this.elements.img.prompt.call(this);
622     if (src) {
623          this.encloseSelection('','',function(str) {
624               if (str) {
625                    return '(('+src+'|'+str+'))';
626               } else {
627                    return '(('+src+'))';
628               }
629          });
630     }
631};
Note: See TracBrowser for help on using the repository browser.

Sites map