Changeset 9


Ignore:
Timestamp:
02/13/05 07:58:08 (13 years ago)
Author:
gogo
Message:

Minor

  • Fix body re
  • Split the button image creation into a function for ease of use.
  • Fixes to the panel system
  • Added indexOf convenience method to Array objects 3 == ['a','b','c'].indexOf('c');
  • Fix to the font dropdown updating (missing break)
  • Focus the popup editor window. Not sure why I fixed that, because popupeditor is going

to be removed totaly (in favour of FullScreen?).

Major

  • Add an auto-link ability for Gecko. This is intended to work the same way as IE,

for example if you type www.example.com then IE automatically makes that into a link, it
does the same for email addresses. It's slightly better than IE's method though as this
allows you to easily undo the "damage" if you didn't want the link created, just hit
CTRL-Z (undo) or ESC (escape) immediatly after the link is created and it gets unlinked.
Note: this adds a lot of code. One day the IE and Gecko specific code should be split out
so it's not loaded if it's not needed.

  • New config option "mozParahandler", this should be set to built-in, dirty or best
    • built-in doesn't alter mozilla's paragraph handling at all, so this probably means

hitting enter will cause a <br/> to be inserted.

  • dirty will use the "quick and dirty" fix which will make mozilla put in paragraphs

instead of breaks in most cases. But it's far from perfect.

  • best will use "hipikat"'s EnterParagraphs? plugin to provide the fix for mozilla

(loading EnterParagraphs? is not necessary it is done automatically if "best" is selected).
The EnterParagraphs? plugin will give the best results, but it will be slower about it.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/htmlarea.js

    r1 r9  
    143143HTMLArea.RE_doctype = /(<!doctype((.|\n)*?)>)\n?/i; 
    144144HTMLArea.RE_head    = /<head>((.|\n)*?)<\/head>/i; 
    145 HTMLArea.RE_body    = /<body>((.|\n)*?)<\/body>/i; 
     145HTMLArea.RE_body    = /<body[^>]*>((.|\n)*?)<\/body>/i; 
    146146HTMLArea.RE_Specials = /([\/\^$*+?.()|{}[\]])/g; 
     147HTMLArea.RE_email    = /[a-z0-9_]{3,}@[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,})+/i; 
     148HTMLArea.RE_url      = /(https?:\/\/)?(([a-z0-9_]+:[a-z0-9_]+@)?[a-z0-9_-]{2,}(\.[a-z0-9_-]{2,}){2,}(:[0-9]+)?(\/\S+)*)/i; 
    147149 
    148150HTMLArea.Config = function () { 
     
    159161  // If false, then passes ^V through to browser editor widget 
    160162  this.htmlareaPaste = false; 
     163 
     164  this.mozParaHandler = 'best'; // set to 'built-in', 'dirty' or 'best' 
     165                                // built-in: will (may) use 'br' instead of 'p' tags 
     166                                // dirty   : will use p and work good enough for the majority of cases, 
     167                                // best    : works the best, but it's about 12kb worth of javascript 
     168                                //   and will probably be slower than 'dirty'.  This is the "EnterParagraphs" 
     169                                //   plugin from "hipikat", rolled in to be part of the core code 
    161170 
    162171  // maximum size of the undo queue 
     
    785794      }); 
    786795 
    787       var i_contain = null; 
    788       if(HTMLArea.is_ie && ((!document.compatMode) || (document.compatMode && document.compatMode == "BackCompat"))) 
    789       { 
    790         i_contain = document.createElement('span'); 
    791       } 
    792       else 
    793       { 
    794         i_contain = document.createElement('div'); 
    795         i_contain.style.position = 'relative'; 
    796       } 
    797  
    798       i_contain.style.overflow = 'hidden'; 
    799       i_contain.style.width = "18px"; 
    800       i_contain.style.height = "18px"; 
    801  
    802       var img = document.createElement("img"); 
    803       if(typeof btn[1] == 'string') 
    804       { 
    805         img.src = btn[1]; 
    806         img.style.width = "18px"; 
    807         img.style.height = "18px"; 
    808       } 
    809       else 
    810       { 
    811         img.src = btn[1][0]; 
    812         img.style.position = 'relative'; 
    813         img.style.top  = btn[1][2] ? ('-' + (18 * (btn[1][2] + 1)) + 'px') : '-18px'; 
    814         img.style.left = btn[1][1] ? ('-' + (18 * (btn[1][1] + 1)) + 'px') : '-18px'; 
    815       } 
    816       i_contain.appendChild(img); 
     796      var i_contain = HTMLArea.makeBtnImg(btn[1]); 
     797      var img = i_contain.firstChild; 
    817798      el.appendChild(i_contain); 
    818799 
     
    877858  this._htmlArea.appendChild(toolbar); 
    878859}; 
     860 
     861use_clone_img = false; 
     862HTMLArea.makeBtnImg = function(imgDef, doc) 
     863{ 
     864  if(!doc) doc = document; 
     865 
     866  if(!doc._htmlareaImgCache) 
     867  { 
     868    doc._htmlareaImgCache = { }; 
     869  } 
     870 
     871  var i_contain = null; 
     872  if(HTMLArea.is_ie && ((!doc.compatMode) || (doc.compatMode && doc.compatMode == "BackCompat"))) 
     873  { 
     874    i_contain = doc.createElement('span'); 
     875  } 
     876  else 
     877  { 
     878    i_contain = doc.createElement('div'); 
     879    i_contain.style.position = 'relative'; 
     880  } 
     881 
     882  i_contain.style.overflow = 'hidden'; 
     883  i_contain.style.width = "18px"; 
     884  i_contain.style.height = "18px"; 
     885 
     886 
     887  var img = null; 
     888  if(typeof imgDef == 'string') 
     889  { 
     890    if(doc._htmlareaImgCache[imgDef]) 
     891    { 
     892      img = doc._htmlareaImgCache[imgDef].cloneNode(); 
     893    } 
     894    else 
     895    { 
     896      img = doc.createElement("img"); 
     897      img.src = imgDef; 
     898      img.style.width = "18px"; 
     899      img.style.height = "18px"; 
     900      if(use_clone_img) 
     901        doc._htmlareaImgCache[imgDef] = img.cloneNode(); 
     902    } 
     903  } 
     904  else 
     905  { 
     906    if(doc._htmlareaImgCache[imgDef[0]]) 
     907    { 
     908      img = doc._htmlareaImgCache[imgDef[0]].cloneNode(); 
     909    } 
     910    else 
     911    { 
     912      img = doc.createElement("img"); 
     913      img.src = imgDef[0]; 
     914      img.style.position = 'relative'; 
     915      if(use_clone_img) 
     916        doc._htmlareaImgCache[imgDef[0]] = img.cloneNode(); 
     917    } 
     918    img.style.top  = imgDef[2] ? ('-' + (18 * (imgDef[2] + 1)) + 'px') : '-18px'; 
     919    img.style.left = imgDef[1] ? ('-' + (18 * (imgDef[1] + 1)) + 'px') : '-18px'; 
     920  } 
     921  i_contain.appendChild(img); 
     922  return i_contain; 
     923} 
    879924 
    880925HTMLArea.prototype._createStatusBar = function() { 
     
    901946  var editor = this;    // we'll need "this" in some nested functions 
    902947 
     948  // If this is gecko, set up the paragraph handling now 
     949  if(HTMLArea.is_gecko) 
     950  { 
     951    switch(editor.config.mozParaHandler) 
     952    { 
     953      case 'best': 
     954      { 
     955        if(typeof EnterParagraphs == 'undefined') 
     956        { 
     957          EnterParagraphs = 'null'; 
     958          HTMLArea._loadback 
     959            (_editor_url + 'plugins/EnterParagraphs/enter-paragraphs.js', function() { editor.registerPlugin('EnterParagraphs'); editor.generate(); } ); 
     960          return false; 
     961        } 
     962      } 
     963      break; 
     964 
     965      case 'dirty'   : 
     966      case 'built-in': 
     967      default        : 
     968      { 
     969        // See _editorEvent 
     970      } 
     971      break; 
     972    } 
     973  } 
     974 
    903975  // get the textarea 
    904976  var textarea = this._textArea; 
     
    11991271  HTMLArea.prototype.removePanel = function(panel) 
    12001272  { 
    1201     panel.side.div.removeChild(panel); 
     1273    this._panels[panel.side].div.removeChild(panel); 
    12021274    var clean = [ ]; 
    1203     for(var i = 0; i < panel.side.panels.length; i++) 
    1204     { 
    1205       if(panel.side.panels[i] != panel) 
    1206       { 
    1207         clean.push(panel.side.panels[i]); 
    1208       } 
    1209     } 
    1210     panel.side.panels = clean; 
    1211     this.notifyOf('panel_change', {'action':'add','panel':panel}); 
     1275    for(var i = 0; i < this._panels[panel.side].panels.length; i++) 
     1276    { 
     1277      if(this._panels[panel.side].panels[i] != panel) 
     1278      { 
     1279        clean.push(this._panels[panel.side].panels[i]); 
     1280      } 
     1281    } 
     1282    this._panels[panel.side].panels = clean; 
     1283    this.notifyOf('panel_change', {'action':'remove','panel':panel}); 
    12121284  } 
    12131285 
    12141286  HTMLArea.prototype.hidePanel = function(panel) 
     1287  { 
     1288    if(panel) 
    12151289  { 
    12161290    panel.style.display = 'none'; 
    12171291    this.notifyOf('panel_change', {'action':'hide','panel':panel}); 
    12181292  } 
     1293  } 
    12191294 
    12201295  HTMLArea.prototype.showPanel = function(panel) 
     1296  { 
     1297    if(panel) 
    12211298  { 
    12221299    panel.style.display = ''; 
    12231300    this.notifyOf('panel_change', {'action':'show','panel':panel}); 
     1301    } 
    12241302  } 
    12251303 
     
    18481926    return false; 
    18491927  } 
    1850  
     1928} 
     1929 
     1930if(!Array.prototype.indexOf) 
     1931{ 
     1932  Array.prototype.indexOf = function(needle) 
     1933  { 
     1934    var haystack = this; 
     1935    for(var i = 0; i < haystack.length; i++) 
     1936    { 
     1937      if(needle == haystack[i]) return i; 
     1938    } 
     1939 
     1940    return null; 
     1941  } 
    18511942} 
    18521943 
     
    19482039      continue; 
    19492040    } 
    1950     switch (cmd) { 
     2041    switch (cmd) 
     2042    { 
    19512043        case "fontname": 
    19522044        case "fontsize": 
     2045      { 
    19532046      if (!text) try { 
    19542047        var value = ("" + doc.queryCommandValue(cmd)).toLowerCase(); 
     
    19572050          break; 
    19582051        } 
     2052 
    19592053        // HACK -- retrieve the config option for this 
    19602054        // combo box.  We rely on the fact that the 
     
    19632057        var options = this.config[cmd]; 
    19642058        var k = 0; 
    1965         for (var j in options) { 
     2059          for (var j in options) 
     2060          { 
    19662061          // FIXME: the following line is scary. 
    1967           if ((j.toLowerCase() == value) || 
    1968               (options[j].substr(0, value.length).toLowerCase() == value)) { 
     2062            if ((j.toLowerCase() == value) || (options[j].substr(0, value.length).toLowerCase() == value)) 
     2063            { 
    19692064            btn.element.selectedIndex = k; 
    19702065            throw "ok"; 
     
    19742069        btn.element.selectedIndex = 0; 
    19752070      } catch(e) {}; 
     2071      } 
     2072      break; 
    19762073 
    19772074      // It's better to search for the format block by tag name from the 
     
    19802077      //  to call your heading blocks 'header 1'.  Stupid MS. 
    19812078      case "formatblock"  : 
     2079      { 
    19822080        var blocks = [ ]; 
    19832081        for(var i in this.config['formatblock']) 
     
    20012099          btn.element.selectedIndex = 0; 
    20022100        } 
     2101      } 
    20032102        break; 
    20042103 
    2005       break; 
    20062104        case "textindicator": 
    20072105      if (!text) { 
     
    28042902    // this object will be passed to the newly opened window 
    28052903    HTMLArea._object = this; 
     2904    var win; 
    28062905    if (HTMLArea.is_ie) { 
    28072906      //if (confirm(HTMLArea.I18N.msg["IE-sucks-full-screen"])) 
    28082907      { 
    2809         window.open(this.popupURL("fullscreen.html"), "ha_fullscreen", 
     2908                                win = window.open(this.popupURL("fullscreen.html"), "ha_fullscreen", 
    28102909              "toolbar=no,location=no,directories=no,status=no,menubar=no," + 
    28112910              "scrollbars=no,resizable=yes,width=640,height=480"); 
    28122911      } 
    28132912    } else { 
    2814       window.open(this.popupURL("fullscreen.html"), "ha_fullscreen", 
     2913                            win = window.open(this.popupURL("fullscreen.html"), "ha_fullscreen", 
    28152914            "toolbar=no,menubar=no,personalbar=no,width=640,height=480," + 
    28162915            "scrollbars=no,resizable=yes"); 
    28172916    } 
     2917    win.focus() 
    28182918    break; 
    28192919      case "undo": 
     
    28782978  var keyEvent = (HTMLArea.is_ie && ev.type == "keydown") || (!HTMLArea.is_ie && ev.type == "keypress"); 
    28792979 
     2980  if(HTMLArea.is_gecko && keyEvent && ev.ctrlKey &&  this._unLink && this._unlinkOnUndo) 
     2981  { 
     2982    if(String.fromCharCode(ev.charCode).toLowerCase() == 'z') 
     2983    { 
     2984      HTMLArea._stopEvent(ev); 
     2985      this._unLink(); 
     2986      editor.updateToolbar(); 
     2987      return; 
     2988    } 
     2989  } 
     2990 
    28802991  if (keyEvent) 
    2881     for (var i in editor.plugins) { 
     2992  { 
     2993    for (var i in editor.plugins) 
     2994    { 
    28822995      var plugin = editor.plugins[i].instance; 
    28832996      if (typeof plugin.onKeyPress == "function") 
     
    28852998          return false; 
    28862999    } 
    2887   if (keyEvent && ev.ctrlKey && !ev.altKey) { 
     3000  } 
     3001 
     3002  if (keyEvent && ev.ctrlKey && !ev.altKey) 
     3003  { 
    28883004    var sel = null; 
    28893005    var range = null; 
     
    29403056    } 
    29413057  } 
    2942   else if (keyEvent) { 
     3058  else if (keyEvent) 
     3059  { 
     3060 
     3061    // IE's textRange and selection object is woefully inadequate, 
     3062    // which means this fancy stuff is gecko only sorry :-| 
     3063    // Die Bill, Die.  (IE supports it somewhat nativly though) 
     3064    if(HTMLArea.is_gecko) 
     3065    { 
     3066      var s = editor._getSelection() 
     3067      var autoWrap = function (textNode, tag) 
     3068      { 
     3069        var rightText = textNode.nextSibling; 
     3070        if(typeof tag == 'string') tag = editor._doc.createElement(tag); 
     3071        var a = textNode.parentNode.insertBefore(tag, rightText); 
     3072        textNode.parentNode.removeChild(textNode); 
     3073        a.appendChild(textNode); 
     3074        rightText.data = ' ' + rightText.data; 
     3075 
     3076        if(HTMLArea.is_ie) 
     3077        { 
     3078          var r = editor._createRange(s); 
     3079          s.moveToElementText(rightText); 
     3080          s.move('character', 1); 
     3081        } 
     3082        else 
     3083        { 
     3084          s.collapse(rightText, 1); 
     3085        } 
     3086        HTMLArea._stopEvent(ev); 
     3087 
     3088        editor._unLink = function() 
     3089        { 
     3090          var t = a.firstChild; 
     3091          a.removeChild(t); 
     3092          a.parentNode.insertBefore(t, a); 
     3093          a.parentNode.removeChild(a); 
     3094          editor._unLink = null; 
     3095          editor._unlinkOnUndo = false; 
     3096        } 
     3097        editor._unlinkOnUndo = true; 
     3098 
     3099        return a; 
     3100      } 
     3101 
     3102      switch(ev.which) 
     3103      { 
     3104        // Space, see if the text just typed looks like a URL, or email address 
     3105        // and link it appropriatly 
     3106        case 32: 
     3107        { 
     3108          if(s && s.isCollapsed && s.anchorNode.nodeType == 3 && s.anchorNode.data.length > 3 && s.anchorNode.data.indexOf('.') >= 0) 
     3109          { 
     3110            var midStart = s.anchorNode.data.substring(0,s.anchorOffset).search(/\S{4,}$/); 
     3111            if(midStart == -1) break; 
     3112 
     3113            if(this._getFirstAncestor(s, 'a')) 
     3114            { 
     3115              break; // already in an anchor 
     3116            } 
     3117 
     3118            var matchData = s.anchorNode.data.substring(0,s.anchorOffset).replace(/^.*?(\S*)$/, '$1'); 
     3119 
     3120            var m        = matchData.match(HTMLArea.RE_email); 
     3121            if(m) 
     3122            { 
     3123              var leftText  = s.anchorNode; 
     3124              var rightText = leftText.splitText(s.anchorOffset); 
     3125              var midText   = leftText.splitText(midStart); 
     3126 
     3127              autoWrap(midText, 'a').href = 'mailto:' + m[0]; 
     3128              break; 
     3129            } 
     3130 
     3131            var m = matchData.match(HTMLArea.RE_url); 
     3132            if(m) 
     3133            { 
     3134              var leftText  = s.anchorNode; 
     3135              var rightText = leftText.splitText(s.anchorOffset); 
     3136              var midText   = leftText.splitText(midStart); 
     3137              autoWrap(midText, 'a').href = (m[1] ? m[1] : 'http://') + m[2]; 
     3138              break; 
     3139            } 
     3140          } 
     3141 
     3142        } 
     3143        break; 
     3144 
     3145        default : 
     3146        { 
     3147          if(ev.keyCode == 27 || (this._unlinkOnUndo && ev.ctrlKey && ev.which == 122) ) 
     3148          { 
     3149            if(this._unLink) 
     3150            { 
     3151              this._unLink(); 
     3152              HTMLArea._stopEvent(ev); 
     3153            } 
     3154            break; 
     3155          } 
     3156          else if(ev.which || ev.keyCode == 8 || ev.keyCode == 46) 
     3157          { 
     3158            this._unlinkOnUndo = false; 
     3159 
     3160            if(s.anchorNode && s.anchorNode.nodeType == 3) 
     3161            { 
     3162              // See if we might be changing a link 
     3163              var a = this._getFirstAncestor(s, 'a'); 
     3164              if(!a) break; // not an anchor 
     3165              if(!a._updateAnchTimeout) 
     3166              { 
     3167                if(   s.anchorNode.data.match(HTMLArea.RE_email) 
     3168                   && (a.href.match('mailto:' + s.anchorNode.data.trim())) 
     3169                  ) 
     3170                { 
     3171                  var textNode = s.anchorNode; 
     3172                  var fn = function() 
     3173                    { 
     3174                      a.href = 'mailto:' + textNode.data.trim(); 
     3175                      a._updateAnchTimeout = setTimeout(fn, 250); 
     3176                    } 
     3177                  a._updateAnchTimeout = setTimeout(fn, 250); 
     3178                  break; 
     3179                } 
     3180 
     3181                var m = s.anchorNode.data.match(HTMLArea.RE_url); 
     3182                if(m &&  a.href.match(s.anchorNode.data.trim()) ) 
     3183                { 
     3184                  var textNode = s.anchorNode; 
     3185                  var fn = function() 
     3186                    { 
     3187                      var m = textNode.data.match(HTMLArea.RE_url); 
     3188                      a.href = (m[1] ? m[1] : 'http://') + m[2]; 
     3189                      a._updateAnchTimeout = setTimeout(fn, 250); 
     3190                    } 
     3191                  a._updateAnchTimeout = setTimeout(fn, 250); 
     3192                } 
     3193              } 
     3194            } 
     3195 
     3196          } 
     3197        } 
     3198        break; 
     3199      } 
     3200    } 
     3201 
    29433202    // other keys here 
    2944     switch (ev.keyCode) { 
     3203    switch (ev.keyCode) 
     3204    { 
    29453205        case 13: // KEY enter 
    2946       if (HTMLArea.is_gecko && !ev.shiftKey) { 
     3206      if (HTMLArea.is_gecko && !ev.shiftKey && this.config.mozParaHandler == 'dirty' ) 
     3207      { 
    29473208        this.dom_checkInsertP(); 
    29483209        HTMLArea._stopEvent(ev); 
     
    38944155      popup += ".html"; 
    38954156    url = _editor_url + "plugins/" + plugin + "/popups/" + popup; 
    3896   } else 
     4157  } else if(file.match(/^\/.*?/)) 
     4158            url = file; 
     4159        else 
    38974160    url = _editor_url + this.config.popupURL + file; 
    38984161  return url; 
     
    40304293      if(req.status == 200) 
    40314294      { 
     4295        if(typeof handler == 'function') 
    40324296        handler(req.responseText, req); 
    40334297      } 
Note: See TracChangeset for help on using the changeset viewer.