Dotclear

source: admin/js/jsToolBar/jsToolBar.wysiwyg.js @ 925:ea7e39c07f7e

Revision 925:ea7e39c07f7e, 25.7 KB checked in by franck <carnet.franck.paul@…>, 13 years ago (diff)

Wysiwig editor : replace <hr> or <HR> to <hr /> (as for BR), fixes #1062

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

Sites map