[ Index ] |
WordPress Cross Reference |
[Summary view] [Print] [Text view]
1 /** 2 * editor_plugin_src.js 3 * 4 * Copyright 2009, Moxiecode Systems AB 5 * Released under LGPL License. 6 * 7 * License: http://tinymce.moxiecode.com/license 8 * Contributing: http://tinymce.moxiecode.com/contributing 9 */ 10 11 (function() { 12 var each = tinymce.each, 13 defs = { 14 paste_auto_cleanup_on_paste : true, 15 paste_enable_default_filters : true, 16 paste_block_drop : false, 17 paste_retain_style_properties : "none", 18 paste_strip_class_attributes : "mso", 19 paste_remove_spans : false, 20 paste_remove_styles : false, 21 paste_remove_styles_if_webkit : true, 22 paste_convert_middot_lists : true, 23 paste_convert_headers_to_strong : false, 24 paste_dialog_width : "450", 25 paste_dialog_height : "400", 26 paste_max_consecutive_linebreaks: 2, 27 paste_text_use_dialog : false, 28 paste_text_sticky : false, 29 paste_text_sticky_default : false, 30 paste_text_notifyalways : false, 31 paste_text_linebreaktype : "combined", 32 paste_text_replacements : [ 33 [/\u2026/g, "..."], 34 [/[\x93\x94\u201c\u201d]/g, '"'], 35 [/[\x60\x91\x92\u2018\u2019]/g, "'"] 36 ] 37 }; 38 39 function getParam(ed, name) { 40 return ed.getParam(name, defs[name]); 41 } 42 43 tinymce.create('tinymce.plugins.PastePlugin', { 44 init : function(ed, url) { 45 var t = this; 46 47 t.editor = ed; 48 t.url = url; 49 50 // Setup plugin events 51 t.onPreProcess = new tinymce.util.Dispatcher(t); 52 t.onPostProcess = new tinymce.util.Dispatcher(t); 53 54 // Register default handlers 55 t.onPreProcess.add(t._preProcess); 56 t.onPostProcess.add(t._postProcess); 57 58 // Register optional preprocess handler 59 t.onPreProcess.add(function(pl, o) { 60 ed.execCallback('paste_preprocess', pl, o); 61 }); 62 63 // Register optional postprocess 64 t.onPostProcess.add(function(pl, o) { 65 ed.execCallback('paste_postprocess', pl, o); 66 }); 67 68 ed.onKeyDown.addToTop(function(ed, e) { 69 // Block ctrl+v from adding an undo level since the default logic in tinymce.Editor will add that 70 if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45)) 71 return false; // Stop other listeners 72 }); 73 74 // Initialize plain text flag 75 ed.pasteAsPlainText = getParam(ed, 'paste_text_sticky_default'); 76 77 // This function executes the process handlers and inserts the contents 78 // force_rich overrides plain text mode set by user, important for pasting with execCommand 79 function process(o, force_rich) { 80 var dom = ed.dom, rng; 81 82 // Execute pre process handlers 83 t.onPreProcess.dispatch(t, o); 84 85 // Create DOM structure 86 o.node = dom.create('div', 0, o.content); 87 88 // If pasting inside the same element and the contents is only one block 89 // remove the block and keep the text since Firefox will copy parts of pre and h1-h6 as a pre element 90 if (tinymce.isGecko) { 91 rng = ed.selection.getRng(true); 92 if (rng.startContainer == rng.endContainer && rng.startContainer.nodeType == 3) { 93 // Is only one block node and it doesn't contain word stuff 94 if (o.node.childNodes.length === 1 && /^(p|h[1-6]|pre)$/i.test(o.node.firstChild.nodeName) && o.content.indexOf('__MCE_ITEM__') === -1) 95 dom.remove(o.node.firstChild, true); 96 } 97 } 98 99 // Execute post process handlers 100 t.onPostProcess.dispatch(t, o); 101 102 // Serialize content 103 o.content = ed.serializer.serialize(o.node, {getInner : 1, forced_root_block : ''}); 104 105 // Plain text option active? 106 if ((!force_rich) && (ed.pasteAsPlainText)) { 107 t._insertPlainText(o.content); 108 109 if (!getParam(ed, "paste_text_sticky")) { 110 ed.pasteAsPlainText = false; 111 ed.controlManager.setActive("pastetext", false); 112 } 113 } else { 114 t._insert(o.content); 115 } 116 } 117 118 // Add command for external usage 119 ed.addCommand('mceInsertClipboardContent', function(u, o) { 120 process(o, true); 121 }); 122 123 if (!getParam(ed, "paste_text_use_dialog")) { 124 ed.addCommand('mcePasteText', function(u, v) { 125 var cookie = tinymce.util.Cookie; 126 127 ed.pasteAsPlainText = !ed.pasteAsPlainText; 128 ed.controlManager.setActive('pastetext', ed.pasteAsPlainText); 129 130 if ((ed.pasteAsPlainText) && (!cookie.get("tinymcePasteText"))) { 131 if (getParam(ed, "paste_text_sticky")) { 132 ed.windowManager.alert(ed.translate('paste.plaintext_mode_sticky')); 133 } else { 134 ed.windowManager.alert(ed.translate('paste.plaintext_mode')); 135 } 136 137 if (!getParam(ed, "paste_text_notifyalways")) { 138 cookie.set("tinymcePasteText", "1", new Date(new Date().getFullYear() + 1, 12, 31)) 139 } 140 } 141 }); 142 } 143 144 ed.addButton('pastetext', {title: 'paste.paste_text_desc', cmd: 'mcePasteText'}); 145 ed.addButton('selectall', {title: 'paste.selectall_desc', cmd: 'selectall'}); 146 147 // This function grabs the contents from the clipboard by adding a 148 // hidden div and placing the caret inside it and after the browser paste 149 // is done it grabs that contents and processes that 150 function grabContent(e) { 151 var n, or, rng, oldRng, sel = ed.selection, dom = ed.dom, body = ed.getBody(), posY, textContent; 152 153 // Check if browser supports direct plaintext access 154 if (e.clipboardData || dom.doc.dataTransfer) { 155 textContent = (e.clipboardData || dom.doc.dataTransfer).getData('Text'); 156 157 if (ed.pasteAsPlainText) { 158 e.preventDefault(); 159 process({content : dom.encode(textContent).replace(/\r?\n/g, '<br />')}); 160 return; 161 } 162 } 163 164 if (dom.get('_mcePaste')) 165 return; 166 167 // Create container to paste into 168 n = dom.add(body, 'div', {id : '_mcePaste', 'class' : 'mcePaste', 'data-mce-bogus' : '1'}, '\uFEFF\uFEFF'); 169 170 // If contentEditable mode we need to find out the position of the closest element 171 if (body != ed.getDoc().body) 172 posY = dom.getPos(ed.selection.getStart(), body).y; 173 else 174 posY = body.scrollTop + dom.getViewPort(ed.getWin()).y; 175 176 // Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles 177 // If also needs to be in view on IE or the paste would fail 178 dom.setStyles(n, { 179 position : 'absolute', 180 left : tinymce.isGecko ? -40 : 0, // Need to move it out of site on Gecko since it will othewise display a ghost resize rect for the div 181 top : posY - 25, 182 width : 1, 183 height : 1, 184 overflow : 'hidden' 185 }); 186 187 if (tinymce.isIE) { 188 // Store away the old range 189 oldRng = sel.getRng(); 190 191 // Select the container 192 rng = dom.doc.body.createTextRange(); 193 rng.moveToElementText(n); 194 rng.execCommand('Paste'); 195 196 // Remove container 197 dom.remove(n); 198 199 // Check if the contents was changed, if it wasn't then clipboard extraction failed probably due 200 // to IE security settings so we pass the junk though better than nothing right 201 if (n.innerHTML === '\uFEFF\uFEFF') { 202 ed.execCommand('mcePasteWord'); 203 e.preventDefault(); 204 return; 205 } 206 207 // Restore the old range and clear the contents before pasting 208 sel.setRng(oldRng); 209 sel.setContent(''); 210 211 // For some odd reason we need to detach the the mceInsertContent call from the paste event 212 // It's like IE has a reference to the parent element that you paste in and the selection gets messed up 213 // when it tries to restore the selection 214 setTimeout(function() { 215 // Process contents 216 process({content : n.innerHTML}); 217 }, 0); 218 219 // Block the real paste event 220 return tinymce.dom.Event.cancel(e); 221 } else { 222 function block(e) { 223 e.preventDefault(); 224 }; 225 226 // Block mousedown and click to prevent selection change 227 dom.bind(ed.getDoc(), 'mousedown', block); 228 dom.bind(ed.getDoc(), 'keydown', block); 229 230 or = ed.selection.getRng(); 231 232 // Move select contents inside DIV 233 n = n.firstChild; 234 rng = ed.getDoc().createRange(); 235 rng.setStart(n, 0); 236 rng.setEnd(n, 2); 237 sel.setRng(rng); 238 239 // Wait a while and grab the pasted contents 240 window.setTimeout(function() { 241 var h = '', nl; 242 243 // Paste divs duplicated in paste divs seems to happen when you paste plain text so lets first look for that broken behavior in WebKit 244 if (!dom.select('div.mcePaste > div.mcePaste').length) { 245 nl = dom.select('div.mcePaste'); 246 247 // WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string 248 each(nl, function(n) { 249 var child = n.firstChild; 250 251 // WebKit inserts a DIV container with lots of odd styles 252 if (child && child.nodeName == 'DIV' && child.style.marginTop && child.style.backgroundColor) { 253 dom.remove(child, 1); 254 } 255 256 // Remove apply style spans 257 each(dom.select('span.Apple-style-span', n), function(n) { 258 dom.remove(n, 1); 259 }); 260 261 // Remove bogus br elements 262 each(dom.select('br[data-mce-bogus]', n), function(n) { 263 dom.remove(n); 264 }); 265 266 // WebKit will make a copy of the DIV for each line of plain text pasted and insert them into the DIV 267 if (n.parentNode.className != 'mcePaste') 268 h += n.innerHTML; 269 }); 270 } else { 271 // Found WebKit weirdness so force the content into paragraphs this seems to happen when you paste plain text from Nodepad etc 272 // So this logic will replace double enter with paragraphs and single enter with br so it kind of looks the same 273 h = '<p>' + dom.encode(textContent).replace(/\r?\n\r?\n/g, '</p><p>').replace(/\r?\n/g, '<br />') + '</p>'; 274 } 275 276 // Remove the nodes 277 each(dom.select('div.mcePaste'), function(n) { 278 dom.remove(n); 279 }); 280 281 // Restore the old selection 282 if (or) 283 sel.setRng(or); 284 285 process({content : h}); 286 287 // Unblock events ones we got the contents 288 dom.unbind(ed.getDoc(), 'mousedown', block); 289 dom.unbind(ed.getDoc(), 'keydown', block); 290 }, 0); 291 } 292 } 293 294 // Check if we should use the new auto process method 295 if (getParam(ed, "paste_auto_cleanup_on_paste")) { 296 // Is it's Opera or older FF use key handler 297 if (tinymce.isOpera || /Firefox\/2/.test(navigator.userAgent)) { 298 ed.onKeyDown.addToTop(function(ed, e) { 299 if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45)) 300 grabContent(e); 301 }); 302 } else { 303 // Grab contents on paste event on Gecko and WebKit 304 ed.onPaste.addToTop(function(ed, e) { 305 return grabContent(e); 306 }); 307 } 308 } 309 310 ed.onInit.add(function() { 311 ed.controlManager.setActive("pastetext", ed.pasteAsPlainText); 312 313 // Block all drag/drop events 314 if (getParam(ed, "paste_block_drop")) { 315 ed.dom.bind(ed.getBody(), ['dragend', 'dragover', 'draggesture', 'dragdrop', 'drop', 'drag'], function(e) { 316 e.preventDefault(); 317 e.stopPropagation(); 318 319 return false; 320 }); 321 } 322 }); 323 324 // Add legacy support 325 t._legacySupport(); 326 }, 327 328 getInfo : function() { 329 return { 330 longname : 'Paste text/word', 331 author : 'Moxiecode Systems AB', 332 authorurl : 'http://tinymce.moxiecode.com', 333 infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste', 334 version : tinymce.majorVersion + "." + tinymce.minorVersion 335 }; 336 }, 337 338 _preProcess : function(pl, o) { 339 var ed = this.editor, 340 h = o.content, 341 grep = tinymce.grep, 342 explode = tinymce.explode, 343 trim = tinymce.trim, 344 len, stripClass; 345 346 //console.log('Before preprocess:' + o.content); 347 348 function process(items) { 349 each(items, function(v) { 350 // Remove or replace 351 if (v.constructor == RegExp) 352 h = h.replace(v, ''); 353 else 354 h = h.replace(v[0], v[1]); 355 }); 356 } 357 358 if (ed.settings.paste_enable_default_filters == false) { 359 return; 360 } 361 362 // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser 363 if (tinymce.isIE && document.documentMode >= 9 && /<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(o.content)) { 364 // IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser 365 process([[/(?:<br> [\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br> [\s\r\n]+|<br>)*/g, '$1']]); 366 367 // IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break 368 process([ 369 [/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact 370 [/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s 371 [/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR 372 ]); 373 } 374 375 // Detect Word content and process it more aggressive 376 if (/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(h) || o.wordContent) { 377 o.wordContent = true; // Mark the pasted contents as word specific content 378 //console.log('Word contents detected.'); 379 380 // Process away some basic content 381 process([ 382 /^\s*( )+/gi, // entities at the start of contents 383 /( |<br[^>]*>)+\s*$/gi // entities at the end of contents 384 ]); 385 386 if (getParam(ed, "paste_convert_headers_to_strong")) { 387 h = h.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>"); 388 } 389 390 if (getParam(ed, "paste_convert_middot_lists")) { 391 process([ 392 [/<!--\[if !supportLists\]-->/gi, '$&__MCE_ITEM__'], // Convert supportLists to a list item marker 393 [/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi, '$1__MCE_ITEM__'], // Convert mso-list and symbol spans to item markers 394 [/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi, '$1__MCE_ITEM__'] // Convert mso-list and symbol paragraphs to item markers (FF) 395 ]); 396 } 397 398 process([ 399 // Word comments like conditional comments etc 400 /<!--[\s\S]+?-->/gi, 401 402 // Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, MS Office namespaced tags, and a few other tags 403 /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi, 404 405 // Convert <s> into <strike> for line-though 406 [/<(\/?)s>/gi, "<$1strike>"], 407 408 // Replace nsbp entites to char since it's easier to handle 409 [/ /gi, "\u00a0"] 410 ]); 411 412 // Remove bad attributes, with or without quotes, ensuring that attribute text is really inside a tag. 413 // If JavaScript had a RegExp look-behind, we could have integrated this with the last process() array and got rid of the loop. But alas, it does not, so we cannot. 414 do { 415 len = h.length; 416 h = h.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi, "$1"); 417 } while (len != h.length); 418 419 // Remove all spans if no styles is to be retained 420 if (getParam(ed, "paste_retain_style_properties").replace(/^none$/i, "").length == 0) { 421 h = h.replace(/<\/?span[^>]*>/gi, ""); 422 } else { 423 // We're keeping styles, so at least clean them up. 424 // CSS Reference: http://msdn.microsoft.com/en-us/library/aa155477.aspx 425 426 process([ 427 // Convert <span style="mso-spacerun:yes">___</span> to string of alternating breaking/non-breaking spaces of same length 428 [/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi, 429 function(str, spaces) { 430 return (spaces.length > 0)? spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : ""; 431 } 432 ], 433 434 // Examine all styles: delete junk, transform some, and keep the rest 435 [/(<[a-z][^>]*)\sstyle="([^"]*)"/gi, 436 function(str, tag, style) { 437 var n = [], 438 i = 0, 439 s = explode(trim(style).replace(/"/gi, "'"), ";"); 440 441 // Examine each style definition within the tag's style attribute 442 each(s, function(v) { 443 var name, value, 444 parts = explode(v, ":"); 445 446 function ensureUnits(v) { 447 return v + ((v !== "0") && (/\d$/.test(v)))? "px" : ""; 448 } 449 450 if (parts.length == 2) { 451 name = parts[0].toLowerCase(); 452 value = parts[1].toLowerCase(); 453 454 // Translate certain MS Office styles into their CSS equivalents 455 switch (name) { 456 case "mso-padding-alt": 457 case "mso-padding-top-alt": 458 case "mso-padding-right-alt": 459 case "mso-padding-bottom-alt": 460 case "mso-padding-left-alt": 461 case "mso-margin-alt": 462 case "mso-margin-top-alt": 463 case "mso-margin-right-alt": 464 case "mso-margin-bottom-alt": 465 case "mso-margin-left-alt": 466 case "mso-table-layout-alt": 467 case "mso-height": 468 case "mso-width": 469 case "mso-vertical-align-alt": 470 n[i++] = name.replace(/^mso-|-alt$/g, "") + ":" + ensureUnits(value); 471 return; 472 473 case "horiz-align": 474 n[i++] = "text-align:" + value; 475 return; 476 477 case "vert-align": 478 n[i++] = "vertical-align:" + value; 479 return; 480 481 case "font-color": 482 case "mso-foreground": 483 n[i++] = "color:" + value; 484 return; 485 486 case "mso-background": 487 case "mso-highlight": 488 n[i++] = "background:" + value; 489 return; 490 491 case "mso-default-height": 492 n[i++] = "min-height:" + ensureUnits(value); 493 return; 494 495 case "mso-default-width": 496 n[i++] = "min-width:" + ensureUnits(value); 497 return; 498 499 case "mso-padding-between-alt": 500 n[i++] = "border-collapse:separate;border-spacing:" + ensureUnits(value); 501 return; 502 503 case "text-line-through": 504 if ((value == "single") || (value == "double")) { 505 n[i++] = "text-decoration:line-through"; 506 } 507 return; 508 509 case "mso-zero-height": 510 if (value == "yes") { 511 n[i++] = "display:none"; 512 } 513 return; 514 } 515 516 // Eliminate all MS Office style definitions that have no CSS equivalent by examining the first characters in the name 517 if (/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(name)) { 518 return; 519 } 520 521 // If it reached this point, it must be a valid CSS style 522 n[i++] = name + ":" + parts[1]; // Lower-case name, but keep value case 523 } 524 }); 525 526 // If style attribute contained any valid styles the re-write it; otherwise delete style attribute. 527 if (i > 0) { 528 return tag + ' style="' + n.join(';') + '"'; 529 } else { 530 return tag; 531 } 532 } 533 ] 534 ]); 535 } 536 } 537 538 // Replace headers with <strong> 539 if (getParam(ed, "paste_convert_headers_to_strong")) { 540 process([ 541 [/<h[1-6][^>]*>/gi, "<p><strong>"], 542 [/<\/h[1-6][^>]*>/gi, "</strong></p>"] 543 ]); 544 } 545 546 process([ 547 // Copy paste from Java like Open Office will produce this junk on FF 548 [/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi, ''] 549 ]); 550 551 // Class attribute options are: leave all as-is ("none"), remove all ("all"), or remove only those starting with mso ("mso"). 552 // Note:- paste_strip_class_attributes: "none", verify_css_classes: true is also a good variation. 553 stripClass = getParam(ed, "paste_strip_class_attributes"); 554 555 if (stripClass !== "none") { 556 function removeClasses(match, g1) { 557 if (stripClass === "all") 558 return ''; 559 560 var cls = grep(explode(g1.replace(/^(["'])(.*)\1$/, "$2"), " "), 561 function(v) { 562 return (/^(?!mso)/i.test(v)); 563 } 564 ); 565 566 return cls.length ? ' class="' + cls.join(" ") + '"' : ''; 567 }; 568 569 h = h.replace(/ class="([^"]+)"/gi, removeClasses); 570 h = h.replace(/ class=([\-\w]+)/gi, removeClasses); 571 } 572 573 // Remove spans option 574 if (getParam(ed, "paste_remove_spans")) { 575 h = h.replace(/<\/?span[^>]*>/gi, ""); 576 } 577 578 //console.log('After preprocess:' + h); 579 580 o.content = h; 581 }, 582 583 /** 584 * Various post process items. 585 */ 586 _postProcess : function(pl, o) { 587 var t = this, ed = t.editor, dom = ed.dom, styleProps; 588 589 if (ed.settings.paste_enable_default_filters == false) { 590 return; 591 } 592 593 if (o.wordContent) { 594 // Remove named anchors or TOC links 595 each(dom.select('a', o.node), function(a) { 596 if (!a.href || a.href.indexOf('#_Toc') != -1) 597 dom.remove(a, 1); 598 }); 599 600 if (getParam(ed, "paste_convert_middot_lists")) { 601 t._convertLists(pl, o); 602 } 603 604 // Process styles 605 styleProps = getParam(ed, "paste_retain_style_properties"); // retained properties 606 607 // Process only if a string was specified and not equal to "all" or "*" 608 if ((tinymce.is(styleProps, "string")) && (styleProps !== "all") && (styleProps !== "*")) { 609 styleProps = tinymce.explode(styleProps.replace(/^none$/i, "")); 610 611 // Retains some style properties 612 each(dom.select('*', o.node), function(el) { 613 var newStyle = {}, npc = 0, i, sp, sv; 614 615 // Store a subset of the existing styles 616 if (styleProps) { 617 for (i = 0; i < styleProps.length; i++) { 618 sp = styleProps[i]; 619 sv = dom.getStyle(el, sp); 620 621 if (sv) { 622 newStyle[sp] = sv; 623 npc++; 624 } 625 } 626 } 627 628 // Remove all of the existing styles 629 dom.setAttrib(el, 'style', ''); 630 631 if (styleProps && npc > 0) 632 dom.setStyles(el, newStyle); // Add back the stored subset of styles 633 else // Remove empty span tags that do not have class attributes 634 if (el.nodeName == 'SPAN' && !el.className) 635 dom.remove(el, true); 636 }); 637 } 638 } 639 640 // Remove all style information or only specifically on WebKit to avoid the style bug on that browser 641 if (getParam(ed, "paste_remove_styles") || (getParam(ed, "paste_remove_styles_if_webkit") && tinymce.isWebKit)) { 642 each(dom.select('*[style]', o.node), function(el) { 643 el.removeAttribute('style'); 644 el.removeAttribute('data-mce-style'); 645 }); 646 } else { 647 if (tinymce.isWebKit) { 648 // 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 ..." /> 649 // Removing the mce_style that contains the real value will force the Serializer engine to compress the styles 650 each(dom.select('*', o.node), function(el) { 651 el.removeAttribute('data-mce-style'); 652 }); 653 } 654 } 655 }, 656 657 /** 658 * Converts the most common bullet and number formats in Office into a real semantic UL/LI list. 659 */ 660 _convertLists : function(pl, o) { 661 var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html; 662 663 // Convert middot lists into real semantic lists 664 each(dom.select('p', o.node), function(p) { 665 var sib, val = '', type, html, idx, parents; 666 667 // Get text node value at beginning of paragraph 668 for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling) 669 val += sib.nodeValue; 670 671 val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/ /g, '\u00a0'); 672 673 // Detect unordered lists look for bullets 674 if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(val)) 675 type = 'ul'; 676 677 // Detect ordered lists 1., a. or ixv. 678 if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(val)) 679 type = 'ol'; 680 681 // Check if node value matches the list pattern: o 682 if (type) { 683 margin = parseFloat(p.style.marginLeft || 0); 684 685 if (margin > lastMargin) 686 levels.push(margin); 687 688 if (!listElm || type != lastType) { 689 listElm = dom.create(type); 690 dom.insertAfter(listElm, p); 691 } else { 692 // Nested list element 693 if (margin > lastMargin) { 694 listElm = li.appendChild(dom.create(type)); 695 } else if (margin < lastMargin) { 696 // Find parent level based on margin value 697 idx = tinymce.inArray(levels, margin); 698 parents = dom.getParents(listElm.parentNode, type); 699 listElm = parents[parents.length - 1 - idx] || listElm; 700 } 701 } 702 703 // Remove middot or number spans if they exists 704 each(dom.select('span', p), function(span) { 705 var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, ''); 706 707 // Remove span with the middot or the number 708 if (type == 'ul' && /^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(html)) 709 dom.remove(span); 710 else if (/^__MCE_ITEM__[\s\S]*\w+\.( |\u00a0)*\s*/.test(html)) 711 dom.remove(span); 712 }); 713 714 html = p.innerHTML; 715 716 // Remove middot/list items 717 if (type == 'ul') 718 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*( |\u00a0)+\s*/, ''); 719 else 720 html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.( |\u00a0)+\s*/, ''); 721 722 // Create li and add paragraph data into the new li 723 li = listElm.appendChild(dom.create('li', 0, html)); 724 dom.remove(p); 725 726 lastMargin = margin; 727 lastType = type; 728 } else 729 listElm = lastMargin = 0; // End list element 730 }); 731 732 // Remove any left over makers 733 html = o.node.innerHTML; 734 if (html.indexOf('__MCE_ITEM__') != -1) 735 o.node.innerHTML = html.replace(/__MCE_ITEM__/g, ''); 736 }, 737 738 /** 739 * Inserts the specified contents at the caret position. 740 */ 741 _insert : function(h, skip_undo) { 742 var ed = this.editor, r = ed.selection.getRng(); 743 744 // First delete the contents seems to work better on WebKit when the selection spans multiple list items or multiple table cells. 745 if (!ed.selection.isCollapsed() && r.startContainer != r.endContainer) 746 ed.getDoc().execCommand('Delete', false, null); 747 748 ed.execCommand('mceInsertContent', false, h, {skip_undo : skip_undo}); 749 }, 750 751 /** 752 * Instead of the old plain text method which tried to re-create a paste operation, the 753 * new approach adds a plain text mode toggle switch that changes the behavior of paste. 754 * This function is passed the same input that the regular paste plugin produces. 755 * It performs additional scrubbing and produces (and inserts) the plain text. 756 * This approach leverages all of the great existing functionality in the paste 757 * plugin, and requires minimal changes to add the new functionality. 758 * Speednet - June 2009 759 */ 760 _insertPlainText : function(content) { 761 var ed = this.editor, 762 linebr = getParam(ed, "paste_text_linebreaktype"), 763 rl = getParam(ed, "paste_text_replacements"), 764 is = tinymce.is; 765 766 function process(items) { 767 each(items, function(v) { 768 if (v.constructor == RegExp) 769 content = content.replace(v, ""); 770 else 771 content = content.replace(v[0], v[1]); 772 }); 773 }; 774 775 if ((typeof(content) === "string") && (content.length > 0)) { 776 // If HTML content with line-breaking tags, then remove all cr/lf chars because only tags will break a line 777 if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(content)) { 778 process([ 779 /[\n\r]+/g 780 ]); 781 } else { 782 // Otherwise just get rid of carriage returns (only need linefeeds) 783 process([ 784 /\r+/g 785 ]); 786 } 787 788 process([ 789 [/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi, "\n\n"], // Block tags get a blank line after them 790 [/<br[^>]*>|<\/tr>/gi, "\n"], // Single linebreak for <br /> tags and table rows 791 [/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"], // Table cells get tabs betweem them 792 /<[a-z!\/?][^>]*>/gi, // Delete all remaining tags 793 [/ /gi, " "], // Convert non-break spaces to regular spaces (remember, *plain text*) 794 [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"] // Cool little RegExp deletes whitespace around linebreak chars. 795 ]); 796 797 var maxLinebreaks = Number(getParam(ed, "paste_max_consecutive_linebreaks")); 798 if (maxLinebreaks > -1) { 799 var maxLinebreaksRegex = new RegExp("\n{" + (maxLinebreaks + 1) + ",}", "g"); 800 var linebreakReplacement = ""; 801 802 while (linebreakReplacement.length < maxLinebreaks) { 803 linebreakReplacement += "\n"; 804 } 805 806 process([ 807 [maxLinebreaksRegex, linebreakReplacement] // Limit max consecutive linebreaks 808 ]); 809 } 810 811 content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content)); 812 813 // Perform default or custom replacements 814 if (is(rl, "array")) { 815 process(rl); 816 } else if (is(rl, "string")) { 817 process(new RegExp(rl, "gi")); 818 } 819 820 // Treat paragraphs as specified in the config 821 if (linebr == "none") { 822 // Convert all line breaks to space 823 process([ 824 [/\n+/g, " "] 825 ]); 826 } else if (linebr == "br") { 827 // Convert all line breaks to <br /> 828 process([ 829 [/\n/g, "<br />"] 830 ]); 831 } else if (linebr == "p") { 832 // Convert all line breaks to <p>...</p> 833 process([ 834 [/\n+/g, "</p><p>"], 835 [/^(.*<\/p>)(<p>)$/, '<p>$1'] 836 ]); 837 } else { 838 // defaults to "combined" 839 // Convert single line breaks to <br /> and double line breaks to <p>...</p> 840 process([ 841 [/\n\n/g, "</p><p>"], 842 [/^(.*<\/p>)(<p>)$/, '<p>$1'], 843 [/\n/g, "<br />"] 844 ]); 845 } 846 847 ed.execCommand('mceInsertContent', false, content); 848 } 849 }, 850 851 /** 852 * This method will open the old style paste dialogs. Some users might want the old behavior but still use the new cleanup engine. 853 */ 854 _legacySupport : function() { 855 var t = this, ed = t.editor; 856 857 // Register command(s) for backwards compatibility 858 ed.addCommand("mcePasteWord", function() { 859 ed.windowManager.open({ 860 file: t.url + "/pasteword.htm", 861 width: parseInt(getParam(ed, "paste_dialog_width")), 862 height: parseInt(getParam(ed, "paste_dialog_height")), 863 inline: 1 864 }); 865 }); 866 867 if (getParam(ed, "paste_text_use_dialog")) { 868 ed.addCommand("mcePasteText", function() { 869 ed.windowManager.open({ 870 file : t.url + "/pastetext.htm", 871 width: parseInt(getParam(ed, "paste_dialog_width")), 872 height: parseInt(getParam(ed, "paste_dialog_height")), 873 inline : 1 874 }); 875 }); 876 } 877 878 // Register button for backwards compatibility 879 ed.addButton("pasteword", {title : "paste.paste_word_desc", cmd : "mcePasteWord"}); 880 } 881 }); 882 883 // Register plugin 884 tinymce.PluginManager.add("paste", tinymce.plugins.PastePlugin); 885 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 25 01:41:18 2014 | WordPress honlapkészítés: online1.hu |