Dotclear

source: admin/js/jsToolBar/jsToolBar.wysiwyg.js @ 1833:81903fe2f6eb

Revision 1833:81903fe2f6eb, 25.8 KB checked in by kevin@…, 12 years ago (diff)

Ticket #1539 : Modification de "Pages"

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
23jsToolBar.prototype.can_wwg = (document.designMode != undefined);
24jsToolBar.prototype.iframe = null;
25jsToolBar.prototype.iwin = null;
26jsToolBar.prototype.ibody = null;
27jsToolBar.prototype.iframe_css = null;
28
29/* Editor methods
30-------------------------------------------------------- */
31jsToolBar.prototype.drawToolBar = jsToolBar.prototype.draw;
32jsToolBar.prototype.draw = function(mode) {
33     mode = mode || 'xhtml';
34     
35     if (this.can_wwg) {
36          this.mode = 'wysiwyg';
37          this.drawToolBar('wysiwyg');
38          this.initWindow();
39     } else {
40          this.drawToolBar(mode);
41     }
42};
43
44jsToolBar.prototype.switchMode = function(mode) {
45     mode = mode || 'xhtml';
46     
47     if (mode == 'xhtml') {
48          this.wwg_mode = true;
49          this.draw(mode);
50     } else {
51          if (this.wwg_mode) {
52               this.syncContents('iframe');
53          }
54          this.wwg_mode = false;
55          this.removeEditor();
56          this.textarea.style.display = '';
57          this.drawToolBar(mode);
58     }
59};
60
61jsToolBar.prototype.syncContents = function(from) {
62     from = from || 'textarea';
63     var This = this;
64     if (from == 'textarea') {
65          initContent();
66     } else {
67          this.validBlockquote();
68          var html = this.applyHtmlFilters(this.ibody.innerHTML);
69          if (html == '<br />') { html = '<p></p>'; }
70          this.textarea.value = html;
71     }
72     
73     function initContent() {
74          if (!This.iframe.contentWindow.document || !This.iframe.contentWindow.document.body) {
75               setTimeout(initContent, 1);
76               return;
77          }
78          This.ibody = This.iframe.contentWindow.document.body;
79         
80          if (This.textarea.value != '' && This.textarea.value != '<p></p>') {
81               This.ibody.innerHTML = This.applyWysiwygFilters(This.textarea.value);
82               if (This.ibody.createTextRange) { //cursor at the begin for IE
83                    var IErange = This.ibody.createTextRange();
84                    IErange.execCommand("SelectAll");
85                    IErange.collapse();
86                    IErange.select();
87               }
88          } else if (window.navigator.product != undefined && 
89                                    window.navigator.product == 'Gecko') {
90               This.ibody.innerHTML = '<p><br _moz_editor_blogus_node="TRUE" _moz_dirty=""/></p>';
91          } else {
92               var idoc = This.iwin.document;
93               var para = idoc.createElement('p');
94               para.appendChild(idoc.createTextNode(''));
95               while (idoc.body.hasChildNodes()) {
96                    idoc.body.removeChild(idoc.body.lastChild);
97               }
98               idoc.body.appendChild(para);
99          }
100     }
101};
102jsToolBar.prototype.htmlFilters = {
103     tagsoup: function(str){
104          return this.tagsoup2xhtml(str);
105     }
106};
107jsToolBar.prototype.applyHtmlFilters = function(str) {
108     for (var fn in this.htmlFilters) {
109           str = this.htmlFilters[fn].call(this, str);
110     }
111     return str;
112};
113jsToolBar.prototype.wysiwygFilters = {};
114jsToolBar.prototype.applyWysiwygFilters = function( str) {
115     for (var fn in this.wysiwygFilters) {
116           str = this.wysiwygFilters[fn].call(this, str);
117     }
118     return str;
119};
120
121jsToolBar.prototype.switchEdit = function() {
122     if (this.wwg_mode) {
123          this.textarea.style.display = '';
124          this.iframe.style.display = 'none';
125          this.syncContents('iframe');
126          this.drawToolBar('xhtml');
127          this.wwg_mode = false;
128          this.focusEditor();
129     } else {
130          this.iframe.style.display = '';
131          this.textarea.style.display = 'none';
132          this.syncContents('textarea');
133          this.drawToolBar('wysiwyg');
134          this.wwg_mode = true;
135          this.focusEditor();
136     }
137     this.setSwitcher();
138};
139
140/** Creates iframe for editor, inits a blank document
141*/
142jsToolBar.prototype.initWindow = function() {
143     var This = this;
144     
145     this.iframe = document.createElement('iframe');
146     this.textarea.parentNode.insertBefore(this.iframe,this.textarea.nextSibling);
147     
148     this.switcher = document.createElement('ul');
149     this.switcher.className = 'jstSwitcher';
150     this.editor.appendChild(this.switcher);
151     
152     this.iframe.height = this.textarea.offsetHeight + 0;
153     this.iframe.width = this.textarea.offsetWidth + 0;
154     
155     if (this.textarea.tabIndex != undefined) {
156          this.iframe.tabIndex = this.textarea.tabIndex;
157     }
158     
159     function initIframe() {
160          var doc = This.iframe.contentWindow.document;
161          if (!doc) {
162               setTimeout(initIframe,1);
163               return false;
164          }
165         
166          doc.open();
167          var html =
168          '<html>\n'+
169          '<head>\n'+
170          '<style type="text/css">'+This.iframe_css+'</style>\n'+
171          (This.base_url != '' ? '<base href="'+This.base_url+'" />' : '')+
172          '</head>\n'+
173          '<body>\n'+
174          '</body>\n'+
175          '</html>';
176         
177          doc.write(html);
178          doc.close();
179          if (document.all) { // for IE
180               doc.designMode = 'on';
181               // warning : doc is now inaccessible for IE6 sp1
182          }
183         
184          This.iwin = This.iframe.contentWindow;
185         
186          This.syncContents('textarea');
187         
188          if (This.wwg_mode == undefined) {
189               This.wwg_mode = true;
190          }
191         
192          if (This.wwg_mode) {
193               This.textarea.style.display = 'none';
194          } else {
195               This.iframe.style.display = 'none';
196          }
197         
198          // update textarea on submit
199          if (This.textarea.form) {
200               chainHandler(This.textarea.form,'onsubmit', function() {
201                    if (This.wwg_mode) {
202                         This.syncContents('iframe');
203                    }
204               });
205          }
206         
207          for (var evt in This.iwinEvents) {
208               var event = This.iwinEvents[evt];
209               This.addIwinEvent(This.iframe.contentWindow.document, event.type, event.fn, This);
210          }
211         
212          This.setSwitcher();
213          try { This.iwin.document.designMode = 'on'; } catch (e) {}; // Firefox needs this
214         
215          return true;
216     }
217     initIframe();
218};
219jsToolBar.prototype.addIwinEvent = function(target, type, fn, scope) {
220     var myFn = function(e){fn.call(scope, e)};
221     addEvent(target, type, myFn, true);
222     // fix memory leak
223     addEvent(scope.iwin, 'unload', function(){
224          removeEvent(target, type, myFn, true);
225     }, true);
226};
227jsToolBar.prototype.iwinEvents = {
228     block1: {
229          type: 'mouseup',
230          fn: function(){ this.adjustBlockLevelCombo() }
231     },
232     block2: {
233          type: 'keyup',
234          fn: function(){ this.adjustBlockLevelCombo() }
235     }
236};
237
238/** Insert a mode switcher after editor area
239*/
240jsToolBar.prototype.switcher_visual_title = 'visual';
241jsToolBar.prototype.switcher_source_title = 'source';
242jsToolBar.prototype.setSwitcher = function() {
243     while (this.switcher.hasChildNodes()) {
244          this.switcher.removeChild(this.switcher.firstChild);
245     }
246     
247     var This = this;
248     function setLink(title,link) {
249          var li = document.createElement('li');
250          if (link) {
251               var a = document.createElement('a');
252               a.href = '#';
253               a.editor = This;
254               a.onclick = function() { this.editor.switchEdit(); return false; };
255               a.appendChild(document.createTextNode(title));
256          } else {
257               li.className = 'jstSwitcherCurrent';
258               a = document.createTextNode(title);
259          }
260         
261          li.appendChild(a);
262          This.switcher.appendChild(li);
263     }
264     
265     setLink(this.switcher_visual_title,!this.wwg_mode);
266     setLink(this.switcher_source_title,this.wwg_mode);
267};
268
269/** Removes editor area and mode switcher
270*/
271jsToolBar.prototype.removeEditor = function() {
272     if (this.iframe != null) {
273          this.iframe.parentNode.removeChild(this.iframe);
274          this.iframe = null;
275     }
276     
277     if (this.switcher != undefined && this.switcher.parentNode != undefined) {
278          this.switcher.parentNode.removeChild(this.switcher);
279     }
280};
281
282/** Focus on the editor area
283*/
284jsToolBar.prototype.focusEditor = function() {
285     if (this.wwg_mode) {
286          try { this.iwin.document.designMode = 'on'; } catch (e) {}; // Firefox needs this
287          var This = this;
288          setTimeout(function() {This.iframe.contentWindow.focus()},1);
289     } else {
290          this.textarea.focus();
291     }
292};
293
294/** Resizer
295*/
296jsToolBar.prototype.resizeSetStartH = function() {
297     if (this.wwg_mode && this.iframe != undefined) {
298          this.dragStartH = this.iframe.offsetHeight;
299          return;
300     }
301     this.dragStartH = this.textarea.offsetHeight + 0;
302};
303jsToolBar.prototype.resizeDragMove = function(event) {
304     var new_height = (this.dragStartH+event.clientY-this.dragStartY)+'px';
305     if (this.iframe != undefined) {
306          this.iframe.style.height = new_height;
307     }
308     this.textarea.style.height = new_height;
309};
310
311/* Editing methods
312-------------------------------------------------------- */
313/** Replaces current selection by given node
314*/
315jsToolBar.prototype.insertNode = function(node) {
316     var range;
317     
318     if (this.iwin.getSelection) { // Gecko
319          var sel = this.iwin.getSelection();
320          range = sel.getRangeAt(0);
321         
322          // deselect all ranges
323          sel.removeAllRanges();
324         
325          // empty range
326          range.deleteContents();
327         
328          // Insert node
329          range.insertNode(node);
330         
331          range.selectNodeContents(node);
332          range.setEndAfter(node);
333          if (range.endContainer.childNodes.length > range.endOffset &&
334          range.endContainer.nodeType != Node.TEXT_NODE) {
335               range.setEnd(range.endContainer.childNodes[range.endOffset], 0);
336          } else {
337               range.setEnd(range.endContainer.childNodes[0]);
338          }
339          sel.addRange(range);
340         
341          sel.collapseToEnd();
342     } else { // IE
343          // lambda element
344          var p = this.iwin.document.createElement('div');
345          p.appendChild(node);
346          range = this.iwin.document.selection.createRange();
347          range.execCommand('delete');
348          // insert innerHTML from element
349          range.pasteHTML(p.innerHTML);
350          range.collapse(false);
351          range.select();
352     }
353     this.iwin.focus();
354};
355
356/** Returns a document fragment with selected nodes
357*/
358jsToolBar.prototype.getSelectedNode = function() {
359     if (this.iwin.getSelection) { // Gecko
360          var sel = this.iwin.getSelection();
361          var range = sel.getRangeAt(0);
362          var content = range.cloneContents();
363     } else { // IE
364          var sel = this.iwin.document.selection;
365          var d = this.iwin.document.createElement('div');
366          d.innerHTML = sel.createRange().htmlText;
367          var content = this.iwin.document.createDocumentFragment();
368          for (var i=0; i < d.childNodes.length; i++) {
369               content.appendChild(d.childNodes[i].cloneNode(true));
370          }
371     }
372     return content;
373};
374
375/** Returns string representation for selected node
376*/
377jsToolBar.prototype.getSelectedText = function() {
378     if (this.iwin.getSelection) { // Gecko
379          return this.iwin.getSelection().toString();
380     } else { // IE
381          var range = this.iwin.document.selection.createRange();
382          return range.text;
383     }
384};
385
386jsToolBar.prototype.replaceNodeByContent = function(node) {
387     var content = this.iwin.document.createDocumentFragment();
388     for (var i=0; i < node.childNodes.length; i++) {
389          content.appendChild(node.childNodes[i].cloneNode(true));
390     }
391     node.parentNode.replaceChild(content, node);
392};
393
394jsToolBar.prototype.getBlockLevel = function() {
395     var blockElts = ['p','h1','h2','h3','h4','h5','h6'];
396     
397     var range, commonAncestorContainer;
398     if (this.iwin.getSelection) { //gecko
399          var selection = this.iwin.getSelection();
400          range = selection.getRangeAt(0);
401          commonAncestorContainer = range.commonAncestorContainer;
402          while (commonAncestorContainer.nodeType != 1) {
403               commonAncestorContainer = commonAncestorContainer.parentNode;
404          }
405     } else { //ie
406          range = this.iwin.document.selection.createRange();
407          commonAncestorContainer = range.parentElement();
408     }
409     
410     var ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
411     while (arrayIndexOf(blockElts, ancestorTagName)==-1 && ancestorTagName!='body') {
412          commonAncestorContainer = commonAncestorContainer.parentNode;
413          ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
414     }
415     if (ancestorTagName == 'body') return null;
416     else return commonAncestorContainer;
417};
418jsToolBar.prototype.adjustBlockLevelCombo = function() {
419     var blockLevel = this.getBlockLevel();
420     if (blockLevel !== null)
421          this.toolNodes.blocks.value = blockLevel.tagName.toLowerCase();
422     else {
423          if (this.mode == 'wysiwyg') this.toolNodes.blocks.value = 'none';
424          if (this.mode == 'xhtml') this.toolNodes.blocks.value = 'nonebis';
425     }
426};
427
428/** HTML code cleanup
429-------------------------------------------------------- */
430jsToolBar.prototype.simpleCleanRegex = new Array(
431     /* Remove every tags we don't need */
432     [/<meta[\w\W]*?>/gim,''],
433     [/<style[\w\W]*?>[\w\W]*?<\/style>/gim, ''],
434     [/<\/?font[\w\W]*?>/gim, ''],
435     
436     
437     /* Replacements */
438     [/<(\/?)(B|b|STRONG)([\s>\/])/g, "<$1strong$3"],
439     [/<(\/?)(I|i|EM)([\s>\/])/g, "<$1em$3"],
440     [/<IMG ([^>]*?[^\/])>/gi, "<img $1 />"],
441     [/<INPUT ([^>]*?[^\/])>/gi, "<input $1 />"],
442     [/<COL ([^>]*?[^\/])>/gi, "<col $1 />"],
443     [/<AREA ([^>]*?[^\/])>/gi, "<area $1 />"],
444     [/<PARAM ([^>]*?[^\/])>/gi, "<param $1 />"],
445     [/<HR ([^>]*?[^\/])>/gi, "<hr $1/>"],
446     [/<BR ([^>]*?[^\/])>/gi, "<br $1/>"],
447     [/<(\/?)U([\s>\/])/gi, "<$1ins$2"],
448     [/<(\/?)STRIKE([\s>\/])/gi, "<$1del$2"],
449     [/<span style="font-weight: normal;">([\w\W]*?)<\/span>/gm, "$1"],
450     [/<span style="font-weight: bold;">([\w\W]*?)<\/span>/gm, "<strong>$1</strong>"],
451     [/<span style="font-style: italic;">([\w\W]*?)<\/span>/gm, "<em>$1</em>"],
452     [/<span style="text-decoration: underline;">([\w\W]*?)<\/span>/gm, "<ins>$1</ins>"],
453     [/<span style="text-decoration: line-through;">([\w\W]*?)<\/span>/gm, "<del>$1</del>"],
454     [/<span style="text-decoration: underline line-through;">([\w\W]*?)<\/span>/gm, "<del><ins>$1</ins></del>"],
455     [/<span style="(font-weight: bold; ?|font-style: italic; ?){2}">([\w\W]*?)<\/span>/gm, "<strong><em>$2</em></strong>"],
456     [/<span style="(font-weight: bold; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/span>/gm, "<ins><strong>$2</strong></ins>"],
457     [/<span style="(font-weight: italic; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/span>/gm, "<ins><em>$2</em></ins>"],
458     [/<span style="(font-weight: bold; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/span>/gm, "<del><strong>$2</strong></del>"],
459     [/<span style="(font-weight: italic; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/span>/gm, "<del><em>$2</em></del>"],
460     [/<span style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline; ?){3}">([\w\W]*?)<\/span>/gm, "<ins><strong><em>$2</em></strong></ins>"],
461     [/<span style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: line-through; ?){3}">([\w\W]*?)<\/span>/gm, "<del><strong><em>$2</em></strong></del>"],
462     [/<span style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline line-through; ?){3}">([\w\W]*?)<\/span>/gm, "<del><ins><strong><em>$2</em></strong></ins></del>"],
463     [/<strong style="font-weight: normal;">([\w\W]*?)<\/strong>/gm, "$1"],
464     [/<([a-z]+) style="font-weight: normal;">([\w\W]*?)<\/\1>/gm, "<$1>$2</$1>"],
465     [/<([a-z]+) style="font-weight: bold;">([\w\W]*?)<\/\1>/gm, "<$1><strong>$2</strong></$1>"],
466     [/<([a-z]+) style="font-style: italic;">([\w\W]*?)<\/\1>/gm, "<$1><em>$2</em></$1>"],
467     [/<([a-z]+) style="text-decoration: underline;">([\w\W]*?)<\/\1>/gm, "<ins><$1>$2</$1></ins>"],
468     [/<([a-z]+) style="text-decoration: line-through;">([\w\W]*?)<\/\1>/gm, "<del><$1>$2</$1></del>"],
469     [/<([a-z]+) style="text-decoration: underline line-through;">([\w\W]*?)<\/\1>/gm, "<del><ins><$1>$2</$1></ins></del>"],
470     [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?){2}">([\w\W]*?)<\/\1>/gm, "<$1><strong><em>$3</em></strong></$1>"],
471     [/<([a-z]+) style="(font-weight: bold; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm, "<ins><$1><strong>$3</strong></$1></ins>"],
472     [/<([a-z]+) style="(font-weight: italic; ?|text-decoration: underline; ?){2}">([\w\W]*?)<\/\1>/gm, "<ins><$1><em>$3</em></$1></ins>"],
473     [/<([a-z]+) style="(font-weight: bold; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm, "<del><$1><strong>$3</strong></$1></del>"],
474     [/<([a-z]+) style="(font-weight: italic; ?|text-decoration: line-through; ?){2}">([\w\W]*?)<\/\1>/gm, "<del><$1><em>$3</em></$1></del>"],
475     [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline; ?){3}">([\w\W]*?)<\/\1>/gm, "<ins><$1><strong><em>$3</em></strong></$1></ins>"],
476     [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: line-through; ?){3}">([\w\W]*?)<\/\1>/gm, "<del><$1><strong><em>$3</em></strong></$1></del>"],
477     [/<([a-z]+) style="(font-weight: bold; ?|font-style: italic; ?|text-decoration: underline line-through; ?){3}">([\w\W]*?)<\/\1>/gm, "<del><ins><$1><strong><em>$3</em></strong></$1></ins></del>"],
478     [/<p><blockquote>(.*)(\n)+<\/blockquote><\/p>/i,"<blockquote>$1</blockquote>\n"],
479     /* mise en forme identique contigue */
480     [/<\/(strong|em|ins|del|q|code)>(\s*?)<\1>/gim, "$2"],
481     [/<(br|BR)>/g, "<br />"],
482     [/<(hr|HR)>/g, "<hr />"],
483     /* opera est trop strict ;)) */
484     [/([^\s])\/>/g, "$1 />"],
485     /* br intempestifs de fin de block */
486     [/<br \/>\s*<\/(h1|h2|h3|h4|h5|h6|ul|ol|li|p|blockquote|div)/gi, "</$1"],
487     [/<\/(h1|h2|h3|h4|h5|h6|ul|ol|li|p|blockquote)>([^\n\u000B\r\f])/gi, "</$1>\n$2"],
488     [/<hr style="width: 100%; height: 2px;" \/>/g, "<hr />"]
489);
490
491/** Cleanup HTML code
492*/
493jsToolBar.prototype.tagsoup2xhtml = function(html) {
494     for (var reg in this.simpleCleanRegex) {
495          html = html.replace(this.simpleCleanRegex[reg][0], this.simpleCleanRegex[reg][1]);
496     }
497     /* tags vides */
498     /* note : on tente de ne pas tenir compte des commentaires html, ceux-ci
499        permettent entre autre d'inserer des commentaires conditionnels pour ie */
500     while ( /(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/.test(html) ) {
501          html = html.replace(/(<[^\/!]>|<[^\/!][^>]*[^\/]>)\s*<\/[^>]*[^-]>/g, "");
502     }
503     
504     /* tous les tags en minuscule */
505     html = html.replace(/<(\/?)([A-Z0-9]+)/g,
506               function(match0, match1, match2) {
507                    return "<" + match1 + match2.toLowerCase();
508               });
509     
510     /* IE laisse souvent des attributs sans guillemets */
511     var myRegexp = /<[^>]+((\s+\w+\s*=\s*)([^"'][\w~@+$,%\/:.#?=&;!*()-]*))[^>]*?>/;
512     while ( myRegexp.test(html)) {
513          html = html.replace(
514               myRegexp,
515               function (str, val1, val2, val3){
516                    var tamponRegex = new RegExp(regexpEscape(val1));
517                    return str.replace(tamponRegex, val2+'"'+val3+'"');
518               }
519          )
520     }
521     
522     /* les navigateurs rajoutent une unite aux longueurs css nulles */
523     /* note: a ameliorer ! */
524     while ( /(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/.test(html)) {
525          html = html.replace(/(<[^>]+style=(["'])[^>]+[\s:]+)0(pt|px)(\2|\s|;)/gi, "$1"+"0$4");
526     }
527     
528     /* correction des fins de lignes : le textarea edite contient des \n
529     * le wysiwyg des \r\n , et le textarea mis a jour SANS etre affiche des \r\n ! */
530     html = html.replace(/\r\n/g,"\n");
531     
532     /* Trim */
533     html = html.replace(/^\s+/gm,'');
534     html = html.replace(/\s+$/gm,'');
535     
536     return html;
537};
538jsToolBar.prototype.validBlockquote = function() {
539     var blockElts = ['address','blockquote','dl','div','fieldset','form','h1',
540                      'h2','h3','h4','h5','h6','hr','ol','p','pre','table','ul'];
541     var BQs = this.iwin.document.getElementsByTagName('blockquote');
542     var bqChilds;
543     
544     for (var bq = 0; bq < BQs.length; bq++) {
545          bqChilds = BQs[bq].childNodes;
546          var frag = this.iwin.document.createDocumentFragment();
547          for (var i = (bqChilds.length-1); i >= 0; i--) {
548               if (bqChilds[i].nodeType == 1 && // Node.ELEMENT_NODE
549                   arrayIndexOf(blockElts, bqChilds[i].tagName.toLowerCase()) >= 0)
550               {
551                    if (frag.childNodes.length > 0) {
552                         var p = this.iwin.document.createElement('p');
553                         p.appendChild(frag);
554                         BQs[bq].replaceChild(p, bqChilds[i+1]);
555                         frag = this.iwin.document.createDocumentFragment();
556                    }
557               } else {
558                    if (frag.childNodes.length > 0) BQs[bq].removeChild(bqChilds[i+1]);
559                    frag.insertBefore(bqChilds[i].cloneNode(true), frag.firstChild);
560               }
561          }
562          if (frag.childNodes.length > 0) {
563               var p = this.iwin.document.createElement('p');
564               p.appendChild(frag);
565               BQs[bq].replaceChild(p, bqChilds[0]);
566          }
567     }
568};
569
570/* Removing text formating */
571jsToolBar.prototype.removeFormatRegexp = new Array(
572     [/(<[a-z][^>]*)margin\s*:[^;]*;/mg, "$1"],
573     [/(<[a-z][^>]*)margin-bottom\s*:[^;]*;/mg, "$1"],
574     [/(<[a-z][^>]*)margin-left\s*:[^;]*;/mg, "$1"],
575     [/(<[a-z][^>]*)margin-right\s*:[^;]*;/mg, "$1"],
576     [/(<[a-z][^>]*)margin-top\s*:[^;]*;/mg, "$1"],
577     
578     [/(<[a-z][^>]*)padding\s*:[^;]*;/mg, "$1"],
579     [/(<[a-z][^>]*)padding-bottom\s*:[^;]*;/mg, "$1"],
580     [/(<[a-z][^>]*)padding-left\s*:[^;]*;/mg, "$1"],
581     [/(<[a-z][^>]*)padding-right\s*:[^;]*;/mg, "$1"],
582     [/(<[a-z][^>]*)padding-top\s*:[^;]*;/mg, "$1"],
583     
584     [/(<[a-z][^>]*)font\s*:[^;]*;/mg, "$1"],
585     [/(<[a-z][^>]*)font-family\s*:[^;]*;/mg, "$1"],
586     [/(<[a-z][^>]*)font-size\s*:[^;]*;/mg, "$1"],
587     [/(<[a-z][^>]*)font-style\s*:[^;]*;/mg, "$1"],
588     [/(<[a-z][^>]*)font-variant\s*:[^;]*;/mg, "$1"],
589     [/(<[a-z][^>]*)font-weight\s*:[^;]*;/mg, "$1"],
590     
591     [/(<[a-z][^>]*)color\s*:[^;]*;/mg, "$1"]
592);
593
594jsToolBar.prototype.removeTextFormating = function(html) {
595     for (var reg in this.removeFormatRegexp) {
596          html = html.replace(this.removeFormatRegexp[reg][0], this.removeFormatRegexp[reg][1]);
597     }
598     
599     html = this.tagsoup2xhtml(html);
600     html = html.replace(/style="\s*?"/mgi,'');
601     return html;
602};
603
604/** Toolbar elements
605-------------------------------------------------------- */
606jsToolBar.prototype.elements.blocks.wysiwyg = {
607     list: ['none','p','h1','h2','h3','h4','h5','h6'],
608     fn: function(opt) {
609          if (opt=='none') {
610               var blockLevel = this.getBlockLevel();
611               if (blockLevel !== null) {
612                    this.replaceNodeByContent(blockLevel);
613               }
614               this.iwin.focus();
615          } else {
616               try { this.iwin.document.execCommand('formatblock',false,'<'+opt+'>');
617               } catch(e){};
618               this.iwin.focus();
619          }
620     }
621};
622
623jsToolBar.prototype.elements.strong.fn.wysiwyg = function() {
624     this.iwin.document.execCommand('bold', false, null);
625     this.iwin.focus();
626};
627
628jsToolBar.prototype.elements.em.fn.wysiwyg = function() {
629     this.iwin.document.execCommand('italic', false, null);
630     this.iwin.focus();
631};
632
633jsToolBar.prototype.elements.ins.fn.wysiwyg = function() {
634     this.iwin.document.execCommand('underline', false, null);
635     this.iwin.focus();
636};
637
638jsToolBar.prototype.elements.del.fn.wysiwyg = function() {
639     this.iwin.document.execCommand('strikethrough', false, null);
640     this.iwin.focus();
641};
642
643jsToolBar.prototype.elements.quote.fn.wysiwyg = function() {
644     var n = this.getSelectedNode();
645     var q = this.iwin.document.createElement('q');
646     q.appendChild(n);
647     this.insertNode(q);
648};
649
650jsToolBar.prototype.elements.code.fn.wysiwyg = function() {
651     var n = this.getSelectedNode();
652     var code = this.iwin.document.createElement('code');
653     code.appendChild(n);
654     this.insertNode(code);
655};
656
657jsToolBar.prototype.elements.br.fn.wysiwyg = function() {
658     var n = this.iwin.document.createElement('br');
659     this.insertNode(n);
660};
661
662jsToolBar.prototype.elements.blockquote.fn.wysiwyg = function() {
663     var n = this.getSelectedNode();
664     var q = this.iwin.document.createElement('blockquote');
665     q.appendChild(n);
666     this.insertNode(q);
667};
668
669jsToolBar.prototype.elements.pre.fn.wysiwyg = function() {
670     this.iwin.document.execCommand('formatblock',false,'<pre>');
671     this.iwin.focus();
672};
673
674jsToolBar.prototype.elements.ul.fn.wysiwyg = function() {
675     this.iwin.document.execCommand('insertunorderedlist',false,null);
676     this.iwin.focus();
677};
678
679jsToolBar.prototype.elements.ol.fn.wysiwyg = function() {
680     this.iwin.document.execCommand('insertorderedlist',false,null);
681     this.iwin.focus();
682};
683
684jsToolBar.prototype.elements.link.fn.wysiwyg = function() {
685     var href, hreflang;
686     var range, commonAncestorContainer;
687     if (this.iwin.getSelection) { //gecko
688          var selection = this.iwin.getSelection();
689          range = selection.getRangeAt(0);
690          commonAncestorContainer = range.commonAncestorContainer;
691          while (commonAncestorContainer.nodeType != 1) {
692               commonAncestorContainer = commonAncestorContainer.parentNode;
693          }
694     } else { //ie
695          range = this.iwin.document.selection.createRange();
696          commonAncestorContainer = range.parentElement();
697     }
698     
699     var ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
700     while (ancestorTagName!='a' && ancestorTagName!='body') {
701          commonAncestorContainer = commonAncestorContainer.parentNode;
702          ancestorTagName = commonAncestorContainer.tagName.toLowerCase();
703     }
704     
705     // Update or remove link?
706     if (ancestorTagName == 'a') {
707          href = commonAncestorContainer.href || '';
708          hreflang = commonAncestorContainer.hreflang || '';
709     }
710     
711     href = window.prompt(this.elements.link.href_prompt,href);
712     
713     // Remove link
714     if (ancestorTagName == 'a' && href=='') {
715          this.replaceNodeByContent(commonAncestorContainer);
716     }
717     if (!href) return; // user cancel
718     
719     hreflang = window.prompt(this.elements.link.hreflang_prompt, hreflang);
720     
721     // Update link
722     if (ancestorTagName == 'a' && href) {
723          commonAncestorContainer.setAttribute('href', href);
724          if (hreflang) {
725               commonAncestorContainer.setAttribute('hreflang', hreflang);
726          } else {
727               commonAncestorContainer.removeAttribute('hreflang');
728          }
729          return;
730     }
731     
732     // Create link
733     var n = this.getSelectedNode();
734     var a = this.iwin.document.createElement('a');
735     a.href = href;
736     if (hreflang) a.setAttribute('hreflang',hreflang);
737     a.appendChild(n);
738     this.insertNode(a);
739};
740
741
742
743// Remove format and Toggle
744jsToolBar.prototype.elements.removeFormat = {
745     type: 'button',
746     title: 'Remove text formating',
747     fn: {}
748};
749jsToolBar.prototype.elements.removeFormat.disabled = !jsToolBar.prototype.can_wwg;
750jsToolBar.prototype.elements.removeFormat.fn.xhtml = function() {
751     var html = this.textarea.value;
752     html = this.removeTextFormating(html);
753     this.textarea.value = html;
754};
755jsToolBar.prototype.elements.removeFormat.fn.wysiwyg = function() {
756     var html = this.iwin.document.body.innerHTML;
757     html = this.removeTextFormating(html);
758     this.iwin.document.body.innerHTML = html;
759};
760/** Utilities
761-------------------------------------------------------- */
762function arrayIndexOf(aArray, aValue){
763     if (typeof Array.indexOf == 'function') {
764          return aArray.indexOf(aValue);
765     } else {
766          var index = -1;
767          var l = aArray.length;
768          for (var i = 0; i < l ; i++) {
769               if (aArray[i] === aValue) {
770                    index = i;
771                    break;
772               }
773          }
774          return index;
775     }
776};
777function addEvent(obj, evType, fn, useCapture) {
778     if (obj.addEventListener){
779          obj.addEventListener(evType, fn, useCapture);
780          return true;
781     } else if (obj.attachEvent){
782          var r = obj.attachEvent("on"+evType, fn);
783          return r;
784     } else {
785          return false;
786     }
787};
788function removeEvent(obj, evType, fn, useCapture) {
789     if (obj.removeEventListener){
790          obj.removeEventListener(evType, fn, useCapture);
791          return true;
792     } else if (obj.detachEvent){
793          var r = obj.detachEvent("on"+evType, fn);
794          return r;
795     } else {
796          return false;
797  }
798};
799function regexpEscape(s) {
800     return s.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g,"\\$1")
801};
Note: See TracBrowser for help on using the repository browser.

Sites map