diff --git a/htdocs/admin/commande.php b/htdocs/admin/commande.php index a569632cc02..5204d5f989c 100644 --- a/htdocs/admin/commande.php +++ b/htdocs/admin/commande.php @@ -555,7 +555,16 @@ print ''; print ''; print ''; print $langs->trans("FreeLegalTextOnOrders").' ('.$langs->trans("AddCRIfTooLong").')
'; -print ''; +if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) +{ + print ''; +} +else +{ + include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php'; + $doleditor=new DolEditor('COMMANDE_FREE_TEXT', $conf->global->COMMANDE_FREE_TEXT,'',80,'dolibarr_details'); + print $doleditor->Create(); +} print ''; print ''; print "\n"; diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index 8beae962d09..2fe76a920b8 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -290,6 +290,55 @@ function pdf_getHeightForLogo($logo, $url = false) return $height; } +/** + * Function to try to calculate height of a HTML Content + * + * @param TCPDF $pdf PDF initialized object + * @param string $htmlcontent HTML Contect + * @see getStringHeight + */ +function pdfGetHeightForHtmlContent(&$pdf, $htmlcontent) +{ + // store current object + $pdf->startTransaction(); + // store starting values + $start_y = $pdf->GetY(); + var_dump($start_y); + $start_page = $pdf->getPage(); + // call your printing functions with your parameters + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + $pdf->writeHTMLCell(0, 0, 0, $start_y, $htmlcontent, 0, 1, false, true, 'J',true); + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + // get the new Y + $end_y = $pdf->GetY(); + $end_page = $pdf->getPage(); + // calculate height + $height = 0; + if ($end_page == $start_page) { + $height = $end_y - $start_y; + } + else + { + for ($page=$start_page; $page <= $end_page; ++$page) { + $this->setPage($page); + if ($page == $start_page) { + // first page + $height = $this->h - $start_y - $this->bMargin; + } elseif ($page == $end_page) { + // last page + $height = $end_y - $this->tMargin; + } else { + $height = $this->h - $this->tMargin - $this->bMargin; + } + } + } + // restore previous object + $pdf = $pdf->rollbackTransaction(); + + return $height; +} + + /** * Returns the name of the thirdparty * @@ -854,11 +903,20 @@ function pdf_pagefoot(&$pdf,$outputlangs,$paramfreetext,$fromcompany,$marge_bass $freetextheight=0; if ($line) // Free text { - $width=20000; $align='L'; // By default, ask a manual break: We use a large value 20000, to not have automatic wrap. This make user understand, he need to add CR on its text. - if (! empty($conf->global->MAIN_USE_AUTOWRAP_ON_FREETEXT)) { - $width=200; $align='C'; + //$line="eee
\nfdsfsdf
\nghfghg
"; + if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) // by default + { + $width=20000; $align='L'; // By default, ask a manual break: We use a large value 20000, to not have automatic wrap. This make user understand, he need to add CR on its text. + if (! empty($conf->global->MAIN_USE_AUTOWRAP_ON_FREETEXT)) { + $width=200; $align='C'; + } + $freetextheight=$pdf->getStringHeight($width,$line); + } + else + { + $freetextheight=pdfGetHeightForHtmlContent($pdf,dol_htmlentitiesbr($line, 1)); // New method (works for HTML content) + //print '
'.$freetextheight;exit; } - $freetextheight=$pdf->getStringHeight($width,$line); } $marginwithfooter=$marge_basse + $freetextheight + (! empty($line1)?3:0) + (! empty($line2)?3:0) + (! empty($line3)?3:0) + (! empty($line4)?3:0); @@ -867,7 +925,14 @@ function pdf_pagefoot(&$pdf,$outputlangs,$paramfreetext,$fromcompany,$marge_bass if ($line) // Free text { $pdf->SetXY($dims['lm'],-$posy); - $pdf->MultiCell(0, 3, $line, 0, $align, 0); + if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) // by default + { + $pdf->MultiCell(0, 3, $line, 0, $align, 0); + } + else + { + $pdf->writeHTMLCell($pdf->page_largeur - $pdf->margin_left - $pdf->margin_right, $freetextheight, $dims['lm'], $dims['hk']-$marginwithfooter, $line); + } $posy-=$freetextheight; } diff --git a/htdocs/includes/ckeditor/ckeditor/README.md b/htdocs/includes/ckeditor/ckeditor/README.md index d6b5e2af515..c5a55cd88f8 100644 --- a/htdocs/includes/ckeditor/ckeditor/README.md +++ b/htdocs/includes/ckeditor/ckeditor/README.md @@ -1,82 +1,39 @@ -CKEditor 4 - Releases -===================== +CKEditor 4 +========== -## Releases Code +Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. +http://ckeditor.com - See LICENSE.md for license information. -This repository contains the official release versions of [CKEditor](http://ckeditor.com). - -There are four versions for each release — `standard-all`, `basic`, `standard`, and `full`. -They differ in the number of plugins that are compiled into the main `ckeditor.js` file as well as the toolbar configuration. - -See the [comparison](http://ckeditor.com/presets) of the `basic`, `standard`, and `full` installation presets for more details. - -The `standard-all` build includes all official CKSource plugins with only those from the `standard` installation preset compiled into the `ckeditor.js` file and enabled in the configuration. - -All versions available in this repository were built using [CKBuilder](http://ckeditor.com/builder), so they are optimized and ready to be used in a production environment. +CKEditor is a text editor to be used inside web pages. It's not a replacement +for desktop text editors like Word or OpenOffice, but a component to be used as +part of web applications and websites. ## Documentation -Developer documentation for CKEditor is available online at: . +The full editor documentation is available online at the following address: +http://docs.ckeditor.com ## Installation -### Git clone +Installing CKEditor is an easy task. Just follow these simple steps: -To install one of the available releases, just clone this repository and switch to the respective branch (see next section): + 1. **Download** the latest version from the CKEditor website: + http://ckeditor.com. You should have already completed this step, but be + sure you have the very latest version. + 2. **Extract** (decompress) the downloaded file into the root of your website. - git clone -b git://github.com/ckeditor/ckeditor-releases.git - -### Git submodule - -If you are using git for your project and you want to integrate CKEditor, we recommend to add this repository as a -[submodule](http://git-scm.com/book/en/Git-Tools-Submodules). - - git submodule add -b git://github.com/ckeditor/ckeditor-releases.git - git commit -m "Added CKEditor submodule in directory." - -### Using Package Managers - -See the [Installing CKEditor with Package Managers](http://docs.ckeditor.com/#!/guide/dev_package_managers) article for more details about installing CKEditor with Bower and Composer. - -## Repository Structure - -### Branches - -This repository contains the following branches: - - - `master` and `latest` – the latest release of the `standard-all` preset (including betas). - - `stable` – the latest stable release of the `standard-all` preset (non-beta). - - `A.B.x` (e.g. `4.3.x`) – the latest release of the `standard-all` preset in the `A.B` branch. - - `(basic|standard|full)/stable` – the latest stable release tag point (non-beta). - - `(basic|standard|full)/latest` – the latest release tag point (including betas). - - `(basic|standard|full)/A.B.x` (e.g. `basic/4.0.x`) – the latest releases in the `A.B` branch. - -### Tags - -**Since version 4.3.3** this repository uses the following tag naming rules: - - - `x.y.z` – contains the `standard-all` editor build, e.g. `4.3.3`, `4.4.0` etc. - - `(basic|standard|full)/x.y.z` – contains the editor build with a given preset, e.g. `basic/4.3.3`. - -The version numbers follow the [Semantic Versioning 2.0.0](http://semver.org/) scheme. - -Up to version **4.3.2** the tags were released in the following form `x.y[.z]/(basic|standard|full)`. -For example: `4.0/basic`, `4.0.1/standard`. This convention was changed in CKEditor 4.3.3 to conform to the Semantic Versioning scheme. +**Note:** CKEditor is by default installed in the `ckeditor` folder. You can +place the files in whichever you want though. ## Checking Your Installation -The editor comes with a few sample pages that can be used to verify if the installation succeeded. Take a look at the `samples` directory. +The editor comes with a few sample pages that can be used to verify that +installation proceeded properly. Take a look at the `samples` directory. -To test your installation, just call the following page for your website: +To test your installation, just call the following page at your website: http:////samples/index.html For example: http://www.example.com/ckeditor/samples/index.html - -### License - -Licensed under the GPL, LGPL, and MPL licenses, at your choice. - -Please check the `LICENSE.md` file for more information about the license. \ No newline at end of file diff --git a/htdocs/includes/ckeditor/ckeditor/_source/CHANGES.md b/htdocs/includes/ckeditor/ckeditor/_source/CHANGES.md new file mode 100644 index 00000000000..a5bec2ba830 --- /dev/null +++ b/htdocs/includes/ckeditor/ckeditor/_source/CHANGES.md @@ -0,0 +1,442 @@ +CKEditor 4 Changelog +==================== + +## CKEditor 4.3.3 + +Fixed Issues: + +* [#11500](http://dev.ckeditor.com/ticket/11500): [Webkit/Blink] Fixed: Selection lost when setting data in another inline editor. Additionally, [`selection.removeAllRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-removeAllRanges) is now scoped to selection's [root](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-property-root). +* [#11104](http://dev.ckeditor.com/ticket/11104): [IE] Fixed: Various issues with scrolling and selection when focusing widgets. +* [#11487](http://dev.ckeditor.com/ticket/11487): Moving mouse over the [Enhanced Image](http://ckeditor.com/addon/image2) widget will no longer change the value returned by the [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) method. +* [#8673](http://dev.ckeditor.com/ticket/8673): [WebKit] Fixed: Cannot select and remove the [Page Break](http://ckeditor.com/addon/pagebreak). +* [#11413](http://dev.ckeditor.com/ticket/11413): Fixed: Incorrect [`editor.execCommand()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-execCommand) behavior. +* [#11438](http://dev.ckeditor.com/ticket/11438): Splitting table cells vertically is no longer changing table structure. +* [#8899](http://dev.ckeditor.com/ticket/8899): Fixed: Links in the [About CKEditor](http://ckeditor.com/addon/about) dialog window now open in a new browser window or tab. +* [#11490](http://dev.ckeditor.com/ticket/11490): Fixed: [Menu button](http://ckeditor.com/addon/menubutton) panel not showing in the source mode. +* [#11417](http://dev.ckeditor.com/ticket/11417): The [`widget.doubleclick`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-event-doubleclick) event is not canceled anymore after editing was triggered. +* [#11253](http://dev.ckeditor.com/ticket/11253): [IE] Fixed: Clipped upload button in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window. +* [#11359](http://dev.ckeditor.com/ticket/11359): Standardized the way anchors are discovered by the [Link](http://ckeditor.com/addon/link) plugin. +* [#11058](http://dev.ckeditor.com/ticket/11058): [IE8] Fixed: Error when deleting a table row. +* [#11508](http://dev.ckeditor.com/ticket/11508): Fixed: [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor) discovering protected attributes within other attributes' values. +* [#11533](http://dev.ckeditor.com/ticket/11533): Widgets: Avoid recurring upcasts if the DOM structure was modified during an upcast. +* [#11400](http://dev.ckeditor.com/ticket/11400): Fixed: The [`domObject.removeAllListeners()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.domObject-method-removeAllListeners) method does not remove custom listeners completely. +* [#11493](http://dev.ckeditor.com/ticket/11493): Fixed: The [`selection.getRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-getRanges) method does not override cached ranges when used with the `onlyEditables` argument. +* [#11390](http://dev.ckeditor.com/ticket/11390): [IE] All [XML](http://ckeditor.com/addon/xml) plugin [methods](http://docs.ckeditor.com/#!/api/CKEDITOR.xml) now work in IE10+. +* [#11542](http://dev.ckeditor.com/ticket/11542): [IE11] Fixed: Blurry toolbar icons when Right-to-Left UI language is set. +* [#11504](http://dev.ckeditor.com/ticket/11504): Fixed: When [`config.fullPage`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fullPage) is set to `true`, entities are not encoded in editor output. +* [#11004](http://dev.ckeditor.com/ticket/11004): Integrated [Enhanced Image](http://ckeditor.com/addon/image2) dialog window with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter). +* [#11439](http://dev.ckeditor.com/ticket/11439): Fixed: Properties get cloned in the Cell Properties dialog window if multiple cells are selected. + +## CKEditor 4.3.2 + +Fixed Issues: + +* [#11331](http://dev.ckeditor.com/ticket/11331): A menu button will have a changed label when selected instead of using the `aria-pressed` attribute. +* [#11177](http://dev.ckeditor.com/ticket/11177): Widget drag handler improvements: + * [#11176](http://dev.ckeditor.com/ticket/11176): Fixed: Initial position is not updated when the widget data object is empty. + * [#11001](http://dev.ckeditor.com/ticket/11001): Fixed: Multiple synchronous layout recalculations are caused by initial drag handler positioning causing performance issues. + * [#11161](http://dev.ckeditor.com/ticket/11161): Fixed: Drag handler is not repositioned in various situations. + * [#11281](http://dev.ckeditor.com/ticket/11281): Fixed: Drag handler and mask are duplicated after widget reinitialization. +* [#11207](http://dev.ckeditor.com/ticket/11207): [Firefox] Fixed: Misplaced [Enhanced Image](http://ckeditor.com/addon/image2) resizer in the inline editor. +* [#11102](http://dev.ckeditor.com/ticket/11102): `CKEDITOR.template` improvements: + * [#11102](http://dev.ckeditor.com/ticket/11102): Added newline character support. + * [#11216](http://dev.ckeditor.com/ticket/11216): Added "\\'" substring support. +* [#11121](http://dev.ckeditor.com/ticket/11121): [Firefox] Fixed: High Contrast mode is enabled when the editor is loaded in a hidden iframe. +* [#11350](http://dev.ckeditor.com/ticket/11350): The default value of [`config.contentsCss`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-contentsCss) is affected by [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl). +* [#11097](http://dev.ckeditor.com/ticket/11097): Improved the [Autogrow](http://ckeditor.com/addon/autogrow) plugin performance when dealing with very big tables. +* [#11290](http://dev.ckeditor.com/ticket/11290): Removed redundant code in the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin. +* [#11133](http://dev.ckeditor.com/ticket/11133): [Page Break](http://ckeditor.com/addon/pagebreak) becomes editable if pasted. +* [#11126](http://dev.ckeditor.com/ticket/11126): Fixed: Native Undo executed once the bottom of the snapshot stack is reached. +* [#11131](http://dev.ckeditor.com/ticket/11131): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Error thrown when switching to source mode if the selection was in widget's nested editable. +* [#11139](http://dev.ckeditor.com/ticket/11139): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Elements Path is not cleared after switching to source mode. +* [#10778](http://dev.ckeditor.com/ticket/10778): Fixed a bug with range enlargement. The range no longer expands to visible whitespace. +* [#11146](http://dev.ckeditor.com/ticket/11146): [IE] Fixed: Preview window switches Internet Explorer to Quirks Mode. +* [#10762](http://dev.ckeditor.com/ticket/10762): [IE] Fixed: JavaScript code displayed in preview window's URL bar. +* [#11186](http://dev.ckeditor.com/ticket/11186): Introduced the [`widgets.repository.addUpcastCallback()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-addUpcastCallback) method that allows to block upcasting given element to a widget. +* [#11307](http://dev.ckeditor.com/ticket/11307): Fixed: Paste as Plain Text conflict with the [MooTools](http://mootools.net) library. +* [#11140](http://dev.ckeditor.com/ticket/11140): [IE11] Fixed: Anchors are not draggable. +* [#11379](http://dev.ckeditor.com/ticket/11379): Changed default contents `line-height` to unitless values to avoid huge text overlapping (like in [#9696](http://dev.ckeditor.com/ticket/9696)). +* [#10787](http://dev.ckeditor.com/ticket/10787): [Firefox] Fixed: Broken replacement of text while pasting into `div`-based editor. +* [#10884](http://dev.ckeditor.com/ticket/10884): Widgets integration with the [Show Blocks](http://ckeditor.com/addon/showblocks) plugin. +* [#11021](http://dev.ckeditor.com/ticket/11021): Fixed: An error thrown when selecting entire editable contents while fake selection is on. +* [#11086](http://dev.ckeditor.com/ticket/11086): [IE8] Re-enable inline widgets drag&drop in Internet Explorer 8. +* [#11372](http://dev.ckeditor.com/ticket/11372): Widgets: Special characters encoded twice in nested editables. +* [#10068](http://dev.ckeditor.com/ticket/10068): Fixed: Support for protocol-relative URLs. +* [#11283](http://dev.ckeditor.com/ticket/11283): [Enhanced Image](http://ckeditor.com/addon/image2): A `
` element with `text-align: center` and an image inside is not recognised correctly. +* [#11196](http://dev.ckeditor.com/ticket/11196): [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp): Allowed additional keyboard button labels to be translated in the dialog window. + +## CKEditor 4.3.1 + +**Important Notes:** + +* To match the naming convention, the `language` button is now `Language` ([#11201](http://dev.ckeditor.com/ticket/11201)). +* [Enhanced Image](http://ckeditor.com/addon/image2) button, context menu, command, and icon names match those of the [Image](http://ckeditor.com/addon/image) plugin ([#11222](http://dev.ckeditor.com/ticket/11222)). + +Fixed Issues: + +* [#11244](http://dev.ckeditor.com/ticket/11244): Changed: The [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method now fires the [`widget.repository.checkWidgets`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-event-checkWidgets) event, so from CKEditor 4.3.1 it is preferred to use the method rather than fire the event. +* [#11171](http://dev.ckeditor.com/ticket/11171): Fixed: [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) and [`editor.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertText) methods do not call the [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method. +* [#11085](http://dev.ckeditor.com/ticket/11085): [IE8] Replaced preview generated by the [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget with a placeholder. +* [#11044](http://dev.ckeditor.com/ticket/11044): Enhanced WAI-ARIA support for the [Language](http://ckeditor.com/addon/language) plugin drop-down menu. +* [#11075](http://dev.ckeditor.com/ticket/11075): With drop-down menu button focused, pressing the *Down Arrow* key will now open the menu and focus its first option. +* [#11165](http://dev.ckeditor.com/ticket/11165): Fixed: The [File Browser](http://ckeditor.com/addon/filebrowser) plugin cannot be removed from the editor. +* [#11159](http://dev.ckeditor.com/ticket/11159): [IE9-10] [Enhanced Image](http://ckeditor.com/addon/image2): Fixed buggy discovery of image dimensions. +* [#11101](http://dev.ckeditor.com/ticket/11101): Drop-down lists no longer break when given double quotes. +* [#11077](http://dev.ckeditor.com/ticket/11077): [Enhanced Image](http://ckeditor.com/addon/image2): Empty undo step recorded when resizing the image. +* [#10853](http://dev.ckeditor.com/ticket/10853): [Enhanced Image](http://ckeditor.com/addon/image2): Widget has paragraph wrapper when de-captioning unaligned image. +* [#11198](http://dev.ckeditor.com/ticket/11198): Widgets: Drag handler is not fully visible when an inline widget is in a heading. +* [#11132](http://dev.ckeditor.com/ticket/11132): [Firefox] Fixed: Caret is lost after drag and drop of an inline widget. +* [#11182](http://dev.ckeditor.com/ticket/11182): [IE10-11] Fixed: Editor crashes (IE11) or works with minor issues (IE10) if a page is loaded in Quirks Mode. See [`env.quirks`](http://docs.ckeditor.com/#!/api/CKEDITOR.env-property-quirks) for more details. +* [#11204](http://dev.ckeditor.com/ticket/11204): Added `figure` and `figcaption` styles to the `contents.css` file so [Enhanced Image](http://ckeditor.com/addon/image2) looks nicer. +* [#11202](http://dev.ckeditor.com/ticket/11202): Fixed: No newline in [BBCode](http://ckeditor.com/addon/bbcode) mode. +* [#10890](http://dev.ckeditor.com/ticket/10890): Fixed: Error thrown when pressing the *Delete* key in a list item. +* [#10055](http://dev.ckeditor.com/ticket/10055): [IE8-10] Fixed: *Delete* pressed on a selected image causes the browser to go back. +* [#11183](http://dev.ckeditor.com/ticket/11183): Fixed: Inserting a horizontal rule or a table in multiple row selection causes a browser crash. Additionally, the [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) method does not insert the element into every range of a selection any more. +* [#11042](http://dev.ckeditor.com/ticket/11042): Fixed: Selection made on an element containing a non-editable element was not auto faked. +* [#11125](http://dev.ckeditor.com/ticket/11125): Fixed: Keyboard navigation through menu and drop-down items will now cycle. +* [#11011](http://dev.ckeditor.com/ticket/11011): Fixed: The [`editor.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) method removes attributes from nested elements. +* [#11179](http://dev.ckeditor.com/ticket/11179): Fixed: [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy) does not cleanup content generated by the [Table Resize](http://ckeditor.com/addon/tableresize) plugin for inline editors. +* [#11237](http://dev.ckeditor.com/ticket/11237): Fixed: Table border attribute value is deleted when pasting content from Microsoft Word. +* [#11250](http://dev.ckeditor.com/ticket/11250): Fixed: HTML entities inside the ` + * ... + * CKEDITOR.replace( 'myfield' ); + * + * var textarea = document.body.appendChild( document.createElement( 'textarea' ) ); + * CKEDITOR.replace( textarea ); + * + * @param {Object/String} element The DOM element (textarea), its ID, or name. + * @param {Object} [config] The specific configuration to apply to this + * editor instance. Configuration set here will override the global CKEditor settings + * (see {@link CKEDITOR.config}). + * @returns {CKEDITOR.editor} The editor instance created. + */ + CKEDITOR.replace = function( element, config ) { + return createInstance( element, config, null, CKEDITOR.ELEMENT_MODE_REPLACE ); + }; + + /** + * Creates a new editor instance at the end of a specific DOM element. + * + *
+ * ... + * CKEDITOR.appendTo( 'editorSpace' ); + * + * @param {Object/String} element The DOM element, its ID, or name. + * @param {Object} [config] The specific configuration to apply to this + * editor instance. Configuration set here will override the global CKEditor settings + * (see {@link CKEDITOR.config}). + * @param {String} [data] Since 3.3. Initial value for the instance. + * @returns {CKEDITOR.editor} The editor instance created. + */ + CKEDITOR.appendTo = function( element, config, data ) + { + return createInstance( element, config, data, CKEDITOR.ELEMENT_MODE_APPENDTO ); + }; + + /** + * Replaces all ` + data = protectElements( data, protectTextareaRegex ); + + // Before anything, we must protect the URL attributes as the + // browser may changing them when setting the innerHTML later in + // the code. + data = protectAttributes( data ); + + // Protect elements than can't be set inside a DIV. E.g. IE removes + // style tags from innerHTML. (#3710) + data = protectElements( data, protectElementsRegex ); + + // Certain elements has problem to go through DOM operation, protect + // them by prefixing 'cke' namespace. (#3591) + data = protectElementsNames( data ); + + // All none-IE browsers ignore self-closed custom elements, + // protecting them into open-close. (#3591) + data = protectSelfClosingElements( data ); + + // Compensate one leading line break after
 open as browsers
+			// eat it up. (#5789)
+			data = protectPreFormatted( data );
+
+			var fixBin = evtData.context || editor.editable().getName(),
+				isPre;
+
+			// Old IEs loose formats when load html into 
.
+			if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && fixBin == 'pre' ) {
+				fixBin = 'div';
+				data = '
' + data + '
'; + isPre = 1; + } + + // Call the browser to help us fixing a possibly invalid HTML + // structure. + var el = editor.document.createElement( fixBin ); + // Add fake character to workaround IE comments bug. (#3801) + el.setHtml( 'a' + data ); + data = el.getHtml().substr( 1 ); + + // Restore shortly protected attribute names. + data = data.replace( new RegExp( ' data-cke-' + CKEDITOR.rnd + '-', 'ig' ), ' ' ); + + isPre && ( data = data.replace( /^
|<\/pre>$/gi, '' ) );
+
+			// Unprotect "some" of the protected elements at this point.
+			data = unprotectElementNames( data );
+
+			data = unprotectElements( data );
+
+			// Restore the comments that have been protected, in this way they
+			// can be properly filtered.
+			data = unprotectRealComments( data );
+
+			// Now use our parser to make further fixes to the structure, as
+			// well as apply the filter.
+			evtData.dataValue = CKEDITOR.htmlParser.fragment.fromHtml(
+				data, evtData.context, evtData.fixForBody === false ? false : getFixBodyTag( evtData.enterMode, editor.config.autoParagraph ) );
+		}, null, null, 5 );
+
+		// Filter incoming "data".
+		// Add element filter before htmlDataProcessor.dataFilter when purifying input data to correct html.
+		editor.on( 'toHtml', function( evt ) {
+			if ( evt.data.filter.applyTo( evt.data.dataValue, true, evt.data.dontFilter, evt.data.enterMode ) )
+				editor.fire( 'dataFiltered' );
+		}, null, null, 6 );
+
+		editor.on( 'toHtml', function( evt ) {
+			evt.data.dataValue.filterChildren( that.dataFilter, true );
+		}, null, null, 10 );
+
+		editor.on( 'toHtml', function( evt ) {
+			var evtData = evt.data,
+				data = evtData.dataValue,
+				writer = new CKEDITOR.htmlParser.basicWriter();
+
+			data.writeChildrenHtml( writer );
+			data = writer.getHtml( true );
+
+			// Protect the real comments again.
+			evtData.dataValue = protectRealComments( data );
+		}, null, null, 15 );
+
+
+		editor.on( 'toDataFormat', function( evt ) {
+			var data = evt.data.dataValue;
+
+			// #10854 - we need to strip leading blockless 
which FF adds + // automatically when editable contains only non-editable content. + // We do that for every browser (so it's a constant behavior) and + // not in BR mode, in which chance of valid leading blockless
is higher. + if ( evt.data.enterMode != CKEDITOR.ENTER_BR ) + data = data.replace( /^
/i, '' ); + + evt.data.dataValue = CKEDITOR.htmlParser.fragment.fromHtml( + data, evt.data.context, getFixBodyTag( evt.data.enterMode, editor.config.autoParagraph ) ); + }, null, null, 5 ); + + editor.on( 'toDataFormat', function( evt ) { + evt.data.dataValue.filterChildren( that.htmlFilter, true ); + }, null, null, 10 ); + + // Transform outcoming "data". + // Add element filter after htmlDataProcessor.htmlFilter when preparing output data HTML. + editor.on( 'toDataFormat', function( evt ) { + evt.data.filter.applyTo( evt.data.dataValue, false, true ); + }, null, null, 11 ); + + editor.on( 'toDataFormat', function( evt ) { + var data = evt.data.dataValue, + writer = that.writer; + + writer.reset(); + data.writeChildrenHtml( writer ); + data = writer.getHtml( true ); + + // Restore those non-HTML protected source. (#4475,#4880) + data = unprotectRealComments( data ); + data = unprotectSource( data, editor ); + + evt.data.dataValue = data; + }, null, null, 15 ); + }; + + CKEDITOR.htmlDataProcessor.prototype = { + /** + * Processes the input (potentially malformed) HTML to a purified form which + * is suitable for using in the WYSIWYG editable. + * + * This method fires the {@link CKEDITOR.editor#toHtml} event which makes it possible + * to hook into the process at various stages. + * + * **Note:** Since CKEditor 4.3 the signature of this method changed and all options + * are now grouped in one `options` object. Previously `context`, `fixForBody` and `dontFilter` + * were passed separately. + * + * @param {String} data The raw data. + * @param {Object} [options] The options object. + * @param {String} [options.context] The tag name of a context element within which + * the input is to be processed, default to be the editable element. + * If `null` is passed, then data will be parsed without context (as children of {@link CKEDITOR.htmlParser.fragment}). + * See {@link CKEDITOR.htmlParser.fragment#fromHtml} for more details. + * @param {Boolean} [options.fixForBody=true] Whether to trigger the auto paragraph for non-block contents. + * @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter}, + * passed instance will be used to filter the content. + * @param {Boolean} [options.dontFilter] Do not filter data with {@link CKEDITOR.filter} (note: transformations + * will be still applied). + * @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}. + * @returns {String} + */ + toHtml: function( data, options, fixForBody, dontFilter ) { + var editor = this.editor, + context, filter, enterMode; + + // Typeof null == 'object', so check truthiness of options too. + if ( options && typeof options == 'object' ) { + context = options.context; + fixForBody = options.fixForBody; + dontFilter = options.dontFilter; + filter = options.filter; + enterMode = options.enterMode; + } + // Backward compatibility. Since CKEDITOR 4.3 every option was a separate argument. + else + context = options; + + // Fall back to the editable as context if not specified. + if ( !context && context !== null ) + context = editor.editable().getName(); + + return editor.fire( 'toHtml', { + dataValue: data, + context: context, + fixForBody: fixForBody, + dontFilter: dontFilter, + filter: filter || editor.filter, + enterMode: enterMode || editor.enterMode + } ).dataValue; + }, + + /** + * See {@link CKEDITOR.dataProcessor#toDataFormat}. + * + * This method fires the {@link CKEDITOR.editor#toDataFormat} event which makes it possible + * to hook into the process at various steps. + * + * @param {String} html + * @param {Object} [options] The options object. + * @param {String} [options.context] The tag name of a context element within which + * the input is to be processed, default to be the editable element. + * @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter}, + * passed instance will be used to apply content transformations to the content. + * @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}. + * @returns {String} + */ + toDataFormat: function( html, options ) { + var context, filter, enterMode; + + // Do not shorten this to `options && options.xxx`, because + // falsy `options` will be passed instead of undefined. + if ( options ) { + context = options.context; + filter = options.filter; + enterMode = options.enterMode; + } + + // Fall back to the editable as context if not specified. + if ( !context && context !== null ) + context = this.editor.editable().getName(); + + return this.editor.fire( 'toDataFormat', { + dataValue: html, + filter: filter || this.editor.filter, + context: context, + enterMode: enterMode || this.editor.enterMode + } ).dataValue; + } + }; + + // Produce a set of filtering rules that handles bogus and filler node at the + // end of block/pseudo block, in the following consequence: + // 1. elements: - this filter removes any bogus node, then check + // if it's an empty block that requires a filler. + // 2. elements:
- After cleaned with bogus, this filter checks the real + // line-break BR to compensate a filler after it. + // + // Terms definitions: + // filler: An element that's either
or &NBSP; at the end of block that established line height. + // bogus: Whenever a filler is proceeded with inline content, it becomes a bogus which is subjected to be removed. + // + // Various forms of the filler: + // In output HTML: Filler should be consistently &NBSP;
at the end of block is always considered as bogus. + // In Wysiwyg HTML: Browser dependent - see env.needsBrFiller. Either BR for when needsBrFiller is true, or &NBSP; otherwise. + //
is NEVER considered as bogus when needsBrFiller is true. + function createBogusAndFillerRules( editor, type ) { + function createFiller( isOutput ) { + return isOutput || CKEDITOR.env.needsNbspFiller ? + new CKEDITOR.htmlParser.text( '\xa0' ) : + new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } ); + } + + // This text block filter, remove any bogus and create the filler on demand. + function blockFilter( isOutput, fillEmptyBlock ) { + + return function( block ) { + + // DO NOT apply the filer if it's a fragment node. + if ( block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) + return; + + cleanBogus( block ); + + // [Opera] it's mandatory for the filler to present inside of empty block when in WYSIWYG. + if ( ( ( CKEDITOR.env.opera && !isOutput ) || + ( typeof fillEmptyBlock == 'function' ? fillEmptyBlock( block ) !== false : fillEmptyBlock ) ) && + isEmptyBlockNeedFiller( block ) ) + { + block.add( createFiller( isOutput ) ); + } + }; + } + + // Append a filler right after the last line-break BR, found at the end of block. + function brFilter( isOutput ) { + return function( br ) { + + // DO NOT apply the filer if parent's a fragment node. + if ( br.parent.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) + return; + + var attrs = br.attributes; + // Dismiss BRs that are either bogus or eol marker. + if ( 'data-cke-bogus' in attrs || + 'data-cke-eol' in attrs ) { + delete attrs [ 'data-cke-bogus' ]; + return; + } + + // Judge the tail line-break BR, and to insert bogus after it. + var next = getNext( br ), previous = getPrevious( br ); + + if ( !next && isBlockBoundary( br.parent ) ) + append( br.parent, createFiller( isOutput ) ); + else if ( isBlockBoundary( next ) && previous && !isBlockBoundary( previous ) ) + createFiller( isOutput ).insertBefore( next ); + }; + } + + // Determinate whether this node is potentially a bogus node. + function maybeBogus( node, atBlockEnd ) { + + // BR that's not from IE<11 DOM, except for a EOL marker. + if ( !( isOutput && !CKEDITOR.env.needsBrFiller ) && + node.type == CKEDITOR.NODE_ELEMENT && node.name == 'br' && + !node.attributes[ 'data-cke-eol' ] ) + return true; + + var match; + // NBSP, possibly. + if ( node.type == CKEDITOR.NODE_TEXT && + ( match = node.value.match( tailNbspRegex ) ) ) + { + // We need to separate tail NBSP out of a text node, for later removal. + if ( match.index ) { + ( new CKEDITOR.htmlParser.text( node.value.substring( 0, match.index ) ) ).insertBefore( node ); + node.value = match[ 0 ]; + } + + // From IE<11 DOM, at the end of a text block, or before block boundary. + if ( !CKEDITOR.env.needsBrFiller && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) ) + return true; + + // From the output. + if ( !isOutput ) { + var previous = node.previous; + + // Following a line-break at the end of block. + if ( previous && previous.name == 'br' ) + return true; + + // Or a single NBSP between two blocks. + if ( !previous || isBlockBoundary( previous ) ) + return true; + } + } + + return false; + } + + // Removes all bogus inside of this block, and to convert fillers into the proper form. + function cleanBogus( block ) { + var bogus = []; + var last = getLast( block ), node, previous; + if ( last ) { + + // Check for bogus at the end of this block. + // e.g.

foo

+ maybeBogus( last, 1 ) && bogus.push( last ); + + while ( last ) { + + // Check for bogus at the end of any pseudo block contained. + if ( isBlockBoundary( last ) && + ( node = getPrevious( last ) ) && + maybeBogus( node ) ) + { + // Bogus must have inline proceeding, instead single BR between two blocks, + // is considered as filler, e.g.


+ if ( ( previous = getPrevious( node ) ) && !isBlockBoundary( previous ) ) + bogus.push( node ); + // Convert the filler into appropriate form. + else { + createFiller( isOutput ).insertAfter( node ); + node.remove(); + } + } + + last = last.previous; + } + } + + // Now remove all bogus collected from above. + for ( var i = 0 ; i < bogus.length ; i++ ) + bogus[ i ].remove(); + } + + // Judge whether it's an empty block that requires a filler node. + function isEmptyBlockNeedFiller( block ) { + + // DO NOT fill empty editable in IE<11. + if ( !isOutput && !CKEDITOR.env.needsBrFiller && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) + return false; + + // 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg; + // 2. For the rest, at least table cell and list item need no filler space. (#6248) + if ( !isOutput && !CKEDITOR.env.needsBrFiller && + ( document.documentMode > 7 || + block.name in CKEDITOR.dtd.tr || + block.name in CKEDITOR.dtd.$listItem ) ) { + return false; + } + + var last = getLast( block ); + return !last || block.name == 'form' && last.name == 'input' ; + } + + var rules = { elements: {} }; + var isOutput = type == 'html'; + + // Build the list of text blocks. + var textBlockTags = CKEDITOR.tools.extend( {}, blockLikeTags ); + for ( var i in textBlockTags ) { + if ( !( '#' in dtd[ i ] ) ) + delete textBlockTags[ i ]; + } + + for ( i in textBlockTags ) + rules.elements[ i ] = blockFilter( isOutput, editor.config.fillEmptyBlocks !== false ); + + // Editable element is to be checked separately. + rules.root = blockFilter( isOutput ); + rules.elements.br = brFilter( isOutput ); + return rules; + } + + function getFixBodyTag( enterMode, autoParagraph ) { + return ( enterMode != CKEDITOR.ENTER_BR && autoParagraph !== false ) ? enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false; + } + + // Regex to scan for   at the end of blocks, which are actually placeholders. + // Safari transforms the   to \xa0. (#4172) + var tailNbspRegex = /(?: |\xa0)$/; + + var protectedSourceMarker = '{cke_protected}'; + + function getLast( node ) { + var last = node.children[ node.children.length - 1 ]; + while ( last && isEmpty( last ) ) + last = last.previous; + return last; + } + + function getNext( node ) { + var next = node.next; + while ( next && isEmpty( next ) ) + next = next.next; + return next; + } + + function getPrevious( node ) { + var previous = node.previous; + while ( previous && isEmpty( previous ) ) + previous = previous.previous; + return previous; + } + + // Judge whether the node is an ghost node to be ignored, when traversing. + function isEmpty( node ) { + return node.type == CKEDITOR.NODE_TEXT && + !CKEDITOR.tools.trim( node.value ) || + node.type == CKEDITOR.NODE_ELEMENT && + node.attributes[ 'data-cke-bookmark' ]; + } + + // Judge whether the node is a block-like element. + function isBlockBoundary( node ) { + return node && + ( node.type == CKEDITOR.NODE_ELEMENT && node.name in blockLikeTags || + node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ); + } + + function append( parent, node ) { + var last = parent.children[ parent.children.length -1 ]; + parent.children.push( node ); + node.parent = parent; + if ( last ) { + last.next = node; + node.previous = last; + } + } + + function getNodeIndex( node ) { + return node.parent ? node.getIndex() : -1; + } + + var dtd = CKEDITOR.dtd, + // Define orders of table elements. + tableOrder = [ 'caption', 'colgroup', 'col', 'thead', 'tfoot', 'tbody' ], + // List of all block elements. + blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$blockLimit, dtd.$block ); + + // + // DATA filter rules ------------------------------------------------------ + // + + var defaultDataFilterRulesEditableOnly = { + elements: { + input: protectReadOnly, + textarea: protectReadOnly + } + }; + + // These rules will also be applied to non-editable content. + var defaultDataFilterRulesForAll = { + attributeNames: [ + // Event attributes (onXYZ) must not be directly set. They can become + // active in the editing area (IE|WebKit). + [ ( /^on/ ), 'data-cke-pa-on' ], + + // Don't let some old expando enter editor. Concerns only IE8, + // but for consistency remove on all browsers. + [ ( /^data-cke-expando$/ ), '' ] + ] + }; + + // Disable form elements editing mode provided by some browsers. (#5746) + function protectReadOnly( element ) { + var attrs = element.attributes; + + // We should flag that the element was locked by our code so + // it'll be editable by the editor functions (#6046). + if ( attrs.contenteditable != 'false' ) + attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1; + + attrs.contenteditable = 'false'; + } + + // + // HTML filter rules ------------------------------------------------------ + // + + var defaultHtmlFilterRulesEditableOnly = { + elements: { + embed: function( element ) { + var parent = element.parent; + + // If the is child of a , copy the width + // and height attributes from it. + if ( parent && parent.name == 'object' ) { + var parentWidth = parent.attributes.width, + parentHeight = parent.attributes.height; + if ( parentWidth ) + element.attributes.width = parentWidth; + if ( parentHeight ) + element.attributes.height = parentHeight; + } + }, + + // Remove empty link but not empty anchor. (#3829) + a: function( element ) { + if ( !( element.children.length || element.attributes.name || element.attributes[ 'data-cke-saved-name' ] ) ) + return false; + } + } + }; + + // These rules will also be applied to non-editable content. + var defaultHtmlFilterRulesForAll = { + elementNames: [ + // Remove the "cke:" namespace prefix. + [ ( /^cke:/ ), '' ], + + // Ignore tags. + [ ( /^\?xml:namespace$/ ), '' ] + ], + + attributeNames: [ + // Attributes saved for changes and protected attributes. + [ ( /^data-cke-(saved|pa)-/ ), '' ], + + // All "data-cke-" attributes are to be ignored. + [ ( /^data-cke-.*/ ), '' ], + + [ 'hidefocus', '' ] + ], + + elements: { + $: function( element ) { + var attribs = element.attributes; + + if ( attribs ) { + // Elements marked as temporary are to be ignored. + if ( attribs[ 'data-cke-temp' ] ) + return false; + + // Remove duplicated attributes - #3789. + var attributeNames = [ 'name', 'href', 'src' ], + savedAttributeName; + for ( var i = 0; i < attributeNames.length; i++ ) { + savedAttributeName = 'data-cke-saved-' + attributeNames[ i ]; + savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] ); + } + } + + return element; + }, + + // The contents of table should be in correct order (#4809). + table: function( element ) { + // Clone the array as it would become empty during the sort call. + var children = element.children.slice( 0 ); + children.sort( function( node1, node2 ) { + var index1, index2; + + // Compare in the predefined order. + if ( node1.type == CKEDITOR.NODE_ELEMENT && + node2.type == node1.type ) { + index1 = CKEDITOR.tools.indexOf( tableOrder, node1.name ); + index2 = CKEDITOR.tools.indexOf( tableOrder, node2.name ); + } + + // Make sure the sort is stable, if no order can be established above. + if ( !( index1 > -1 && index2 > -1 && index1 != index2 ) ) { + index1 = getNodeIndex( node1 ); + index2 = getNodeIndex( node2 ); + } + + return index1 > index2 ? 1 : -1; + } ); + }, + + // Restore param elements into self-closing. + param: function( param ) { + param.children = []; + param.isEmpty = true; + return param; + }, + + // Remove dummy span in webkit. + span: function( element ) { + if ( element.attributes[ 'class' ] == 'Apple-style-span' ) + delete element.name; + }, + + html: function( element ) { + delete element.attributes.contenteditable; + delete element.attributes[ 'class' ]; + }, + + body: function( element ) { + delete element.attributes.spellcheck; + delete element.attributes.contenteditable; + }, + + style: function( element ) { + var child = element.children[ 0 ]; + if ( child && child.value ) + child.value = CKEDITOR.tools.trim( child.value ); + + if ( !element.attributes.type ) + element.attributes.type = 'text/css'; + }, + + title: function( element ) { + var titleText = element.children[ 0 ]; + + // Append text-node to title tag if not present (i.e. non-IEs) (#9882). + !titleText && append( element, titleText = new CKEDITOR.htmlParser.text() ); + + // Transfer data-saved title to title tag. + titleText.value = element.attributes[ 'data-cke-title' ] || ''; + }, + + input: unprotectReadyOnly, + textarea: unprotectReadyOnly + }, + + attributes: { + 'class': function( value, element ) { + // Remove all class names starting with "cke_". + return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false; + } + } + }; + + if ( CKEDITOR.env.ie ) { + // IE outputs style attribute in capital letters. We should convert + // them back to lower case, while not hurting the values (#5930) + defaultHtmlFilterRulesForAll.attributes.style = function( value, element ) { + return value.replace( /(^|;)([^\:]+)/g, function( match ) { + return match.toLowerCase(); + } ); + }; + } + + // Disable form elements editing mode provided by some browsers. (#5746) + function unprotectReadyOnly( element ) { + var attrs = element.attributes; + switch ( attrs[ 'data-cke-editable' ] ) { + case 'true': + attrs.contenteditable = 'true'; + break; + case '1': + delete attrs.contenteditable; + break; + } + } + + // + // Preprocessor filters --------------------------------------------------- + // + + var protectElementRegex = /<(a|area|img|input|source)\b([^>]*)>/gi, + // Be greedy while looking for protected attributes. This will let us avoid an unfortunate + // situation when "nested attributes", which may appear valid, are also protected. + // I.e. if we consider the following HTML: + // + // + // + // then the "non-greedy match" returns: + // + // 'href' => '"X"' // It's wrong! Href is not an attribute of . + // + // while greedy match returns: + // + // 'data-x' => '<a href="X"' + // + // which, can be easily filtered out (#11508). + protectAttributeRegex = /([\w-]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi, + protectAttributeNameRegex = /^(href|src|name)$/i; + + // Note: we use lazy star '*?' to prevent eating everything up to the last occurrence of or . + var protectElementsRegex = /(?:])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi, + protectTextareaRegex = /(])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi, + encodedElementsRegex = /([^<]*)<\/cke:encoded>/gi; + + var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi, + unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi; + + var protectSelfClosingRegex = /]*?)\/?>(?!\s*<\/cke:\1)/gi; + + function protectAttributes( html ) { + return html.replace( protectElementRegex, function( element, tag, attributes ) { + return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) { + // Avoid corrupting the inline event attributes (#7243). + // We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218) + if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 ) + return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr; + + return fullAttr; + } ) + '>'; + } ); + } + + function protectElements( html, regex ) { + return html.replace( regex, function( match, tag, content ) { + // Encode < and > in textarea because this won't be done by a browser, since + // textarea will be protected during passing data through fix bin. + if ( match.indexOf( '/g, '>' ) + ''; + + return '' + encodeURIComponent( match ) + ''; + } ); + } + + function unprotectElements( html ) { + return html.replace( encodedElementsRegex, function( match, encoded ) { + return decodeURIComponent( encoded ); + } ); + } + + function protectElementsNames( html ) { + return html.replace( protectElementNamesRegex, '$1cke:$2' ); + } + + function unprotectElementNames( html ) { + return html.replace( unprotectElementNamesRegex, '$1$2' ); + } + + function protectSelfClosingElements( html ) { + return html.replace( protectSelfClosingRegex, '' ); + } + + function protectPreFormatted( html ) { + return CKEDITOR.env.opera ? html : html.replace( /(]*>)(\r\n|\n)/g, '$1$2$2' ); + } + + function protectRealComments( html ) { + return html.replace( //g, function( match ) { + return ''; + } ); + } + + function unprotectRealComments( html ) { + return html.replace( //g, function( match, data ) { + return decodeURIComponent( data ); + } ); + } + + function unprotectSource( html, editor ) { + var store = editor._.dataStore; + + return html.replace( //g, function( match, data ) { + return decodeURIComponent( data ); + } ).replace( /\{cke_protected_(\d+)\}/g, function( match, id ) { + return store && store[ id ] || ''; + } ); + } + + function protectSource( data, editor ) { + var protectedHtml = [], + protectRegexes = editor.config.protectedSource, + store = editor._.dataStore || ( editor._.dataStore = { id: 1 } ), + tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g; + + var regexes = [ + // Script tags will also be forced to be protected, otherwise + // IE will execute them. + ( //gi ), + + //