[1] | 1 | /** |
---|
| 2 | * $Id: editor_plugin_src.js 264 2007-04-26 20:53:09Z spocke $ |
---|
| 3 | * |
---|
| 4 | * @author Moxiecode |
---|
| 5 | * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. |
---|
| 6 | */ |
---|
| 7 | |
---|
| 8 | (function() { |
---|
| 9 | var Event = tinymce.dom.Event, grep = tinymce.grep, each = tinymce.each, inArray = tinymce.inArray, isOldWebKit = tinymce.isOldWebKit; |
---|
| 10 | |
---|
| 11 | tinymce.create('tinymce.plugins.Safari', { |
---|
| 12 | init : function(ed) { |
---|
| 13 | var t = this, dom; |
---|
| 14 | |
---|
| 15 | // Ignore on non webkit |
---|
| 16 | if (!tinymce.isWebKit) |
---|
| 17 | return; |
---|
| 18 | |
---|
| 19 | t.editor = ed; |
---|
| 20 | t.webKitFontSizes = ['x-small', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large']; |
---|
| 21 | t.namedFontSizes = ['xx-small', 'x-small','small','medium','large','x-large', 'xx-large']; |
---|
| 22 | |
---|
| 23 | // Safari will crash if the build in createlink command is used |
---|
| 24 | /* ed.addCommand('CreateLink', function(u, v) { |
---|
| 25 | ed.execCommand("mceInsertContent", false, '<a href="' + dom.encode(v) + '">' + ed.selection.getContent() + '</a>'); |
---|
| 26 | });*/ |
---|
| 27 | |
---|
| 28 | // Workaround for FormatBlock bug, http://bugs.webkit.org/show_bug.cgi?id=16004 |
---|
| 29 | ed.addCommand('FormatBlock', function(u, v) { |
---|
| 30 | var dom = ed.dom, e = dom.getParent(ed.selection.getNode(), dom.isBlock); |
---|
| 31 | |
---|
| 32 | if (e) |
---|
| 33 | dom.replace(dom.create(v), e, 1); |
---|
| 34 | else |
---|
| 35 | ed.getDoc().execCommand("FormatBlock", false, v); |
---|
| 36 | }); |
---|
| 37 | |
---|
| 38 | // Workaround for InsertHTML bug, http://bugs.webkit.org/show_bug.cgi?id=16382 |
---|
| 39 | ed.addCommand('mceInsertContent', function(u, v) { |
---|
| 40 | ed.getDoc().execCommand("InsertText", false, 'mce_marker'); |
---|
| 41 | ed.getBody().innerHTML = ed.getBody().innerHTML.replace(/mce_marker/g, v + '<span id="_mce_tmp">XX</span>'); |
---|
| 42 | ed.selection.select(ed.dom.get('_mce_tmp')); |
---|
| 43 | ed.getDoc().execCommand("Delete", false, ' '); |
---|
| 44 | }); |
---|
| 45 | |
---|
| 46 | // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973 |
---|
| 47 | ed.onKeyPress.add(function(ed, e) { |
---|
| 48 | if (e.keyCode == 13 && e.shiftKey) { |
---|
| 49 | t._insertBR(ed); |
---|
| 50 | Event.cancel(e); |
---|
| 51 | } |
---|
| 52 | }); |
---|
| 53 | |
---|
| 54 | // Safari returns incorrect values |
---|
| 55 | ed.addQueryValueHandler('FontSize', function(u, v) { |
---|
| 56 | var e, v; |
---|
| 57 | |
---|
| 58 | // Check for the real font size at the start of selection |
---|
| 59 | if ((e = ed.dom.getParent(ed.selection.getStart(), 'span')) && (v = e.style.fontSize)) |
---|
| 60 | return tinymce.inArray(t.namedFontSizes, v) + 1; |
---|
| 61 | |
---|
| 62 | // Check for the real font size at the end of selection |
---|
| 63 | if ((e = ed.dom.getParent(ed.selection.getEnd(), 'span')) && (v = e.style.fontSize)) |
---|
| 64 | return tinymce.inArray(t.namedFontSizes, v) + 1; |
---|
| 65 | |
---|
| 66 | // Return default value it's better than nothing right! |
---|
| 67 | return ed.getDoc().queryCommandValue('FontSize'); |
---|
| 68 | }); |
---|
| 69 | |
---|
| 70 | // Safari returns incorrect values |
---|
| 71 | ed.addQueryValueHandler('FontName', function(u, v) { |
---|
| 72 | var e, v; |
---|
| 73 | |
---|
| 74 | // Check for the real font name at the start of selection |
---|
| 75 | if ((e = ed.dom.getParent(ed.selection.getStart(), 'span')) && (v = e.style.fontFamily)) |
---|
| 76 | return v.replace(/, /g, ','); |
---|
| 77 | |
---|
| 78 | // Check for the real font name at the end of selection |
---|
| 79 | if ((e = ed.dom.getParent(ed.selection.getEnd(), 'span')) && (v = e.style.fontFamily)) |
---|
| 80 | return v.replace(/, /g, ','); |
---|
| 81 | |
---|
| 82 | // Return default value it's better than nothing right! |
---|
| 83 | return ed.getDoc().queryCommandValue('FontName'); |
---|
| 84 | }); |
---|
| 85 | |
---|
| 86 | // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 |
---|
| 87 | ed.onClick.add(function(ed, e) { |
---|
| 88 | e = e.target; |
---|
| 89 | |
---|
| 90 | if (e.nodeName == 'IMG') { |
---|
| 91 | t.selElm = e; |
---|
| 92 | ed.selection.select(e); |
---|
| 93 | } else |
---|
| 94 | t.selElm = null; |
---|
| 95 | }); |
---|
| 96 | |
---|
| 97 | ed.onBeforeExecCommand.add(function(ed, c, b) { |
---|
| 98 | var r = t.bookmarkRng; |
---|
| 99 | |
---|
| 100 | // Restore selection |
---|
| 101 | if (r) { |
---|
| 102 | ed.selection.setRng(r); |
---|
| 103 | t.bookmarkRng = null; |
---|
| 104 | //console.debug('restore', r.startContainer, r.startOffset, r.endContainer, r.endOffset); |
---|
| 105 | } |
---|
| 106 | }); |
---|
| 107 | |
---|
| 108 | ed.onInit.add(function() { |
---|
| 109 | t._fixWebKitSpans(); |
---|
| 110 | |
---|
| 111 | ed.windowManager.onOpen.add(function() { |
---|
| 112 | var r = ed.selection.getRng(); |
---|
| 113 | |
---|
| 114 | // Store selection if valid |
---|
| 115 | if (r.startContainer != ed.getDoc()) { |
---|
| 116 | t.bookmarkRng = r.cloneRange(); |
---|
| 117 | //console.debug('store', r.startContainer, r.startOffset, r.endContainer, r.endOffset); |
---|
| 118 | } |
---|
| 119 | }); |
---|
| 120 | |
---|
| 121 | ed.windowManager.onClose.add(function() { |
---|
| 122 | t.bookmarkRng = null; |
---|
| 123 | }); |
---|
| 124 | |
---|
| 125 | if (isOldWebKit) |
---|
| 126 | t._patchSafari2x(ed); |
---|
| 127 | }); |
---|
| 128 | |
---|
| 129 | ed.onSetContent.add(function() { |
---|
| 130 | dom = ed.dom; |
---|
| 131 | |
---|
| 132 | // Convert strong,b,em,u,strike to spans |
---|
| 133 | each(['strong','b','em','u','strike','sub','sup','a'], function(v) { |
---|
| 134 | each(grep(dom.select(v)).reverse(), function(n) { |
---|
| 135 | var nn = n.nodeName.toLowerCase(), st; |
---|
| 136 | |
---|
| 137 | // Convert anchors into images |
---|
| 138 | if (nn == 'a') { |
---|
| 139 | if (n.name) |
---|
| 140 | dom.replace(dom.create('img', {mce_name : 'a', name : n.name, 'class' : 'mceItemAnchor'}), n); |
---|
| 141 | |
---|
| 142 | return; |
---|
| 143 | } |
---|
| 144 | |
---|
| 145 | switch (nn) { |
---|
| 146 | case 'b': |
---|
| 147 | case 'strong': |
---|
| 148 | if (nn == 'b') |
---|
| 149 | nn = 'strong'; |
---|
| 150 | |
---|
| 151 | st = 'font-weight: bold;'; |
---|
| 152 | break; |
---|
| 153 | |
---|
| 154 | case 'em': |
---|
| 155 | st = 'font-style: italic;'; |
---|
| 156 | break; |
---|
| 157 | |
---|
| 158 | case 'u': |
---|
| 159 | st = 'text-decoration: underline;'; |
---|
| 160 | break; |
---|
| 161 | |
---|
| 162 | case 'sub': |
---|
| 163 | st = 'vertical-align: sub;'; |
---|
| 164 | break; |
---|
| 165 | |
---|
| 166 | case 'sup': |
---|
| 167 | st = 'vertical-align: super;'; |
---|
| 168 | break; |
---|
| 169 | |
---|
| 170 | case 'strike': |
---|
| 171 | st = 'text-decoration: line-through;'; |
---|
| 172 | break; |
---|
| 173 | } |
---|
| 174 | |
---|
| 175 | dom.replace(dom.create('span', {mce_name : nn, style : st, 'class' : 'Apple-style-span'}), n, 1); |
---|
| 176 | }); |
---|
| 177 | }); |
---|
| 178 | }); |
---|
| 179 | |
---|
| 180 | ed.onPreProcess.add(function(ed, o) { |
---|
| 181 | dom = ed.dom; |
---|
| 182 | |
---|
| 183 | each(grep(o.node.getElementsByTagName('span')).reverse(), function(n) { |
---|
| 184 | var v, bg; |
---|
| 185 | |
---|
| 186 | if (o.get) { |
---|
| 187 | if (dom.hasClass(n, 'Apple-style-span')) { |
---|
| 188 | bg = n.style.backgroundColor; |
---|
| 189 | |
---|
| 190 | switch (dom.getAttrib(n, 'mce_name')) { |
---|
| 191 | case 'font': |
---|
| 192 | if (!ed.settings.convert_fonts_to_spans) |
---|
| 193 | dom.setAttrib(n, 'style', ''); |
---|
| 194 | break; |
---|
| 195 | |
---|
| 196 | case 'strong': |
---|
| 197 | case 'em': |
---|
| 198 | case 'sub': |
---|
| 199 | case 'sup': |
---|
| 200 | dom.setAttrib(n, 'style', ''); |
---|
| 201 | break; |
---|
| 202 | |
---|
| 203 | case 'strike': |
---|
| 204 | case 'u': |
---|
| 205 | if (!ed.settings.inline_styles) |
---|
| 206 | dom.setAttrib(n, 'style', ''); |
---|
| 207 | else |
---|
| 208 | dom.setAttrib(n, 'mce_name', ''); |
---|
| 209 | |
---|
| 210 | break; |
---|
| 211 | |
---|
| 212 | default: |
---|
| 213 | if (!ed.settings.inline_styles) |
---|
| 214 | dom.setAttrib(n, 'style', ''); |
---|
| 215 | } |
---|
| 216 | |
---|
| 217 | |
---|
| 218 | if (bg) |
---|
| 219 | n.style.backgroundColor = bg; |
---|
| 220 | } |
---|
| 221 | } |
---|
| 222 | |
---|
| 223 | if (dom.hasClass(n, 'mceItemRemoved')) |
---|
| 224 | dom.remove(n, 1); |
---|
| 225 | }); |
---|
| 226 | }); |
---|
| 227 | |
---|
| 228 | ed.onPostProcess.add(function(ed, o) { |
---|
| 229 | // Safari adds BR at end of all block elements |
---|
| 230 | o.content = o.content.replace(/<br \/><\/(h[1-6]|div|p|address|pre)>/g, '</$1>'); |
---|
| 231 | |
---|
| 232 | // Safari adds id="undefined" to HR elements |
---|
| 233 | o.content = o.content.replace(/ id=\"undefined\"/g, ''); |
---|
| 234 | }); |
---|
| 235 | }, |
---|
| 236 | |
---|
| 237 | _fixWebKitSpans : function() { |
---|
| 238 | var t = this, ed = t.editor; |
---|
| 239 | |
---|
| 240 | if (!isOldWebKit) { |
---|
| 241 | // Use mutator events on new WebKit |
---|
| 242 | Event.add(ed.getDoc(), 'DOMNodeInserted', function(e) { |
---|
| 243 | e = e.target; |
---|
| 244 | |
---|
| 245 | if (e && e.nodeType == 1) |
---|
| 246 | t._fixAppleSpan(e); |
---|
| 247 | }); |
---|
| 248 | } else { |
---|
| 249 | // Do post command processing in old WebKit since the browser crashes on Mutator events :( |
---|
| 250 | ed.onExecCommand.add(function() { |
---|
| 251 | each(ed.dom.select('span'), function(n) { |
---|
| 252 | t._fixAppleSpan(n); |
---|
| 253 | }); |
---|
| 254 | |
---|
| 255 | ed.nodeChanged(); |
---|
| 256 | }); |
---|
| 257 | } |
---|
| 258 | }, |
---|
| 259 | |
---|
| 260 | _fixAppleSpan : function(e) { |
---|
| 261 | var ed = this.editor, dom = ed.dom, fz = this.webKitFontSizes, fzn = this.namedFontSizes, s = ed.settings, st, p; |
---|
| 262 | |
---|
| 263 | if (dom.getAttrib(e, 'mce_fixed')) |
---|
| 264 | return; |
---|
| 265 | |
---|
| 266 | // Handle Apple style spans |
---|
| 267 | if (e.nodeName == 'SPAN' && e.className == 'Apple-style-span') { |
---|
| 268 | st = e.style; |
---|
| 269 | |
---|
| 270 | if (!s.convert_fonts_to_spans) { |
---|
| 271 | if (st.fontSize) { |
---|
| 272 | dom.setAttrib(e, 'mce_name', 'font'); |
---|
| 273 | dom.setAttrib(e, 'size', inArray(fz, st.fontSize) + 1); |
---|
| 274 | } |
---|
| 275 | |
---|
| 276 | if (st.fontFamily) { |
---|
| 277 | dom.setAttrib(e, 'mce_name', 'font'); |
---|
| 278 | dom.setAttrib(e, 'face', st.fontFamily); |
---|
| 279 | } |
---|
| 280 | |
---|
| 281 | if (st.color) { |
---|
| 282 | dom.setAttrib(e, 'mce_name', 'font'); |
---|
| 283 | dom.setAttrib(e, 'color', dom.toHex(st.color)); |
---|
| 284 | } |
---|
| 285 | |
---|
| 286 | if (st.backgroundColor) { |
---|
| 287 | dom.setAttrib(e, 'mce_name', 'font'); |
---|
| 288 | dom.setStyle(e, 'background-color', st.backgroundColor); |
---|
| 289 | } |
---|
| 290 | } else { |
---|
| 291 | if (st.fontSize) |
---|
| 292 | dom.setStyle(e, 'fontSize', fzn[inArray(fz, st.fontSize)]); |
---|
| 293 | } |
---|
| 294 | |
---|
| 295 | if (st.fontWeight == 'bold') |
---|
| 296 | dom.setAttrib(e, 'mce_name', 'strong'); |
---|
| 297 | |
---|
| 298 | if (st.fontStyle == 'italic') |
---|
| 299 | dom.setAttrib(e, 'mce_name', 'em'); |
---|
| 300 | |
---|
| 301 | if (st.textDecoration == 'underline') |
---|
| 302 | dom.setAttrib(e, 'mce_name', 'u'); |
---|
| 303 | |
---|
| 304 | if (st.textDecoration == 'line-through') |
---|
| 305 | dom.setAttrib(e, 'mce_name', 'strike'); |
---|
| 306 | |
---|
| 307 | if (st.verticalAlign == 'super') |
---|
| 308 | dom.setAttrib(e, 'mce_name', 'sup'); |
---|
| 309 | |
---|
| 310 | if (st.verticalAlign == 'sub') |
---|
| 311 | dom.setAttrib(e, 'mce_name', 'sub'); |
---|
| 312 | |
---|
| 313 | dom.setAttrib(e, 'mce_fixed', '1'); |
---|
| 314 | } |
---|
| 315 | }, |
---|
| 316 | |
---|
| 317 | _patchSafari2x : function(ed) { |
---|
| 318 | var t = this, setContent, getNode, dom = ed.dom, lr; |
---|
| 319 | |
---|
| 320 | // Inline dialogs |
---|
| 321 | if (ed.windowManager.onBeforeOpen) { |
---|
| 322 | ed.windowManager.onBeforeOpen.add(function() { |
---|
| 323 | r = ed.selection.getRng(); |
---|
| 324 | }); |
---|
| 325 | } |
---|
| 326 | |
---|
| 327 | // Fake select on 2.x |
---|
| 328 | ed.selection.select = function(n) { |
---|
| 329 | this.getSel().setBaseAndExtent(n, 0, n, 1); |
---|
| 330 | }; |
---|
| 331 | |
---|
| 332 | getNode = ed.selection.getNode; |
---|
| 333 | ed.selection.getNode = function() { |
---|
| 334 | return t.selElm || getNode.call(this); |
---|
| 335 | }; |
---|
| 336 | |
---|
| 337 | // Fake range on Safari 2.x |
---|
| 338 | ed.selection.getRng = function() { |
---|
| 339 | var t = this, s = t.getSel(), d = ed.getDoc(), r, rb, ra, di; |
---|
| 340 | |
---|
| 341 | // Fake range on Safari 2.x |
---|
| 342 | if (s.anchorNode) { |
---|
| 343 | r = d.createRange(); |
---|
| 344 | |
---|
| 345 | try { |
---|
| 346 | // Setup before range |
---|
| 347 | rb = d.createRange(); |
---|
| 348 | rb.setStart(s.anchorNode, s.anchorOffset); |
---|
| 349 | rb.collapse(1); |
---|
| 350 | |
---|
| 351 | // Setup after range |
---|
| 352 | ra = d.createRange(); |
---|
| 353 | ra.setStart(s.focusNode, s.focusOffset); |
---|
| 354 | ra.collapse(1); |
---|
| 355 | |
---|
| 356 | // Setup start/end points by comparing locations |
---|
| 357 | di = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0; |
---|
| 358 | r.setStart(di ? s.anchorNode : s.focusNode, di ? s.anchorOffset : s.focusOffset); |
---|
| 359 | r.setEnd(di ? s.focusNode : s.anchorNode, di ? s.focusOffset : s.anchorOffset); |
---|
| 360 | |
---|
| 361 | lr = r; |
---|
| 362 | } catch (ex) { |
---|
| 363 | // Sometimes fails, at least we tried to do it by the book. I hope Safari 2.x will go disappear soooon!!! |
---|
| 364 | } |
---|
| 365 | } |
---|
| 366 | |
---|
| 367 | return r || lr; |
---|
| 368 | }; |
---|
| 369 | |
---|
| 370 | // Fix setContent so it works |
---|
| 371 | setContent = ed.selection.setContent; |
---|
| 372 | ed.selection.setContent = function(h, s) { |
---|
| 373 | var r = this.getRng(), b; |
---|
| 374 | |
---|
| 375 | try { |
---|
| 376 | setContent.call(this, h, s); |
---|
| 377 | } catch (ex) { |
---|
| 378 | // Workaround for Safari 2.x |
---|
| 379 | b = dom.create('body'); |
---|
| 380 | b.innerHTML = h; |
---|
| 381 | |
---|
| 382 | each(b.childNodes, function(n) { |
---|
| 383 | r.insertNode(n.cloneNode(true)); |
---|
| 384 | }); |
---|
| 385 | } |
---|
| 386 | }; |
---|
| 387 | }, |
---|
| 388 | |
---|
| 389 | _insertBR : function(ed) { |
---|
| 390 | var dom = ed.dom, s = ed.selection, r = s.getRng(), br; |
---|
| 391 | |
---|
| 392 | // Insert BR element |
---|
| 393 | r.insertNode(br = dom.create('br')); |
---|
| 394 | |
---|
| 395 | // Place caret after BR |
---|
| 396 | r.setStartAfter(br); |
---|
| 397 | r.setEndAfter(br); |
---|
| 398 | s.setRng(r); |
---|
| 399 | |
---|
| 400 | // Could not place caret after BR then insert an nbsp entity and move the caret |
---|
| 401 | if (s.getSel().focusNode == br.previousSibling) { |
---|
| 402 | s.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br)); |
---|
| 403 | s.collapse(1); |
---|
| 404 | } |
---|
| 405 | |
---|
| 406 | // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117 |
---|
| 407 | ed.getWin().scrollTo(0, dom.getPos(s.getRng().startContainer).y); |
---|
| 408 | } |
---|
| 409 | }); |
---|
| 410 | |
---|
| 411 | // Register plugin |
---|
| 412 | tinymce.PluginManager.add('safari', tinymce.plugins.Safari); |
---|
| 413 | })(); |
---|
| 414 | |
---|