| [ Index ] |
PHP Cross Reference of Joomla 1.5.26 DE |
[Summary view] [Print] [Text view]
1 /** 2 * $Id: editor_plugin_src.js 1199 2009-08-18 11:55:59Z spocke $ 3 * 4 * @author Moxiecode 5 * @copyright Copyright © 2004-2008, Moxiecode Systems AB, All rights reserved. 6 */ 7 8 (function() { 9 var each = tinymce.each; 10 11 tinymce.create('tinymce.plugins.PastePlugin', { 12 init : function(ed, url) { 13 var t = this, cb; 14 15 t.editor = ed; 16 t.url = url; 17 18 // Setup plugin events 19 t.onPreProcess = new tinymce.util.Dispatcher(t); 20 t.onPostProcess = new tinymce.util.Dispatcher(t); 21 22 // Register default handlers 23 t.onPreProcess.add(t._preProcess); 24 t.onPostProcess.add(t._postProcess); 25 26 // Register optional preprocess handler 27 t.onPreProcess.add(function(pl, o) { 28 ed.execCallback('paste_preprocess', pl, o); 29 }); 30 31 // Register optional postprocess 32 t.onPostProcess.add(function(pl, o) { 33 ed.execCallback('paste_postprocess', pl, o); 34 }); 35 36 // This function executes the process handlers and inserts the contents 37 function process(o) { 38 var dom = ed.dom; 39 40 // Execute pre process handlers 41 t.onPreProcess.dispatch(t, o); 42 43 // Create DOM structure 44 o.node = dom.create('div', 0, o.content); 45 46 // Execute post process handlers 47 t.onPostProcess.dispatch(t, o); 48 49 // Serialize content 50 o.content = ed.serializer.serialize(o.node, {getInner : 1}); 51 52 // Insert cleaned content. We need to handle insertion of contents containing block elements separately 53 if (/<(p|h[1-6]|ul|ol)/.test(o.content)) 54 t._insertBlockContent(ed, dom, o.content); 55 else 56 t._insert(o.content); 57 }; 58 59 // Add command for external usage 60 ed.addCommand('mceInsertClipboardContent', function(u, o) { 61 process(o); 62 }); 63 64 // This function grabs the contents from the clipboard by adding a 65 // hidden div and placing the caret inside it and after the browser paste 66 // is done it grabs that contents and processes that 67 function grabContent(e) { 68 var n, or, rng, sel = ed.selection, dom = ed.dom, body = ed.getBody(), posY; 69 70 if (dom.get('_mcePaste')) 71 return; 72 73 // Create container to paste into 74 n = dom.add(body, 'div', {id : '_mcePaste'}, ' '); 75 76 // If contentEditable mode we need to find out the position of the closest element 77 if (body != ed.getDoc().body) 78 posY = dom.getPos(ed.selection.getStart(), body).y; 79 else 80 posY = body.scrollTop; 81 82 // Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles 83 dom.setStyles(n, { 84 position : 'absolute', 85 left : -10000, 86 top : posY, 87 width : 1, 88 height : 1, 89 overflow : 'hidden' 90 }); 91 92 if (tinymce.isIE) { 93 // Select the container 94 rng = dom.doc.body.createTextRange(); 95 rng.moveToElementText(n); 96 rng.execCommand('Paste'); 97 98 // Remove container 99 dom.remove(n); 100 101 // Check if the contents was changed, if it wasn't then clipboard extraction failed probably due 102 // to IE security settings so we pass the junk though better than nothing right 103 if (n.innerHTML === ' ') 104 return; 105 106 // Process contents 107 process({content : n.innerHTML}); 108 109 // Block the real paste event 110 return tinymce.dom.Event.cancel(e); 111 } else { 112 or = ed.selection.getRng(); 113 114 // Move caret into hidden div 115 n = n.firstChild; 116 rng = ed.getDoc().createRange(); 117 rng.setStart(n, 0); 118 rng.setEnd(n, 1); 119 sel.setRng(rng); 120 121 // Wait a while and grab the pasted contents 122 window.setTimeout(function() { 123 var h = ''; 124 125 // WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string 126 each(dom.select('div[id=_mcePaste]').reverse(), function(n) { 127 h += (dom.select('> span.Apple-style-span div', n)[0] || dom.select('> span.Apple-style-span', n)[0] || n).innerHTML; 128 dom.remove(n); 129 }); 130 131 // Restore the old selection 132 if (or) 133 sel.setRng(or); 134 135 process({content : h}); 136 }, 0); 137 } 138 }; 139 140 // Check if we should use the new auto process method 141 if (ed.getParam('paste_auto_cleanup_on_paste', true)) { 142 // Is it's Opera or older FF use key handler 143 if (tinymce.isOpera || /Firefox\/2/.test(navigator.userAgent)) { 144 ed.onKeyDown.add(function(ed, e) { 145 if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45)) 146 grabContent(e); 147 }); 148 } else { 149 // Grab contents on paste event on Gecko and WebKit 150 ed.onPaste.addToTop(function(ed, e) { 151 return grabContent(e); 152 }); 153 } 154 } 155 156 // Block all drag/drop events 157 if (ed.getParam('paste_block_drop')) { 158 ed.onInit.add(function() { 159 ed.dom.bind(ed.getBody(), ['dragend', 'dragover', 'draggesture', 'dragdrop', 'drop', 'drag'], function(e) { 160 e.preventDefault(); 161 e.stopPropagation(); 162 163 return false; 164 }); 165 }); 166 } 167 168 // Add legacy support 169 t._legacySupport(); 170 }, 171 172 getInfo : function() { 173 return { 174 longname : 'Paste text/word', 175 author : 'Moxiecode Systems AB', 176 authorurl : 'http://tinymce.moxiecode.com', 177 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste', 178 version : tinymce.majorVersion + "." + tinymce.minorVersion 179 }; 180 }, 181 182 _preProcess : function(pl, o) { 183 var ed = this.editor, h = o.content, process, stripClass; 184 185 //console.log('Before preprocess:' + o.content); 186 187 function process(items) { 188 each(items, function(v) { 189 // Remove or replace 190 if (v.constructor == RegExp) 191 h = h.replace(v, ''); 192 else 193 h = h.replace(v[0], v[1]); 194 }); 195 }; 196 197 // Detect Word content and process it more aggressive 198 if (/(class=\"?Mso|style=\"[^\"]*\bmso\-|w:WordDocument)/.test(h) || o.wordContent) { 199 o.wordContent = true; // Mark the pasted contents as word specific content 200 //console.log('Word contents detected.'); 201 202 // Process away some basic content 203 process([ 204 /^\s*( )+/g, // nbsp entities at the start of contents 205 /( |<br[^>]*>)+\s*$/g // nbsp entities at the end of contents 206 ]); 207 208 if (ed.getParam('paste_convert_middot_lists', true)) { 209 process([ 210 [/<!--\[if !supportLists\]-->/gi, '$&__MCE_ITEM__'], // Convert supportLists to a list item marker 211 [/(<span[^>]+:\s*symbol[^>]+>)/gi, '$1__MCE_ITEM__'], // Convert symbol spans to list items 212 [/(<span[^>]+mso-list:[^>]+>)/gi, '$1__MCE_ITEM__'] // Convert mso-list to item marker 213 ]); 214 } 215 216 process([ 217 /<!--[\s\S]+?-->/gi, // Word comments 218 /<\/?(img|font|meta|link|style|div|v:\w+)[^>]*>/gi, // Remove some tags including VML content 219 /<\\?\?xml[^>]*>/gi, // XML namespace declarations 220 /<\/?o:[^>]*>/gi, // MS namespaced elements <o:tag> 221 / (id|name|language|type|on\w+|v:\w+)=\"([^\"]*)\"/gi, // on.., class, style and language attributes with quotes 222 / (id|name|language|type|on\w+|v:\w+)=(\w+)/gi, // on.., class, style and language attributes without quotes (IE) 223 [/<(\/?)s>/gi, '<$1strike>'], // Convert <s> into <strike> for line-though 224 /<script[^>]+>[\s\S]*?<\/script>/gi, // All scripts elements for msoShowComment for example 225 [/ /g, '\u00a0'] // Replace nsbp entites to char since it's easier to handle 226 ]); 227 228 // Remove all spans if no styles is to be retained 229 if (!ed.getParam('paste_retain_style_properties')) { 230 process([ 231 /<\/?(span)[^>]*>/gi 232 ]); 233 } 234 } 235 236 // Allow for class names to be retained if desired; either all, or just the ones from Word 237 // Note that the paste_strip_class_attributes: 'none, verify_css_classes: true is also a good variation. 238 stripClass = ed.getParam('paste_strip_class_attributes', 'all'); 239 if (stripClass != 'none') { 240 if (stripClass == 'all') { 241 process([ 242 / class=\"([^\"]*)\"/gi, // class attributes with quotes 243 / class=(\w+)/gi // class attributes without quotes (IE) 244 ]); 245 } else { // Only strip the 'mso*' classes 246 process([ 247 / class=\"(mso[^\"]*)\"/gi, // class attributes with quotes 248 / class=(mso\w+)/gi // class attributes without quotes (IE) 249 ]); 250 } 251 } 252 253 // Remove spans option 254 if (ed.getParam('paste_remove_spans')) { 255 process([ 256 /<\/?(span)[^>]*>/gi 257 ]); 258 } 259 260 //console.log('After preprocess:' + h); 261 262 o.content = h; 263 }, 264 265 /** 266 * Various post process items. 267 */ 268 _postProcess : function(pl, o) { 269 var t = this, ed = t.editor, dom = ed.dom, styleProps; 270 271 if (o.wordContent) { 272 // Remove named anchors or TOC links 273 each(dom.select('a', o.node), function(a) { 274 if (!a.href || a.href.indexOf('#_Toc') != -1) 275 dom.remove(a, 1); 276 }); 277 278 if (t.editor.getParam('paste_convert_middot_lists', true)) 279 t._convertLists(pl, o); 280 281 // Process styles 282 styleProps = ed.getParam('paste_retain_style_properties'); // retained properties 283 284 // If string property then split it 285 if (tinymce.is(styleProps, 'string')) 286 styleProps = tinymce.explode(styleProps); 287 288 // Retains some style properties 289 each(dom.select('*', o.node), function(el) { 290 var newStyle = {}, npc = 0, i, sp, sv; 291 292 // Store a subset of the existing styles 293 if (styleProps) { 294 for (i = 0; i < styleProps.length; i++) { 295 sp = styleProps[i]; 296 sv = dom.getStyle(el, sp); 297 298 if (sv) { 299 newStyle[sp] = sv; 300 npc++; 301 } 302 } 303 } 304 305 // Remove all of the existing styles 306 dom.setAttrib(el, 'style', ''); 307 308 if (styleProps && npc > 0) 309 dom.setStyles(el, newStyle); // Add back the stored subset of styles 310 else // Remove empty span tags that do not have class attributes 311 if (el.nodeName == 'SPAN' && !el.className) 312 dom.remove(el, true); 313 }); 314 } 315 316 // Remove all style information or only specifically on WebKit to avoid the style bug on that browser 317 if (ed.getParam("paste_remove_styles") || (ed.getParam("paste_remove_styles_if_webkit") && tinymce.isWebKit)) { 318 each(dom.select('*[style]', o.node), function(el) { 319 el.removeAttribute('style'); 320 el.removeAttribute('mce_style'); 321 }); 322 } else { 323 if (tinymce.isWebKit) { 324 // We need to compress the styles on WebKit since if you paste <img border="0" /> it will become <img border="0" style="... lots of junk ..." /> 325 // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles 326 each(dom.select('*', o.node), function(el) { 327 el.removeAttribute('mce_style'); 328 }); 329 } 330 } 331 }, 332 333 /** 334 * Converts the most common bullet and number formats in Office into a real semantic UL/LI list. 335 */ 336 _convertLists : function(pl, o) { 337 var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html; 338 339 // Convert middot lists into real semantic lists 340 each(dom.select('p', o.node), function(p) { 341 var sib, val = '', type, html, idx, parents; 342 343 // Get text node value at beginning of paragraph 344 for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling) 345 val += sib.nodeValue; 346 347 val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/ /g, '\u00a0'); 348 349 // Detect unordered lists look for bullets 350 if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o]\s*\u00a0*/.test(val)) 351 type = 'ul'; 352 353 // Detect ordered lists 1., a. or ixv. 354 if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0{2,}/.test(val)) 355 type = 'ol'; 356 357 // Check if node value matches the list pattern: o 358 if (type) { 359 margin = parseFloat(p.style.marginLeft || 0); 360 361 if (margin > lastMargin) 362 levels.push(margin); 363 364 if (!listElm || type != lastType) { 365 listElm = dom.create(type); 366 dom.insertAfter(listElm, p); 367 } else { 368 // Nested list element 369 if (margin > lastMargin) { 370 listElm = li.appendChild(dom.create(type)); 371 } else if (margin < lastMargin) { 372 // Find parent level based on margin value 373 idx = tinymce.inArray(levels, margin); 374 parents = dom.getParents(listElm.parentNode, type); 375 listElm = parents[parents.length - 1 - idx] || listElm; 376 } 377 } 378 379 // Remove middot or number spans if they exists 380 each(dom.select('span', p), function(span) { 381 var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, ''); 382 383 // Remove span with the middot or the number 384 if (type == 'ul' && /^[\u2022\u00b7\u00a7\u00d8o]/.test(html)) 385 dom.remove(span); 386 else if (/^[\s\S]*\w+\.( |\u00a0)*\s*/.test(html)) 387 dom.remove(span); 388 }); 389 390 html = p.innerHTML; 391 392 // Remove middot/list items 393 if (type == 'ul') 394 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o]\s*( |\u00a0)+\s*/, ''); 395 else 396 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.( |\u00a0)+\s*/, ''); 397 398 // Create li and add paragraph data into the new li 399 li = listElm.appendChild(dom.create('li', 0, html)); 400 dom.remove(p); 401 402 lastMargin = margin; 403 lastType = type; 404 } else 405 listElm = lastMargin = 0; // End list element 406 }); 407 408 // Remove any left over makers 409 html = o.node.innerHTML; 410 if (html.indexOf('__MCE_ITEM__') != -1) 411 o.node.innerHTML = html.replace(/__MCE_ITEM__/g, ''); 412 }, 413 414 /** 415 * This method will split the current block parent and insert the contents inside the split position. 416 * This logic can be improved so text nodes at the start/end remain in the start/end block elements 417 */ 418 _insertBlockContent : function(ed, dom, content) { 419 var parentBlock, marker, sel = ed.selection, last, elm, vp, y, elmHeight; 420 421 function select(n) { 422 var r; 423 424 if (tinymce.isIE) { 425 r = ed.getDoc().body.createTextRange(); 426 r.moveToElementText(n); 427 r.collapse(false); 428 r.select(); 429 } else { 430 sel.select(n, 1); 431 sel.collapse(false); 432 } 433 }; 434 435 // Insert a marker for the caret position 436 this._insert('<span id="_marker"> </span>', 1); 437 marker = dom.get('_marker'); 438 parentBlock = dom.getParent(marker, 'p,h1,h2,h3,h4,h5,h6,ul,ol,th,td'); 439 440 // If it's a parent block but not a table cell 441 if (parentBlock && !/TD|TH/.test(parentBlock.nodeName)) { 442 // Split parent block 443 marker = dom.split(parentBlock, marker); 444 445 // Insert nodes before the marker 446 each(dom.create('div', 0, content).childNodes, function(n) { 447 last = marker.parentNode.insertBefore(n.cloneNode(true), marker); 448 }); 449 450 // Move caret after marker 451 select(last); 452 } else { 453 dom.setOuterHTML(marker, content); 454 sel.select(ed.getBody(), 1); 455 sel.collapse(0); 456 } 457 458 dom.remove('_marker'); // Remove marker if it's left 459 460 // Get element, position and height 461 elm = sel.getStart(); 462 vp = dom.getViewPort(ed.getWin()); 463 y = ed.dom.getPos(elm).y; 464 elmHeight = elm.clientHeight; 465 466 // Is element within viewport if not then scroll it into view 467 if (y < vp.y || y + elmHeight > vp.y + vp.h) 468 ed.getDoc().body.scrollTop = y < vp.y ? y : y - vp.h + 25; 469 }, 470 471 /** 472 * Inserts the specified contents at the caret position. 473 */ 474 _insert : function(h, skip_undo) { 475 var ed = this.editor; 476 477 // First delete the contents seems to work better on WebKit 478 if (!ed.selection.isCollapsed()) 479 ed.getDoc().execCommand('Delete', false, null); 480 481 // It's better to use the insertHTML method on Gecko since it will combine paragraphs correctly before inserting the contents 482 ed.execCommand(tinymce.isGecko ? 'insertHTML' : 'mceInsertContent', false, h, {skip_undo : skip_undo}); 483 }, 484 485 /** 486 * This method will open the old style paste dialogs. Some users might want the old behavior but still use the new cleanup engine. 487 */ 488 _legacySupport : function() { 489 var t = this, ed = t.editor; 490 491 // Register commands for backwards compatibility 492 each(['mcePasteText', 'mcePasteWord'], function(cmd) { 493 ed.addCommand(cmd, function() { 494 ed.windowManager.open({ 495 file : t.url + (cmd == 'mcePasteText' ? '/pastetext.htm' : '/pasteword.htm'), 496 width : parseInt(ed.getParam("paste_dialog_width", "450")), 497 height : parseInt(ed.getParam("paste_dialog_height", "400")), 498 inline : 1 499 }); 500 }); 501 }); 502 503 // Register buttons for backwards compatibility 504 ed.addButton('pastetext', {title : 'paste.paste_text_desc', cmd : 'mcePasteText'}); 505 ed.addButton('pasteword', {title : 'paste.paste_word_desc', cmd : 'mcePasteWord'}); 506 ed.addButton('selectall', {title : 'paste.selectall_desc', cmd : 'selectall'}); 507 } 508 }); 509 510 // Register plugin 511 tinymce.PluginManager.add('paste', tinymce.plugins.PastePlugin); 512 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Mar 28 15:54:07 2012 | Cross-referenced by PHPXref 0.7.1 |