Changeset 969 for trunk/modules/Opera


Ignore:
Timestamp:
02/07/08 13:10:47 (12 years ago)
Author:
gogo
Message:

Two part commit for initial Opera 9.2 (or better, 9.5) support, see #938 .

Location:
trunk/modules/Opera
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/modules/Opera/Gecko.js

    r963 r969  
    1111    --      This copyright notice MUST stay intact for use. 
    1212    -- 
    13     -- This is the Gecko compatability plugin, part of the Xinha core. 
     13    -- This is the Opera compatability plugin, part of the Xinha core. 
    1414    -- 
    1515    --  The file is loaded as a special plugin by the Xinha Core when 
    16     --  Xinha is being run under a Gecko based browser with the Midas 
     16    --  Xinha is being run under an Opera based browser with the Midas 
    1717    --  editing API. 
    1818    -- 
     
    2222    --  Design Notes:: 
    2323    --   Most methods here will simply be overriding Xinha.prototype.<method> 
    24     --   and should be called that, but methods specific to Gecko should  
    25     --   be a part of the Gecko.prototype, we won't trample on namespace 
     24    --   and should be called that, but methods specific to Opera should  
     25    --   be a part of the Opera.prototype, we won't trample on namespace 
    2626    --   that way. 
    2727    -- 
     
    3232    --------------------------------------------------------------------------*/ 
    3333                                                     
    34 Gecko._pluginInfo = { 
    35   name          : "Gecko", 
     34Opera._pluginInfo = { 
     35  name          : "Opera", 
    3636  origin        : "Xinha Core", 
    3737  version       : "$LastChangedRevision$".replace(/^[^:]*: (.*) \$$/, '$1'), 
    3838  developer     : "The Xinha Core Developer Team", 
    3939  developer_url : "$HeadURL$".replace(/^[^:]*: (.*) \$$/, '$1'), 
    40   sponsor       : "", 
    41   sponsor_url   : "", 
     40  sponsor       : "Gogo Internet Services Limited", 
     41  sponsor_url   : "http://www.gogo.co.nz/", 
    4242  license       : "htmlArea" 
    4343}; 
    4444 
    45 function Gecko(editor) { 
     45function Opera(editor) { 
    4646  this.editor = editor;   
    47   editor.Gecko = this; 
    48 } 
    49  
    50 /** Allow Gecko to handle some key events in a special way. 
    51  */ 
    52    
    53 Gecko.prototype.onKeyPress = function(ev) 
     47  editor.Opera = this; 
     48} 
     49 
     50/** Allow Opera to handle some key events in a special way. 
     51 */ 
     52   
     53Opera.prototype.onKeyPress = function(ev) 
    5454{ 
    5555  var editor = this.editor; 
     
    285285} 
    286286 
    287 /** When backspace is hit, the Gecko onKeyPress will execute this method. 
     287/** When backspace is hit, the Opera onKeyPress will execute this method. 
    288288 *  I don't remember what the exact purpose of this is though :-( 
    289289 */ 
    290290  
    291 Gecko.prototype.handleBackspace = function() 
     291Opera.prototype.handleBackspace = function() 
    292292{ 
    293293  var editor = this.editor; 
     
    326326}; 
    327327 
    328 Gecko.prototype.inwardHtml = function(html) 
    329 { 
    330    // Midas uses b and i internally instead of strong and em 
    331    // Xinha will use strong and em externally (see Xinha.prototype.outwardHtml)    
    332    html = html.replace(/<(\/?)strong(\s|>|\/)/ig, "<$1b$2"); 
    333    html = html.replace(/<(\/?)em(\s|>|\/)/ig, "<$1i$2");     
    334     
    335    // Both IE and Gecko use strike internally instead of del (#523) 
     328Opera.prototype.inwardHtml = function(html) 
     329{ 
     330   // Both IE and Opera use strike internally instead of del (#523) 
    336331   // Xinha will present del externally (see Xinha.prototype.outwardHtml 
    337332   html = html.replace(/<(\/?)del(\s|>|\/)/ig, "<$1strike$2"); 
     
    340335} 
    341336 
    342 Gecko.prototype.outwardHtml = function(html) 
    343 { 
    344   // ticket:56, the "greesemonkey" plugin for Firefox adds this junk, 
    345   // so we strip it out.  Original submitter gave a plugin, but that's 
    346   // a bit much just for this IMHO - james 
    347   html = html.replace(/<script[\s]*src[\s]*=[\s]*['"]chrome:\/\/.*?["']>[\s]*<\/script>/ig, ''); 
     337Opera.prototype.outwardHtml = function(html) 
     338{ 
    348339 
    349340  return html; 
    350341} 
    351342 
    352 Gecko.prototype.onExecCommand = function(cmdID, UI, param) 
     343Opera.prototype.onExecCommand = function(cmdID, UI, param) 
    353344{    
    354   try 
    355   { 
    356     // useCSS deprecated & replaced by styleWithCSS 
    357     this.editor._doc.execCommand('useCSS', false, true); //switch useCSS off (true=off) 
    358     this.editor._doc.execCommand('styleWithCSS', false, false); //switch styleWithCSS off      
    359   } catch (ex) {} 
    360345     
    361346  switch(cmdID) 
    362347  { 
    363     case 'paste': 
     348     
     349    /*case 'paste': 
    364350    { 
    365351      alert(Xinha._lc("The Paste button does not work in Mozilla based web browsers (technical security reasons). Press CTRL-V on your keyboard to paste directly.")); 
    366352      return true; // Indicate paste is done, stop command being issued to browser by Xinha.prototype.execCommand 
    367353    } 
    368     break; 
     354    break;*/ 
     355     
    369356    case 'removeformat': 
    370357      var editor = this.editor; 
     
    391378      return true; 
    392379    break; 
     380     
    393381  } 
    394382   
    395383  return false; 
    396384} 
    397 Gecko.prototype.onMouseDown = function(ev) 
     385 
     386Opera.prototype.onMouseDown = function(ev) 
    398387{    
    399   // Gecko doesn't select hr's on single click 
    400   if (ev.target.tagName.toLowerCase() == "hr") 
    401   { 
    402     var sel = this.editor.getSelection(); 
    403     var range = this.editor.createRange(sel); 
    404     range.selectNode(ev.target); 
    405   } 
     388   
    406389} 
    407390 
     
    411394/*--------------------------------------------------------------------------*/ 
    412395 
     396 
     397 
    413398/** Insert a node at the current selection point.  
    414399 * @param toBeInserted DomNode 
     
    417402Xinha.prototype.insertNodeAtSelection = function(toBeInserted) 
    418403{ 
    419   if ( toBeInserted.ownerDocument != this._doc ) // as of FF3, Gecko is strict regarding the ownerDocument of an element 
     404  if ( toBeInserted.ownerDocument != this._doc ) 
    420405  { 
    421406    try  
    422         { 
    423                 toBeInserted = this._doc.adoptNode( toBeInserted ); 
    424         } catch (e) {} 
    425   } 
    426   var sel = this.getSelection(); 
     407    { 
     408      toBeInserted = this._doc.adoptNode( toBeInserted ); 
     409    } catch (e) {} 
     410  } 
     411   
     412  this.focusEditor(); 
     413   
     414  var sel = this.getSelection();       
    427415  var range = this.createRange(sel); 
    428   // remove the current selection 
    429   sel.removeAllRanges(); 
     416     
    430417  range.deleteContents(); 
     418   
    431419  var node = range.startContainer; 
    432420  var pos = range.startOffset; 
    433421  var selnode = toBeInserted; 
     422   
     423  sel.removeAllRanges(); 
    434424   
    435425  switch ( node.nodeType ) 
     
    458448      } 
    459449    break; 
     450     
    460451    case 1: // Node.ELEMENT_NODE 
    461452      if ( toBeInserted.nodeType == 11 /* Node.DOCUMENT_FRAGMENT_NODE */ ) 
     
    654645Xinha.prototype.getSelection = function() 
    655646{ 
    656   return this._iframe.contentWindow.getSelection(); 
     647  var sel = this._iframe.contentWindow.getSelection(); 
     648  if(sel && sel.focusNode && sel.focusNode.tagName && sel.focusNode.tagName == 'HTML') 
     649  { // Bad news batman, this selection is wonky   
     650    var bod = this._doc.getElementsByTagName('body')[0]; 
     651    var rng = this.createRange(); 
     652    rng.selectNodeContents(bod); 
     653    sel.removeAllRanges(); 
     654    sel.addRange(rng); 
     655    sel.collapseToEnd();     
     656  } 
     657  return sel;  
    657658}; 
    658659   
     
    717718}; 
    718719 
    719 //Control character for retaining edit location when switching modes 
    720 Xinha.prototype.cc = String.fromCharCode(8286);  
    721  
     720 
     721/* Caret position remembering when switch between text and html mode. 
     722 * Largely this is the same as for Gecko except: 
     723 * 
     724 * Opera does not have window.find() so we use instead an element with known 
     725 * id (<span id="XinhaOperaCaretMarker">MARK</span>) so that we can  
     726 * do _doc.getElementById() on it. 
     727 *  
     728 * Opera for some reason will not set the insert point (flashing caret) 
     729 * anyway though, in theory, the iframe is focused (in findCC below) and then 
     730 * the selection (containing the span above) is collapsed, it should show 
     731 * caret.  I don't know why not.  Seems to require user to actually 
     732 * click to get the caret to show (or type anything without it acting wierd)?  
     733 * Very annoying. 
     734 * 
     735 */  
     736  
    722737Xinha.prototype.setCC = function ( target ) 
    723738{ 
    724   var cc = this.cc; 
     739  // Do a two step caret insertion, first a single char, then we'll replace that with the  
     740  // id'd span. 
     741  var cc = String.fromCharCode(8286); 
     742   
    725743  try 
    726744  { 
     
    741759      ta.value = ta.value.replace(new RegExp ('(<script[^>]*>[^'+cc+']*?)('+cc+')([^'+cc+']*?<\/script>)'), "$1$3$2"); 
    742760      ta.value = ta.value.replace(new RegExp ('^([^'+cc+']*)('+cc+')([^'+cc+']*<body[^>]*>)(.*?)'), "$1$3$2$4"); 
     761       
     762      ta.value = ta.value.replace(cc, '<span id="XinhaOperaCaretMarker">MARK</span>'); 
    743763    } 
    744764    else 
    745765    { 
    746766      var sel = this.getSelection(); 
    747       sel.getRangeAt(0).insertNode( this._doc.createTextNode( cc ) ); 
     767       
     768      var mark =  this._doc.createElement('span'); 
     769      mark.id  = 'XinhaOperaCaretMarker'; 
     770      sel.getRangeAt(0).insertNode( mark ); 
     771       
    748772    } 
    749773  } catch (e) {} 
     
    753777{ 
    754778  if ( target == 'textarea' ) 
    755   { 
    756   var ta = this._textArea; 
    757   var pos = ta.value.indexOf( this.cc ); 
    758   if ( pos == -1 ) return; 
    759   var end = pos + this.cc.length; 
    760   var before =  ta.value.substring( 0, pos ); 
    761   var after = ta.value.substring( end, ta.value.length ); 
    762   ta.value = before ; 
    763  
    764   ta.scrollTop = ta.scrollHeight; 
    765   var scrollPos = ta.scrollTop; 
    766    
    767   ta.value += after; 
    768   ta.setSelectionRange(pos,pos); 
    769  
    770   ta.focus(); 
    771    
    772   ta.scrollTop = scrollPos; 
     779  {   
     780    var ta = this._textArea; 
     781    var pos = ta.value.search( /(<span\s+id="XinhaOperaCaretMarker"\s*\/?>((\s|(MARK))*<\/span>)?)/ );     
     782    if ( pos == -1 ) return; 
     783    var cc = RegExp.$1; 
     784    var end = pos + cc.length; 
     785    var before =  ta.value.substring( 0, pos ); 
     786    var after = ta.value.substring( end, ta.value.length ); 
     787    ta.value = before ; 
     788   
     789    ta.scrollTop = ta.scrollHeight; 
     790    var scrollPos = ta.scrollTop; 
     791     
     792    ta.value += after; 
     793    ta.setSelectionRange(pos,pos); 
     794   
     795    ta.focus(); 
     796     
     797    ta.scrollTop = scrollPos; 
    773798 
    774799  } 
    775800  else 
    776   { 
    777     try 
    778     { 
    779       this._iframe.contentWindow.find( this.cc ); 
     801  {     
     802    var marker = this._doc.getElementById('XinhaOperaCaretMarker'); 
     803    if(marker)  
     804    { 
     805      this.focusEditor();// this._iframe.contentWindow.focus(); 
     806       
     807      var rng = this.createRange(); 
     808      rng.selectNode(marker); 
     809       
    780810      var sel = this.getSelection(); 
    781       sel.getRangeAt(0).deleteContents(); 
    782       this.scrollToElement(); 
    783       this._iframe.contentWindow.focus(); 
    784     } catch (e) {} 
    785   } 
    786 }; 
     811      sel.addRange(rng);  
     812      sel.collapseToStart(); 
     813       
     814      this.scrollToElement(marker); 
     815       
     816      marker.parentNode.removeChild(marker); 
     817      return; 
     818    } 
     819  } 
     820}; 
     821 
    787822/*--------------------------------------------------------------------------*/ 
    788823/*------------ EXTEND SOME STANDARD "Xinha.prototype" METHODS --------------*/ 
    789824/*--------------------------------------------------------------------------*/ 
    790825 
     826/* 
    791827Xinha.prototype._standardToggleBorders = Xinha.prototype._toggleBorders; 
    792828Xinha.prototype._toggleBorders = function() 
    793829{ 
    794830  var result = this._standardToggleBorders(); 
    795    
     831     
    796832  // flashing the display forces moz to listen (JB:18-04-2005) - #102 
    797833  var tables = this._doc.getElementsByTagName('TABLE'); 
     
    804840  return result; 
    805841}; 
     842*/ 
    806843 
    807844/** Return the doctype of a document, if set 
     
    822859  return d; 
    823860}; 
     861 
     862Xinha.prototype._standardInitIframe = Xinha.prototype.initIframe; 
     863Xinha.prototype.initIframe = function() 
     864{ 
     865  if(!this._iframeLoadDone)  
     866  {     
     867    if(this._iframe.contentWindow && this._iframe.contentWindow.xinhaReadyToRoll) 
     868    { 
     869      this._iframeLoadDone = true; 
     870      this._standardInitIframe(); 
     871    } 
     872    else 
     873    { 
     874      // Timeout a little (Opera is a bit dodgy about using an event) 
     875      var editor = this; 
     876      setTimeout( function() { editor.initIframe() }, 5); 
     877    } 
     878  } 
     879} 
     880 
     881// For some reason, Opera doesn't listen to addEventListener for at least select elements with event = change 
     882// so we will have to intercept those and use a Dom0 event, these are used eg for the fontname/size/format 
     883// dropdowns in the toolbar 
     884Xinha._addEventOperaOrig = Xinha._addEvent; 
     885Xinha._addEvent = function(el, evname, func) 
     886{ 
     887  if(el.tagName && el.tagName.toLowerCase() == 'select' && evname == 'change') 
     888  { 
     889    return Xinha.addDom0Event(el,evname,func); 
     890  } 
     891   
     892  return Xinha._addEventOperaOrig(el,evname,func); 
     893} 
Note: See TracChangeset for help on using the changeset viewer.