source: trunk/modules/WebKit/WebKit.js @ 998

Last change on this file since 998 was 998, checked in by ray, 11 years ago
  • #1201 Add inwardHtml/outwardHtml functions to Config object
  • Improved method for finding editing position in Gecko/Webkit?
  • Property svn:keywords set to LastChangedDate LastChangedRevision LastChangedBy HeadURL Id
File size: 23.7 KB
Line 
1
2  /*--------------------------------------:noTabs=true:tabSize=2:indentSize=2:--
3    --  Xinha (is not htmlArea) - http://xinha.gogo.co.nz/
4    --
5    --  Use of Xinha is granted by the terms of the htmlArea License (based on
6    --  BSD license)  please read license.txt in this package for details.
7    --
8    --  Xinha was originally based on work by Mihai Bazon which is:
9    --      Copyright (c) 2003-2004 dynarch.com.
10    --      Copyright (c) 2002-2003 interactivetools.com, inc.
11    --      This copyright notice MUST stay intact for use.
12    --
13    -- This is the WebKit (Safari) compatability plugin, part of the Xinha core.
14    --
15    --  The file is loaded as a special plugin by the Xinha Core when
16    --  Xinha is being run under a Webkit based browser such as Safari
17    --
18    --  It provides implementation and specialisation for various methods
19    --  in the core where different approaches per browser are required.
20    --
21    --  Design Notes::
22    --   Most methods here will simply be overriding Xinha.prototype.<method>
23    --   and should be called that, but methods specific to Webkit should
24    --   be a part of the WebKit.prototype, we won't trample on namespace
25    --   that way.
26    --
27    --  $HeadURL$
28    --  $LastChangedDate$
29    --  $LastChangedRevision$
30    --  $LastChangedBy$
31    --------------------------------------------------------------------------*/
32                                                   
33WebKit._pluginInfo = {
34  name          : "WebKit",
35  origin        : "Xinha Core",
36  version       : "$LastChangedRevision$".replace(/^[^:]*: (.*) \$$/, '$1'),
37  developer     : "The Xinha Core Developer Team",
38  developer_url : "$HeadURL$".replace(/^[^:]*: (.*) \$$/, '$1'),
39  sponsor       : "",
40  sponsor_url   : "",
41  license       : "htmlArea"
42};
43
44function WebKit(editor) {
45  this.editor = editor; 
46  editor.WebKit = this;
47}
48
49/** Allow Webkit to handle some key events in a special way.
50 */
51 
52WebKit.prototype.onKeyPress = function(ev)
53{
54  var editor = this.editor;
55  var s = editor.getSelection();
56 
57  // Handle shortcuts
58  if(editor.isShortCut(ev))
59  {
60    switch(editor.getKey(ev).toLowerCase())
61    {
62      case 'z':
63        if(editor._unLink && editor._unlinkOnUndo)
64        {
65          Xinha._stopEvent(ev);
66          editor._unLink();
67          editor.updateToolbar();
68          return true;
69        }
70      break;
71         
72          case 'a':
73        // ctrl-a selects all, but
74      break;
75         
76      case 'v':
77        // If we are not using htmlareaPaste, don't let Xinha try and be fancy but let the
78        // event be handled normally by the browser (don't stopEvent it)
79        if(!editor.config.htmlareaPaste)
80        {         
81          return true;
82        }
83      break;
84    }
85  }
86 
87  // Handle normal characters
88  switch(editor.getKey(ev))
89  {
90    // Space, see if the text just typed looks like a URL, or email address
91    // and link it appropriatly
92    case ' ':
93      var autoWrap = function (textNode, tag)
94      {
95        var rightText = textNode.nextSibling;
96        if ( typeof tag == 'string')
97        {
98          tag = editor._doc.createElement(tag);
99        }
100        var a = textNode.parentNode.insertBefore(tag, rightText);
101        Xinha.removeFromParent(textNode);
102        a.appendChild(textNode);
103        rightText.data = ' ' + rightText.data;
104   
105        s.collapse(rightText, 1);
106   
107        editor._unLink = function()
108        {
109          var t = a.firstChild;
110          a.removeChild(t);
111          a.parentNode.insertBefore(t, a);
112          Xinha.removeFromParent(a);
113          editor._unLink = null;
114          editor._unlinkOnUndo = false;
115        };
116        editor._unlinkOnUndo = true;
117   
118        return a;
119      };
120 
121      if ( editor.config.convertUrlsToLinks && s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0 )
122      {
123        var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/);
124        if ( midStart == -1 )
125        {
126          break;
127        }
128
129        if ( editor._getFirstAncestor(s, 'a') )
130        {
131          break; // already in an anchor
132        }
133
134        var matchData = s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/, '$1');
135
136        var mEmail = matchData.match(Xinha.RE_email);
137        if ( mEmail )
138        {
139          var leftTextEmail  = s.anchorNode;
140          var rightTextEmail = leftTextEmail.splitText(s.anchorOffset);
141          var midTextEmail   = leftTextEmail.splitText(midStart);
142
143          autoWrap(midTextEmail, 'a').href = 'mailto:' + mEmail[0];
144          break;
145        }
146
147        RE_date = /([0-9]+\.)+/; //could be date or ip or something else ...
148        RE_ip = /(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/;
149        var mUrl = matchData.match(Xinha.RE_url);
150        if ( mUrl )
151        {
152          if (RE_date.test(matchData))
153          {
154            break; //ray: disabling linking of IP numbers because of general bugginess (see Ticket #1085)
155            /*if (!RE_ip.test(matchData))
156            {
157              break;
158            }*/
159          }
160          var leftTextUrl  = s.anchorNode;
161          var rightTextUrl = leftTextUrl.splitText(s.anchorOffset);
162          var midTextUrl   = leftTextUrl.splitText(midStart);
163          autoWrap(midTextUrl, 'a').href = (mUrl[1] ? mUrl[1] : 'http://') + mUrl[2];
164          break;
165        }
166      }
167    break;
168  }
169 
170  // Handle special keys
171  switch ( ev.keyCode )
172  {   
173    case 13: // ENTER
174      if( ev.shiftKey  )
175      {
176        //TODO: here we need to add insert new line
177      }
178    break;
179
180    case 27: // ESCAPE
181      if ( editor._unLink )
182      {
183        editor._unLink();
184        Xinha._stopEvent(ev);
185      }
186 
187    break;
188   
189    case 8: // KEY backspace
190    case 46: // KEY delete
191      // We handle the mozilla backspace directly??
192      if ( !ev.shiftKey && this.handleBackspace() )
193      {
194        Xinha._stopEvent(ev);
195      }
196    break;
197    default:
198        editor._unlinkOnUndo = false;
199
200        // Handle the "auto-linking", specifically this bit of code sets up a handler on
201        // an self-titled anchor (eg <a href="http://www.gogo.co.nz/">www.gogo.co.nz</a>)
202        // when the text content is edited, such that it will update the href on the anchor
203       
204        if ( s.anchorNode && s.anchorNode.nodeType == 3 )
205        {
206          // See if we might be changing a link
207          var a = editor._getFirstAncestor(s, 'a');
208          // @todo: we probably need here to inform the setTimeout below that we not changing a link and not start another setTimeout
209          if ( !a )
210          {
211            break; // not an anchor
212          }
213         
214          if ( !a._updateAnchTimeout )
215          {
216            if ( s.anchorNode.data.match(Xinha.RE_email) && a.href.match('mailto:' + s.anchorNode.data.trim()) )
217            {
218              var textNode = s.anchorNode;
219              var fnAnchor = function()
220              {
221                a.href = 'mailto:' + textNode.data.trim();
222                // @fixme: why the hell do another timeout is started ?
223                //         This lead to never ending timer if we dont remove this line
224                //         But when removed, the email is not correctly updated
225                //
226                // - to fix this we should make fnAnchor check to see if textNode.data has
227                //   stopped changing for say 5 seconds and if so we do not make this setTimeout
228                a._updateAnchTimeout = setTimeout(fnAnchor, 250);
229              };
230              a._updateAnchTimeout = setTimeout(fnAnchor, 1000);
231              break;
232            }
233
234            var m = s.anchorNode.data.match(Xinha.RE_url);
235
236            if ( m && a.href.match(new RegExp( 'http(s)?://' + Xinha.escapeStringForRegExp( s.anchorNode.data.trim() ) ) ) )
237            {
238              var txtNode = s.anchorNode;
239              var fnUrl = function()
240              {
241                // Sometimes m is undefined becase the url is not an url anymore (was www.url.com and become for example www.url)
242                // ray: shouldn't the link be un-linked then?
243                m = txtNode.data.match(Xinha.RE_url);
244                if(m)
245                {
246                  a.href = (m[1] ? m[1] : 'http://') + m[2];
247                }
248               
249                // @fixme: why the hell do another timeout is started ?
250                //         This lead to never ending timer if we dont remove this line
251                //         But when removed, the url is not correctly updated
252                //
253                // - to fix this we should make fnUrl check to see if textNode.data has
254                //   stopped changing for say 5 seconds and if so we do not make this setTimeout
255                a._updateAnchTimeout = setTimeout(fnUrl, 250);
256              };
257              a._updateAnchTimeout = setTimeout(fnUrl, 1000);
258            }
259          }       
260        }               
261    break;
262  }
263
264  return false; // Let other plugins etc continue from here.
265}
266
267/** When backspace is hit, the Gecko onKeyPress will execute this method.
268 *  I don't remember what the exact purpose of this is though :-(
269 * 
270 */
271 
272WebKit.prototype.handleBackspace = function()
273{
274  var editor = this.editor;
275  setTimeout(
276    function()
277    {
278      var sel   = editor.getSelection();
279      var range = editor.createRange(sel);
280      var SC = range.startContainer;
281      var SO = range.startOffset;
282      var EC = range.endContainer;
283      var EO = range.endOffset;
284      var newr = SC.nextSibling;
285      if ( SC.nodeType == 3 )
286      {
287        SC = SC.parentNode;
288      }
289      if ( ! ( /\S/.test(SC.tagName) ) )
290      {
291        var p = document.createElement("p");
292        while ( SC.firstChild )
293        {
294          p.appendChild(SC.firstChild);
295        }
296        SC.parentNode.insertBefore(p, SC);
297        Xinha.removeFromParent(SC);
298        var r = range.cloneRange();
299        r.setStartBefore(newr);
300        r.setEndAfter(newr);
301        r.extractContents();
302        sel.removeAllRanges();
303        sel.addRange(r);
304      }
305    },
306    10);
307};
308
309WebKit.prototype.inwardHtml = function(html)
310{
311   return html;
312}
313
314WebKit.prototype.outwardHtml = function(html)
315{
316  return html;
317}
318
319WebKit.prototype.onExecCommand = function(cmdID, UI, param)
320{   
321  this.editor._doc.execCommand('styleWithCSS', false, false); //switch styleWithCSS off; seems to make no difference though
322   
323  switch(cmdID)
324  {
325    case 'paste':
326      alert(Xinha._lc("The Paste button does not work in the Safari browser for security reasons. Press CTRL-V on your keyboard to paste directly."));
327      return true; // Indicate paste is done, stop command being issued to browser by Xinha.prototype.execCommand
328    break;
329    case 'removeformat':
330      var editor = this.editor;
331      var sel = editor.getSelection();
332      var selSave = editor.saveSelection(sel);
333      var range = editor.createRange(sel);
334
335      var els = editor._doc.getElementsByTagName('*');
336      els = Xinha.collectionToArray(els);
337      var start = ( range.startContainer.nodeType == 1 ) ? range.startContainer : range.startContainer.parentNode;
338      var i,el,newNode, fragment, child,r2 = editor._doc.createRange();
339
340      function clean (el)
341      {
342        if (el.nodeType != 1) return;
343        el.removeAttribute('style');
344        for (var j=0; j<el.childNodes.length;j++)
345        {
346          clean(el.childNodes[j]);
347        }
348        if ( (el.tagName.toLowerCase() == 'span' && !el.attributes.length ) || el.tagName.toLowerCase() == 'font')
349        {
350          r2.selectNodeContents(el);
351          fragment = r2.extractContents();
352          while (fragment.firstChild)
353          {
354            child = fragment.removeChild(fragment.firstChild);
355            el.parentNode.insertBefore(child, el);
356          }
357          el.parentNode.removeChild(el);
358        }
359      }
360      if (sel.isCollapsed)
361      {
362        els = editor._doc.body.childNodes;
363        for (i = 0; i < els.length; i++)
364        {
365          el = els[i];
366          if (el.nodeType != 1) continue;
367          if (el.tagName.toLowerCase() == 'span')
368          {
369            newNode = editor.convertNode(el, 'div');
370            el.parentNode.replaceChild(newNode, el);
371            el = newNode;
372          }
373          clean(el);
374        }
375      }
376      else
377      {
378        for (i=0; i<els.length;i++)
379        {
380          el = els[i];
381          if ( range.isPointInRange(el, 0) || (els[i] == start && range.startOffset == 0))
382          {
383            clean(el);
384          }
385        }
386      }
387
388      r2.detach();
389      editor.restoreSelection(selSave);
390      return true;
391    break;
392  }
393
394  return false;
395}
396WebKit.prototype.onMouseDown = function(ev)
397{
398  // selection of hr in Safari seems utterly impossible :(
399  if (ev.target.tagName.toLowerCase() == "hr" || ev.target.tagName.toLowerCase() == "img")
400  {
401    this.editor.selectNodeContents(ev.target);
402  }
403}
404
405
406/*--------------------------------------------------------------------------*/
407/*------- IMPLEMENTATION OF THE ABSTRACT "Xinha.prototype" METHODS ---------*/
408/*--------------------------------------------------------------------------*/
409
410/** Insert a node at the current selection point.
411 * @param toBeInserted DomNode
412 */
413
414Xinha.prototype.insertNodeAtSelection = function(toBeInserted)
415{
416  var sel = this.getSelection();
417  var range = this.createRange(sel);
418  // remove the current selection
419  sel.removeAllRanges();
420  range.deleteContents();
421  var node = range.startContainer;
422  var pos = range.startOffset;
423  var selnode = toBeInserted;
424  switch ( node.nodeType )
425  {
426    case 3: // Node.TEXT_NODE
427      // we have to split it at the caret position.
428      if ( toBeInserted.nodeType == 3 )
429      {
430        // do optimized insertion
431        node.insertData(pos, toBeInserted.data);
432        range = this.createRange();
433        range.setEnd(node, pos + toBeInserted.length);
434        range.setStart(node, pos + toBeInserted.length);
435        sel.addRange(range);
436      }
437      else
438      {
439        node = node.splitText(pos);
440        if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ )
441        {
442          selnode = selnode.firstChild;
443        }
444        node.parentNode.insertBefore(toBeInserted, node);
445        this.selectNodeContents(selnode);
446        this.updateToolbar();
447      }
448    break;
449    case 1: // Node.ELEMENT_NODE
450      if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ )
451      {
452        selnode = selnode.firstChild;
453      }
454      node.insertBefore(toBeInserted, node.childNodes[pos]);
455      this.selectNodeContents(selnode);
456      this.updateToolbar();
457    break;
458  }
459};
460 
461/** Get the parent element of the supplied or current selection.
462 *  @param   sel optional selection as returned by getSelection
463 *  @returns DomNode
464 */
465 
466Xinha.prototype.getParentElement = function(sel)
467{
468  if ( typeof sel == 'undefined' )
469  {
470    sel = this.getSelection();
471  }
472  var range = this.createRange(sel);
473  try
474  {
475    var p = range.commonAncestorContainer;
476    if ( !range.collapsed && range.startContainer == range.endContainer &&
477        range.startOffset - range.endOffset <= 1 && range.startContainer.hasChildNodes() )
478    {
479      p = range.startContainer.childNodes[range.startOffset];
480    }
481
482    while ( p.nodeType == 3 )
483    {
484      p = p.parentNode;
485    }
486    return p;
487  }
488  catch (ex)
489  {
490    return null;
491  }
492};
493
494/**
495 * Returns the selected element, if any.  That is,
496 * the element that you have last selected in the "path"
497 * at the bottom of the editor, or a "control" (eg image)
498 *
499 * @returns null | DomNode
500 */
501
502Xinha.prototype.activeElement = function(sel)
503{
504  if ( ( sel === null ) || this.selectionEmpty(sel) )
505  {
506    return null;
507  }
508
509  // For Mozilla we just see if the selection is not collapsed (something is selected)
510  // and that the anchor (start of selection) is an element.  This might not be totally
511  // correct, we possibly should do a simlar check to IE?
512  if ( !sel.isCollapsed )
513  {     
514    if ( sel.anchorNode.childNodes.length > sel.anchorOffset && sel.anchorNode.childNodes[sel.anchorOffset].nodeType == 1 )
515    {
516      return sel.anchorNode.childNodes[sel.anchorOffset];
517    }
518    else if ( sel.anchorNode.nodeType == 1 )
519    {
520      return sel.anchorNode;
521    }
522    else
523    {
524      return null; // return sel.anchorNode.parentNode;
525    }
526  }
527  return null;
528};
529
530/**
531 * Determines if the given selection is empty (collapsed).
532 * @param selection Selection object as returned by getSelection
533 * @returns true|false
534 */
535 
536Xinha.prototype.selectionEmpty = function(sel)
537{
538  if ( !sel )
539  {
540    return true;
541  }
542
543  if ( typeof sel.isCollapsed != 'undefined' )
544  {     
545    return sel.isCollapsed;
546  }
547
548  return true;
549};
550
551/**
552 * Returns a range object to be stored
553 * and later restored with Xinha.prototype.restoreSelection()
554 *
555 * @returns Range
556 */
557Xinha.prototype.saveSelection = function()
558{
559  return this.createRange(this.getSelection()).cloneRange();
560}
561/**
562 * Restores a selection previously stored
563 * @param savedSelection Range object as returned by Xinha.prototype.restoreSelection()
564 */
565Xinha.prototype.restoreSelection = function(savedSelection)
566{
567  var sel = this.getSelection();
568  sel.removeAllRanges();
569  sel.addRange(savedSelection);
570}
571/**
572 * Selects the contents of the given node.  If the node is a "control" type element, (image, form input, table)
573 * the node itself is selected for manipulation.
574 *
575 * @param node DomNode
576 * @param pos  Set to a numeric position inside the node to collapse the cursor here if possible.
577 */
578 
579Xinha.prototype.selectNodeContents = function(node, pos)
580{
581  this.focusEditor();
582  this.forceRedraw();
583  var range;
584  var collapsed = typeof pos == "undefined" ? true : false;
585  var sel = this.getSelection();
586  range = this._doc.createRange();
587  // Tables and Images get selected as "objects" rather than the text contents
588  if ( collapsed && node.tagName && node.tagName.toLowerCase().match(/table|img|input|textarea|select/) )
589  {
590    range.selectNode(node);
591  }
592  else
593  {
594    range.selectNodeContents(node);
595    //(collapsed) && range.collapse(pos);
596  }
597  sel.removeAllRanges();
598  sel.addRange(range);
599};
600 
601/** Insert HTML at the current position, deleting the selection if any.
602 * 
603 *  @param html string
604 */
605 
606Xinha.prototype.insertHTML = function(html)
607{
608  var sel = this.getSelection();
609  var range = this.createRange(sel);
610  this.focusEditor();
611  // construct a new document fragment with the given HTML
612  var fragment = this._doc.createDocumentFragment();
613  var div = this._doc.createElement("div");
614  div.innerHTML = html;
615  while ( div.firstChild )
616  {
617    // the following call also removes the node from div
618    fragment.appendChild(div.firstChild);
619  }
620  // this also removes the selection
621  var node = this.insertNodeAtSelection(fragment);
622};
623
624/** Get the HTML of the current selection.  HTML returned has not been passed through outwardHTML.
625 *
626 * @returns string
627 */
628 
629Xinha.prototype.getSelectedHTML = function()
630{
631  var sel = this.getSelection();
632  if (sel.isCollapsed) return '';
633  var range = this.createRange(sel);
634
635  if ( range )
636  {
637    return Xinha.getHTML(range.cloneContents(), false, this);
638  }
639  else return '';
640};
641 
642
643/** Get a Selection object of the current selection.  Note that selection objects are browser specific.
644 *
645 * @returns Selection
646 */
647 
648Xinha.prototype.getSelection = function()
649{
650  return this._iframe.contentWindow.getSelection();
651};
652 
653/** Create a Range object from the given selection.  Note that range objects are browser specific.
654 *
655 *  @param sel Selection object (see getSelection)
656 *  @returns Range
657 */
658 
659Xinha.prototype.createRange = function(sel)
660{
661  this.activateEditor();
662  if ( typeof sel != "undefined" )
663  {
664    try
665    {
666      return sel.getRangeAt(0);
667    }
668    catch(ex)
669    {
670      return this._doc.createRange();
671    }
672  }
673  else
674  {
675    return this._doc.createRange();
676  }
677};
678
679/** Determine if the given event object is a keydown/press event.
680 *
681 *  @param event Event
682 *  @returns true|false
683 */
684 
685Xinha.prototype.isKeyEvent = function(event)
686{
687  return event.type == "keypress";
688}
689
690/** Return the character (as a string) of a keyEvent  - ie, press the 'a' key and
691 *  this method will return 'a', press SHIFT-a and it will return 'A'.
692 *
693 *  @param   keyEvent
694 *  @returns string
695 */
696                                   
697Xinha.prototype.getKey = function(keyEvent)
698{
699 // with ctrl pressed Safari does not give the charCode, unfortunately this (shortcuts) is about the only thing this function is for
700  var key = String.fromCharCode(parseInt(keyEvent.keyIdentifier.replace(/^U\+/,''),16));
701  if (keyEvent.shiftKey) return key;
702  else return key.toLowerCase();
703}
704
705/** Return the HTML string of the given Element, including the Element.
706 *
707 * @param element HTML Element DomNode
708 * @returns string
709 */
710 
711Xinha.getOuterHTML = function(element)
712{
713  return (new XMLSerializer()).serializeToString(element);
714};
715
716Xinha.prototype.cc = String.fromCharCode(8286);
717
718Xinha.prototype.setCC = function ( target )
719{
720  var cc = this.cc;
721  try
722  {
723    if ( target == "textarea" )
724    {
725      var ta = this._textArea;
726      var index = ta.selectionStart;
727      var before = ta.value.substring( 0, index )
728      var after = ta.value.substring( index, ta.value.length );
729
730      if ( after.match(/^[^<]*>/) ) // make sure cursor is in an editable area (outside tags, script blocks, enities and inside the body)
731      {
732        var tagEnd = after.indexOf(">") + 1;
733        ta.value = before + after.substring( 0, tagEnd ) + cc + after.substring( tagEnd, after.length );
734      }
735      else ta.value = before + cc + after;
736      ta.value = ta.value.replace(new RegExp ('(&[^'+cc+']*?)('+cc+')([^'+cc+']*?;)'), "$1$3$2");
737      ta.value = ta.value.replace(new RegExp ('(<script[^>]*>[^'+cc+']*?)('+cc+')([^'+cc+']*?<\/script>)'), "$1$3$2");
738      ta.value = ta.value.replace(new RegExp ('^([^'+cc+']*)('+cc+')([^'+cc+']*<body[^>]*>)(.*?)'), "$1$3$2$4");
739    }
740    else
741    {
742      var sel = this.getSelection();
743      sel.getRangeAt(0).insertNode( this._doc.createTextNode( cc ) );
744    }
745  } catch (e) {}
746};
747
748Xinha.prototype.findCC = function ( target )
749{
750  if ( target == 'textarea' )
751  {
752  var ta = this._textArea;
753  var pos = ta.value.indexOf( this.cc );
754  if ( pos == -1 ) return;
755  var end = pos + this.cc.length;
756  var before =  ta.value.substring( 0, pos );
757  var after = ta.value.substring( end, ta.value.length );
758  ta.value = before ;
759
760  ta.scrollTop = ta.scrollHeight;
761  var scrollPos = ta.scrollTop;
762 
763  ta.value += after;
764  ta.setSelectionRange(pos,pos);
765
766  ta.focus();
767 
768  ta.scrollTop = scrollPos;
769
770  }
771  else
772  {
773    var self = this;
774    try
775    {
776      var doc = this._doc;
777      doc.body.innerHTML = doc.body.innerHTML.replace(new RegExp(this.cc),'<span id="XinhaEditingPostion"></span>');
778      var posEl = doc.getElementById('XinhaEditingPostion');
779      this.selectNodeContents(posEl);
780      this.scrollToElement(posEl);
781      posEl.parentNode.removeChild(posEl);
782
783      this._iframe.contentWindow.focus();
784    } catch (e) {}
785  }
786};
787/*--------------------------------------------------------------------------*/
788/*------------ EXTEND SOME STANDARD "Xinha.prototype" METHODS --------------*/
789/*--------------------------------------------------------------------------*/
790
791Xinha.prototype._standardToggleBorders = Xinha.prototype._toggleBorders;
792Xinha.prototype._toggleBorders = function()
793{
794  var result = this._standardToggleBorders();
795 
796  // flashing the display forces moz to listen (JB:18-04-2005) - #102
797  var tables = this._doc.getElementsByTagName('TABLE');
798  for(var i = 0; i < tables.length; i++)
799  {
800    tables[i].style.display="none";
801    tables[i].style.display="table";
802  }
803 
804  return result;
805};
806
807/** Return the doctype of a document, if set
808 *
809 * @param doc DOM element document
810 * @returns string the actual doctype
811 */
812Xinha.getDoctype = function (doc)
813{
814  var d = '';
815  if (doc.doctype)
816  {
817    d += '<!DOCTYPE ' + doc.doctype.name + " PUBLIC ";
818    d +=  doc.doctype.publicId ? '"' + doc.doctype.publicId + '"' : ''; 
819    d +=  doc.doctype.systemId ? ' "'+ doc.doctype.systemId + '"' : '';
820    d += ">";
821  }
822  return d;
823};
Note: See TracBrowser for help on using the repository browser.