2
0
forked from Wavyzz/dolibarr

Restore manual install of ckeditor. It seems upgrade with phpcomposer

make some plugins not working.
This commit is contained in:
Laurent Destailleur
2015-09-18 12:10:47 +02:00
parent 9d7a77f2e5
commit a4ab5f5552
5669 changed files with 147640 additions and 9398 deletions

View File

@@ -555,7 +555,16 @@ print '<input type="hidden" name="token" value="'.$_SESSION['newtoken'].'">';
print '<input type="hidden" name="action" value="set_COMMANDE_FREE_TEXT">'; print '<input type="hidden" name="action" value="set_COMMANDE_FREE_TEXT">';
print '<tr '.$bc[$var].'><td colspan="2">'; print '<tr '.$bc[$var].'><td colspan="2">';
print $langs->trans("FreeLegalTextOnOrders").' ('.$langs->trans("AddCRIfTooLong").')<br>'; print $langs->trans("FreeLegalTextOnOrders").' ('.$langs->trans("AddCRIfTooLong").')<br>';
print '<textarea name="COMMANDE_FREE_TEXT" class="flat" cols="120">'.$conf->global->COMMANDE_FREE_TEXT.'</textarea>'; if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT))
{
print '<textarea name="COMMANDE_FREE_TEXT" class="flat" cols="120">'.$conf->global->COMMANDE_FREE_TEXT.'</textarea>';
}
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 '</td><td align="right">'; print '</td><td align="right">';
print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">'; print '<input type="submit" class="button" value="'.$langs->trans("Modify").'">';
print "</td></tr>\n"; print "</td></tr>\n";

View File

@@ -290,6 +290,55 @@ function pdf_getHeightForLogo($logo, $url = false)
return $height; 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 * Returns the name of the thirdparty
* *
@@ -854,11 +903,20 @@ function pdf_pagefoot(&$pdf,$outputlangs,$paramfreetext,$fromcompany,$marge_bass
$freetextheight=0; $freetextheight=0;
if ($line) // Free text 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. //$line="eee<br>\nfd<strong>sf</strong>sdf<br>\nghfghg<br>";
if (! empty($conf->global->MAIN_USE_AUTOWRAP_ON_FREETEXT)) { if (empty($conf->global->PDF_ALLOW_HTML_FOR_FREE_TEXT)) // by default
$width=200; $align='C'; {
$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 '<br>'.$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); $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 if ($line) // Free text
{ {
$pdf->SetXY($dims['lm'],-$posy); $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; $posy-=$freetextheight;
} }

View File

@@ -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). 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
There are four versions for each release &mdash; `standard-all`, `basic`, `standard`, and `full`. part of web applications and websites.
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.
## Documentation ## Documentation
Developer documentation for CKEditor is available online at: <http://docs.ckeditor.com>. The full editor documentation is available online at the following address:
http://docs.ckeditor.com
## Installation ## 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 <release branch> git://github.com/ckeditor/ckeditor-releases.git **Note:** CKEditor is by default installed in the `ckeditor` folder. You can
place the files in whichever you want though.
### 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 <release branch> git://github.com/ckeditor/ckeditor-releases.git <clone dir>
git commit -m "Added CKEditor submodule in <clone dir> 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` &ndash; the latest release of the `standard-all` preset (including betas).
- `stable` &ndash; the latest stable release of the `standard-all` preset (non-beta).
- `A.B.x` (e.g. `4.3.x`) &ndash; the latest release of the `standard-all` preset in the `A.B` branch.
- `(basic|standard|full)/stable` &ndash; the latest stable release tag point (non-beta).
- `(basic|standard|full)/latest` &ndash; the latest release tag point (including betas).
- `(basic|standard|full)/A.B.x` (e.g. `basic/4.0.x`) &ndash; 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` &ndash; contains the `standard-all` editor build, e.g. `4.3.3`, `4.4.0` etc.
- `(basic|standard|full)/x.y.z` &ndash; 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.
## Checking Your Installation ## 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://<your site>/<CKEditor installation path>/samples/index.html http://<your site>/<CKEditor installation path>/samples/index.html
For example: For example:
http://www.example.com/ckeditor/samples/index.html 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.

View File

@@ -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 `<div>` 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 `<textarea>` element are not encoded.
* [#11260](http://dev.ckeditor.com/ticket/11260): Fixed: Initially disabled buttons are not read by JAWS as disabled.
* [#11200](http://dev.ckeditor.com/ticket/11200): Added [Clipboard](http://ckeditor.com/addon/clipboard) plugin as a dependency for [Widget](http://ckeditor.com/addon/widget) to fix drag and drop.
## CKEditor 4.3
New Features:
* [#10612](http://dev.ckeditor.com/ticket/10612): Internet Explorer 11 support.
* [#10869](http://dev.ckeditor.com/ticket/10869): Widgets: Added better integration with the [Elements Path](http://ckeditor.com/addon/elementspath) plugin.
* [#10886](http://dev.ckeditor.com/ticket/10886): Widgets: Added tooltip to the drag handle.
* [#10933](http://dev.ckeditor.com/ticket/10933): Widgets: Introduced drag and drop of block widgets with the [Line Utilities](http://ckeditor.com/addon/lineutils) plugin.
* [#10936](http://dev.ckeditor.com/ticket/10936): Widget System changes for easier integration with other dialog systems.
* [#10895](http://dev.ckeditor.com/ticket/10895): [Enhanced Image](http://ckeditor.com/addon/image2): Added file browser integration.
* [#11002](http://dev.ckeditor.com/ticket/11002): Added the [`draggable`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.definition-property-draggable) option to disable drag and drop support for widgets.
* [#10937](http://dev.ckeditor.com/ticket/10937): [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget improvements:
* loading indicator ([#10948](http://dev.ckeditor.com/ticket/10948)),
* applying paragraph changes (like font color change) to iframe ([#10841](http://dev.ckeditor.com/ticket/10841)),
* Firefox and IE9 clipboard fixes ([#10857](http://dev.ckeditor.com/ticket/10857)),
* fixing same origin policy issue ([#10840](http://dev.ckeditor.com/ticket/10840)),
* fixing undo bugs ([#10842](http://dev.ckeditor.com/ticket/10842), [#10930](http://dev.ckeditor.com/ticket/10930)),
* fixing other minor bugs.
* [#10862](http://dev.ckeditor.com/ticket/10862): [Placeholder](http://ckeditor.com/addon/placeholder) plugin was rewritten as a widget.
* [#10822](http://dev.ckeditor.com/ticket/10822): Added styles system integration with non-editable elements (for example widgets) and their nested editables. Styles cannot change non-editable content and are applied in nested editable only if allowed by its type and content filter.
* [#10856](http://dev.ckeditor.com/ticket/10856): Menu buttons will now toggle the visibility of their panels when clicked multiple times. [Language](http://ckeditor.com/addon/language) plugin fixes: Added active language highlighting, added an option to remove the language.
* [#10028](http://dev.ckeditor.com/ticket/10028): New [`config.dialog_noConfirmCancel`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-dialog_noConfirmCancel) configuration option that eliminates the need to confirm closing of a dialog window when the user changed any of its fields.
* [#10848](http://dev.ckeditor.com/ticket/10848): Integrate remaining plugins ([Styles](http://ckeditor.com/addon/stylescombo), [Format](http://ckeditor.com/addon/format), [Font](http://ckeditor.com/addon/font), [Color Button](http://ckeditor.com/addon/colorbutton), [Language](http://ckeditor.com/addon/language) and [Indent](http://ckeditor.com/addon/indent)) with [active filter](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeFilter).
* [#10855](http://dev.ckeditor.com/ticket/10855): Change the extension of emoticons in the [BBCode](http://ckeditor.com/addon/bbcode) sample from GIF to PNG.
Fixed Issues:
* [#10831](http://dev.ckeditor.com/ticket/10831): [Enhanced Image](http://ckeditor.com/addon/image2): Merged `image2inline` and `image2block` into one `image2` widget.
* [#10835](http://dev.ckeditor.com/ticket/10835): [Enhanced Image](http://ckeditor.com/addon/image2): Improved visibility of the resize handle.
* [#10836](http://dev.ckeditor.com/ticket/10836): [Enhanced Image](http://ckeditor.com/addon/image2): Preserve custom mouse cursor while resizing the image.
* [#10939](http://dev.ckeditor.com/ticket/10939): [Firefox] [Enhanced Image](http://ckeditor.com/addon/image2): hovering the image causes it to change.
* [#10866](http://dev.ckeditor.com/ticket/10866): Fixed: Broken *Tab* key navigation in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window.
* [#10833](http://dev.ckeditor.com/ticket/10833): Fixed: *Lock ratio* option should be on by default in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window.
* [#10881](http://dev.ckeditor.com/ticket/10881): Various improvements to *Enter* key behavior in nested editables.
* [#10879](http://dev.ckeditor.com/ticket/10879): [Remove Format](http://ckeditor.com/addon/removeformat) should not leak from a nested editable.
* [#10877](http://dev.ckeditor.com/ticket/10877): Fixed: [WebSpellChecker](http://ckeditor.com/addon/wsc) fails to apply changes if a nested editable was focused.
* [#10877](http://dev.ckeditor.com/ticket/10877): Fixed: [SCAYT](http://ckeditor.com/addon/wsc) blocks typing in nested editables.
* [#11079](http://dev.ckeditor.com/ticket/11079): Add button icons to the [Placeholder](http://ckeditor.com/addon/placeholder) sample.
* [#10870](http://dev.ckeditor.com/ticket/10870): The `paste` command is no longer being disabled when the clipboard is empty.
* [#10854](http://dev.ckeditor.com/ticket/10854): Fixed: Firefox prepends `<br>` to `<body>`, so it is stripped by the HTML data processor.
* [#10823](http://dev.ckeditor.com/ticket/10823): Fixed: [Link](http://ckeditor.com/addon/link) plugin does not work with non-editable content.
* [#10828](http://dev.ckeditor.com/ticket/10828): [Magic Line](http://ckeditor.com/addon/magicline) integration with the Widget System.
* [#10865](http://dev.ckeditor.com/ticket/10865): Improved hiding copybin, so copying widgets works smoothly.
* [#11066](http://dev.ckeditor.com/ticket/11066): Widget's private parts use CSS reset.
* [#11027](http://dev.ckeditor.com/ticket/11027): Fixed: Block commands break on widgets; added the [`contentDomInvalidated`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-contentDomInvalidated) event.
* [#10430](http://dev.ckeditor.com/ticket/10430): Resolve dependence of the [Image](http://ckeditor.com/addon/image) plugin on the [Form Elements](http://ckeditor.com/addon/forms) plugin.
* [#10911](http://dev.ckeditor.com/ticket/10911): Fixed: Browser *Alt* hotkeys will no longer be blocked while a widget is focused.
* [#11082](http://dev.ckeditor.com/ticket/11082): Fixed: Selected widget is not copied or cut when using toolbar buttons or context menu.
* [#11083](http://dev.ckeditor.com/ticket/11083): Fixed list and div element application to block widgets.
* [#10887](http://dev.ckeditor.com/ticket/10887): Internet Explorer 8 compatibility issues related to the Widget System.
* [#11074](http://dev.ckeditor.com/ticket/11074): Temporarily disabled inline widget drag and drop, because of seriously buggy native `range#moveToPoint` method.
* [#11098](http://dev.ckeditor.com/ticket/11098): Fixed: Wrong selection position after undoing widget drag and drop.
* [#11110](http://dev.ckeditor.com/ticket/11110): Fixed: IFrame and Flash objects are being incorrectly pasted in certain conditions.
* [#11129](http://dev.ckeditor.com/ticket/11129): Page break is lost when loading data.
* [#11123](http://dev.ckeditor.com/ticket/11123): [Firefox] Widget is destroyed after being dragged outside of `<body>`.
* [#11124](http://dev.ckeditor.com/ticket/11124): Fixed the [Elements Path](http://ckeditor.com/addon/elementspath) in an editor using the [Div Editing Area](http://ckeditor.com/addon/divarea).
## CKEditor 4.3 Beta
New Features:
* [#9764](http://dev.ckeditor.com/ticket/9764): Widget System.
* [Widget plugin](http://ckeditor.com/addon/widget) introducing the [Widget API](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget).
* New [`editor.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-enterMode) and [`editor.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-shiftEnterMode) properties &ndash; normalized versions of [`config.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode) and [`config.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-shiftEnterMode).
* Dynamic editor settings. Starting from CKEditor 4.3 Beta, *Enter* mode values and [content filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) instances may be changed dynamically (for example when the caret was placed in an element in which editor features should be adjusted). When you are implementing a new editor feature, you should base its behavior on [dynamic](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeEnterMode) or [static](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-enterMode) *Enter* mode values depending on whether this feature works in selection context or globally on editor content.
* Dynamic *Enter* mode values &ndash; [`editor.setActiveEnterMode()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setActiveEnterMode) method, [`editor.activeEnterModeChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-activeEnterModeChange) event, and two properties: [`editor.activeEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeEnterMode) and [`editor.activeShiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeShiftEnterMode).
* Dynamic content filter instances &ndash; [`editor.setActiveFilter()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setActiveFilter) method, [`editor.activeFilterChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-activeFilterChange) event, and [`editor.activeFilter`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-activeFilter) property.
* "Fake" selection was introduced. It makes it possible to virtually select any element when the real selection remains hidden. See the [`selection.fake()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-fake) method.
* Default [`htmlParser.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter) rules are not applied to non-editable elements (elements with `contenteditable` attribute set to `false` and their descendants) anymore. To add a rule which will be applied to all elements you need to pass an additional argument to the [`filter.addRules()`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter-method-addRules) method.
* Dozens of new methods were introduced &ndash; most interesting ones:
* [`document.find()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.document-method-find),
* [`document.findOne()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.document-method-findOne),
* [`editable.insertElementIntoRange()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editable-method-insertElementIntoRange),
* [`range.moveToClosestEditablePosition()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.range-method-moveToClosestEditablePosition),
* New methods for [`htmlParser.node`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.node) and [`htmlParser.element`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.element).
* [#10659](http://dev.ckeditor.com/ticket/10659): New [Enhanced Image](http://ckeditor.com/addon/image2) plugin that introduces a widget with integrated image captions, an option to center images, and dynamic "click and drag" resizing.
* [#10664](http://dev.ckeditor.com/ticket/10664): New [Mathematical Formulas](http://ckeditor.com/addon/mathjax) plugin that introduces the MathJax widget.
* [#7987](https://dev.ckeditor.com/ticket/7987): New [Language](http://ckeditor.com/addon/language) plugin that implements Language toolbar button to support [WCAG 3.1.2 Language of Parts](http://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html).
* [#10708](http://dev.ckeditor.com/ticket/10708): New [smileys](http://ckeditor.com/addon/smiley).
## CKEditor 4.2.3
Fixed Issues:
* [#10994](http://dev.ckeditor.com/ticket/10994): Fixed: Loading external jQuery library when opening the [jQuery Adapter](http://docs.ckeditor.com/#!/guide/dev_jquery) sample directly from file.
* [#10975](http://dev.ckeditor.com/ticket/10975): [IE] Fixed: Error thrown while opening the color palette.
* [#9929](http://dev.ckeditor.com/ticket/9929): [Blink/WebKit] Fixed: A non-breaking space is created once a character is deleted and a regular space is typed.
* [#10963](http://dev.ckeditor.com/ticket/10963): Fixed: JAWS issue with the keyboard shortcut for [Magic Line](http://ckeditor.com/addon/magicline).
* [#11096](http://dev.ckeditor.com/ticket/11096): Fixed: TypeError: Object has no method 'is'.
## CKEditor 4.2.2
Fixed Issues:
* [#9314](http://dev.ckeditor.com/ticket/9314): Fixed: Incorrect error message on closing a dialog window without saving changs.
* [#10308](http://dev.ckeditor.com/ticket/10308): [IE10] Fixed: Unspecified error when deleting a row.
* [#10945](http://dev.ckeditor.com/ticket/10945): [Chrome] Fixed: Clicking with a mouse inside the editor does not show the caret.
* [#10912](http://dev.ckeditor.com/ticket/10912): Prevent default action when content of a non-editable link is clicked.
* [#10913](http://dev.ckeditor.com/ticket/10913): Fixed [`CKEDITOR.plugins.addExternal()`](http://docs.ckeditor.com/#!/api/CKEDITOR.resourceManager-method-addExternal) not handling paths including file name specified.
* [#10666](http://dev.ckeditor.com/ticket/10666): Fixed [`CKEDITOR.tools.isArray()`](http://docs.ckeditor.com/#!/api/CKEDITOR.tools-method-isArray) not working cross frame.
* [#10910](http://dev.ckeditor.com/ticket/10910): [IE9] Fixed JavaScript error thrown in Compatibility Mode when clicking and/or typing in the editing area.
* [#10868](http://dev.ckeditor.com/ticket/10868): [IE8] Prevent the browser from crashing when applying the Inline Quotation style.
* [#10915](http://dev.ckeditor.com/ticket/10915): Fixed: Invalid CSS filter in the Kama skin.
* [#10914](http://dev.ckeditor.com/ticket/10914): Plugins [Indent List](http://ckeditor.com/addon/indentlist) and [Indent Block](http://ckeditor.com/addon/indentblock) are now included in the build configuration.
* [#10812](http://dev.ckeditor.com/ticket/10812): Fixed [`range.createBookmark2()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.range-method-createBookmark2) incorrectly normalizing offsets. This bug was causing many issues: [#10850](http://dev.ckeditor.com/ticket/10850), [#10842](http://dev.ckeditor.com/ticket/10842).
* [#10951](http://dev.ckeditor.com/ticket/10951): Reviewed and optimized focus handling on panels (combo, menu buttons, color buttons, and context menu) to enhance accessibility. Fixed [#10705](http://dev.ckeditor.com/ticket/10705), [#10706](http://dev.ckeditor.com/ticket/10706) and [#10707](http://dev.ckeditor.com/ticket/10707).
* [#10704](http://dev.ckeditor.com/ticket/10704): Fixed a JAWS issue with the Select Color dialog window title not being announced.
* [#10753](http://dev.ckeditor.com/ticket/10753): The floating toolbar in inline instances now has a dedicated accessibility label.
## CKEditor 4.2.1
Fixed Issues:
* [#10301](http://dev.ckeditor.com/ticket/10301): [IE9-10] Undo fails after 3+ consecutive paste actions with a JavaScript error.
* [#10689](http://dev.ckeditor.com/ticket/10689): Save toolbar button saves only the first editor instance.
* [#10368](http://dev.ckeditor.com/ticket/10368): Move language reading direction definition (`dir`) from main language file to core.
* [#9330](http://dev.ckeditor.com/ticket/9330): Fixed pasting anchors from MS Word.
* [#8103](http://dev.ckeditor.com/ticket/8103): Fixed pasting nested lists from MS Word.
* [#9958](http://dev.ckeditor.com/ticket/9958): [IE9] Pressing the "OK" button will trigger the `onbeforeunload` event in the popup dialog.
* [#10662](http://dev.ckeditor.com/ticket/10662): Fixed styles from the Styles drop-down list not registering to the ACF in case when the [Shared Spaces plugin](http://ckeditor.com/addon/sharedspace) is used.
* [#9654](http://dev.ckeditor.com/ticket/9654): Problems with Internet Explorer 10 Quirks Mode.
* [#9816](http://dev.ckeditor.com/ticket/9816): Floating toolbar does not reposition vertically in several cases.
* [#10646](http://dev.ckeditor.com/ticket/10646): Removing a selected sublist or nested table with *Backspace/Delete* removes the parent element.
* [#10623](http://dev.ckeditor.com/ticket/10623): [WebKit] Page is scrolled when opening a drop-down list.
* [#10004](http://dev.ckeditor.com/ticket/10004): [ChromeVox] Button names are not announced.
* [#10731](http://dev.ckeditor.com/ticket/10731): [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin breaks cloning of editor configuration.
* It is now possible to set per instance [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin configuration instead of setting the configuration globally.
## CKEditor 4.2
**Important Notes:**
* Dropped compatibility support for Internet Explorer 7 and Firefox 3.6.
* Both the Basic and the Standard distribution packages will not contain the new [Indent Block](http://ckeditor.com/addon/indentblock) plugin. Because of this the [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) might remove block indentations from existing contents. If you want to prevent this, either [add an appropriate ACF rule to your filter](http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules) or create a custom build based on the Basic/Standard package and add the Indent Block plugin in [CKBuilder](http://ckeditor.com/builder).
New Features:
* [#10027](http://dev.ckeditor.com/ticket/10027): Separated list and block indentation into two plugins: [Indent List](http://ckeditor.com/addon/indentlist) and [Indent Block](http://ckeditor.com/addon/indentblock).
* [#8244](http://dev.ckeditor.com/ticket/8244): Use *(Shift+)Tab* to indent and outdent lists.
* [#10281](http://dev.ckeditor.com/ticket/10281): The [jQuery Adapter](http://docs.ckeditor.com/#!/guide/dev_jquery) is now available. Several jQuery-related issues fixed: [#8261](http://dev.ckeditor.com/ticket/8261), [#9077](http://dev.ckeditor.com/ticket/9077), [#8710](http://dev.ckeditor.com/ticket/8710), [#8530](http://dev.ckeditor.com/ticket/8530), [#9019](http://dev.ckeditor.com/ticket/9019), [#6181](http://dev.ckeditor.com/ticket/6181), [#7876](http://dev.ckeditor.com/ticket/7876), [#6906](http://dev.ckeditor.com/ticket/6906).
* [#10042](http://dev.ckeditor.com/ticket/10042): Introduced [`config.title`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-title) setting to change the human-readable title of the editor.
* [#9794](http://dev.ckeditor.com/ticket/9794): Added [`editor.onChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-change) event.
* [#9923](http://dev.ckeditor.com/ticket/9923): HiDPI support in the editor UI. HiDPI icons for [Moono skin](http://ckeditor.com/addon/moono) added.
* [#8031](http://dev.ckeditor.com/ticket/8031): Handle `required` attributes on `<textarea>` elements &mdash; introduced [`editor.required`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-required) event.
* [#10280](http://dev.ckeditor.com/ticket/10280): Ability to replace `<textarea>` elements with the inline editor.
Fixed Issues:
* [#10599](http://dev.ckeditor.com/ticket/10599): [Indent](http://ckeditor.com/addon/indent) plugin is no longer required by the [List](http://ckeditor.com/addon/list) plugin.
* [#10370](http://dev.ckeditor.com/ticket/10370): Inconsistency in data events between framed and inline editors.
* [#10438](http://dev.ckeditor.com/ticket/10438): [FF, IE] No selection is done on an editable element on executing [`editor.setData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setData).
## CKEditor 4.1.3
New Features:
* Added new translation: Indonesian.
Fixed Issues:
* [#10644](http://dev.ckeditor.com/ticket/10644): Fixed a critical bug when pasting plain text in Blink-based browsers.
* [#5189](http://dev.ckeditor.com/ticket/5189): [Find/Replace](http://ckeditor.com/addon/find) dialog window: rename "Cancel" button to "Close".
* [#10562](http://dev.ckeditor.com/ticket/10562): [Housekeeping] Unified CSS gradient filter formats in the [Moono](http://ckeditor.com/addon/moono) skin.
* [#10537](http://dev.ckeditor.com/ticket/10537): Advanced Content Filter should register a default rule for [`config.shiftEnterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-shiftEnterMode).
* [#10610](http://dev.ckeditor.com/ticket/10610): [`CKEDITOR.dialog.addIframe()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dialog-static-method-addIframe) incorrectly sets the iframe size in dialog windows.
## CKEditor 4.1.2
New Features:
* Added new translation: Sinhala.
Fixed Issues:
* [#10339](http://dev.ckeditor.com/ticket/10339): Fixed: Error thrown when inserted data was totally stripped out after filtering and processing.
* [#10298](http://dev.ckeditor.com/ticket/10298): Fixed: Data processor breaks attributes containing protected parts.
* [#10367](http://dev.ckeditor.com/ticket/10367): Fixed: [`editable.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editable-method-insertText) loses characters when `RegExp` replace controls are being inserted.
* [#10165](http://dev.ckeditor.com/ticket/10165): [IE] Access denied error when `document.domain` has been altered.
* [#9761](http://dev.ckeditor.com/ticket/9761): Update the *Backspace* key state in [`keystrokeHandler.blockedKeystrokes`](http://docs.ckeditor.com/#!/api/CKEDITOR.keystrokeHandler-property-blockedKeystrokes) when calling [`editor.setReadOnly()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-setReadOnly).
* [#6504](http://dev.ckeditor.com/ticket/6504): Fixed: Race condition while loading several [`config.customConfig`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-customConfig) files.
* [#10146](http://dev.ckeditor.com/ticket/10146): [Firefox] Empty lines are being removed while [`config.enterMode`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode) is [`CKEDITOR.ENTER_BR`](http://docs.ckeditor.com/#!/api/CKEDITOR-property-ENTER_BR).
* [#10360](http://dev.ckeditor.com/ticket/10360): Fixed: ARIA `role="application"` should not be used for dialog windows.
* [#10361](http://dev.ckeditor.com/ticket/10361): Fixed: ARIA `role="application"` should not be used for floating panels.
* [#10510](http://dev.ckeditor.com/ticket/10510): Introduced unique voice labels to differentiate between different editor instances.
* [#9945](http://dev.ckeditor.com/ticket/9945): [iOS] Scrolling not possible on iPad.
* [#10389](http://dev.ckeditor.com/ticket/10389): Fixed: Invalid HTML in the "Text and Table" template.
* [WebSpellChecker](http://ckeditor.com/addon/wsc) plugin user interface was changed to match CKEditor 4 style.
## CKEditor 4.1.1
New Features:
* Added new translation: Albanian.
Fixed Issues:
* [#10172](http://dev.ckeditor.com/ticket/10172): Pressing *Delete* or *Backspace* in an empty table cell moves the cursor to the next/previous cell.
* [#10219](http://dev.ckeditor.com/ticket/10219): Error thrown when destroying an editor instance in parallel with a `mouseup` event.
* [#10265](http://dev.ckeditor.com/ticket/10265): Wrong loop type in the [File Browser](http://ckeditor.com/addon/filebrowser) plugin.
* [#10249](http://dev.ckeditor.com/ticket/10249): Wrong undo/redo states at start.
* [#10268](http://dev.ckeditor.com/ticket/10268): [Show Blocks](http://ckeditor.com/addon/showblocks) does not recover after switching to Source view.
* [#9995](http://dev.ckeditor.com/ticket/9995): HTML code in the `<textarea>` should not be modified by the [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor).
* [#10320](http://dev.ckeditor.com/ticket/10320): [Justify](http://ckeditor.com/addon/justify) plugin should add elements to Advanced Content Filter based on current [Enter mode](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-enterMode).
* [#10260](http://dev.ckeditor.com/ticket/10260): Fixed: Advanced Content Filter blocks [`tabSpaces`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-tabSpaces). Unified `data-cke-*` attributes filtering.
* [#10315](http://dev.ckeditor.com/ticket/10315): [WebKit] [Undo manager](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.undo.UndoManager) should not record snapshots after a filling character was added/removed.
* [#10291](http://dev.ckeditor.com/ticket/10291): [WebKit] Space after a filling character should be secured.
* [#10330](http://dev.ckeditor.com/ticket/10330): [WebKit] The filling character is not removed on `keydown` in specific cases.
* [#10285](http://dev.ckeditor.com/ticket/10285): Fixed: Styled text pasted from MS Word causes an infinite loop.
* [#10131](http://dev.ckeditor.com/ticket/10131): Fixed: [`undoManager.update()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.undo.UndoManager-method-update) does not refresh the command state.
* [#10337](http://dev.ckeditor.com/ticket/10337): Fixed: Unable to remove `<s>` using [Remove Format](http://ckeditor.com/addon/removeformat).
## CKEditor 4.1
Fixed Issues:
* [#10192](http://dev.ckeditor.com/ticket/10192): Closing lists with the *Enter* key does not work with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) in several cases.
* [#10191](http://dev.ckeditor.com/ticket/10191): Fixed allowed content rules unification, so the [`filter.allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.filter-property-allowedContent) property always contains rules in the same format.
* [#10224](http://dev.ckeditor.com/ticket/10224): Advanced Content Filter does not remove non-empty `<a>` elements anymore.
* Minor issues in plugin integration with Advanced Content Filter:
* [#10166](http://dev.ckeditor.com/ticket/10166): Added transformation from the `align` attribute to `float` style to preserve backward compatibility after the introduction of Advanced Content Filter.
* [#10195](http://dev.ckeditor.com/ticket/10195): [Image](http://ckeditor.com/addon/image) plugin no longer registers rules for links to Advanced Content Filter.
* [#10213](http://dev.ckeditor.com/ticket/10213): [Justify](http://ckeditor.com/addon/justify) plugin is now correctly registering rules to Advanced Content Filter when [`config.justifyClasses`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-justifyClasses) is defined.
## CKEditor 4.1 RC
New Features:
* [#9829](http://dev.ckeditor.com/ticket/9829): Advanced Content Filter - data and features activation based on editor configuration.
Brand new data filtering system that works in 2 modes:
* Based on loaded features (toolbar items, plugins) - the data will be filtered according to what the editor in its
current configuration can handle.
* Based on [`config.allowedContent`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent) rules - the data
will be filtered and the editor features (toolbar items, commands, keystrokes) will be enabled if they are allowed.
See the `datafiltering.html` sample, [guides](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) and [`CKEDITOR.filter` API documentation](http://docs.ckeditor.com/#!/api/CKEDITOR.filter).
* [#9387](http://dev.ckeditor.com/ticket/9387): Reintroduced [Shared Spaces](http://ckeditor.com/addon/sharedspace) - the ability to display toolbar and bottom editor space in selected locations and to share them by different editor instances.
* [#9907](http://dev.ckeditor.com/ticket/9907): Added the [`contentPreview`](http://docs.ckeditor.com/#!/api/CKEDITOR-event-contentPreview) event for preview data manipulation.
* [#9713](http://dev.ckeditor.com/ticket/9713): Introduced the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin that brings raw HTML editing for inline editor instances.
* Included in [#9829](http://dev.ckeditor.com/ticket/9829): Introduced new events, [`toHtml`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toHtml) and [`toDataFormat`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toDataFormat), allowing for better integration with data processing.
* [#9981](http://dev.ckeditor.com/ticket/9981): Added ability to filter [`htmlParser.fragment`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.fragment), [`htmlParser.element`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.element) etc. by many [`htmlParser.filter`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlParser.filter)s before writing structure to an HTML string.
* Included in [#10103](http://dev.ckeditor.com/ticket/10103):
* Introduced the [`editor.status`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-status) property to make it easier to check the current status of the editor.
* Default [`command`](http://docs.ckeditor.com/#!/api/CKEDITOR.command) state is now [`CKEDITOR.TRISTATE_DISABLE`](http://docs.ckeditor.com/#!/api/CKEDITOR-property-TRISTATE_DISABLED). It will be activated on [`editor.instanceReady`](http://docs.ckeditor.com/#!/api/CKEDITOR-event-instanceReady) or immediately after being added if the editor is already initialized.
* [#9796](http://dev.ckeditor.com/ticket/9796): Introduced `<s>` as a default tag for strikethrough, which replaces obsolete `<strike>` in HTML5.
## CKEditor 4.0.3
Fixed Issues:
* [#10196](http://dev.ckeditor.com/ticket/10196): Fixed context menus not opening with keyboard shortcuts when [Autogrow](http://ckeditor.com/addon/autogrow) is enabled.
* [#10212](http://dev.ckeditor.com/ticket/10212): [IE7-10] Undo command throws errors after multiple switches between Source and WYSIWYG view.
* [#10219](http://dev.ckeditor.com/ticket/10219): [Inline editor] Error thrown after calling [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy).
## CKEditor 4.0.2
Fixed Issues:
* [#9779](http://dev.ckeditor.com/ticket/9779): Fixed overriding [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl) with `CKEDITOR_GETURL`.
* [#9772](http://dev.ckeditor.com/ticket/9772): Custom buttons in the dialog window footer have different look and size ([Moono](http://ckeditor.com/addon/moono), [Kama](http://ckeditor.com/addon/kama) skins).
* [#9029](http://dev.ckeditor.com/ticket/9029): Custom styles added with the [`stylesSet.add()`](http://docs.ckeditor.com/#!/api/CKEDITOR.stylesSet-method-add) are displayed in the wrong order.
* [#9887](http://dev.ckeditor.com/ticket/9887): Disable [Magic Line](http://ckeditor.com/addon/magicline) when [`editor.readOnly`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-readOnly) is set.
* [#9882](http://dev.ckeditor.com/ticket/9882): Fixed empty document title on [`editor.getData()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-getData) if set via the Document Properties dialog window.
* [#9773](http://dev.ckeditor.com/ticket/9773): Fixed rendering problems with selection fields in the Kama skin.
* [#9851](http://dev.ckeditor.com/ticket/9851): The [`selectionChange`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-selectionChange) event is not fired when mouse selection ended outside editable.
* [#9903](http://dev.ckeditor.com/ticket/9903): [Inline editor] Bad positioning of floating space with page horizontal scroll.
* [#9872](http://dev.ckeditor.com/ticket/9872): [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) returns `true` when called onload. Removed the obsolete `editor.mayBeDirty` flag.
* [#9893](http://dev.ckeditor.com/ticket/9893): [IE] Fixed broken toolbar when editing mixed direction content in Quirks mode.
* [#9845](http://dev.ckeditor.com/ticket/9845): Fixed TAB navigation in the [Link](http://ckeditor.com/addon/link) dialog window when the Anchor option is used and no anchors are available.
* [#9883](http://dev.ckeditor.com/ticket/9883): Maximizing was making the entire page editable with [divarea](http://ckeditor.com/addon/divarea)-based editors.
* [#9940](http://dev.ckeditor.com/ticket/9940): [Firefox] Navigating back to a page with the editor was making the entire page editable.
* [#9966](http://dev.ckeditor.com/ticket/9966): Fixed: Unable to type square brackets with French keyboard layout. Changed [Magic Line](http://ckeditor.com/addon/magicline) keystrokes.
* [#9507](http://dev.ckeditor.com/ticket/9507): [Firefox] Selection is moved before editable position when the editor is focused for the first time.
* [#9947](http://dev.ckeditor.com/ticket/9947): [WebKit] Editor overflows parent container in some edge cases.
* [#10105](http://dev.ckeditor.com/ticket/10105): Fixed: Broken [sourcearea](http://ckeditor.com/addon/sourcearea) view when an RTL language is set.
* [#10123](http://dev.ckeditor.com/ticket/10123): [WebKit] Fixed: Several dialog windows have broken layout since the latest WebKit release.
* [#10152](http://dev.ckeditor.com/ticket/10152): Fixed: Invalid ARIA property used on menu items.
## CKEditor 4.0.1.1
Fixed Issues:
* Security update: Added protection against XSS attack and possible path disclosure in the PHP sample.
## CKEditor 4.0.1
Fixed Issues:
* [#9655](http://dev.ckeditor.com/ticket/9655): Support for IE Quirks Mode in the new [Moono skin](http://ckeditor.com/addon/moono).
* Accessibility issues (mainly in inline editor): [#9364](http://dev.ckeditor.com/ticket/9364), [#9368](http://dev.ckeditor.com/ticket/9368), [#9369](http://dev.ckeditor.com/ticket/9369), [#9370](http://dev.ckeditor.com/ticket/9370), [#9541](http://dev.ckeditor.com/ticket/9541), [#9543](http://dev.ckeditor.com/ticket/9543), [#9841](http://dev.ckeditor.com/ticket/9841), [#9844](http://dev.ckeditor.com/ticket/9844).
* [Magic Line](http://ckeditor.com/addon/magicline) plugin:
* [#9481](http://dev.ckeditor.com/ticket/9481): Added accessibility support for Magic Line.
* [#9509](http://dev.ckeditor.com/ticket/9509): Added Magic Line support for forms.
* [#9573](http://dev.ckeditor.com/ticket/9573): Magic Line does not disappear on `mouseout` in a specific case.
* [#9754](http://dev.ckeditor.com/ticket/9754): [WebKit] Cutting & pasting simple unformatted text generates an inline wrapper in WebKit browsers.
* [#9456](http://dev.ckeditor.com/ticket/9456): [Chrome] Properly paste bullet list style from MS Word.
* [#9699](http://dev.ckeditor.com/ticket/9699), [#9758](http://dev.ckeditor.com/ticket/9758): Improved selection locking when selecting by dragging.
* Context menu:
* [#9712](http://dev.ckeditor.com/ticket/9712): Opening the context menu destroys editor focus.
* [#9366](http://dev.ckeditor.com/ticket/9366): Context menu should be displayed over the floating toolbar.
* [#9706](http://dev.ckeditor.com/ticket/9706): Context menu generates a JavaScript error in inline mode when the editor is attached to a header element.
* [#9800](http://dev.ckeditor.com/ticket/9800): Hide float panel when resizing the window.
* [#9721](http://dev.ckeditor.com/ticket/9721): Padding in content of div-based editor puts the editing area under the bottom UI space.
* [#9528](http://dev.ckeditor.com/ticket/9528): Host page `box-sizing` style should not influence the editor UI elements.
* [#9503](http://dev.ckeditor.com/ticket/9503): [Form Elements](http://ckeditor.com/addon/forms) plugin adds context menu listeners only on supported input types. Added support for `tel`, `email`, `search` and `url` input types.
* [#9769](http://dev.ckeditor.com/ticket/9769): Improved floating toolbar positioning in a narrow window.
* [#9875](http://dev.ckeditor.com/ticket/9875): Table dialog window does not populate width correctly.
* [#8675](http://dev.ckeditor.com/ticket/8675): Deleting cells in a nested table removes the outer table cell.
* [#9815](http://dev.ckeditor.com/ticket/9815): Cannot edit dialog window fields in an editor initialized in the jQuery UI modal dialog.
* [#8888](http://dev.ckeditor.com/ticket/8888): CKEditor dialog windows do not show completely in a small window.
* [#9360](http://dev.ckeditor.com/ticket/9360): [Inline editor] Blocks shown for a `<div>` element stay permanently even after the user exits editing the `<div>`.
* [#9531](http://dev.ckeditor.com/ticket/9531): [Firefox & Inline editor] Toolbar is lost when closing the Format drop-down list by clicking its button.
* [#9553](http://dev.ckeditor.com/ticket/9553): Table width incorrectly set when the `border-width` style is specified.
* [#9594](http://dev.ckeditor.com/ticket/9594): Cannot tab past CKEditor when it is in read-only mode.
* [#9658](http://dev.ckeditor.com/ticket/9658): [IE9] Justify not working on selected images.
* [#9686](http://dev.ckeditor.com/ticket/9686): Added missing contents styles for `<pre>` elements.
* [#9709](http://dev.ckeditor.com/ticket/9709): [Paste from Word](http://ckeditor.com/addon/pastefromword) should not depend on configuration from other styles.
* [#9726](http://dev.ckeditor.com/ticket/9726): Removed [Color Dialog](http://ckeditor.com/addon/colordialog) plugin dependency from [Table Tools](http://ckeditor.com/addon/tabletools).
* [#9765](http://dev.ckeditor.com/ticket/9765): Toolbar Collapse command documented incorrectly in the [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp) dialog window.
* [#9771](http://dev.ckeditor.com/ticket/9771): [WebKit & Opera] Fixed scrolling issues when pasting.
* [#9787](http://dev.ckeditor.com/ticket/9787): [IE9] `onChange` is not fired for checkboxes in dialogs.
* [#9842](http://dev.ckeditor.com/ticket/9842): [Firefox 17] When opening a toolbar menu for the first time and pressing the *Down Arrow* key, focus goes to the next toolbar button instead of the menu options.
* [#9847](http://dev.ckeditor.com/ticket/9847): [Elements Path](http://ckeditor.com/addon/elementspath) should not be initialized in the inline editor.
* [#9853](http://dev.ckeditor.com/ticket/9853): [`editor.addRemoveFormatFilter()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-addRemoveFormatFilter) is exposed before it really works.
* [#8893](http://dev.ckeditor.com/ticket/8893): Value of the [`pasteFromWordCleanupFile`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-pasteFromWordCleanupFile) configuration option is now taken from the instance configuration.
* [#9693](http://dev.ckeditor.com/ticket/9693): Removed "Live Preview" checkbox from UI color picker.
## CKEditor 4.0
The first stable release of the new CKEditor 4 code line.
The CKEditor JavaScript API has been kept compatible with CKEditor 4, whenever
possible. The list of relevant changes can be found in the [API Changes page of
the CKEditor 4 documentation][1].
[1]: http://docs.ckeditor.com/#!/guide/dev_api_changes "API Changes"

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
CKEditor 4
==========
Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
http://ckeditor.com - See LICENSE.md for license information.
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
The full editor documentation is available online at the following address:
http://docs.ckeditor.com
## Installation
Installing CKEditor is an easy task. Just follow these simple steps:
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.
**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 that
installation proceeded properly. Take a look at the `samples` directory.
To test your installation, just call the following page at your website:
http://<your site>/<CKEditor installation path>/samples/index.html
For example:
http://www.example.com/ckeditor/samples/index.html

View File

@@ -0,0 +1,165 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* This file was added automatically by CKEditor builder.
* You may re-use it at any time to build CKEditor again.
*
* If you would like to build CKEditor online again
* (for example to upgrade), visit one the following links:
*
* (1) http://ckeditor.com/builder
* Visit online builder to build CKEditor from scratch.
*
* (2) http://ckeditor.com/builder/6df6f619a26d5e0aab9c5392e3ab2c31
* Visit online builder to build CKEditor, starting with the same setup as before.
*
* (3) http://ckeditor.com/builder/download/6df6f619a26d5e0aab9c5392e3ab2c31
* Straight download link to the latest version of CKEditor (Optimized) with the same setup as before.
*
* NOTE:
* This file is not used by CKEditor, you may remove it.
* Changing this file will not change your CKEditor configuration.
*/
var CKBUILDER_CONFIG = {
skin: 'moonocolor',
preset: 'full',
ignore: [
'dev',
'.gitignore',
'.gitattributes',
'README.md',
'.mailmap'
],
plugins : {
'a11yhelp' : 1,
'about' : 1,
'basicstyles' : 1,
'bidi' : 1,
'blockquote' : 1,
'clipboard' : 1,
'colorbutton' : 1,
'colordialog' : 1,
'contextmenu' : 1,
'dialogadvtab' : 1,
'div' : 1,
'elementspath' : 1,
'enterkey' : 1,
'entities' : 1,
'filebrowser' : 1,
'find' : 1,
'flash' : 1,
'floatingspace' : 1,
'font' : 1,
'format' : 1,
'forms' : 1,
'horizontalrule' : 1,
'htmlwriter' : 1,
'iframe' : 1,
'image' : 1,
'indentblock' : 1,
'indentlist' : 1,
'justify' : 1,
'language' : 1,
'link' : 1,
'list' : 1,
'liststyle' : 1,
'magicline' : 1,
'maximize' : 1,
'newpage' : 1,
'pagebreak' : 1,
'pastefromword' : 1,
'pastetext' : 1,
'preview' : 1,
'print' : 1,
'removeformat' : 1,
'resize' : 1,
'save' : 1,
'scayt' : 1,
'selectall' : 1,
'showblocks' : 1,
'showborders' : 1,
'smiley' : 1,
'sourcearea' : 1,
'specialchar' : 1,
'stylescombo' : 1,
'tab' : 1,
'table' : 1,
'tabletools' : 1,
'templates' : 1,
'toolbar' : 1,
'undo' : 1,
'wsc' : 1,
'wysiwygarea' : 1
},
languages : {
'af' : 1,
'ar' : 1,
'bg' : 1,
'bn' : 1,
'bs' : 1,
'ca' : 1,
'cs' : 1,
'cy' : 1,
'da' : 1,
'de' : 1,
'el' : 1,
'en' : 1,
'en-au' : 1,
'en-ca' : 1,
'en-gb' : 1,
'eo' : 1,
'es' : 1,
'et' : 1,
'eu' : 1,
'fa' : 1,
'fi' : 1,
'fo' : 1,
'fr' : 1,
'fr-ca' : 1,
'gl' : 1,
'gu' : 1,
'he' : 1,
'hi' : 1,
'hr' : 1,
'hu' : 1,
'id' : 1,
'is' : 1,
'it' : 1,
'ja' : 1,
'ka' : 1,
'km' : 1,
'ko' : 1,
'ku' : 1,
'lt' : 1,
'lv' : 1,
'mk' : 1,
'mn' : 1,
'ms' : 1,
'nb' : 1,
'nl' : 1,
'no' : 1,
'pl' : 1,
'pt' : 1,
'pt-br' : 1,
'ro' : 1,
'ru' : 1,
'si' : 1,
'sk' : 1,
'sl' : 1,
'sq' : 1,
'sr' : 1,
'sr-latn' : 1,
'sv' : 1,
'th' : 1,
'tr' : 1,
'ug' : 1,
'uk' : 1,
'vi' : 1,
'zh' : 1,
'zh-cn' : 1
}
};

View File

@@ -0,0 +1,42 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
// Compressed version of core/ckeditor_base.js. See original for instructions.
/*jsl:ignore*/
window.CKEDITOR||(window.CKEDITOR=function(){var b={timestamp:"",version:"%VERSION%",revision:"%REV%",rnd:Math.floor(900*Math.random())+100,_:{pending:[]},status:"unloaded",basePath:function(){var a=window.CKEDITOR_BASEPATH||"";if(!a)for(var b=document.getElementsByTagName("script"),c=0;c<b.length;c++){var d=b[c].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if(d){a=d[1];break}}-1==a.indexOf(":/")&&"//"!=a.slice(0,2)&&(a=0===a.indexOf("/")?location.href.match(/^.*?:\/\/[^\/]*/)[0]+a:location.href.match(/^[^\?]*\/(?:)/)[0]+a);if(!a)throw'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';return a}(),getUrl:function(a){-1==a.indexOf(":/")&&0!==a.indexOf("/")&&(a=this.basePath+a);this.timestamp&&"/"!=a.charAt(a.length-1)&&!/[&?]t=/.test(a)&&(a+=(0<=a.indexOf("?")?"&":"?")+"t="+this.timestamp);return a},domReady:function(){function a(){try{document.addEventListener?(document.removeEventListener("DOMContentLoaded",a,!1),b()):document.attachEvent&&"complete"===document.readyState&&(document.detachEvent("onreadystatechange",a),b())}catch(d){}}function b(){for(var a;a=c.shift();)a()}var c=[];return function(b){c.push(b);"complete"===document.readyState&&setTimeout(a,1);if(1==c.length)if(document.addEventListener)document.addEventListener("DOMContentLoaded",a,!1),window.addEventListener("load",a,!1);else if(document.attachEvent){document.attachEvent("onreadystatechange",a);window.attachEvent("onload",a);b=!1;try{b=!window.frameElement}catch(e){}if(document.documentElement.doScroll&&b){var f=function(){try{document.documentElement.doScroll("left")}catch(b){setTimeout(f,1);return}a()};f()}}}}()},e=window.CKEDITOR_GETURL;if(e){var g=b.getUrl;b.getUrl=function(a){return e.call(b,a)||g.call(b,a)}}return b}());
/*jsl:end*/
if ( CKEDITOR.loader )
CKEDITOR.loader.load( 'ckeditor' );
else {
// Set the script name to be loaded by the loader.
CKEDITOR._autoLoad = 'ckeditor';
// Include the loader script.
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = CKEDITOR.getUrl( 'core/loader.js' );
document.body.appendChild( script );
} else
document.write( '<script type="text/javascript" src="' + CKEDITOR.getUrl( 'core/loader.js' ) + '"></script>' );
}
/**
* The skin to load for all created instances, it may be the name of the skin
* folder inside the editor installation path, or the name and the path separated
* by a comma.
*
* **Note:** This is a global configuration that applies to all instances.
*
* CKEDITOR.skinName = 'moono';
*
* CKEDITOR.skinName = 'myskin,/customstuff/myskin/';
*
* @cfg {String} [skinName='moono']
* @member CKEDITOR
*/
CKEDITOR.skinName = 'moono';

View File

@@ -0,0 +1,17 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.html or http://ckeditor.com/license
*/
CKEDITOR.editorConfig = function( config ) {
// %REMOVE_START%
// The configuration options below are needed when running CKEditor from source files.
config.plugins = 'dialogui,dialog,about,a11yhelp,dialogadvtab,basicstyles,bidi,blockquote,clipboard,button,panelbutton,panel,floatpanel,colorbutton,colordialog,templates,menu,contextmenu,div,resize,toolbar,elementspath,enterkey,entities,popup,filebrowser,find,fakeobjects,flash,floatingspace,listblock,richcombo,font,forms,format,horizontalrule,htmlwriter,iframe,wysiwygarea,image,indent,indentblock,indentlist,smiley,justify,menubutton,language,link,list,liststyle,magicline,maximize,newpage,pagebreak,pastetext,pastefromword,preview,print,removeformat,save,selectall,showblocks,showborders,sourcearea,specialchar,scayt,stylescombo,tab,table,tabletools,undo,wsc';
config.skin = 'moonocolor';
// %REMOVE_END%
// Define changes to default configuration here. For example:
// config.language = 'fr';
// config.uiColor = '#AADC6E';
};

View File

@@ -0,0 +1,123 @@
/*
Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.md or http://ckeditor.com/license
*/
body
{
/* Font */
font-family: sans-serif, Arial, Verdana, "Trebuchet MS";
font-size: 12px;
/* Text color */
color: #333;
/* Remove the background color to make it transparent */
background-color: #fff;
margin: 20px;
}
.cke_editable
{
font-size: 13px;
line-height: 1.6;
}
blockquote
{
font-style: italic;
font-family: Georgia, Times, "Times New Roman", serif;
padding: 2px 0;
border-style: solid;
border-color: #ccc;
border-width: 0;
}
.cke_contents_ltr blockquote
{
padding-left: 20px;
padding-right: 8px;
border-left-width: 5px;
}
.cke_contents_rtl blockquote
{
padding-left: 8px;
padding-right: 20px;
border-right-width: 5px;
}
a
{
color: #0782C1;
}
ol,ul,dl
{
/* IE7: reset rtl list margin. (#7334) */
*margin-right: 0px;
/* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/
padding: 0 40px;
}
h1,h2,h3,h4,h5,h6
{
font-weight: normal;
line-height: 1.2;
}
hr
{
border: 0px;
border-top: 1px solid #ccc;
}
img.right
{
border: 1px solid #ccc;
float: right;
margin-left: 15px;
padding: 5px;
}
img.left
{
border: 1px solid #ccc;
float: left;
margin-right: 15px;
padding: 5px;
}
pre
{
white-space: pre-wrap; /* CSS 2.1 */
word-wrap: break-word; /* IE7 */
}
.marker
{
background-color: Yellow;
}
span[lang]
{
font-style: italic;
}
figure
{
text-align: center;
border: solid 1px #ccc;
border-radius: 2px;
background: rgba(0,0,0,0.05);
padding: 10px;
margin: 10px 20px;
display: block; /* For IE8 */
}
figure figcaption
{
text-align: center;
display: block; /* For IE8 */
}

View File

@@ -0,0 +1,74 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview API initialization code.
*/
( function() {
// Disable HC detection in WebKit. (#5429)
if ( CKEDITOR.env.webkit )
CKEDITOR.env.hc = false;
else {
// Check whether high contrast is active by creating a colored border.
var hcDetect = CKEDITOR.dom.element.createFromHtml( '<div style="width:0;height:0;position:absolute;left:-10000px;' +
'border:1px solid;border-color:red blue"></div>', CKEDITOR.document );
hcDetect.appendTo( CKEDITOR.document.getHead() );
// Update CKEDITOR.env.
// Catch exception needed sometimes for FF. (#4230)
try {
var top = hcDetect.getComputedStyle( 'border-top-color' ),
right = hcDetect.getComputedStyle( 'border-right-color' );
// We need to check if getComputedStyle returned any value, because on FF
// it returnes empty string if CKEditor is loaded in hidden iframe. (#11121)
CKEDITOR.env.hc = !!( top && top == right );
} catch ( e ) {
CKEDITOR.env.hc = false;
}
hcDetect.remove();
}
if ( CKEDITOR.env.hc )
CKEDITOR.env.cssClass += ' cke_hc';
// Initially hide UI spaces when relevant skins are loading, later restored by skin css.
CKEDITOR.document.appendStyleText( '.cke{visibility:hidden;}' );
// Mark the editor as fully loaded.
CKEDITOR.status = 'loaded';
CKEDITOR.fireOnce( 'loaded' );
// Process all instances created by the "basic" implementation.
var pending = CKEDITOR._.pending;
if ( pending ) {
delete CKEDITOR._.pending;
for ( var i = 0; i < pending.length; i++ ) {
CKEDITOR.editor.prototype.constructor.apply( pending[ i ][ 0 ], pending[ i ][ 1 ] );
CKEDITOR.add( pending[ i ][ 0 ] );
}
}
} )();
/**
* Indicates that CKEditor is running on a High Contrast environment.
*
* if ( CKEDITOR.env.hc )
* alert( 'You\'re running on High Contrast mode. The editor interface will get adapted to provide you a better experience.' );
*
* @property {Boolean} hc
* @member CKEDITOR.env
*/
/**
* Fired when a CKEDITOR core object is fully loaded and ready for interaction.
*
* @event loaded
* @member CKEDITOR
*/

View File

@@ -0,0 +1,204 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Contains the third and last part of the {@link CKEDITOR} object
* definition.
*/
/** @class CKEDITOR */
// Remove the CKEDITOR.loadFullCore reference defined on ckeditor_basic.
delete CKEDITOR.loadFullCore;
/**
* Stores references to all editor instances created. The name of the properties
* in this object correspond to instance names, and their values contain the
* {@link CKEDITOR.editor} object representing them.
*
* alert( CKEDITOR.instances.editor1.name ); // 'editor1'
*
* @property {Object}
*/
CKEDITOR.instances = {};
/**
* The document of the window storing the CKEDITOR object.
*
* alert( CKEDITOR.document.getBody().getName() ); // 'body'
*
* @property {CKEDITOR.dom.document}
*/
CKEDITOR.document = new CKEDITOR.dom.document( document );
/**
* Adds an editor instance to the global {@link CKEDITOR} object. This function
* is available for internal use mainly.
*
* @param {CKEDITOR.editor} editor The editor instance to be added.
*/
CKEDITOR.add = function( editor ) {
CKEDITOR.instances[ editor.name ] = editor;
editor.on( 'focus', function() {
if ( CKEDITOR.currentInstance != editor ) {
CKEDITOR.currentInstance = editor;
CKEDITOR.fire( 'currentInstance' );
}
} );
editor.on( 'blur', function() {
if ( CKEDITOR.currentInstance == editor ) {
CKEDITOR.currentInstance = null;
CKEDITOR.fire( 'currentInstance' );
}
} );
CKEDITOR.fire( 'instance', null, editor );
};
/**
* Removes an editor instance from the global {@link CKEDITOR} object. This function
* is available for internal use only. External code must use {@link CKEDITOR.editor#method-destroy}.
*
* @private
* @param {CKEDITOR.editor} editor The editor instance to be removed.
*/
CKEDITOR.remove = function( editor ) {
delete CKEDITOR.instances[ editor.name ];
};
( function() {
var tpls = {};
/**
* Adds a named {@link CKEDITOR.template} instance to be reused among all editors.
* This will return the existing one if a template with same name is already
* defined. Additionally, it fires the "template" event to allow template source customization.
*
* @param {String} name The name which identifies a UI template.
* @param {String} source The source string for constructing this template.
* @returns {CKEDITOR.template} The created template instance.
*/
CKEDITOR.addTemplate = function( name, source ) {
var tpl = tpls[ name ];
if ( tpl )
return tpl;
// Make it possible to customize the template through event.
var params = { name: name, source: source };
CKEDITOR.fire( 'template', params );
return ( tpls[ name ] = new CKEDITOR.template( params.source ) );
};
/**
* Retrieves a defined template created with {@link CKEDITOR#addTemplate}.
*
* @param {String} name The template name.
*/
CKEDITOR.getTemplate = function( name ) {
return tpls[ name ];
};
} )();
( function() {
var styles = [];
/**
* Adds CSS rules to be appended to the editor document.
* This method is mostly used by plugins to add custom styles to the editor
* document. For basic content styling the `contents.css` file should be
* used instead.
*
* **Note:** This function should be called before the creation of editor instances.
*
* // Add styles for all headings inside editable contents.
* CKEDITOR.addCss( '.cke_editable h1,.cke_editable h2,.cke_editable h3 { border-bottom: 1px dotted red }' );
*
* @param {String} css The style rules to be appended.
* @see CKEDITOR.config#contentsCss
*/
CKEDITOR.addCss = function( css ) {
styles.push( css );
};
/**
* Returns a string will all CSS rules passed to the {@link CKEDITOR#addCss} method.
*
* @returns {String} A string containing CSS rules.
*/
CKEDITOR.getCss = function() {
return styles.join( '\n' );
};
} )();
// Perform global clean up to free as much memory as possible
// when there are no instances left
CKEDITOR.on( 'instanceDestroyed', function() {
if ( CKEDITOR.tools.isEmpty( this.instances ) )
CKEDITOR.fire( 'reset' );
} );
// Load the bootstrap script.
CKEDITOR.loader.load( '_bootstrap' ); // %REMOVE_LINE%
// Tri-state constants.
/**
* Used to indicate the ON or ACTIVE state.
*
* @readonly
* @property {Number} [=1]
*/
CKEDITOR.TRISTATE_ON = 1;
/**
* Used to indicate the OFF or INACTIVE state.
*
* @readonly
* @property {Number} [=2]
*/
CKEDITOR.TRISTATE_OFF = 2;
/**
* Used to indicate the DISABLED state.
*
* @readonly
* @property {Number} [=0]
*/
CKEDITOR.TRISTATE_DISABLED = 0;
/**
* The editor which is currently active (has user focus).
*
* function showCurrentEditorName() {
* if ( CKEDITOR.currentInstance )
* alert( CKEDITOR.currentInstance.name );
* else
* alert( 'Please focus an editor first.' );
* }
*
* @property {CKEDITOR.editor} currentInstance
* @see CKEDITOR#event-currentInstance
*/
/**
* Fired when the CKEDITOR.currentInstance object reference changes. This may
* happen when setting the focus on different editor instances in the page.
*
* var editor; // A variable to store a reference to the current editor.
* CKEDITOR.on( 'currentInstance', function() {
* editor = CKEDITOR.currentInstance;
* } );
*
* @event currentInstance
*/
/**
* Fired when the last instance has been destroyed. This event is used to perform
* global memory cleanup.
*
* @event reset
*/

View File

@@ -0,0 +1,315 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Contains the first and essential part of the {@link CKEDITOR}
* object definition.
*/
// #### Compressed Code
// Must be updated on changes in the script as well as updated in the ckeditor.js file.
// window.CKEDITOR||(window.CKEDITOR=function(){var b={timestamp:"",version:"%VERSION%",revision:"%REV%",rnd:Math.floor(900*Math.random())+100,_:{pending:[]},status:"unloaded",basePath:function(){var a=window.CKEDITOR_BASEPATH||"";if(!a)for(var b=document.getElementsByTagName("script"),c=0;c<b.length;c++){var d=b[c].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if(d){a=d[1];break}}-1==a.indexOf(":/")&&"//"!=a.slice(0,2)&&(a=0===a.indexOf("/")?location.href.match(/^.*?:\/\/[^\/]*/)[0]+a:location.href.match(/^[^\?]*\/(?:)/)[0]+a);if(!a)throw'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';return a}(),getUrl:function(a){-1==a.indexOf(":/")&&0!==a.indexOf("/")&&(a=this.basePath+a);this.timestamp&&"/"!=a.charAt(a.length-1)&&!/[&?]t=/.test(a)&&(a+=(0<=a.indexOf("?")?"&":"?")+"t="+this.timestamp);return a},domReady:function(){function a(){try{document.addEventListener?(document.removeEventListener("DOMContentLoaded",a,!1),b()):document.attachEvent&&"complete"===document.readyState&&(document.detachEvent("onreadystatechange",a),b())}catch(d){}}function b(){for(var a;a=c.shift();)a()}var c=[];return function(b){c.push(b);"complete"===document.readyState&&setTimeout(a,1);if(1==c.length)if(document.addEventListener)document.addEventListener("DOMContentLoaded",a,!1),window.addEventListener("load",a,!1);else if(document.attachEvent){document.attachEvent("onreadystatechange",a);window.attachEvent("onload",a);b=!1;try{b=!window.frameElement}catch(e){}if(document.documentElement.doScroll&&b){var f=function(){try{document.documentElement.doScroll("left")}catch(b){setTimeout(f,1);return}a()};f()}}}}()},e=window.CKEDITOR_GETURL;if(e){var g=b.getUrl;b.getUrl=function(a){return e.call(b,a)||g.call(b,a)}}return b}());
// The Closure Compiler online service should be used when updating this manually:
// http://closure-compiler.appspot.com/
// #### Raw code
// ATTENTION: read the above "Compressed Code" notes when changing this code.
if ( !window.CKEDITOR ) {
/**
* This is the API entry point. The entire CKEditor code runs under this object.
* @class CKEDITOR
* @singleton
*/
window.CKEDITOR = ( function() {
var CKEDITOR = {
/**
* A constant string unique for each release of CKEditor. Its value
* is used, by default, to build the URL for all resources loaded
* by the editor code, guaranteeing clean cache results when
* upgrading.
*
* alert( CKEDITOR.timestamp ); // e.g. '87dm'
*/
timestamp: '', // %REMOVE_LINE%
/* // %REMOVE_LINE%
// The production implementation contains a fixed timestamp, unique
// for each release and generated by the releaser.
// (Base 36 value of each component of YYMMDDHH - 4 chars total - e.g. 87bm == 08071122)
timestamp: '%TIMESTAMP%',
*/ // %REMOVE_LINE%
/**
* Contains the CKEditor version number.
*
* alert( CKEDITOR.version ); // e.g. 'CKEditor 3.4.1'
*/
version: '%VERSION%',
/**
* Contains the CKEditor revision number.
* The revision number is incremented automatically, following each
* modification to the CKEditor source code.
*
* alert( CKEDITOR.revision ); // e.g. '3975'
*/
revision: '%REV%',
/**
* A 3-digit random integer, valid for the entire life of the CKEDITOR object.
*
* alert( CKEDITOR.rnd ); // e.g. 319
*
* @property {Number}
*/
rnd: Math.floor( Math.random() * ( 999 /*Max*/ - 100 /*Min*/ + 1 ) ) + 100 /*Min*/,
/**
* Private object used to hold core stuff. It should not be used outside of
* the API code as properties defined here may change at any time
* without notice.
*
* @private
*/
_: {
pending: []
},
/**
* Indicates the API loading status. The following statuses are available:
*
* * **unloaded**: the API is not yet loaded.
* * **basic_loaded**: the basic API features are available.
* * **basic_ready**: the basic API is ready to load the full core code.
* * **loaded**: the API can be fully used.
*
* Example:
*
* if ( CKEDITOR.status == 'loaded' ) {
* // The API can now be fully used.
* doSomething();
* } else {
* // Wait for the full core to be loaded and fire its loading.
* CKEDITOR.on( 'load', doSomething );
* CKEDITOR.loadFullCore && CKEDITOR.loadFullCore();
* }
*/
status: 'unloaded',
/**
* The full URL for the CKEditor installation directory.
* It is possible to manually provide the base path by setting a
* global variable named `CKEDITOR_BASEPATH`. This global variable
* must be set **before** the editor script loading.
*
* alert( CKEDITOR.basePath ); // e.g. 'http://www.example.com/ckeditor/'
*
* @property {String}
*/
basePath: ( function() {
// ATTENTION: fixes to this code must be ported to
// var basePath in "core/loader.js".
// Find out the editor directory path, based on its <script> tag.
var path = window.CKEDITOR_BASEPATH || '';
if ( !path ) {
var scripts = document.getElementsByTagName( 'script' );
for ( var i = 0; i < scripts.length; i++ ) {
var match = scripts[ i ].src.match( /(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i );
if ( match ) {
path = match[ 1 ];
break;
}
}
}
// In IE (only) the script.src string is the raw value entered in the
// HTML source. Other browsers return the full resolved URL instead.
if ( path.indexOf( ':/' ) == -1 && path.slice( 0, 2 ) != '//' ) {
// Absolute path.
if ( path.indexOf( '/' ) === 0 )
path = location.href.match( /^.*?:\/\/[^\/]*/ )[ 0 ] + path;
// Relative path.
else
path = location.href.match( /^[^\?]*\/(?:)/ )[ 0 ] + path;
}
if ( !path )
throw 'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';
return path;
} )(),
/**
* Gets the full URL for CKEditor resources. By default, URLs
* returned by this function contain a querystring parameter ("t")
* set to the {@link CKEDITOR#timestamp} value.
*
* It is possible to provide a custom implementation of this
* function by setting a global variable named `CKEDITOR_GETURL`.
* This global variable must be set **before** the editor script
* loading. If the custom implementation returns nothing (`==null`), the
* default implementation is used.
*
* // e.g. 'http://www.example.com/ckeditor/skins/default/editor.css?t=87dm'
* alert( CKEDITOR.getUrl( 'skins/default/editor.css' ) );
*
* // e.g. 'http://www.example.com/skins/default/editor.css?t=87dm'
* alert( CKEDITOR.getUrl( '/skins/default/editor.css' ) );
*
* // e.g. 'http://www.somesite.com/skins/default/editor.css?t=87dm'
* alert( CKEDITOR.getUrl( 'http://www.somesite.com/skins/default/editor.css' ) );
*
* @param {String} resource The resource whose full URL we want to get.
* It may be a full, absolute, or relative URL.
* @returns {String} The full URL.
*/
getUrl: function( resource ) {
// If this is not a full or absolute path.
if ( resource.indexOf( ':/' ) == -1 && resource.indexOf( '/' ) !== 0 )
resource = this.basePath + resource;
// Add the timestamp, except for directories.
if ( this.timestamp && resource.charAt( resource.length - 1 ) != '/' && !( /[&?]t=/ ).test( resource ) )
resource += ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) + 't=' + this.timestamp;
return resource;
},
/**
* Specify a function to execute when the DOM is fully loaded.
*
* If called after the DOM has been initialized, the function passed in will
* be executed immediately.
*
* @method
* @todo
*/
domReady: ( function() {
// Based on the original jQuery code.
var callbacks = [];
function onReady() {
try {
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
document.removeEventListener( 'DOMContentLoaded', onReady, false );
executeCallbacks();
}
// Make sure body exists, at least, in case IE gets a little overzealous.
else if ( document.attachEvent && document.readyState === 'complete' ) {
document.detachEvent( 'onreadystatechange', onReady );
executeCallbacks();
}
} catch ( er ) {}
}
function executeCallbacks() {
var i;
while ( ( i = callbacks.shift() ) )
i();
}
return function( fn ) {
callbacks.push( fn );
// Catch cases where this is called after the
// browser event has already occurred.
if ( document.readyState === 'complete' )
// Handle it asynchronously to allow scripts the opportunity to delay ready
setTimeout( onReady, 1 );
// Run below once on demand only.
if ( callbacks.length != 1 )
return;
// For IE>8, Firefox, Opera and Webkit.
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( 'DOMContentLoaded', onReady, false );
// A fallback to window.onload, that will always work
window.addEventListener( 'load', onReady, false );
}
// If old IE event model is used
else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent( 'onreadystatechange', onReady );
// A fallback to window.onload, that will always work
window.attachEvent( 'onload', onReady );
// If IE and not a frame
// continually check to see if the document is ready
// use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
var toplevel = false;
try {
toplevel = !window.frameElement;
} catch ( e ) {}
if ( document.documentElement.doScroll && toplevel ) {
function scrollCheck() {
try {
document.documentElement.doScroll( 'left' );
} catch ( e ) {
setTimeout( scrollCheck, 1 );
return;
}
onReady();
}
scrollCheck();
}
}
};
} )()
};
// Make it possible to override the "url" function with a custom
// implementation pointing to a global named CKEDITOR_GETURL.
var newGetUrl = window.CKEDITOR_GETURL;
if ( newGetUrl ) {
var originalGetUrl = CKEDITOR.getUrl;
CKEDITOR.getUrl = function( resource ) {
return newGetUrl.call( CKEDITOR, resource ) || originalGetUrl.call( CKEDITOR, resource );
};
}
return CKEDITOR;
} )();
}
/**
* Function called upon loading a custom configuration file that can
* modify the editor instance configuration ({@link CKEDITOR.editor#config}).
* It is usually defined inside the custom configuration files that can
* include developer defined settings.
*
* // This is supposed to be placed in the config.js file.
* CKEDITOR.editorConfig = function( config ) {
* // Define changes to default configuration here. For example:
* config.language = 'fr';
* config.uiColor = '#AADC6E';
* };
*
* @method editorConfig
* @param {CKEDITOR.config} config A configuration object containing the
* settings defined for a {@link CKEDITOR.editor} instance up to this
* function call. Note that not all settings may still be available. See
* [Configuration Loading Order](http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Setting_Configurations#Configuration_Loading_Order)
* for details.
*/
// PACKAGER_RENAME( CKEDITOR )

View File

@@ -0,0 +1,94 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Contains the second part of the {@link CKEDITOR} object
* definition, which defines the basic editor features to be available in
* the root ckeditor_basic.js file.
*/
if ( CKEDITOR.status == 'unloaded' ) {
( function() {
CKEDITOR.event.implementOn( CKEDITOR );
/**
* Forces the full CKEditor core code, in the case only the basic code has been
* loaded (`ckeditor_basic.js`). This method self-destroys (becomes undefined) in
* the first call or as soon as the full code is available.
*
* // Check if the full core code has been loaded and load it.
* if ( CKEDITOR.loadFullCore )
* CKEDITOR.loadFullCore();
*
* @member CKEDITOR
*/
CKEDITOR.loadFullCore = function() {
// If the basic code is not ready, just mark it to be loaded.
if ( CKEDITOR.status != 'basic_ready' ) {
CKEDITOR.loadFullCore._load = 1;
return;
}
// Destroy this function.
delete CKEDITOR.loadFullCore;
// Append the script to the head.
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = CKEDITOR.basePath + 'ckeditor.js';
script.src = CKEDITOR.basePath + 'ckeditor_source.js'; // %REMOVE_LINE%
document.getElementsByTagName( 'head' )[ 0 ].appendChild( script );
};
/**
* The time to wait (in seconds) to load the full editor code after the
* page load, if the "ckeditor_basic" file is used. If set to zero, the
* editor is loaded on demand, as soon as an instance is created.
*
* This value must be set on the page before the page load completion.
*
* // Loads the full source after five seconds.
* CKEDITOR.loadFullCoreTimeout = 5;
*
* @property
* @member CKEDITOR
*/
CKEDITOR.loadFullCoreTimeout = 0;
// Documented at ckeditor.js.
CKEDITOR.add = function( editor ) {
// For now, just put the editor in the pending list. It will be
// processed as soon as the full code gets loaded.
var pending = this._.pending || ( this._.pending = [] );
pending.push( editor );
};
( function() {
var onload = function() {
var loadFullCore = CKEDITOR.loadFullCore,
loadFullCoreTimeout = CKEDITOR.loadFullCoreTimeout;
if ( !loadFullCore )
return;
CKEDITOR.status = 'basic_ready';
if ( loadFullCore && loadFullCore._load )
loadFullCore();
else if ( loadFullCoreTimeout ) {
setTimeout( function() {
if ( CKEDITOR.loadFullCore )
CKEDITOR.loadFullCore();
}, loadFullCoreTimeout * 1000 );
}
};
CKEDITOR.domReady( onload );
} )();
CKEDITOR.status = 'basic_loaded';
} )();
}

View File

@@ -0,0 +1,271 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* Represents a command that can be executed on an editor instance.
*
* var command = new CKEDITOR.command( editor, {
* exec: function( editor ) {
* alert( editor.document.getBody().getHtml() );
* }
* } );
*
* @class
* @mixins CKEDITOR.event
* @constructor Creates a command class instance.
* @param {CKEDITOR.editor} editor The editor instance this command will be
* related to.
* @param {CKEDITOR.commandDefinition} commandDefinition The command
* definition.
*/
CKEDITOR.command = function( editor, commandDefinition ) {
/**
* Lists UI items that are associated to this command. This list can be
* used to interact with the UI on command execution (by the execution code
* itself, for example).
*
* alert( 'Number of UI items associated to this command: ' + command.uiItems.length );
*/
this.uiItems = [];
/**
* Executes the command.
*
* command.exec(); // The command gets executed.
*
* @param {Object} [data] Any data to pass to the command. Depends on the
* command implementation and requirements.
* @returns {Boolean} A boolean indicating that the command has been successfully executed.
*/
this.exec = function( data ) {
if ( this.state == CKEDITOR.TRISTATE_DISABLED || !this.checkAllowed() )
return false;
if ( this.editorFocus ) // Give editor focus if necessary (#4355).
editor.focus();
if ( this.fire( 'exec' ) === false )
return true;
return ( commandDefinition.exec.call( this, editor, data ) !== false );
};
/**
* Explicitly update the status of the command, by firing the {@link CKEDITOR.command#event-refresh} event,
* as well as invoke the {@link CKEDITOR.commandDefinition#refresh} method if defined, this method
* is to allow different parts of the editor code to contribute in command status resolution.
*
* @param {CKEDITOR.editor} editor The editor instance.
* @param {CKEDITOR.dom.elementPath} path
*/
this.refresh = function( editor, path ) {
// Do nothing is we're on read-only and this command doesn't support it.
// We don't need to disabled the command explicitely here, because this
// is already done by the "readOnly" event listener.
if ( !this.readOnly && editor.readOnly )
return true;
// Disable commands that are not allowed in the current selection path context.
if ( this.context && !path.isContextFor( this.context ) ) {
this.disable();
return true;
}
// Disable commands that are not allowed by the active filter.
if ( !this.checkAllowed( true ) ) {
this.disable();
return true;
}
// Make the "enabled" state a default for commands enabled from start.
if ( !this.startDisabled )
this.enable();
// Disable commands which shouldn't be enabled in this mode.
if ( this.modes && !this.modes[ editor.mode ] )
this.disable();
if ( this.fire( 'refresh', { editor: editor, path: path } ) === false )
return true;
return ( commandDefinition.refresh && commandDefinition.refresh.apply( this, arguments ) !== false );
};
var allowed;
/**
* Checks whether this command is allowed by the active allowed
* content filter ({@link CKEDITOR.editor#activeFilter}). This means
* that if command implements {@link CKEDITOR.feature} interface it will be tested
* by the {@link CKEDITOR.filter#checkFeature} method.
*
* @since 4.1
* @param {Boolean} [noCache] Skip cache for example due to active filter change. Since CKEditor 4.2.
* @returns {Boolean} Whether this command is allowed.
*/
this.checkAllowed = function( noCache ) {
if ( !noCache && typeof allowed == 'boolean' )
return allowed;
return allowed = editor.activeFilter.checkFeature( this );
};
CKEDITOR.tools.extend( this, commandDefinition, {
/**
* The editor modes within which the command can be executed. The
* execution will have no action if the current mode is not listed
* in this property.
*
* // Enable the command in both WYSIWYG and Source modes.
* command.modes = { wysiwyg:1,source:1 };
*
* // Enable the command in Source mode only.
* command.modes = { source:1 };
*
* @see CKEDITOR.editor#mode
*/
modes: { wysiwyg: 1 },
/**
* Indicates that the editor will get the focus before executing
* the command.
*
* // Do not force the editor to have focus when executing the command.
* command.editorFocus = false;
*
* @property {Boolean} [=true]
*/
editorFocus: 1,
/**
* Indicates that this command is sensible to the selection context.
* If `true`, the {@link CKEDITOR.command#method-refresh} method will be
* called for this command on the {@link CKEDITOR.editor#event-selectionChange} event.
*
* @property {Boolean} [=false]
*/
contextSensitive: !!commandDefinition.context,
/**
* Indicates the editor state. Possible values are:
*
* * {@link CKEDITOR#TRISTATE_DISABLED}: the command is
* disabled. It's execution will have no effect. Same as {@link #disable}.
* * {@link CKEDITOR#TRISTATE_ON}: the command is enabled
* and currently active in the editor (for context sensitive commands, for example).
* * {@link CKEDITOR#TRISTATE_OFF}: the command is enabled
* and currently inactive in the editor (for context sensitive commands, for example).
*
* Do not set this property directly, using the {@link #setState} method instead.
*
* if ( command.state == CKEDITOR.TRISTATE_DISABLED )
* alert( 'This command is disabled' );
*
* @property {Number} [=CKEDITOR.TRISTATE_DISABLED]
*/
state: CKEDITOR.TRISTATE_DISABLED
} );
// Call the CKEDITOR.event constructor to initialize this instance.
CKEDITOR.event.call( this );
};
CKEDITOR.command.prototype = {
/**
* Enables the command for execution. The command state (see
* {@link CKEDITOR.command#property-state}) available before disabling it is restored.
*
* command.enable();
* command.exec(); // Execute the command.
*/
enable: function() {
if ( this.state == CKEDITOR.TRISTATE_DISABLED && this.checkAllowed() )
this.setState( ( !this.preserveState || ( typeof this.previousState == 'undefined' ) ) ? CKEDITOR.TRISTATE_OFF : this.previousState );
},
/**
* Disables the command for execution. The command state (see
* {@link CKEDITOR.command#property-state}) will be set to {@link CKEDITOR#TRISTATE_DISABLED}.
*
* command.disable();
* command.exec(); // "false" - Nothing happens.
*/
disable: function() {
this.setState( CKEDITOR.TRISTATE_DISABLED );
},
/**
* Sets the command state.
*
* command.setState( CKEDITOR.TRISTATE_ON );
* command.exec(); // Execute the command.
* command.setState( CKEDITOR.TRISTATE_DISABLED );
* command.exec(); // 'false' - Nothing happens.
* command.setState( CKEDITOR.TRISTATE_OFF );
* command.exec(); // Execute the command.
*
* @param {Number} newState The new state. See {@link #property-state}.
* @returns {Boolean} Returns `true` if the command state changed.
*/
setState: function( newState ) {
// Do nothing if there is no state change.
if ( this.state == newState )
return false;
if ( newState != CKEDITOR.TRISTATE_DISABLED && !this.checkAllowed() )
return false;
this.previousState = this.state;
// Set the new state.
this.state = newState;
// Fire the "state" event, so other parts of the code can react to the
// change.
this.fire( 'state' );
return true;
},
/**
* Toggles the on/off (active/inactive) state of the command. This is
* mainly used internally by context sensitive commands.
*
* command.toggleState();
*/
toggleState: function() {
if ( this.state == CKEDITOR.TRISTATE_OFF )
this.setState( CKEDITOR.TRISTATE_ON );
else if ( this.state == CKEDITOR.TRISTATE_ON )
this.setState( CKEDITOR.TRISTATE_OFF );
}
};
CKEDITOR.event.implementOn( CKEDITOR.command.prototype );
/**
* Indicates the previous command state.
*
* alert( command.previousState );
*
* @property {Number} previousState
* @see #state
*/
/**
* Fired when the command state changes.
*
* command.on( 'state', function() {
* // Alerts the new state.
* alert( this.state );
* } );
*
* @event state
*/
/**
* @event refresh
* @todo
*/

View File

@@ -0,0 +1,139 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the "virtual" {@link CKEDITOR.commandDefinition} class,
* which contains the defintion of a command. This file is for
* documentation purposes only.
*/
/**
* Virtual class that illustrates the features of command objects to be
* passed to the {@link CKEDITOR.editor#addCommand} function.
*
* @class CKEDITOR.commandDefinition
* @abstract
*/
/**
* The function to be fired when the commend is executed.
*
* editorInstance.addCommand( 'sample', {
* exec: function( editor ) {
* alert( 'Executing a command for the editor name "' + editor.name + '"!' );
* }
* } );
*
* @method exec
* @param {CKEDITOR.editor} editor The editor within which run the command.
* @param {Object} [data] Additional data to be used to execute the command.
* @returns {Boolean} Whether the command has been successfully executed.
* Defaults to `true`, if nothing is returned.
*/
/**
* Whether the command need to be hooked into the redo/undo system.
*
* editorInstance.addCommand( 'alertName', {
* exec: function( editor ) {
* alert( editor.name );
* },
* canUndo: false // No support for undo/redo.
* } );
*
* @property {Boolean} [canUndo=true]
*/
/**
* Whether the command is asynchronous, which means that the
* {@link CKEDITOR.editor#event-afterCommandExec} event will be fired by the
* command itself manually, and that the return value of this command is not to
* be returned by the {@link #exec} function.
*
* editorInstance.addCommand( 'loadOptions', {
* exec: function( editor ) {
* // Asynchronous operation below.
* CKEDITOR.ajax.loadXml( 'data.xml', function() {
* editor.fire( 'afterCommandExec' );
* } );
* },
* async: true // The command need some time to complete after exec function returns.
* } );
*
* @property {Boolean} [async=false]
*/
/**
* Whether the command should give focus to the editor before execution.
*
* editorInstance.addCommand( 'maximize', {
* exec: function( editor ) {
* // ...
* },
* editorFocus: false // The command doesn't require focusing the editing document.
* } );
*
* @property {Boolean} [editorFocus=true]
* @see CKEDITOR.command#editorFocus
*/
/**
* Whether the command state should be set to {@link CKEDITOR#TRISTATE_DISABLED} on startup.
*
* editorInstance.addCommand( 'unlink', {
* exec: function( editor ) {
* // ...
* },
* startDisabled: true // Command is unavailable until selection is inside a link.
* } );
*
* @property {Boolean} [startDisabled=false]
*/
/**
* Indicates that this command is sensible to the selection context.
* If `true`, the {@link CKEDITOR.command#method-refresh} method will be
* called for this command on selection changes, with a single parameter
* representing the current elements path.
*
* @property {Boolean} [contextSensitive=true]
*/
/**
* Defined by command definition a function to determinate the command state, it will be invoked
* when editor has it's `states` or `selection` changed.
*
* **Note:** The function provided must be calling {@link CKEDITOR.command#setState} in all circumstance,
* if it is intended to update the command state.
*
* @method refresh
* @param {CKEDITOR.editor} editor
* @param {CKEDITOR.dom.elementPath} path
*/
/**
* Sets the element name used to reflect the command state on selection changes.
* If the selection is in a place where the element is not allowed, the command
* will be disabled.
* Setting this property overrides {@link #contextSensitive} to `true`.
*
* @property {Boolean} [context=true]
*/
/**
* The editor modes within which the command can be executed. The execution
* will have no action if the current mode is not listed in this property.
*
* editorInstance.addCommand( 'link', {
* exec: function( editor ) {
* // ...
* },
* modes: { wysiwyg:1 } // Command is available in wysiwyg mode only.
* } );
*
* @property {Object} [modes={ wysiwyg:1 }]
* @see CKEDITOR.command#modes
*/

View File

@@ -0,0 +1,383 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.config} object that stores the
* default configuration settings.
*/
/**
* Used in conjunction with {@link CKEDITOR.config#enterMode}
* and {@link CKEDITOR.config#shiftEnterMode} configuration
* settings to make the editor produce `<p>` tags when
* using the *Enter* key.
*
* @readonly
* @property {Number} [=1]
* @member CKEDITOR
*/
CKEDITOR.ENTER_P = 1;
/**
* Used in conjunction with {@link CKEDITOR.config#enterMode}
* and {@link CKEDITOR.config#shiftEnterMode} configuration
* settings to make the editor produce `<br>` tags when
* using the *Enter* key.
*
* @readonly
* @property {Number} [=2]
* @member CKEDITOR
*/
CKEDITOR.ENTER_BR = 2;
/**
* Used in conjunction with {@link CKEDITOR.config#enterMode}
* and {@link CKEDITOR.config#shiftEnterMode} configuration
* settings to make the editor produce `<div>` tags when
* using the *Enter* key.
*
* @readonly
* @property {Number} [=3]
* @member CKEDITOR
*/
CKEDITOR.ENTER_DIV = 3;
/**
* Stores default configuration settings. Changes to this object are
* reflected in all editor instances, if not specified otherwise for a particular
* instance.
*
* @class
* @singleton
*/
CKEDITOR.config = {
/**
* The URL path for the custom configuration file to be loaded. If not
* overloaded with inline configuration, it defaults to the `config.js`
* file present in the root of the CKEditor installation directory.
*
* CKEditor will recursively load custom configuration files defined inside
* other custom configuration files.
*
* // Load a specific configuration file.
* CKEDITOR.replace( 'myfield', { customConfig: '/myconfig.js' } );
*
* // Do not load any custom configuration file.
* CKEDITOR.replace( 'myfield', { customConfig: '' } );
*
* @cfg {String} [="<CKEditor folder>/config.js"]
*/
customConfig: 'config.js',
/**
* Whether the replaced element (usually a `<textarea>`)
* is to be updated automatically when posting the form containing the editor.
*
* @cfg
*/
autoUpdateElement: true,
/**
* The user interface language localization to use. If left empty, the editor
* will automatically be localized to the user language. If the user language is not supported,
* the language specified in the {@link CKEDITOR.config#defaultLanguage}
* configuration setting is used.
*
* // Load the German interface.
* config.language = 'de';
*
* @cfg
*/
language: '',
/**
* The language to be used if the {@link CKEDITOR.config#language}
* setting is left empty and it is not possible to localize the editor to the user language.
*
* config.defaultLanguage = 'it';
*
* @cfg
*/
defaultLanguage: 'en',
/**
* The writting direction of the language used to write the editor
* contents. Allowed values are:
*
* * `''` (empty string) - indicate content direction will be the same with either the editor
* UI direction or page element direction depending on the creators:
* * Themed UI: The same with user interface language direction;
* * Inline: The same with the editable element text direction;
* * `'ltr'` - for Left-To-Right language (like English);
* * `'rtl'` - for Right-To-Left languages (like Arabic).
*
* Example:
*
* config.contentsLangDirection = 'rtl';
*
* @cfg
*/
contentsLangDirection: '',
/**
* Sets the behavior of the *Enter* key. It also determines other behavior
* rules of the editor, like whether the `<br>` element is to be used
* as a paragraph separator when indenting text.
* The allowed values are the following constants that cause the behavior outlined below:
*
* * {@link CKEDITOR#ENTER_P} (1) &ndash; new `<p>` paragraphs are created;
* * {@link CKEDITOR#ENTER_BR} (2) &ndash; lines are broken with `<br>` elements;
* * {@link CKEDITOR#ENTER_DIV} (3) &ndash; new `<div>` blocks are created.
*
* **Note**: It is recommended to use the {@link CKEDITOR#ENTER_P} setting because of
* its semantic value and correctness. The editor is optimized for this setting.
*
* // Not recommended.
* config.enterMode = CKEDITOR.ENTER_BR;
*
* @cfg {Number} [=CKEDITOR.ENTER_P]
*/
enterMode: CKEDITOR.ENTER_P,
/**
* Force the use of {@link CKEDITOR.config#enterMode} as line break regardless
* of the context. If, for example, {@link CKEDITOR.config#enterMode} is set
* to {@link CKEDITOR#ENTER_P}, pressing the *Enter* key inside a
* `<div>` element will create a new paragraph with `<p>`
* instead of a `<div>`.
*
* // Not recommended.
* config.forceEnterMode = true;
*
* @since 3.2.1
* @cfg
*/
forceEnterMode: false,
/**
* Similarly to the {@link CKEDITOR.config#enterMode} setting, it defines the behavior
* of the *Shift+Enter* key combination.
*
* The allowed values are the following constants the behavior outlined below:
*
* * {@link CKEDITOR#ENTER_P} (1) &ndash; new `<p>` paragraphs are created;
* * {@link CKEDITOR#ENTER_BR} (2) &ndash; lines are broken with `<br>` elements;
* * {@link CKEDITOR#ENTER_DIV} (3) &ndash; new `<div>` blocks are created.
*
* Example:
*
* config.shiftEnterMode = CKEDITOR.ENTER_P;
*
* @cfg {Number} [=CKEDITOR.ENTER_BR]
*/
shiftEnterMode: CKEDITOR.ENTER_BR,
/**
* Sets the `DOCTYPE` to be used when loading the editor content as HTML.
*
* // Set the DOCTYPE to the HTML 4 (Quirks) mode.
* config.docType = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">';
*
* @cfg
*/
docType: '<!DOCTYPE html>',
/**
* Sets the `id` attribute to be used on the `body` element
* of the editing area. This can be useful when you intend to reuse the original CSS
* file you are using on your live website and want to assign the editor the same ID
* as the section that will include the contents. In this way ID-specific CSS rules will
* be enabled.
*
* config.bodyId = 'contents_id';
*
* @since 3.1
* @cfg
*/
bodyId: '',
/**
* Sets the `class` attribute to be used on the `body` element
* of the editing area. This can be useful when you intend to reuse the original CSS
* file you are using on your live website and want to assign the editor the same class
* as the section that will include the contents. In this way class-specific CSS rules will
* be enabled.
*
* config.bodyClass = 'contents';
*
* **Note:** Editor needs to load stylesheets containing contents styles. You can either
* copy them to the `contents.css` file that editor loads by default or set the {@link #contentsCss}
* option.
*
* **Note:** This setting applies only to the classic editor (the one that uses `iframe`).
*
* @since 3.1
* @cfg
*/
bodyClass: '',
/**
* Indicates whether the contents to be edited are being input as a full HTML page.
* A full page includes the `<html>`, `<head>`, and `<body>` elements.
* The final output will also reflect this setting, including the
* `<body>` contents only if this setting is disabled.
*
* config.fullPage = true;
*
* @since 3.1
* @cfg
*/
fullPage: false,
/**
* The height of the editing area (that includes the editor content). This
* can be an integer, for pixel sizes, or any CSS-defined length unit.
*
* **Note:** Percent units (%) are not supported.
*
* config.height = 500; // 500 pixels.
* config.height = '25em'; // CSS length.
* config.height = '300px'; // CSS length.
*
* @cfg {Number/String}
*/
height: 200,
/**
* Comma separated list of plugins to be used for an editor instance,
* besides, the actual plugins that to be loaded could be still affected by two other settings:
* {@link CKEDITOR.config#extraPlugins} and {@link CKEDITOR.config#removePlugins}.
*
* @cfg {String} [="<default list of plugins>"]
*/
plugins: '', // %REMOVE_LINE%
/**
* A list of additional plugins to be loaded. This setting makes it easier
* to add new plugins without having to touch {@link CKEDITOR.config#plugins} setting.
*
* config.extraPlugins = 'myplugin,anotherplugin';
*
* @cfg
*/
extraPlugins: '',
/**
* A list of plugins that must not be loaded. This setting makes it possible
* to avoid loading some plugins defined in the {@link CKEDITOR.config#plugins}
* setting, without having to touch it.
*
* **Note:** Plugin required by other plugin cannot be removed (error will be thrown).
* So e.g. if `contextmenu` is required by `tabletools`, then it can be removed
* only if `tabletools` isn't loaded.
*
* config.removePlugins = 'elementspath,save,font';
*
* @cfg
*/
removePlugins: '',
/**
* List of regular expressions to be executed on input HTML,
* indicating HTML source code that when matched, must **not** be available in the WYSIWYG
* mode for editing.
*
* config.protectedSource.push( /<\?[\s\S]*?\?>/g ); // PHP code
* config.protectedSource.push( /<%[\s\S]*?%>/g ); // ASP code
* config.protectedSource.push( /(<asp:[^\>]+>[\s|\S]*?<\/asp:[^\>]+>)|(<asp:[^\>]+\/>)/gi ); // ASP.Net code
*
* @cfg
*/
protectedSource: [],
/**
* The editor `tabindex` value.
*
* config.tabIndex = 1;
*
* @cfg
*/
tabIndex: 0,
/**
* The editor UI outer width. This can be an integer, for pixel sizes, or
* any CSS-defined unit.
*
* Unlike the {@link CKEDITOR.config#height} setting, this
* one will set the outer width of the entire editor UI, not for the
* editing area only.
*
* config.width = 850; // 850 pixels wide.
* config.width = '75%'; // CSS unit.
*
* @cfg {String/Number}
*/
width: '',
/**
* The base Z-index for floating dialog windows and popups.
*
* config.baseFloatZIndex = 2000;
*
* @cfg
*/
baseFloatZIndex: 10000,
/**
* The keystrokes that are blocked by default as the browser implementation
* is buggy. These default keystrokes are handled by the editor.
*
* // Default setting.
* config.blockedKeystrokes = [
* CKEDITOR.CTRL + 66, // CTRL+B
* CKEDITOR.CTRL + 73, // CTRL+I
* CKEDITOR.CTRL + 85, // CTRL+U
* CKEDITOR.CTRL + 89, // CTRL+Y
* CKEDITOR.CTRL + 90, // CTRL+Z
* CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 // CTRL+SHIFT+Z
* ];
*
* @cfg {Array} [blockedKeystrokes=see example]
*/
blockedKeystrokes: [
CKEDITOR.CTRL + 66, // CTRL+B
CKEDITOR.CTRL + 73, // CTRL+I
CKEDITOR.CTRL + 85, // CTRL+U
CKEDITOR.CTRL + 89, // CTRL+Y
CKEDITOR.CTRL + 90, // CTRL+Z
CKEDITOR.CTRL + CKEDITOR.SHIFT + 90 // CTRL+SHIFT+Z
]
};
/**
* Indicates that some of the editor features, like alignment and text
* direction, should use the "computed value" of the feature to indicate its
* on/off state instead of using the "real value".
*
* If enabled in a Left-To-Right written document, the "Left Justify"
* alignment button will be shown as active, even if the alignment style is not
* explicitly applied to the current paragraph in the editor.
*
* config.useComputedState = false;
*
* @since 3.4
* @cfg {Boolean} [useComputedState=true]
*/
/**
* The base user interface color to be used by the editor. Not all skins are
* compatible with this setting.
*
* // Using a color code.
* config.uiColor = '#AADC6E';
*
* // Using an HTML color name.
* config.uiColor = 'Gold';
*
* @cfg {String} uiColor
*/
// PACKAGER_RENAME( CKEDITOR.config )

View File

@@ -0,0 +1,153 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
( function() {
/** @class CKEDITOR */
/**
* Turns a DOM element with `contenteditable` attribute set to `true` into a
* CKEditor instance. Check {@link CKEDITOR.dtd#$editable} for the list of
* allowed element names.
*
* <div contenteditable="true" id="content">...</div>
* ...
* CKEDITOR.inline( 'content' );
*
* It is also possible to create an inline editor from the `<textarea>` element.
* If you do so, an additional `<div>` element with editable content will be created
* directly after the `<textarea>` element and the `<textarea>` element will be hidden.
*
* @param {Object/String} element The DOM element or its ID.
* @param {Object} [instanceConfig] The specific configurations to apply to this editor instance.
* See {@link CKEDITOR.config}.
* @returns {CKEDITOR.editor} The editor instance created.
*/
CKEDITOR.inline = function( element, instanceConfig ) {
if ( !CKEDITOR.env.isCompatible )
return null;
element = CKEDITOR.dom.element.get( element );
// Avoid multiple inline editor instances on the same element.
if ( element.getEditor() )
throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.';
var editor = new CKEDITOR.editor( instanceConfig, element, CKEDITOR.ELEMENT_MODE_INLINE ),
textarea = element.is( 'textarea' ) ? element : null;
if ( textarea ) {
editor.setData( textarea.getValue(), null, true );
//Change element from textarea to div
element = CKEDITOR.dom.element.createFromHtml(
'<div contenteditable="' + !!editor.readOnly + '" class="cke_textarea_inline">' +
textarea.getValue() +
'</div>',
CKEDITOR.document );
element.insertAfter( textarea );
textarea.hide();
// Attaching the concrete form.
if ( textarea.$.form )
editor._attachToForm();
} else {
// Initial editor data is simply loaded from the page element content to make
// data retrieval possible immediately after the editor creation.
editor.setData( element.getHtml(), null, true );
}
// Once the editor is loaded, start the UI.
editor.on( 'loaded', function() {
editor.fire( 'uiReady' );
// Enable editing on the element.
editor.editable( element );
// Editable itself is the outermost element.
editor.container = element;
// Load and process editor data.
editor.setData( editor.getData( 1 ) );
// Clean on startup.
editor.resetDirty();
editor.fire( 'contentDom' );
// Inline editing defaults to "wysiwyg" mode, so plugins don't
// need to make special handling for this "mode-less" environment.
editor.mode = 'wysiwyg';
editor.fire( 'mode' );
// The editor is completely loaded for interaction.
editor.status = 'ready';
editor.fireOnce( 'instanceReady' );
CKEDITOR.fire( 'instanceReady', null, editor );
// give priority to plugins that relay on editor#loaded for bootstrapping.
}, null, null, 10000 );
// Handle editor destroying.
editor.on( 'destroy', function() {
// Remove container from DOM if inline-textarea editor.
// Show <textarea> back again.
if ( textarea ) {
editor.container.clearCustomData();
editor.container.remove();
textarea.show();
}
editor.element.clearCustomData();
delete editor.element;
} );
return editor;
};
/**
* Calls {@link CKEDITOR#inline} for all page elements with
* `contenteditable` attribute set to `true`.
*
*/
CKEDITOR.inlineAll = function() {
var el, data;
for ( var name in CKEDITOR.dtd.$editable ) {
var elements = CKEDITOR.document.getElementsByTag( name );
for ( var i = 0, len = elements.count(); i < len; i++ ) {
el = elements.getItem( i );
if ( el.getAttribute( 'contenteditable' ) == 'true' ) {
// Fire the "inline" event, making it possible to customize
// the instance settings and eventually cancel the creation.
data = {
element: el,
config: {}
};
if ( CKEDITOR.fire( 'inline', data ) !== false )
CKEDITOR.inline( el, data.config );
}
}
}
};
CKEDITOR.domReady( function() {
!CKEDITOR.disableAutoInline && CKEDITOR.inlineAll();
} );
} )();
/**
* Disables creating the inline editor automatically for elements with
* `contenteditable` attribute set to the `true`.
*
* CKEDITOR.disableAutoInline = true;
*
* @cfg {Boolean} [disableAutoInline=false]
*/

View File

@@ -0,0 +1,457 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/** @class CKEDITOR */
/**
* The class name used to identify `<textarea>` elements to be replaced
* by CKEditor instances. Set it to empty/`null` to disable this feature.
*
* CKEDITOR.replaceClass = 'rich_editor';
*
* @cfg {String} [replaceClass='ckeditor']
*/
CKEDITOR.replaceClass = 'ckeditor';
( function() {
/**
* Replaces a `<textarea>` or a DOM element (`<div>`) with a CKEditor
* instance. For textareas, the initial value in the editor will be the
* textarea value. For DOM elements, their `innerHTML` will be used
* instead. We recommend using `<textarea>` and `<div>` elements only.
*
* <textarea id="myfield" name="myfield"></textarea>
* ...
* 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.
*
* <div id="editorSpace"></div>
* ...
* 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 `<textarea>` elements available in the document with
* editor instances.
*
* // Replace all <textarea> elements in the page.
* CKEDITOR.replaceAll();
*
* // Replace all <textarea class="myClassName"> elements in the page.
* CKEDITOR.replaceAll( 'myClassName' );
*
* // Selectively replace <textarea> elements, based on custom assertions.
* CKEDITOR.replaceAll( function( textarea, config ) {
* // An assertion function that needs to be evaluated for the <textarea>
* // to be replaced. It must explicitely return "false" to ignore a
* // specific <textarea>.
* // You can also customize the editor instance by having the function
* // modify the "config" parameter.
* } );
*
* @param {String} [className] The `<textarea>` class name.
* @param {Function} [function] An assertion function that must return `true` for a `<textarea>`
* to be replaced with the editor. If the function returns `false`, the `<textarea>` element
* will not be replaced.
*/
CKEDITOR.replaceAll = function() {
var textareas = document.getElementsByTagName( 'textarea' );
for ( var i = 0; i < textareas.length; i++ ) {
var config = null,
textarea = textareas[ i ];
// The "name" and/or "id" attribute must exist.
if ( !textarea.name && !textarea.id )
continue;
if ( typeof arguments[ 0 ] == 'string' ) {
// The textarea class name could be passed as the function
// parameter.
var classRegex = new RegExp( '(?:^|\\s)' + arguments[ 0 ] + '(?:$|\\s)' );
if ( !classRegex.test( textarea.className ) )
continue;
} else if ( typeof arguments[ 0 ] == 'function' ) {
// An assertion function could be passed as the function parameter.
// It must explicitly return "false" to ignore a specific <textarea>.
config = {};
if ( arguments[ 0 ]( textarea, config ) === false )
continue;
}
this.replace( textarea, config );
}
};
/** @class CKEDITOR.editor */
/**
* Registers an editing mode. This function is to be used mainly by plugins.
*
* @param {String} mode The mode name.
* @param {Function} exec The function that performs the actual mode change.
*/
CKEDITOR.editor.prototype.addMode = function( mode, exec ) {
( this._.modes || ( this._.modes = {} ) )[ mode ] = exec;
};
/**
* Changes the editing mode of this editor instance.
*
* **Note:** The mode switch could be asynchronous depending on the mode provider.
* Use the `callback` to hook subsequent code.
*
* // Switch to "source" view.
* CKEDITOR.instances.editor1.setMode( 'source' );
* // Switch to "wysiwyg" view and be notified on completion.
* CKEDITOR.instances.editor1.setMode( 'wysiwyg', function() { alert( 'wysiwyg mode loaded!' ); } );
*
* @param {String} [newMode] If not specified, the {@link CKEDITOR.config#startupMode} will be used.
* @param {Function} [callback] Optional callback function which is invoked once the mode switch has succeeded.
*/
CKEDITOR.editor.prototype.setMode = function( newMode, callback ) {
var editor = this;
var modes = this._.modes;
// Mode loading quickly fails.
if ( newMode == editor.mode || !modes || !modes[ newMode ] )
return;
editor.fire( 'beforeSetMode', newMode );
if ( editor.mode ) {
var isDirty = editor.checkDirty();
editor._.previousMode = editor.mode;
editor.fire( 'beforeModeUnload' );
// Detach the current editable.
editor.editable( 0 );
// Clear up the mode space.
editor.ui.space( 'contents' ).setHtml( '' );
editor.mode = '';
}
// Fire the mode handler.
this._.modes[ newMode ]( function() {
// Set the current mode.
editor.mode = newMode;
if ( isDirty !== undefined )
!isDirty && editor.resetDirty();
// Delay to avoid race conditions (setMode inside setMode).
setTimeout( function() {
editor.fire( 'mode' );
callback && callback.call( editor );
}, 0 );
} );
};
/**
* Resizes the editor interface.
*
* editor.resize( 900, 300 );
*
* editor.resize( '100%', 450, true );
*
* @param {Number/String} width The new width. It can be an integer denoting a value
* in pixels or a CSS size value with unit.
* @param {Number/String} height The new height. It can be an integer denoting a value
* in pixels or a CSS size value with unit.
* @param {Boolean} [isContentHeight] Indicates that the provided height is to
* be applied to the editor content area, and not to the entire editor
* interface. Defaults to `false`.
* @param {Boolean} [resizeInner] Indicates that it is the inner interface
* element that must be resized, not the outer element. The default theme
* defines the editor interface inside a pair of `<span>` elements
* (`<span><span>...</span></span>`). By default the first,
* outer `<span>` element receives the sizes. If this parameter is set to
* `true`, the second, inner `<span>` is resized instead.
*/
CKEDITOR.editor.prototype.resize = function( width, height, isContentHeight, resizeInner ) {
var container = this.container,
contents = this.ui.space( 'contents' ),
contentsFrame = CKEDITOR.env.webkit && this.document && this.document.getWindow().$.frameElement,
outer = resizeInner ? container.getChild( 1 ) : container;
// Set as border box width. (#5353)
outer.setSize( 'width', width, true );
// WebKit needs to refresh the iframe size to avoid rendering issues. (1/2) (#8348)
contentsFrame && ( contentsFrame.style.width = '1%' );
// Get the height delta between the outer table and the content area.
// If we're setting the content area's height, then we don't need the delta.
var delta = isContentHeight ? 0 : ( outer.$.offsetHeight || 0 ) - ( contents.$.clientHeight || 0 );
contents.setStyle( 'height', Math.max( height - delta, 0 ) + 'px' );
// WebKit needs to refresh the iframe size to avoid rendering issues. (2/2) (#8348)
contentsFrame && ( contentsFrame.style.width = '100%' );
// Emit a resize event.
this.fire( 'resize' );
};
/**
* Gets the element that can be used to check the editor size. This method
* is mainly used by the `resize` plugin, which adds a UI handle that can be used
* to resize the editor.
*
* @param {Boolean} forContents Whether to return the "contents" part of the theme instead of the container.
* @returns {CKEDITOR.dom.element} The resizable element.
*/
CKEDITOR.editor.prototype.getResizable = function( forContents ) {
return forContents ? this.ui.space( 'contents' ) : this.container;
};
function createInstance( element, config, data, mode ) {
if ( !CKEDITOR.env.isCompatible )
return null;
element = CKEDITOR.dom.element.get( element );
// Avoid multiple inline editor instances on the same element.
if ( element.getEditor() )
throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.';
// Create the editor instance.
var editor = new CKEDITOR.editor( config, element, mode );
if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
// Do not replace the textarea right now, just hide it. The effective
// replacement will be done later in the editor creation lifecycle.
element.setStyle( 'visibility', 'hidden' );
// #8031 Remember if textarea was required and remove the attribute.
editor._.required = element.hasAttribute( 'required' );
element.removeAttribute( 'required' );
}
data && editor.setData( data, null, true );
// Once the editor is loaded, start the UI.
editor.on( 'loaded', function() {
loadTheme( editor );
if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE && editor.config.autoUpdateElement && element.$.form )
editor._attachToForm();
editor.setMode( editor.config.startupMode, function() {
// Clean on startup.
editor.resetDirty();
// Editor is completely loaded for interaction.
editor.status = 'ready';
editor.fireOnce( 'instanceReady' );
CKEDITOR.fire( 'instanceReady', null, editor );
} );
} );
editor.on( 'destroy', destroy );
return editor;
}
function destroy() {
var editor = this,
container = editor.container,
element = editor.element;
if ( container ) {
container.clearCustomData();
container.remove();
}
if ( element ) {
element.clearCustomData();
if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
element.show();
if ( editor._.required )
element.setAttribute( 'required', 'required' );
}
delete editor.element;
}
}
var themedTpl;
function loadTheme( editor ) {
var name = editor.name,
element = editor.element,
elementMode = editor.elementMode;
// Get the HTML for the predefined spaces.
var topHtml = editor.fire( 'uiSpace', { space: 'top', html: '' } ).html;
var bottomHtml = editor.fire( 'uiSpace', { space: 'bottom', html: '' } ).html;
if ( !themedTpl ) {
themedTpl = CKEDITOR.addTemplate( 'maincontainer', '<{outerEl}' +
' id="cke_{name}"' +
' class="{id} cke cke_reset cke_chrome cke_editor_{name} cke_{langDir} ' + CKEDITOR.env.cssClass + '" ' +
' dir="{langDir}"' +
' lang="{langCode}"' +
' role="application"' +
' aria-labelledby="cke_{name}_arialbl">' +
'<span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span>' +
'<{outerEl} class="cke_inner cke_reset" role="presentation">' +
'{topHtml}' +
'<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation"></{outerEl}>' +
'{bottomHtml}' +
'</{outerEl}>' +
'</{outerEl}>' );
}
var container = CKEDITOR.dom.element.createFromHtml( themedTpl.output( {
id: editor.id,
name: name,
langDir: editor.lang.dir,
langCode: editor.langCode,
voiceLabel: [ editor.lang.editor, editor.name ].join( ', ' ),
topHtml: topHtml ? '<span id="' + editor.ui.spaceId( 'top' ) + '" class="cke_top cke_reset_all" role="presentation" style="height:auto">' + topHtml + '</span>' : '',
contentId: editor.ui.spaceId( 'contents' ),
bottomHtml: bottomHtml ? '<span id="' + editor.ui.spaceId( 'bottom' ) + '" class="cke_bottom cke_reset_all" role="presentation">' + bottomHtml + '</span>' : '',
outerEl: CKEDITOR.env.ie ? 'span' : 'div' // #9571
} ) );
if ( elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
element.hide();
container.insertAfter( element );
} else
element.append( container );
editor.container = container;
// Make top and bottom spaces unelectable, but not content space,
// otherwise the editable area would be affected.
topHtml && editor.ui.space( 'top' ).unselectable();
bottomHtml && editor.ui.space( 'bottom' ).unselectable();
var width = editor.config.width, height = editor.config.height;
if ( width )
container.setStyle( 'width', CKEDITOR.tools.cssLength( width ) );
// The editor height is applied to the contents space.
if ( height )
editor.ui.space( 'contents' ).setStyle( 'height', CKEDITOR.tools.cssLength( height ) );
// Disable browser context menu for editor's chrome.
container.disableContextMenu();
// Redirect the focus into editor for webkit. (#5713)
CKEDITOR.env.webkit && container.on( 'focus', function() {
editor.focus();
} );
editor.fireOnce( 'uiReady' );
}
// Replace all textareas with the default class name.
CKEDITOR.domReady( function() {
CKEDITOR.replaceClass && CKEDITOR.replaceAll( CKEDITOR.replaceClass );
} );
} )();
/**
* The current editing mode. An editing mode basically provides
* different ways of editing or viewing the contents.
*
* alert( CKEDITOR.instances.editor1.mode ); // (e.g.) 'wysiwyg'
*
* @readonly
* @property {String} mode
*/
/**
* The mode to load at the editor startup. It depends on the plugins
* loaded. By default, the `wysiwyg` and `source` modes are available.
*
* config.startupMode = 'source';
*
* @cfg {String} [startupMode='wysiwyg']
* @member CKEDITOR.config
*/
CKEDITOR.config.startupMode = 'wysiwyg';
/**
* Fired after the editor instance is resized through
* the {@link CKEDITOR.editor#method-resize CKEDITOR.resize} method.
*
* @event resize
* @param {CKEDITOR.editor} editor This editor instance.
*/
/**
* Fired before changing the editing mode. See also
* {@link #beforeSetMode} and {@link #event-mode}.
*
* @event beforeModeUnload
* @param {CKEDITOR.editor} editor This editor instance.
*/
/**
* Fired before the editor mode is set. See also
* {@link #event-mode} and {@link #beforeModeUnload}.
*
* @since 3.5.3
* @event beforeSetMode
* @param {CKEDITOR.editor} editor This editor instance.
* @param {String} data The name of the mode which is about to be set.
*/
/**
* Fired after setting the editing mode. See also {@link #beforeSetMode} and {@link #beforeModeUnload}
*
* @event mode
* @param {CKEDITOR.editor} editor This editor instance.
*/
/**
* Fired when the editor (replacing a `<textarea>` which has a `required` attribute) is empty during form submission.
*
* This event replaces native required fields validation that the browsers cannot
* perform when CKEditor replaces `<textarea>` elements.
*
* You can cancel this event to prevent the page from submitting data.
*
* editor.on( 'required', function( evt ) {
* alert( 'Article content is required.' );
* evt.cancel();
* } );
*
* @event required
* @param {CKEDITOR.editor} editor This editor instance.
*/

View File

@@ -0,0 +1,70 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the "virtual" {@link CKEDITOR.dataProcessor} class, which
* defines the basic structure of data processor objects to be
* set to {@link CKEDITOR.editor.dataProcessor}.
*/
/**
* If defined, points to the data processor which is responsible to translate
* and transform the editor data on input and output.
* Generaly it will point to an instance of {@link CKEDITOR.htmlDataProcessor},
* which handles HTML data. The editor may also handle other data formats by
* using different data processors provided by specific plugins.
*
* @property {CKEDITOR.dataProcessor} dataProcessor
* @member CKEDITOR.editor
*/
/**
* Represents a data processor, which is responsible to translate and
* transform the editor data on input and output.
*
* This class is here for documentation purposes only and is not really part of
* the API. It serves as the base ("interface") for data processors implementation.
*
* @class CKEDITOR.dataProcessor
* @abstract
*/
/**
* Transforms input data into HTML to be loaded in the editor.
* While the editor is able to handle non HTML data (like BBCode), at runtime
* it can handle HTML data only. The role of the data processor is transforming
* the input data into HTML through this function.
*
* // Tranforming BBCode data, having a custom BBCode data processor.
* var data = 'This is [b]an example[/b].';
* var html = editor.dataProcessor.toHtml( data ); // '<p>This is <b>an example</b>.</p>'
*
* @method toHtml
* @param {String} data The input data to be transformed.
* @param {String} [fixForBody] The tag name to be used if the data must be
* fixed because it is supposed to be loaded direcly into the `<body>`
* tag. This is generally not used by non-HTML data processors.
* @todo fixForBody type - compare to htmlDataProcessor.
*/
/**
* Transforms HTML into data to be outputted by the editor, in the format
* expected by the data processor.
*
* While the editor is able to handle non HTML data (like BBCode), at runtime
* it can handle HTML data only. The role of the data processor is transforming
* the HTML data containined by the editor into a specific data format through
* this function.
*
* // Tranforming into BBCode data, having a custom BBCode data processor.
* var html = '<p>This is <b>an example</b>.</p>';
* var data = editor.dataProcessor.toDataFormat( html ); // 'This is [b]an example[/b].'
*
* @method toDataFormat
* @param {String} html The HTML to be transformed.
* @param {String} fixForBody The tag name to be used if the output data is
* coming from `<body>` and may be eventually fixed for it. This is
* generally not used by non-HTML data processors.
*/

View File

@@ -0,0 +1,13 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom} object, which contains DOM
* manipulation objects and function.
*/
CKEDITOR.dom = {};
// PACKAGER_RENAME( CKEDITOR.dom )

View File

@@ -0,0 +1,53 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.comment} class, which represents
* a DOM comment node.
*/
/**
* Represents a DOM comment node.
*
* var nativeNode = document.createComment( 'Example' );
* var comment = new CKEDITOR.dom.comment( nativeNode );
*
* var comment = new CKEDITOR.dom.comment( 'Example' );
*
* @class
* @extends CKEDITOR.dom.node
* @constructor Creates a comment class instance.
* @param {Object/String} comment A native DOM comment node or a string containing
* the text to use to create a new comment node.
* @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
* the node in case of new node creation. Defaults to the current document.
*/
CKEDITOR.dom.comment = function( comment, ownerDocument ) {
if ( typeof comment == 'string' )
comment = ( ownerDocument ? ownerDocument.$ : document ).createComment( comment );
CKEDITOR.dom.domObject.call( this, comment );
};
CKEDITOR.dom.comment.prototype = new CKEDITOR.dom.node();
CKEDITOR.tools.extend( CKEDITOR.dom.comment.prototype, {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_COMMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_COMMENT]
*/
type: CKEDITOR.NODE_COMMENT,
/**
* Gets the outer HTML of this comment.
*
* @returns {String} The HTML `<!-- comment value -->`.
*/
getOuterHtml: function() {
return '<!--' + this.$.nodeValue + '-->';
}
} );

View File

@@ -0,0 +1,316 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
* represents a DOM document.
*/
/**
* Represents a DOM document.
*
* var document = new CKEDITOR.dom.document( document );
*
* @class
* @extends CKEDITOR.dom.domObject
* @constructor Creates a document class instance.
* @param {Object} domDocument A native DOM document.
*/
CKEDITOR.dom.document = function( domDocument ) {
CKEDITOR.dom.domObject.call( this, domDocument );
};
// PACKAGER_RENAME( CKEDITOR.dom.document )
CKEDITOR.dom.document.prototype = new CKEDITOR.dom.domObject();
CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_DOCUMENT]
*/
type: CKEDITOR.NODE_DOCUMENT,
/**
* Appends a CSS file to the document.
*
* CKEDITOR.document.appendStyleSheet( '/mystyles.css' );
*
* @param {String} cssFileUrl The CSS file URL.
*/
appendStyleSheet: function( cssFileUrl ) {
if ( this.$.createStyleSheet )
this.$.createStyleSheet( cssFileUrl );
else {
var link = new CKEDITOR.dom.element( 'link' );
link.setAttributes( {
rel: 'stylesheet',
type: 'text/css',
href: cssFileUrl
} );
this.getHead().append( link );
}
},
/**
* Creates a CSS style sheet and inserts it into the document.
*
* @param cssStyleText {String} CSS style text.
* @returns {Object} The created DOM native style sheet object.
*/
appendStyleText: function( cssStyleText ) {
if ( this.$.createStyleSheet ) {
var styleSheet = this.$.createStyleSheet( "" );
styleSheet.cssText = cssStyleText;
} else {
var style = new CKEDITOR.dom.element( 'style', this );
style.append( new CKEDITOR.dom.text( cssStyleText, this ) );
this.getHead().append( style );
}
return styleSheet || style.$.sheet;
},
/**
* Creates {@link CKEDITOR.dom.element} instance in this document.
*
* @returns {CKEDITOR.dom.element}
* @todo
*/
createElement: function( name, attribsAndStyles ) {
var element = new CKEDITOR.dom.element( name, this );
if ( attribsAndStyles ) {
if ( attribsAndStyles.attributes )
element.setAttributes( attribsAndStyles.attributes );
if ( attribsAndStyles.styles )
element.setStyles( attribsAndStyles.styles );
}
return element;
},
/**
* Creates {@link CKEDITOR.dom.text} instance in this document.
*
* @param {String} text Value of the text node.
* @returns {CKEDITOR.dom.element}
*/
createText: function( text ) {
return new CKEDITOR.dom.text( text, this );
},
/**
* Moves the selection focus to this document's window.
*/
focus: function() {
this.getWindow().focus();
},
/**
* Returns the element that is currently designated as the active element in the document.
*
* **Note:** Only one element can be active at a time in a document.
* An active element does not necessarily have focus,
* but an element with focus is always the active element in a document.
*
* @returns {CKEDITOR.dom.element}
*/
getActive: function() {
return new CKEDITOR.dom.element( this.$.activeElement );
},
/**
* Gets an element based on its id.
*
* var element = CKEDITOR.document.getById( 'myElement' );
* alert( element.getId() ); // 'myElement'
*
* @param {String} elementId The element id.
* @returns {CKEDITOR.dom.element} The element instance, or null if not found.
*/
getById: function( elementId ) {
var $ = this.$.getElementById( elementId );
return $ ? new CKEDITOR.dom.element( $ ) : null;
},
/**
* Gets a node based on its address. See {@link CKEDITOR.dom.node#getAddress}.
*
* @param {Array} address
* @param {Boolean} [normalized=false]
*/
getByAddress: function( address, normalized ) {
var $ = this.$.documentElement;
for ( var i = 0; $ && i < address.length; i++ ) {
var target = address[ i ];
if ( !normalized ) {
$ = $.childNodes[ target ];
continue;
}
var currentIndex = -1;
for ( var j = 0; j < $.childNodes.length; j++ ) {
var candidate = $.childNodes[ j ];
if ( normalized === true && candidate.nodeType == 3 && candidate.previousSibling && candidate.previousSibling.nodeType == 3 )
continue;
currentIndex++;
if ( currentIndex == target ) {
$ = candidate;
break;
}
}
}
return $ ? new CKEDITOR.dom.node( $ ) : null;
},
/**
* Gets elements list based on given tag name.
*
* @param {String} tagName The element tag name.
* @returns {CKEDITOR.dom.nodeList} The nodes list.
*/
getElementsByTag: function( tagName, namespace ) {
if ( !( CKEDITOR.env.ie && !( document.documentMode > 8 ) ) && namespace )
tagName = namespace + ':' + tagName;
return new CKEDITOR.dom.nodeList( this.$.getElementsByTagName( tagName ) );
},
/**
* Gets the `<head>` element for this document.
*
* var element = CKEDITOR.document.getHead();
* alert( element.getName() ); // 'head'
*
* @returns {CKEDITOR.dom.element} The `<head>` element.
*/
getHead: function() {
var head = this.$.getElementsByTagName( 'head' )[ 0 ];
if ( !head )
head = this.getDocumentElement().append( new CKEDITOR.dom.element( 'head' ), true );
else
head = new CKEDITOR.dom.element( head );
return head;
},
/**
* Gets the `<body>` element for this document.
*
* var element = CKEDITOR.document.getBody();
* alert( element.getName() ); // 'body'
*
* @returns {CKEDITOR.dom.element} The `<body>` element.
*/
getBody: function() {
return new CKEDITOR.dom.element( this.$.body );
},
/**
* Gets the DOM document element for this document.
*
* @returns {CKEDITOR.dom.element} The DOM document element.
*/
getDocumentElement: function() {
return new CKEDITOR.dom.element( this.$.documentElement );
},
/**
* Gets the window object that holds this document.
*
* @returns {CKEDITOR.dom.window} The window object.
*/
getWindow: function() {
return new CKEDITOR.dom.window( this.$.parentWindow || this.$.defaultView );
},
/**
* Defines the document contents through document.write. Note that the
* previous document contents will be lost (cleaned).
*
* document.write(
* '<html>' +
* '<head><title>Sample Doc</title></head>' +
* '<body>Document contents created by code</body>' +
* '</html>'
* );
*
* @since 3.5
* @param {String} html The HTML defining the document contents.
*/
write: function( html ) {
// Don't leave any history log in IE. (#5657)
this.$.open( 'text/html', 'replace' );
// Support for custom document.domain in IE.
//
// The script must be appended because if placed before the
// doctype, IE will go into quirks mode and mess with
// the editable, e.g. by changing its default height.
if ( CKEDITOR.env.ie )
html = html.replace( /(?:^\s*<!DOCTYPE[^>]*?>)|^/i, '$&\n<script data-cke-temp="1">(' + CKEDITOR.tools.fixDomain + ')();</script>' );
this.$.write( html );
this.$.close();
},
/**
* Wrapper for `querySelectorAll`. Returns a list of elements within this document that match
* specified `selector`.
*
* **Note:** returned list is not a live collection (like a result of native `querySelectorAll`).
*
* @since 4.3
* @param {String} selector
* @returns {CKEDITOR.dom.nodeList}
*/
find: function( selector ) {
return new CKEDITOR.dom.nodeList( this.$.querySelectorAll( selector ) );
},
/**
* Wrapper for `querySelector`. Returns first element within this document that matches
* specified `selector`.
*
* @since 4.3
* @param {String} selector
* @returns {CKEDITOR.dom.element}
*/
findOne: function( selector ) {
var el = this.$.querySelector( selector );
return el ? new CKEDITOR.dom.element( el ) : null;
},
/**
* IE8 only method. It returns document fragment which has all HTML5 elements enabled.
*
* @since 4.3
* @private
* @returns DocumentFragment
*/
_getHtml5ShivFrag: function() {
var $frag = this.getCustomData( 'html5ShivFrag' );
if ( !$frag ) {
$frag = this.$.createDocumentFragment();
CKEDITOR.tools.enableHtml5Elements( $frag, true );
this.setCustomData( 'html5ShivFrag', $frag );
}
return $frag;
}
} );

View File

@@ -0,0 +1,45 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* DocumentFragment is a "lightweight" or "minimal" Document object. It is
* commonly used to extract a portion of a document's tree or to create a new
* fragment of a document. Various operations may take DocumentFragment objects
* as arguments and results in all the child nodes of the DocumentFragment being
* moved to the child list of this node.
*
* @class
* @constructor Creates a document fragment class instance.
* @param {Object} nodeOrDoc
* @todo example and param doc
*/
CKEDITOR.dom.documentFragment = function( nodeOrDoc ) {
nodeOrDoc = nodeOrDoc || CKEDITOR.document;
if ( nodeOrDoc.type == CKEDITOR.NODE_DOCUMENT )
this.$ = nodeOrDoc.$.createDocumentFragment();
else
this.$ = nodeOrDoc;
};
CKEDITOR.tools.extend( CKEDITOR.dom.documentFragment.prototype, CKEDITOR.dom.element.prototype, {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_DOCUMENT_FRAGMENT]
*/
type: CKEDITOR.NODE_DOCUMENT_FRAGMENT,
/**
* Inserts document fragment's contents after specified node.
*
* @param {CKEDITOR.dom.node} node
*/
insertAfterNode: function( node ) {
node = node.$;
node.parentNode.insertBefore( this.$, node.nextSibling );
}
}, true, { 'append': 1, 'appendBogus': 1, 'getFirst': 1, 'getLast': 1, 'getParent': 1, 'getNext': 1, 'getPrevious': 1, 'appendTo': 1, 'moveChildren': 1, 'insertBefore': 1, 'insertAfterNode': 1, 'replace': 1, 'trim': 1, 'type': 1, 'ltrim': 1, 'rtrim': 1, 'getDocument': 1, 'getChildCount': 1, 'getChild': 1, 'getChildren': 1 } );

View File

@@ -0,0 +1,262 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.editor} class, which is the base
* for other classes representing DOM objects.
*/
/**
* Represents a DOM object. This class is not intended to be used directly. It
* serves as the base class for other classes representing specific DOM
* objects.
*
* @class
* @mixins CKEDITOR.event
* @constructor Creates a domObject class instance.
* @param {Object} nativeDomObject A native DOM object.
*/
CKEDITOR.dom.domObject = function( nativeDomObject ) {
if ( nativeDomObject ) {
/**
* The native DOM object represented by this class instance.
*
* var element = new CKEDITOR.dom.element( 'span' );
* alert( element.$.nodeType ); // '1'
*
* @readonly
* @property {Object}
*/
this.$ = nativeDomObject;
}
};
CKEDITOR.dom.domObject.prototype = ( function() {
// Do not define other local variables here. We want to keep the native
// listener closures as clean as possible.
var getNativeListener = function( domObject, eventName ) {
return function( domEvent ) {
// In FF, when reloading the page with the editor focused, it may
// throw an error because the CKEDITOR global is not anymore
// available. So, we check it here first. (#2923)
if ( typeof CKEDITOR != 'undefined' )
domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) );
};
};
return {
/**
* Get the private `_` object which is bound to the native
* DOM object using {@link #getCustomData}.
*
* var elementA = new CKEDITOR.dom.element( nativeElement );
* elementA.getPrivate().value = 1;
* ...
* var elementB = new CKEDITOR.dom.element( nativeElement );
* elementB.getPrivate().value; // 1
*
* @returns {Object} The private object.
*/
getPrivate: function() {
var priv;
// Get the main private object from the custom data. Create it if not defined.
if ( !( priv = this.getCustomData( '_' ) ) )
this.setCustomData( '_', ( priv = {} ) );
return priv;
},
// Docs inherited from event.
on: function( eventName ) {
// We customize the "on" function here. The basic idea is that we'll have
// only one listener for a native event, which will then call all listeners
// set to the event.
// Get the listeners holder object.
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
if ( !nativeListeners ) {
nativeListeners = {};
this.setCustomData( '_cke_nativeListeners', nativeListeners );
}
// Check if we have a listener for that event.
if ( !nativeListeners[ eventName ] ) {
var listener = nativeListeners[ eventName ] = getNativeListener( this, eventName );
if ( this.$.addEventListener )
this.$.addEventListener( eventName, listener, !!CKEDITOR.event.useCapture );
else if ( this.$.attachEvent )
this.$.attachEvent( 'on' + eventName, listener );
}
// Call the original implementation.
return CKEDITOR.event.prototype.on.apply( this, arguments );
},
// Docs inherited from event.
removeListener: function( eventName ) {
// Call the original implementation.
CKEDITOR.event.prototype.removeListener.apply( this, arguments );
// If we don't have listeners for this event, clean the DOM up.
if ( !this.hasListeners( eventName ) ) {
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
var listener = nativeListeners && nativeListeners[ eventName ];
if ( listener ) {
if ( this.$.removeEventListener )
this.$.removeEventListener( eventName, listener, false );
else if ( this.$.detachEvent )
this.$.detachEvent( 'on' + eventName, listener );
delete nativeListeners[ eventName ];
}
}
},
/**
* Removes any listener set on this object.
*
* To avoid memory leaks we must assure that there are no
* references left after the object is no longer needed.
*/
removeAllListeners: function() {
var nativeListeners = this.getCustomData( '_cke_nativeListeners' );
for ( var eventName in nativeListeners ) {
var listener = nativeListeners[ eventName ];
if ( this.$.detachEvent )
this.$.detachEvent( 'on' + eventName, listener );
else if ( this.$.removeEventListener )
this.$.removeEventListener( eventName, listener, false );
delete nativeListeners[ eventName ];
}
// Remove events from events object so fire() method will not call
// listeners (#11400).
CKEDITOR.event.prototype.removeAllListeners.call( this );
}
};
} )();
( function( domObjectProto ) {
var customData = {};
CKEDITOR.on( 'reset', function() {
customData = {};
} );
/**
* Determines whether the specified object is equal to the current object.
*
* var doc = new CKEDITOR.dom.document( document );
* alert( doc.equals( CKEDITOR.document ) ); // true
* alert( doc == CKEDITOR.document ); // false
*
* @param {Object} object The object to compare with the current object.
* @returns {Boolean} `true` if the object is equal.
*/
domObjectProto.equals = function( object ) {
// Try/Catch to avoid IE permission error when object is from different document.
try {
return ( object && object.$ === this.$ );
} catch ( er ) {
return false;
}
};
/**
* Sets a data slot value for this object. These values are shared by all
* instances pointing to that same DOM object.
*
* **Note:** The created data slot is only guarantied to be available on this unique dom node,
* thus any wish to continue access it from other element clones (either created by
* clone node or from `innerHtml`) will fail, for such usage, please use
* {@link CKEDITOR.dom.element#setAttribute} instead.
*
* var element = new CKEDITOR.dom.element( 'span' );
* element.setCustomData( 'hasCustomData', true );
*
* @param {String} key A key used to identify the data slot.
* @param {Object} value The value to set to the data slot.
* @returns {CKEDITOR.dom.domObject} This DOM object instance.
* @chainable
*/
domObjectProto.setCustomData = function( key, value ) {
var expandoNumber = this.getUniqueId(),
dataSlot = customData[ expandoNumber ] || ( customData[ expandoNumber ] = {} );
dataSlot[ key ] = value;
return this;
};
/**
* Gets the value set to a data slot in this object.
*
* var element = new CKEDITOR.dom.element( 'span' );
* alert( element.getCustomData( 'hasCustomData' ) ); // e.g. 'true'
* alert( element.getCustomData( 'nonExistingKey' ) ); // null
*
* @param {String} key The key used to identify the data slot.
* @returns {Object} This value set to the data slot.
*/
domObjectProto.getCustomData = function( key ) {
var expandoNumber = this.$[ 'data-cke-expando' ],
dataSlot = expandoNumber && customData[ expandoNumber ];
return ( dataSlot && key in dataSlot ) ? dataSlot[ key ] : null;
};
/**
* Removes the value in data slot under given `key`.
*
* @param {String} key
* @returns {Object} Removed value or `null` if not found.
*/
domObjectProto.removeCustomData = function( key ) {
var expandoNumber = this.$[ 'data-cke-expando' ],
dataSlot = expandoNumber && customData[ expandoNumber ],
retval, hadKey;
if ( dataSlot ) {
retval = dataSlot[ key ];
hadKey = key in dataSlot;
delete dataSlot[ key ];
}
return hadKey ? retval : null;
};
/**
* Removes any data stored on this object.
* To avoid memory leaks we must assure that there are no
* references left after the object is no longer needed.
*/
domObjectProto.clearCustomData = function() {
// Clear all event listeners
this.removeAllListeners();
var expandoNumber = this.$[ 'data-cke-expando' ];
expandoNumber && delete customData[ expandoNumber ];
};
/**
* Gets an ID that can be used to identiquely identify this DOM object in
* the running session.
*
* @returns {Number} A unique ID.
*/
domObjectProto.getUniqueId = function() {
return this.$[ 'data-cke-expando' ] || ( this.$[ 'data-cke-expando' ] = CKEDITOR.tools.getNextNumber() );
};
// Implement CKEDITOR.event.
CKEDITOR.event.implementOn( domObjectProto );
} )( CKEDITOR.dom.domObject.prototype );

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,251 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
( function() {
var pathBlockLimitElements = {},
pathBlockElements = {},
tag;
// Elements that are considered the "Block limit" in an element path.
for ( tag in CKEDITOR.dtd.$blockLimit ) {
// Exclude from list roots.
if ( !( tag in CKEDITOR.dtd.$list ) )
pathBlockLimitElements[ tag ] = 1;
}
// Elements that are considered the "End level Block" in an element path.
for ( tag in CKEDITOR.dtd.$block ) {
// Exclude block limits, and empty block element, e.g. hr.
if ( !( tag in CKEDITOR.dtd.$blockLimit || tag in CKEDITOR.dtd.$empty ) )
pathBlockElements[ tag ] = 1;
}
// Check if an element contains any block element.
function checkHasBlock( element ) {
var childNodes = element.getChildren();
for ( var i = 0, count = childNodes.count(); i < count; i++ ) {
var child = childNodes.getItem( i );
if ( child.type == CKEDITOR.NODE_ELEMENT && CKEDITOR.dtd.$block[ child.getName() ] )
return true;
}
return false;
}
/**
* Retrieve the list of nodes walked from the start node up to the editable element of the editor.
*
* @class
* @constructor Creates an element path class instance.
* @param {CKEDITOR.dom.element} startNode From which the path should start.
* @param {CKEDITOR.dom.element} root To which element the path should stop, defaults to the `body` element.
*/
CKEDITOR.dom.elementPath = function( startNode, root ) {
var block = null,
blockLimit = null,
elements = [],
e = startNode,
elementName;
// Backward compact.
root = root || startNode.getDocument().getBody();
do {
if ( e.type == CKEDITOR.NODE_ELEMENT ) {
elements.push( e );
if ( !this.lastElement ) {
this.lastElement = e;
// If an object or non-editable element is fully selected at the end of the element path,
// it must not become the block limit.
if ( e.is( CKEDITOR.dtd.$object ) || e.getAttribute( 'contenteditable' ) == 'false' )
continue;
}
if ( e.equals( root ) )
break;
if ( !blockLimit ) {
elementName = e.getName();
// First editable element becomes a block limit, because it cannot be split.
if ( e.getAttribute( 'contenteditable' ) == 'true' )
blockLimit = e;
// "Else" because element cannot be both - block and block levelimit.
else if ( !block && pathBlockElements[ elementName ] )
block = e;
if ( pathBlockLimitElements[ elementName ] ) {
// End level DIV is considered as the block, if no block is available. (#525)
// But it must NOT be the root element (checked above).
if ( !block && elementName == 'div' && !checkHasBlock( e ) )
block = e;
else
blockLimit = e;
}
}
}
}
while ( ( e = e.getParent() ) );
// Block limit defaults to root.
if ( !blockLimit )
blockLimit = root;
/**
* First non-empty block element which:
*
* * is not a {@link CKEDITOR.dtd#$blockLimit},
* * or is a `div` which does not contain block elements and is not a `root`.
*
* This means a first, splittable block in elements path.
*
* @readonly
* @property {CKEDITOR.dom.element}
*/
this.block = block;
/**
* See the {@link CKEDITOR.dtd#$blockLimit} description.
*
* @readonly
* @property {CKEDITOR.dom.element}
*/
this.blockLimit = blockLimit;
/**
* The root of the elements path - `root` argument passed to class constructor or a `body` element.
*
* @readonly
* @property {CKEDITOR.dom.element}
*/
this.root = root;
/**
* An array of elements (from `startNode` to `root`) in the path.
*
* @readonly
* @property {CKEDITOR.dom.element[]}
*/
this.elements = elements;
/**
* The last element of the elements path - `startNode` or its parent.
*
* @readonly
* @property {CKEDITOR.dom.element} lastElement
*/
};
} )();
CKEDITOR.dom.elementPath.prototype = {
/**
* Compares this element path with another one.
*
* @param {CKEDITOR.dom.elementPath} otherPath The elementPath object to be
* compared with this one.
* @returns {Boolean} `true` if the paths are equal, containing the same
* number of elements and the same elements in the same order.
*/
compare: function( otherPath ) {
var thisElements = this.elements;
var otherElements = otherPath && otherPath.elements;
if ( !otherElements || thisElements.length != otherElements.length )
return false;
for ( var i = 0; i < thisElements.length; i++ ) {
if ( !thisElements[ i ].equals( otherElements[ i ] ) )
return false;
}
return true;
},
/**
* Search the path elements that meets the specified criteria.
*
* @param {String/Array/Function/Object/CKEDITOR.dom.element} query The criteria that can be
* either a tag name, list (array and object) of tag names, element or an node evaluator function.
* @param {Boolean} [excludeRoot] Not taking path root element into consideration.
* @param {Boolean} [fromTop] Search start from the topmost element instead of bottom.
* @returns {CKEDITOR.dom.element} The first matched dom element or `null`.
*/
contains: function( query, excludeRoot, fromTop ) {
var evaluator;
if ( typeof query == 'string' )
evaluator = function( node ) {
return node.getName() == query;
};
if ( query instanceof CKEDITOR.dom.element )
evaluator = function( node ) {
return node.equals( query );
};
else if ( CKEDITOR.tools.isArray( query ) )
evaluator = function( node ) {
return CKEDITOR.tools.indexOf( query, node.getName() ) > -1;
};
else if ( typeof query == 'function' )
evaluator = query;
else if ( typeof query == 'object' )
evaluator = function( node ) {
return node.getName() in query;
};
var elements = this.elements,
length = elements.length;
excludeRoot && length--;
if ( fromTop ) {
elements = Array.prototype.slice.call( elements, 0 );
elements.reverse();
}
for ( var i = 0; i < length; i++ ) {
if ( evaluator( elements[ i ] ) )
return elements[ i ];
}
return null;
},
/**
* Check whether the elements path is the proper context for the specified
* tag name in the DTD.
*
* @param {String} tag The tag name.
* @returns {Boolean}
*/
isContextFor: function( tag ) {
var holder;
// Check for block context.
if ( tag in CKEDITOR.dtd.$block ) {
// Indeterminate elements which are not subjected to be splitted or surrounded must be checked first.
var inter = this.contains( CKEDITOR.dtd.$intermediate );
holder = inter || ( this.root.equals( this.block ) && this.block ) || this.blockLimit;
return !!holder.getDtd()[ tag ];
}
return true;
},
/**
* Retrieve the text direction for this elements path.
*
* @returns {'ltr'/'rtl'}
*/
direction: function() {
var directionNode = this.block || this.blockLimit || this.root;
return directionNode.getDirection( 1 );
}
};

View File

@@ -0,0 +1,208 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.event} class, which
* represents the a native DOM event object.
*/
/**
* Represents a native DOM event object.
*
* @class
* @constructor Creates an event class instance.
* @param {Object} domEvent A native DOM event object.
*/
CKEDITOR.dom.event = function( domEvent ) {
/**
* The native DOM event object represented by this class instance.
*
* @readonly
*/
this.$ = domEvent;
};
CKEDITOR.dom.event.prototype = {
/**
* Gets the key code associated to the event.
*
* alert( event.getKey() ); // '65' is 'a' has been pressed
*
* @returns {Number} The key code.
*/
getKey: function() {
return this.$.keyCode || this.$.which;
},
/**
* Gets a number represeting the combination of the keys pressed during the
* event. It is the sum with the current key code and the {@link CKEDITOR#CTRL},
* {@link CKEDITOR#SHIFT} and {@link CKEDITOR#ALT} constants.
*
* alert( event.getKeystroke() == 65 ); // 'a' key
* alert( event.getKeystroke() == CKEDITOR.CTRL + 65 ); // CTRL + 'a' key
* alert( event.getKeystroke() == CKEDITOR.CTRL + CKEDITOR.SHIFT + 65 ); // CTRL + SHIFT + 'a' key
*
* @returns {Number} The number representing the keys combination.
*/
getKeystroke: function() {
var keystroke = this.getKey();
if ( this.$.ctrlKey || this.$.metaKey )
keystroke += CKEDITOR.CTRL;
if ( this.$.shiftKey )
keystroke += CKEDITOR.SHIFT;
if ( this.$.altKey )
keystroke += CKEDITOR.ALT;
return keystroke;
},
/**
* Prevents the original behavior of the event to happen. It can optionally
* stop propagating the event in the event chain.
*
* var element = CKEDITOR.document.getById( 'myElement' );
* element.on( 'click', function( ev ) {
* // The DOM event object is passed by the 'data' property.
* var domEvent = ev.data;
* // Prevent the click to chave any effect in the element.
* domEvent.preventDefault();
* } );
*
* @param {Boolean} [stopPropagation=false] Stop propagating this event in the
* event chain.
*/
preventDefault: function( stopPropagation ) {
var $ = this.$;
if ( $.preventDefault )
$.preventDefault();
else
$.returnValue = false;
if ( stopPropagation )
this.stopPropagation();
},
/**
* Stops this event propagation in the event chain.
*/
stopPropagation: function() {
var $ = this.$;
if ( $.stopPropagation )
$.stopPropagation();
else
$.cancelBubble = true;
},
/**
* Returns the DOM node where the event was targeted to.
*
* var element = CKEDITOR.document.getById( 'myElement' );
* element.on( 'click', function( ev ) {
* // The DOM event object is passed by the 'data' property.
* var domEvent = ev.data;
* // Add a CSS class to the event target.
* domEvent.getTarget().addClass( 'clicked' );
* } );
*
* @returns {CKEDITOR.dom.node} The target DOM node.
*/
getTarget: function() {
var rawNode = this.$.target || this.$.srcElement;
return rawNode ? new CKEDITOR.dom.node( rawNode ) : null;
},
/**
* Returns an integer value that indicates the current processing phase of an event.
* For browsers that doesn't support event phase, {@link CKEDITOR#EVENT_PHASE_AT_TARGET} is always returned.
*
* @returns {Number} One of {@link CKEDITOR#EVENT_PHASE_CAPTURING},
* {@link CKEDITOR#EVENT_PHASE_AT_TARGET}, or {@link CKEDITOR#EVENT_PHASE_BUBBLING}.
*/
getPhase: function() {
return this.$.eventPhase || 2;
},
/**
* Retrieves the coordinates of the mouse pointer relative to the top-left
* corner of the document, in mouse related event.
*
* element.on( 'mousemouse', function( ev ) {
* var pageOffset = ev.data.getPageOffset();
* alert( pageOffset.x ); // page offset X
* alert( pageOffset.y ); // page offset Y
* } );
*
* @returns {Object} The object contains the position.
* @returns {Number} return.x
* @returns {Number} return.y
*/
getPageOffset : function() {
var doc = this.getTarget().getDocument().$;
var pageX = this.$.pageX || this.$.clientX + ( doc.documentElement.scrollLeft || doc.body.scrollLeft );
var pageY = this.$.pageY || this.$.clientY + ( doc.documentElement.scrollTop || doc.body.scrollTop );
return { x : pageX, y : pageY };
}
};
// For the followind constants, we need to go over the Unicode boundaries
// (0x10FFFF) to avoid collision.
/**
* CTRL key (0x110000).
*
* @readonly
* @property {Number} [=0x110000]
* @member CKEDITOR
*/
CKEDITOR.CTRL = 0x110000;
/**
* SHIFT key (0x220000).
*
* @readonly
* @property {Number} [=0x220000]
* @member CKEDITOR
*/
CKEDITOR.SHIFT = 0x220000;
/**
* ALT key (0x440000).
*
* @readonly
* @property {Number} [=0x440000]
* @member CKEDITOR
*/
CKEDITOR.ALT = 0x440000;
/**
* Capturing phase.
*
* @readonly
* @property {Number} [=1]
* @member CKEDITOR
*/
CKEDITOR.EVENT_PHASE_CAPTURING = 1;
/**
* Event at target.
*
* @readonly
* @property {Number} [=2]
* @member CKEDITOR
*/
CKEDITOR.EVENT_PHASE_AT_TARGET = 2;
/**
* Bubbling phase.
*
* @readonly
* @property {Number} [=3]
* @member CKEDITOR
*/
CKEDITOR.EVENT_PHASE_BUBBLING = 3;

View File

@@ -0,0 +1,500 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @ignore
* File overview: DOM iterator, which iterates over list items, lines and paragraphs.
*/
'use strict';
( function() {
/**
* Represents iterator class. It can be used to iterate
* over all elements (or even text nodes in case of {@link #enlargeBr} set to `false`)
* which establish "paragraph-like" spaces within passed range.
*
* // <h1>[foo</h1><p>bar]</p>
* var iterator = range.createIterator();
* iterator.getNextParagraph(); // h1 element
* iterator.getNextParagraph(); // p element
*
* // <ul><li>[foo</li><li>bar]</li>
* // With enforceRealBlocks set to false iterator will return two list item elements.
* // With enforceRealBlocks set to true iterator will return two paragraphs and the DOM will be changed to:
* // <ul><li><p>foo</p></li><li><p>bar</p></li>
*
* @class CKEDITOR.dom.iterator
* @constructor Creates an iterator class instance.
* @param {CKEDITOR.dom.range} range
*/
function iterator( range ) {
if ( arguments.length < 1 )
return;
this.range = range;
this.forceBrBreak = 0;
// (#3730).
/**
* Whether include `<br>`s into the enlarged range. Should be
* set to `false` when using iterator in {@link CKEDITOR#ENTER_BR} mode.
*
* @property {Boolean} [enlargeBr=true]
*/
this.enlargeBr = 1;
/**
* Whether iterator should create transformable block
* if the current one contains text and it cannot be transformed.
* For example new blocks will be established in elements like
* `<li>` or `<td>`.
*
* @property {Boolean} [enforceRealBlocks=false]
*/
this.enforceRealBlocks = 0;
this._ || ( this._ = {} );
}
/**
* Default iterator's filter. It is set only for nested iterators.
*
* @since 4.3
* @readonly
* @property {CKEDITOR.filter} filter
*/
/**
* Iterator's active filter. It is set by the {@link #getNextParagraph} method
* when it enters nested editable.
*
* @since 4.3
* @readonly
* @property {CKEDITOR.filter} activeFilter
*/
var beginWhitespaceRegex = /^[\r\n\t ]+$/,
// Ignore bookmark nodes.(#3783)
bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ),
whitespacesGuard = CKEDITOR.dom.walker.whitespaces( true ),
skipGuard = function( node ) {
return bookmarkGuard( node ) && whitespacesGuard( node );
};
// Get a reference for the next element, bookmark nodes are skipped.
function getNextSourceNode( node, startFromSibling, lastNode ) {
var next = node.getNextSourceNode( startFromSibling, null, lastNode );
while ( !bookmarkGuard( next ) )
next = next.getNextSourceNode( startFromSibling, null, lastNode );
return next;
}
iterator.prototype = {
/**
* Returns next paragraph-like element or `null` if reached the end of range.
*
* @param {String} [blockTag='p'] Name of a block element which will be established by
* iterator in block-less elements (see {@link #enforceRealBlocks}).
*/
getNextParagraph: function( blockTag ) {
// The block element to be returned.
var block;
// The range object used to identify the paragraph contents.
var range;
// Indicats that the current element in the loop is the last one.
var isLast;
// Instructs to cleanup remaining BRs.
var removePreviousBr, removeLastBr;
blockTag = blockTag || 'p';
// We're iterating over nested editable.
if ( this._.nestedEditable ) {
// Get next block from nested iterator and returns it if was found.
block = this._.nestedEditable.iterator.getNextParagraph( blockTag );
if ( block ) {
// Inherit activeFilter from the nested iterator.
this.activeFilter = this._.nestedEditable.iterator.activeFilter;
return block;
}
// No block in nested iterator means that we reached the end of the nested editable.
// Reset the active filter to the default filter (or undefined if this iterator didn't have it).
this.activeFilter = this.filter;
// Try to find next nested editable or get back to parent (this) iterator.
if ( startNestedEditableIterator( this, blockTag, this._.nestedEditable.container, this._.nestedEditable.remaining ) ) {
// Inherit activeFilter from the nested iterator.
this.activeFilter = this._.nestedEditable.iterator.activeFilter;
return this._.nestedEditable.iterator.getNextParagraph( blockTag );
} else
this._.nestedEditable = null;
}
// Block-less range should be checked first.
if ( !this.range.root.getDtd()[ blockTag ] )
return null;
// This is the first iteration. Let's initialize it.
if ( !this._.started )
range = startIterator.call( this );
var currentNode = this._.nextNode,
lastNode = this._.lastNode;
this._.nextNode = null;
while ( currentNode ) {
// closeRange indicates that a paragraph boundary has been found,
// so the range can be closed.
var closeRange = 0,
parentPre = currentNode.hasAscendant( 'pre' );
// includeNode indicates that the current node is good to be part
// of the range. By default, any non-element node is ok for it.
var includeNode = ( currentNode.type != CKEDITOR.NODE_ELEMENT ),
continueFromSibling = 0;
// If it is an element node, let's check if it can be part of the range.
if ( !includeNode ) {
var nodeName = currentNode.getName();
// Non-editable block was found - return it and move to processing
// its nested editables if they exist.
if ( CKEDITOR.dtd.$block[ nodeName ] && currentNode.getAttribute( 'contenteditable' ) == 'false' ) {
block = currentNode;
// Setup iterator for first of nested editables.
// If there's no editable, then algorithm will move to next element after current block.
startNestedEditableIterator( this, blockTag, block );
// Gets us straight to the end of getParagraph() because block variable is set.
break;
} else if ( currentNode.isBlockBoundary( this.forceBrBreak && !parentPre && { br: 1 } ) ) {
// <br> boundaries must be part of the range. It will
// happen only if ForceBrBreak.
if ( nodeName == 'br' )
includeNode = 1;
else if ( !range && !currentNode.getChildCount() && nodeName != 'hr' ) {
// If we have found an empty block, and haven't started
// the range yet, it means we must return this block.
block = currentNode;
isLast = currentNode.equals( lastNode );
break;
}
// The range must finish right before the boundary,
// including possibly skipped empty spaces. (#1603)
if ( range ) {
range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
// The found boundary must be set as the next one at this
// point. (#1717)
if ( nodeName != 'br' )
this._.nextNode = currentNode;
}
closeRange = 1;
} else {
// If we have child nodes, let's check them.
if ( currentNode.getFirst() ) {
// If we don't have a range yet, let's start it.
if ( !range ) {
range = this.range.clone();
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
}
currentNode = currentNode.getFirst();
continue;
}
includeNode = 1;
}
} else if ( currentNode.type == CKEDITOR.NODE_TEXT ) {
// Ignore normal whitespaces (i.e. not including &nbsp; or
// other unicode whitespaces) before/after a block node.
if ( beginWhitespaceRegex.test( currentNode.getText() ) )
includeNode = 0;
}
// The current node is good to be part of the range and we are
// starting a new range, initialize it first.
if ( includeNode && !range ) {
range = this.range.clone();
range.setStartAt( currentNode, CKEDITOR.POSITION_BEFORE_START );
}
// The last node has been found.
isLast = ( ( !closeRange || includeNode ) && currentNode.equals( lastNode ) );
// If we are in an element boundary, let's check if it is time
// to close the range, otherwise we include the parent within it.
if ( range && !closeRange ) {
while ( !currentNode.getNext( skipGuard ) && !isLast ) {
var parentNode = currentNode.getParent();
if ( parentNode.isBlockBoundary( this.forceBrBreak && !parentPre && { br: 1 } ) ) {
closeRange = 1;
includeNode = 0;
isLast = isLast || ( parentNode.equals( lastNode ) );
// Make sure range includes bookmarks at the end of the block. (#7359)
range.setEndAt( parentNode, CKEDITOR.POSITION_BEFORE_END );
break;
}
currentNode = parentNode;
includeNode = 1;
isLast = ( currentNode.equals( lastNode ) );
continueFromSibling = 1;
}
}
// Now finally include the node.
if ( includeNode )
range.setEndAt( currentNode, CKEDITOR.POSITION_AFTER_END );
currentNode = getNextSourceNode( currentNode, continueFromSibling, lastNode );
isLast = !currentNode;
// We have found a block boundary. Let's close the range and move out of the
// loop.
if ( isLast || ( closeRange && range ) )
break;
}
// Now, based on the processed range, look for (or create) the block to be returned.
if ( !block ) {
// If no range has been found, this is the end.
if ( !range ) {
this._.docEndMarker && this._.docEndMarker.remove();
this._.nextNode = null;
return null;
}
var startPath = new CKEDITOR.dom.elementPath( range.startContainer, range.root );
var startBlockLimit = startPath.blockLimit,
checkLimits = { div: 1, th: 1, td: 1 };
block = startPath.block;
if ( !block && startBlockLimit && !this.enforceRealBlocks && checkLimits[ startBlockLimit.getName() ] && range.checkStartOfBlock() && range.checkEndOfBlock() && !startBlockLimit.equals( range.root ) )
block = startBlockLimit;
else if ( !block || ( this.enforceRealBlocks && block.getName() == 'li' ) ) {
// Create the fixed block.
block = this.range.document.createElement( blockTag );
// Move the contents of the temporary range to the fixed block.
range.extractContents().appendTo( block );
block.trim();
// Insert the fixed block into the DOM.
range.insertNode( block );
removePreviousBr = removeLastBr = true;
} else if ( block.getName() != 'li' ) {
// If the range doesn't includes the entire contents of the
// block, we must split it, isolating the range in a dedicated
// block.
if ( !range.checkStartOfBlock() || !range.checkEndOfBlock() ) {
// The resulting block will be a clone of the current one.
block = block.clone( false );
// Extract the range contents, moving it to the new block.
range.extractContents().appendTo( block );
block.trim();
// Split the block. At this point, the range will be in the
// right position for our intents.
var splitInfo = range.splitBlock();
removePreviousBr = !splitInfo.wasStartOfBlock;
removeLastBr = !splitInfo.wasEndOfBlock;
// Insert the new block into the DOM.
range.insertNode( block );
}
} else if ( !isLast ) {
// LIs are returned as is, with all their children (due to the
// nested lists). But, the next node is the node right after
// the current range, which could be an <li> child (nested
// lists) or the next sibling <li>.
this._.nextNode = ( block.equals( lastNode ) ? null : getNextSourceNode( range.getBoundaryNodes().endNode, 1, lastNode ) );
}
}
if ( removePreviousBr ) {
var previousSibling = block.getPrevious();
if ( previousSibling && previousSibling.type == CKEDITOR.NODE_ELEMENT ) {
if ( previousSibling.getName() == 'br' )
previousSibling.remove();
else if ( previousSibling.getLast() && previousSibling.getLast().$.nodeName.toLowerCase() == 'br' )
previousSibling.getLast().remove();
}
}
if ( removeLastBr ) {
var lastChild = block.getLast();
if ( lastChild && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.getName() == 'br' ) {
// Remove br filler on browser which do not need it.
if ( !CKEDITOR.env.needsBrFiller || lastChild.getPrevious( bookmarkGuard ) || lastChild.getNext( bookmarkGuard ) )
lastChild.remove();
}
}
// Get a reference for the next element. This is important because the
// above block can be removed or changed, so we can rely on it for the
// next interation.
if ( !this._.nextNode )
this._.nextNode = ( isLast || block.equals( lastNode ) || !lastNode ) ? null : getNextSourceNode( block, 1, lastNode );
return block;
}
};
// @context CKEDITOR.dom.iterator
// @returns Collapsed range which will be reused when during furter processing.
function startIterator() {
var range = this.range.clone(),
// Indicate at least one of the range boundaries is inside a preformat block.
touchPre;
// Shrink the range to exclude harmful "noises" (#4087, #4450, #5435).
range.shrink( CKEDITOR.SHRINK_ELEMENT, true );
touchPre = range.endContainer.hasAscendant( 'pre', true ) || range.startContainer.hasAscendant( 'pre', true );
range.enlarge( this.forceBrBreak && !touchPre || !this.enlargeBr ? CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS : CKEDITOR.ENLARGE_BLOCK_CONTENTS );
if ( !range.collapsed ) {
var walker = new CKEDITOR.dom.walker( range.clone() ),
ignoreBookmarkTextEvaluator = CKEDITOR.dom.walker.bookmark( true, true );
// Avoid anchor inside bookmark inner text.
walker.evaluator = ignoreBookmarkTextEvaluator;
this._.nextNode = walker.next();
// TODO: It's better to have walker.reset() used here.
walker = new CKEDITOR.dom.walker( range.clone() );
walker.evaluator = ignoreBookmarkTextEvaluator;
var lastNode = walker.previous();
this._.lastNode = lastNode.getNextSourceNode( true );
// We may have an empty text node at the end of block due to [3770].
// If that node is the lastNode, it would cause our logic to leak to the
// next block.(#3887)
if ( this._.lastNode && this._.lastNode.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( this._.lastNode.getText() ) && this._.lastNode.getParent().isBlockBoundary() ) {
var testRange = this.range.clone();
testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END );
if ( testRange.checkEndOfBlock() ) {
var path = new CKEDITOR.dom.elementPath( testRange.endContainer, testRange.root ),
lastBlock = path.block || path.blockLimit;
this._.lastNode = lastBlock.getNextSourceNode( true );
}
}
// The end of document or range.root was reached, so we need a marker node inside.
if ( !this._.lastNode || !range.root.contains( this._.lastNode ) ) {
this._.lastNode = this._.docEndMarker = range.document.createText( '' );
this._.lastNode.insertAfter( lastNode );
}
// Let's reuse this variable.
range = null;
}
this._.started = 1;
return range;
}
// Does a nested editables lookup inside editablesContainer.
// If remainingEditables is set will lookup inside this array.
// @param {CKEDITOR.dom.element} editablesContainer
// @param {CKEDITOR.dom.element[]} [remainingEditables]
function getNestedEditableIn( editablesContainer, remainingEditables ) {
if ( remainingEditables == undefined )
remainingEditables = findNestedEditables( editablesContainer );
var editable;
while ( ( editable = remainingEditables.shift() ) ) {
if ( isIterableEditable( editable ) )
return { element: editable, remaining: remainingEditables };
}
return null;
}
// Checkes whether we can iterate over this editable.
function isIterableEditable( editable ) {
// Reject blockless editables.
return editable.getDtd().p;
}
// Finds nested editables within container. Does not return
// editables nested in another editable (twice).
function findNestedEditables( container ) {
var editables = [];
container.forEach( function( element ) {
if ( element.getAttribute( 'contenteditable' ) == 'true' ) {
editables.push( element );
return false; // Skip children.
}
}, CKEDITOR.NODE_ELEMENT, true );
return editables;
}
// Looks for a first nested editable after previousEditable (if passed) and creates
// nested iterator for it.
function startNestedEditableIterator( parentIterator, blockTag, editablesContainer, remainingEditables ) {
var editable = getNestedEditableIn( editablesContainer, remainingEditables );
if ( !editable )
return 0;
var filter = CKEDITOR.filter.instances[ editable.element.data( 'cke-filter' ) ];
// If current editable has a filter and this filter does not allow for block tag,
// search for next nested editable in remaining ones.
if ( filter && !filter.check( blockTag ) )
return startNestedEditableIterator( parentIterator, blockTag, editablesContainer, editable.remaining );
var range = new CKEDITOR.dom.range( editable.element );
range.selectNodeContents( editable.element );
var iterator = range.createIterator();
// This setting actually does not change anything in this case,
// because entire range contents is selected, so there're no <br>s to be included.
// But it seems right to copy it too.
iterator.enlargeBr = parentIterator.enlargeBr;
// Inherit configuration from parent iterator.
iterator.enforceRealBlocks = parentIterator.enforceRealBlocks;
// Set the activeFilter (which can be overriden when this iteator will start nested iterator)
// and the default filter, which will make it possible to reset to
// current iterator's activeFilter after leaving nested editable.
iterator.activeFilter = iterator.filter = filter;
parentIterator._.nestedEditable = {
element: editable.element,
container: editablesContainer,
remaining: editable.remaining,
iterator: iterator
};
return 1;
}
/**
* Creates {CKEDITOR.dom.iterator} instance for this range.
*
* @member CKEDITOR.dom.range
* @returns {CKEDITOR.dom.iterator}
*/
CKEDITOR.dom.range.prototype.createIterator = function() {
return new iterator( this );
};
} )();

View File

@@ -0,0 +1,748 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.node} class which is the base
* class for classes that represent DOM nodes.
*/
/**
* Base class for classes representing DOM nodes. This constructor may return
* an instance of a class that inherits from this class, like
* {@link CKEDITOR.dom.element} or {@link CKEDITOR.dom.text}.
*
* @class
* @extends CKEDITOR.dom.domObject
* @constructor Creates a node class instance.
* @param {Object} domNode A native DOM node.
* @see CKEDITOR.dom.element
* @see CKEDITOR.dom.text
*/
CKEDITOR.dom.node = function( domNode ) {
if ( domNode ) {
var type = domNode.nodeType == CKEDITOR.NODE_DOCUMENT ? 'document' : domNode.nodeType == CKEDITOR.NODE_ELEMENT ? 'element' : domNode.nodeType == CKEDITOR.NODE_TEXT ? 'text' : domNode.nodeType == CKEDITOR.NODE_COMMENT ? 'comment' : domNode.nodeType == CKEDITOR.NODE_DOCUMENT_FRAGMENT ? 'documentFragment' : 'domObject'; // Call the base constructor otherwise.
return new CKEDITOR.dom[ type ]( domNode );
}
return this;
};
CKEDITOR.dom.node.prototype = new CKEDITOR.dom.domObject();
/**
* Element node type.
*
* @readonly
* @property {Number} [=1]
* @member CKEDITOR
*/
CKEDITOR.NODE_ELEMENT = 1;
/**
* Document node type.
*
* @readonly
* @property {Number} [=9]
* @member CKEDITOR
*/
CKEDITOR.NODE_DOCUMENT = 9;
/**
* Text node type.
*
* @readonly
* @property {Number} [=3]
* @member CKEDITOR
*/
CKEDITOR.NODE_TEXT = 3;
/**
* Comment node type.
*
* @readonly
* @property {Number} [=8]
* @member CKEDITOR
*/
CKEDITOR.NODE_COMMENT = 8;
/**
* Document fragment node type.
*
* @readonly
* @property {Number} [=11]
* @member CKEDITOR
*/
CKEDITOR.NODE_DOCUMENT_FRAGMENT = 11;
CKEDITOR.POSITION_IDENTICAL = 0;
CKEDITOR.POSITION_DISCONNECTED = 1;
CKEDITOR.POSITION_FOLLOWING = 2;
CKEDITOR.POSITION_PRECEDING = 4;
CKEDITOR.POSITION_IS_CONTAINED = 8;
CKEDITOR.POSITION_CONTAINS = 16;
CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, {
/**
* Makes this node a child of another element.
*
* var p = new CKEDITOR.dom.element( 'p' );
* var strong = new CKEDITOR.dom.element( 'strong' );
* strong.appendTo( p );
*
* // Result: '<p><strong></strong></p>'.
*
* @param {CKEDITOR.dom.element} element The target element to which this node will be appended.
* @returns {CKEDITOR.dom.element} The target element.
*/
appendTo: function( element, toStart ) {
element.append( this, toStart );
return element;
},
/**
* Clone this node.
*
* **Note**: Values set by {#setCustomData} won't be available in the clone.
*
* @param {Boolean} [includeChildren=false] If `true` then all node's
* children will be cloned recursively.
* @param {Boolean} [cloneId=false] Whether ID attributes should be cloned too.
* @returns {CKEDITOR.dom.node} Clone of this node.
*/
clone: function( includeChildren, cloneId ) {
var $clone = this.$.cloneNode( includeChildren );
var removeIds = function( node ) {
// Reset data-cke-expando only when has been cloned (IE and only for some types of objects).
if ( node[ 'data-cke-expando' ] )
node[ 'data-cke-expando' ] = false;
if ( node.nodeType != CKEDITOR.NODE_ELEMENT )
return;
if ( !cloneId )
node.removeAttribute( 'id', false );
if ( includeChildren ) {
var childs = node.childNodes;
for ( var i = 0; i < childs.length; i++ )
removeIds( childs[ i ] );
}
};
// The "id" attribute should never be cloned to avoid duplication.
removeIds( $clone );
return new CKEDITOR.dom.node( $clone );
},
/**
* Check if node is preceded by any sibling.
*
* @returns {Boolean}
*/
hasPrevious: function() {
return !!this.$.previousSibling;
},
/**
* Check if node is succeeded by any sibling.
*
* @returns {Boolean}
*/
hasNext: function() {
return !!this.$.nextSibling;
},
/**
* Inserts this element after a node.
*
* var em = new CKEDITOR.dom.element( 'em' );
* var strong = new CKEDITOR.dom.element( 'strong' );
* strong.insertAfter( em );
*
* // Result: '<em></em><strong></strong>'
*
* @param {CKEDITOR.dom.node} node The node that will precede this element.
* @returns {CKEDITOR.dom.node} The node preceding this one after insertion.
*/
insertAfter: function( node ) {
node.$.parentNode.insertBefore( this.$, node.$.nextSibling );
return node;
},
/**
* Inserts this element before a node.
*
* var em = new CKEDITOR.dom.element( 'em' );
* var strong = new CKEDITOR.dom.element( 'strong' );
* strong.insertBefore( em );
*
* // result: '<strong></strong><em></em>'
*
* @param {CKEDITOR.dom.node} node The node that will succeed this element.
* @returns {CKEDITOR.dom.node} The node being inserted.
*/
insertBefore: function( node ) {
node.$.parentNode.insertBefore( this.$, node.$ );
return node;
},
/**
* Inserts node before this node.
*
* var em = new CKEDITOR.dom.element( 'em' );
* var strong = new CKEDITOR.dom.element( 'strong' );
* strong.insertBeforeMe( em );
*
* // result: '<em></em><strong></strong>'
*
* @param {CKEDITOR.dom.node} node The node that will preceed this element.
* @returns {CKEDITOR.dom.node} The node being inserted.
*/
insertBeforeMe: function( node ) {
this.$.parentNode.insertBefore( node.$, this.$ );
return node;
},
/**
* Retrieves a uniquely identifiable tree address for this node.
* The tree address returned is an array of integers, with each integer
* indicating a child index of a DOM node, starting from
* `document.documentElement`.
*
* For example, assuming `<body>` is the second child
* of `<html>` (`<head>` being the first),
* and we would like to address the third child under the
* fourth child of `<body>`, the tree address returned would be:
* `[1, 3, 2]`.
*
* The tree address cannot be used for finding back the DOM tree node once
* the DOM tree structure has been modified.
*
* @param {Boolean} [normalized=false] See {@link #getIndex}.
* @returns {Array} The address.
*/
getAddress: function( normalized ) {
var address = [];
var $documentElement = this.getDocument().$.documentElement;
var node = this.$;
while ( node && node != $documentElement ) {
var parentNode = node.parentNode;
if ( parentNode ) {
// Get the node index. For performance, call getIndex
// directly, instead of creating a new node object.
address.unshift( this.getIndex.call( { $: node }, normalized ) );
}
node = parentNode;
}
return address;
},
/**
* Gets the document containing this element.
*
* var element = CKEDITOR.document.getById( 'example' );
* alert( element.getDocument().equals( CKEDITOR.document ) ); // true
*
* @returns {CKEDITOR.dom.document} The document.
*/
getDocument: function() {
return new CKEDITOR.dom.document( this.$.ownerDocument || this.$.parentNode.ownerDocument );
},
/**
* Get index of a node in an array of its parent.childNodes.
*
* Let's assume having childNodes array:
*
* [ emptyText, element1, text, text, element2 ]
* element1.getIndex(); // 1
* element1.getIndex( true ); // 0
* element2.getIndex(); // 4
* element2.getIndex( true ); // 2
*
* @param {Boolean} normalized When `true` empty text nodes and one followed
* by another one text node are not counted in.
* @returns {Number} Index of a node.
*/
getIndex: function( normalized ) {
// Attention: getAddress depends on this.$
// getIndex is called on a plain object: { $ : node }
var current = this.$,
index = -1,
isNormalizing;
if ( !this.$.parentNode )
return index;
do {
// Bypass blank node and adjacent text nodes.
if ( normalized && current != this.$ && current.nodeType == CKEDITOR.NODE_TEXT && ( isNormalizing || !current.nodeValue ) )
continue;
index++;
isNormalizing = current.nodeType == CKEDITOR.NODE_TEXT;
}
while ( ( current = current.previousSibling ) )
return index;
},
/**
* @todo
*/
getNextSourceNode: function( startFromSibling, nodeType, guard ) {
// If "guard" is a node, transform it in a function.
if ( guard && !guard.call ) {
var guardNode = guard;
guard = function( node ) {
return !node.equals( guardNode );
};
}
var node = ( !startFromSibling && this.getFirst && this.getFirst() ),
parent;
// Guarding when we're skipping the current element( no children or 'startFromSibling' ).
// send the 'moving out' signal even we don't actually dive into.
if ( !node ) {
if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
return null;
node = this.getNext();
}
while ( !node && ( parent = ( parent || this ).getParent() ) ) {
// The guard check sends the "true" paramenter to indicate that
// we are moving "out" of the element.
if ( guard && guard( parent, true ) === false )
return null;
node = parent.getNext();
}
if ( !node )
return null;
if ( guard && guard( node ) === false )
return null;
if ( nodeType && nodeType != node.type )
return node.getNextSourceNode( false, nodeType, guard );
return node;
},
/**
* @todo
*/
getPreviousSourceNode: function( startFromSibling, nodeType, guard ) {
if ( guard && !guard.call ) {
var guardNode = guard;
guard = function( node ) {
return !node.equals( guardNode );
};
}
var node = ( !startFromSibling && this.getLast && this.getLast() ),
parent;
// Guarding when we're skipping the current element( no children or 'startFromSibling' ).
// send the 'moving out' signal even we don't actually dive into.
if ( !node ) {
if ( this.type == CKEDITOR.NODE_ELEMENT && guard && guard( this, true ) === false )
return null;
node = this.getPrevious();
}
while ( !node && ( parent = ( parent || this ).getParent() ) ) {
// The guard check sends the "true" paramenter to indicate that
// we are moving "out" of the element.
if ( guard && guard( parent, true ) === false )
return null;
node = parent.getPrevious();
}
if ( !node )
return null;
if ( guard && guard( node ) === false )
return null;
if ( nodeType && node.type != nodeType )
return node.getPreviousSourceNode( false, nodeType, guard );
return node;
},
/**
* Gets the node that preceed this element in its parent's child list.
*
* var element = CKEDITOR.dom.element.createFromHtml( '<div><i>prev</i><b>Example</b></div>' );
* var first = element.getLast().getPrev();
* alert( first.getName() ); // 'i'
*
* @param {Function} [evaluator] Filtering the result node.
* @returns {CKEDITOR.dom.node} The previous node or null if not available.
*/
getPrevious: function( evaluator ) {
var previous = this.$,
retval;
do {
previous = previous.previousSibling;
// Avoid returning the doc type node.
// http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-412266927
retval = previous && previous.nodeType != 10 && new CKEDITOR.dom.node( previous );
}
while ( retval && evaluator && !evaluator( retval ) )
return retval;
},
/**
* Gets the node that follows this element in its parent's child list.
*
* var element = CKEDITOR.dom.element.createFromHtml( '<div><b>Example</b><i>next</i></div>' );
* var last = element.getFirst().getNext();
* alert( last.getName() ); // 'i'
*
* @param {Function} [evaluator] Filtering the result node.
* @returns {CKEDITOR.dom.node} The next node or null if not available.
*/
getNext: function( evaluator ) {
var next = this.$,
retval;
do {
next = next.nextSibling;
retval = next && new CKEDITOR.dom.node( next );
}
while ( retval && evaluator && !evaluator( retval ) )
return retval;
},
/**
* Gets the parent element for this node.
*
* var node = editor.document.getBody().getFirst();
* var parent = node.getParent();
* alert( parent.getName() ); // 'body'
*
* @param {Boolean} [allowFragmentParent=false] Consider also parent node that is of
* fragment type {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
* @returns {CKEDITOR.dom.element} The parent element.
*/
getParent: function( allowFragmentParent ) {
var parent = this.$.parentNode;
return ( parent && ( parent.nodeType == CKEDITOR.NODE_ELEMENT || allowFragmentParent && parent.nodeType == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) ) ? new CKEDITOR.dom.node( parent ) : null;
},
/**
* Returns array containing node parents and node itself. By default nodes are in _descending_ order.
*
* // Assuming that body has paragraph as first child.
* var node = editor.document.getBody().getFirst();
* var parents = node.getParents();
* alert( parents[ 0 ].getName() + ',' + parents[ 2 ].getName() ); // 'html,p'
*
* @param {Boolean} [closerFirst=false] Determines order of returned nodes.
* @returns {Array} Returns array of {@link CKEDITOR.dom.node}.
*/
getParents: function( closerFirst ) {
var node = this;
var parents = [];
do {
parents[ closerFirst ? 'push' : 'unshift' ]( node );
}
while ( ( node = node.getParent() ) )
return parents;
},
/**
* @todo
*/
getCommonAncestor: function( node ) {
if ( node.equals( this ) )
return this;
if ( node.contains && node.contains( this ) )
return node;
var start = this.contains ? this : this.getParent();
do {
if ( start.contains( node ) ) return start;
}
while ( ( start = start.getParent() ) );
return null;
},
/**
* @todo
*/
getPosition: function( otherNode ) {
var $ = this.$;
var $other = otherNode.$;
if ( $.compareDocumentPosition )
return $.compareDocumentPosition( $other );
// IE and Safari have no support for compareDocumentPosition.
if ( $ == $other )
return CKEDITOR.POSITION_IDENTICAL;
// Only element nodes support contains and sourceIndex.
if ( this.type == CKEDITOR.NODE_ELEMENT && otherNode.type == CKEDITOR.NODE_ELEMENT ) {
if ( $.contains ) {
if ( $.contains( $other ) )
return CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING;
if ( $other.contains( $ ) )
return CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
}
if ( 'sourceIndex' in $ )
return ( $.sourceIndex < 0 || $other.sourceIndex < 0 ) ? CKEDITOR.POSITION_DISCONNECTED : ( $.sourceIndex < $other.sourceIndex ) ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
}
// For nodes that don't support compareDocumentPosition, contains
// or sourceIndex, their "address" is compared.
var addressOfThis = this.getAddress(),
addressOfOther = otherNode.getAddress(),
minLevel = Math.min( addressOfThis.length, addressOfOther.length );
// Determinate preceed/follow relationship.
for ( var i = 0; i <= minLevel - 1; i++ ) {
if ( addressOfThis[ i ] != addressOfOther[ i ] ) {
if ( i < minLevel )
return addressOfThis[ i ] < addressOfOther[ i ] ? CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_FOLLOWING;
break;
}
}
// Determinate contains/contained relationship.
return ( addressOfThis.length < addressOfOther.length ) ? CKEDITOR.POSITION_CONTAINS + CKEDITOR.POSITION_PRECEDING : CKEDITOR.POSITION_IS_CONTAINED + CKEDITOR.POSITION_FOLLOWING;
},
/**
* Gets the closest ancestor node of this node, specified by its name.
*
* // Suppose we have the following HTML structure:
* // <div id="outer"><div id="inner"><p><b>Some text</b></p></div></div>
* // If node == <b>
* ascendant = node.getAscendant( 'div' ); // ascendant == <div id="inner">
* ascendant = node.getAscendant( 'b' ); // ascendant == null
* ascendant = node.getAscendant( 'b', true ); // ascendant == <b>
* ascendant = node.getAscendant( { div:1,p:1 } ); // Searches for the first 'div' or 'p': ascendant == <div id="inner">
*
* @since 3.6.1
* @param {String} reference The name of the ancestor node to search or
* an object with the node names to search for.
* @param {Boolean} [includeSelf] Whether to include the current
* node in the search.
* @returns {CKEDITOR.dom.node} The located ancestor node or null if not found.
*/
getAscendant: function( reference, includeSelf ) {
var $ = this.$,
name;
if ( !includeSelf )
$ = $.parentNode;
while ( $ ) {
if ( $.nodeName && ( name = $.nodeName.toLowerCase(), ( typeof reference == 'string' ? name == reference : name in reference ) ) )
return new CKEDITOR.dom.node( $ );
try {
$ = $.parentNode;
} catch ( e ) {
$ = null;
}
}
return null;
},
/**
* @todo
*/
hasAscendant: function( name, includeSelf ) {
var $ = this.$;
if ( !includeSelf )
$ = $.parentNode;
while ( $ ) {
if ( $.nodeName && $.nodeName.toLowerCase() == name )
return true;
$ = $.parentNode;
}
return false;
},
/**
* @todo
*/
move: function( target, toStart ) {
target.append( this.remove(), toStart );
},
/**
* Removes this node from the document DOM.
*
* var element = CKEDITOR.document.getById( 'MyElement' );
* element.remove();
*
* @param {Boolean} [preserveChildren=false] Indicates that the children
* elements must remain in the document, removing only the outer tags.
*/
remove: function( preserveChildren ) {
var $ = this.$;
var parent = $.parentNode;
if ( parent ) {
if ( preserveChildren ) {
// Move all children before the node.
for ( var child;
( child = $.firstChild ); ) {
parent.insertBefore( $.removeChild( child ), $ );
}
}
parent.removeChild( $ );
}
return this;
},
/**
* @todo
*/
replace: function( nodeToReplace ) {
this.insertBefore( nodeToReplace );
nodeToReplace.remove();
},
/**
* @todo
*/
trim: function() {
this.ltrim();
this.rtrim();
},
/**
* @todo
*/
ltrim: function() {
var child;
while ( this.getFirst && ( child = this.getFirst() ) ) {
if ( child.type == CKEDITOR.NODE_TEXT ) {
var trimmed = CKEDITOR.tools.ltrim( child.getText() ),
originalLength = child.getLength();
if ( !trimmed ) {
child.remove();
continue;
} else if ( trimmed.length < originalLength ) {
child.split( originalLength - trimmed.length );
// IE BUG: child.remove() may raise JavaScript errors here. (#81)
this.$.removeChild( this.$.firstChild );
}
}
break;
}
},
/**
* @todo
*/
rtrim: function() {
var child;
while ( this.getLast && ( child = this.getLast() ) ) {
if ( child.type == CKEDITOR.NODE_TEXT ) {
var trimmed = CKEDITOR.tools.rtrim( child.getText() ),
originalLength = child.getLength();
if ( !trimmed ) {
child.remove();
continue;
} else if ( trimmed.length < originalLength ) {
child.split( trimmed.length );
// IE BUG: child.getNext().remove() may raise JavaScript errors here.
// (#81)
this.$.lastChild.parentNode.removeChild( this.$.lastChild );
}
}
break;
}
if ( CKEDITOR.env.needsBrFiller ) {
child = this.$.lastChild;
if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) {
// Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324).
child.parentNode.removeChild( child );
}
}
},
/**
* Checks if this node is read-only (should not be changed).
*
* **Note:** When `attributeCheck` is not used, this method only work for elements
* that are already presented in the document, otherwise the result
* is not guaranteed, it's mainly for performance consideration.
*
* // For the following HTML:
* // <div contenteditable="false">Some <b>text</b></div>
*
* // If "ele" is the above <div>
* element.isReadOnly(); // true
*
* @since 3.5
* @returns {Boolean}
*/
isReadOnly: function() {
var element = this;
if ( this.type != CKEDITOR.NODE_ELEMENT )
element = this.getParent();
if ( element && typeof element.$.isContentEditable != 'undefined' )
return !( element.$.isContentEditable || element.data( 'cke-editable' ) );
else {
// Degrade for old browsers which don't support "isContentEditable", e.g. FF3
while ( element ) {
if ( element.data( 'cke-editable' ) )
break;
if ( element.getAttribute( 'contentEditable' ) == 'false' )
return true;
else if ( element.getAttribute( 'contentEditable' ) == 'true' )
break;
element = element.getParent();
}
// Reached the root of DOM tree, no editable found.
return !element;
}
}
} );

View File

@@ -0,0 +1,43 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* Represents a list of {@link CKEDITOR.dom.node} objects.
* It's a wrapper for native nodes list.
*
* var nodeList = CKEDITOR.document.getBody().getChildren();
* alert( nodeList.count() ); // number [0;N]
*
* @class
* @constructor Creates a document class instance.
* @param {Object} nativeList
*/
CKEDITOR.dom.nodeList = function( nativeList ) {
this.$ = nativeList;
};
CKEDITOR.dom.nodeList.prototype = {
/**
* Get count of nodes in this list.
*
* @returns {Number}
*/
count: function() {
return this.$.length;
},
/**
* Get node from the list.
*
* @returns {CKEDITOR.dom.node}
*/
getItem: function( index ) {
if ( index < 0 || index >= this.$.length )
return null;
var $node = this.$[ index ];
return $node ? new CKEDITOR.dom.node( $node ) : null;
}
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,201 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
( function() {
/**
* Represents a list os CKEDITOR.dom.range objects, which can be easily
* iterated sequentially.
*
* @class
* @extends Array
* @constructor Creates a rangeList class instance.
* @param {CKEDITOR.dom.range/CKEDITOR.dom.range[]} [ranges] The ranges contained on this list.
* Note that, if an array of ranges is specified, the range sequence
* should match its DOM order. This class will not help to sort them.
*/
CKEDITOR.dom.rangeList = function( ranges ) {
if ( ranges instanceof CKEDITOR.dom.rangeList )
return ranges;
if ( !ranges )
ranges = [];
else if ( ranges instanceof CKEDITOR.dom.range )
ranges = [ ranges ];
return CKEDITOR.tools.extend( ranges, mixins );
};
var mixins = {
/**
* Creates an instance of the rangeList iterator, it should be used
* only when the ranges processing could be DOM intrusive, which
* means it may pollute and break other ranges in this list.
* Otherwise, it's enough to just iterate over this array in a for loop.
*
* @returns {CKEDITOR.dom.rangeListIterator}
*/
createIterator: function() {
var rangeList = this,
bookmark = CKEDITOR.dom.walker.bookmark(),
guard = function( node ) {
return !( node.is && node.is( 'tr' ) );
},
bookmarks = [],
current;
return {
/**
* Retrieves the next range in the list.
*
* @member CKEDITOR.dom.rangeListIterator
* @param {Boolean} [mergeConsequent=false] Whether join two adjacent
* ranges into single, e.g. consequent table cells.
*/
getNextRange: function( mergeConsequent ) {
current = current == undefined ? 0 : current + 1;
var range = rangeList[ current ];
// Multiple ranges might be mangled by each other.
if ( range && rangeList.length > 1 ) {
// Bookmarking all other ranges on the first iteration,
// the range correctness after it doesn't matter since we'll
// restore them before the next iteration.
if ( !current ) {
// Make sure bookmark correctness by reverse processing.
for ( var i = rangeList.length - 1; i >= 0; i-- )
bookmarks.unshift( rangeList[ i ].createBookmark( true ) );
}
if ( mergeConsequent ) {
// Figure out how many ranges should be merged.
var mergeCount = 0;
while ( rangeList[ current + mergeCount + 1 ] ) {
var doc = range.document,
found = 0,
left = doc.getById( bookmarks[ mergeCount ].endNode ),
right = doc.getById( bookmarks[ mergeCount + 1 ].startNode ),
next;
// Check subsequent range.
while ( 1 ) {
next = left.getNextSourceNode( false );
if ( !right.equals( next ) ) {
// This could be yet another bookmark or
// walking across block boundaries.
if ( bookmark( next ) || ( next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() ) ) {
left = next;
continue;
}
} else
found = 1;
break;
}
if ( !found )
break;
mergeCount++;
}
}
range.moveToBookmark( bookmarks.shift() );
// Merge ranges finally after moving to bookmarks.
while ( mergeCount-- ) {
next = rangeList[ ++current ];
next.moveToBookmark( bookmarks.shift() );
range.setEnd( next.endContainer, next.endOffset );
}
}
return range;
}
};
},
/**
* Create bookmarks for all ranges. See {@link CKEDITOR.dom.range#createBookmark}.
*
* @param {Boolean} [serializable=false] See {@link CKEDITOR.dom.range#createBookmark}.
* @returns {Array} Array of bookmarks.
*/
createBookmarks: function( serializable ) {
var retval = [],
bookmark;
for ( var i = 0; i < this.length; i++ ) {
retval.push( bookmark = this[ i ].createBookmark( serializable, true ) );
// Updating the container & offset values for ranges
// that have been touched.
for ( var j = i + 1; j < this.length; j++ ) {
this[ j ] = updateDirtyRange( bookmark, this[ j ] );
this[ j ] = updateDirtyRange( bookmark, this[ j ], true );
}
}
return retval;
},
/**
* Create "unobtrusive" bookmarks for all ranges. See {@link CKEDITOR.dom.range#createBookmark2}.
*
* @param {Boolean} [normalized=false] See {@link CKEDITOR.dom.range#createBookmark2}.
* @returns {Array} Array of bookmarks.
*/
createBookmarks2: function( normalized ) {
var bookmarks = [];
for ( var i = 0; i < this.length; i++ )
bookmarks.push( this[ i ].createBookmark2( normalized ) );
return bookmarks;
},
/**
* Move each range in the list to the position specified by a list of bookmarks.
*
* @param {Array} bookmarks The list of bookmarks, each one matching a range in the list.
*/
moveToBookmarks: function( bookmarks ) {
for ( var i = 0; i < this.length; i++ )
this[ i ].moveToBookmark( bookmarks[ i ] );
}
};
// Update the specified range which has been mangled by previous insertion of
// range bookmark nodes.(#3256)
function updateDirtyRange( bookmark, dirtyRange, checkEnd ) {
var serializable = bookmark.serializable,
container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ],
offset = checkEnd ? 'endOffset' : 'startOffset';
var bookmarkStart = serializable ? dirtyRange.document.getById( bookmark.startNode ) : bookmark.startNode;
var bookmarkEnd = serializable ? dirtyRange.document.getById( bookmark.endNode ) : bookmark.endNode;
if ( container.equals( bookmarkStart.getPrevious() ) ) {
dirtyRange.startOffset = dirtyRange.startOffset - container.getLength() - bookmarkEnd.getPrevious().getLength();
container = bookmarkEnd.getNext();
} else if ( container.equals( bookmarkEnd.getPrevious() ) ) {
dirtyRange.startOffset = dirtyRange.startOffset - container.getLength();
container = bookmarkEnd.getNext();
}
container.equals( bookmarkStart.getParent() ) && dirtyRange[ offset ]++;
container.equals( bookmarkEnd.getParent() ) && dirtyRange[ offset ]++;
// Update and return this range.
dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ] = container;
return dirtyRange;
}
} )();
/**
* (Virtual Class) Do not call this constructor. This class is not really part
* of the API. It just describes the return type of {@link CKEDITOR.dom.rangeList#createIterator}.
*
* @class CKEDITOR.dom.rangeListIterator
*/

View File

@@ -0,0 +1,139 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.text} class, which represents
* a DOM text node.
*/
/**
* Represents a DOM text node.
*
* var nativeNode = document.createTextNode( 'Example' );
* var text = CKEDITOR.dom.text( nativeNode );
*
* var text = CKEDITOR.dom.text( 'Example' );
*
* @class
* @extends CKEDITOR.dom.node
* @constructor Creates a text class instance.
* @param {Object/String} text A native DOM text node or a string containing
* the text to use to create a new text node.
* @param {CKEDITOR.dom.document} [ownerDocument] The document that will contain
* the node in case of new node creation. Defaults to the current document.
*/
CKEDITOR.dom.text = function( text, ownerDocument ) {
if ( typeof text == 'string' )
text = ( ownerDocument ? ownerDocument.$ : document ).createTextNode( text );
// Theoretically, we should call the base constructor here
// (not CKEDITOR.dom.node though). But, IE doesn't support expando
// properties on text node, so the features provided by domObject will not
// work for text nodes (which is not a big issue for us).
//
// CKEDITOR.dom.domObject.call( this, element );
this.$ = text;
};
CKEDITOR.dom.text.prototype = new CKEDITOR.dom.node();
CKEDITOR.tools.extend( CKEDITOR.dom.text.prototype, {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_TEXT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_TEXT]
*/
type: CKEDITOR.NODE_TEXT,
/**
* Gets length of node's value.
*
* @returns {Number}
*/
getLength: function() {
return this.$.nodeValue.length;
},
/**
* Gets node's value.
*
* @returns {String}
*/
getText: function() {
return this.$.nodeValue;
},
/**
* Sets node's value.
*
* @param {String} text
*/
setText: function( text ) {
this.$.nodeValue = text;
},
/**
* Breaks this text node into two nodes at the specified offset,
* keeping both in the tree as siblings. This node then only contains
* all the content up to the offset point. A new text node, which is
* inserted as the next sibling of this node, contains all the content
* at and after the offset point. When the offset is equal to the
* length of this node, the new node has no data.
*
* @param {Number} The position at which to split, starting from zero.
* @returns {CKEDITOR.dom.text} The new text node.
*/
split: function( offset ) {
// Saved the children count and text length beforehand.
var parent = this.$.parentNode,
count = parent.childNodes.length,
length = this.getLength();
var doc = this.getDocument();
var retval = new CKEDITOR.dom.text( this.$.splitText( offset ), doc );
if ( parent.childNodes.length == count )
{
// If the offset is after the last char, IE creates the text node
// on split, but don't include it into the DOM. So, we have to do
// that manually here.
if ( offset >= length )
{
retval = doc.createText( '' );
retval.insertAfter( this );
}
else
{
// IE BUG: IE8+ does not update the childNodes array in DOM after splitText(),
// we need to make some DOM changes to make it update. (#3436)
var workaround = doc.createText( '' );
workaround.insertAfter( retval );
workaround.remove();
}
}
return retval;
},
/**
* Extracts characters from indexA up to but not including `indexB`.
*
* @param {Number} indexA An integer between `0` and one less than the
* length of the text.
* @param {Number} [indexB] An integer between `0` and the length of the
* string. If omitted, extracts characters to the end of the text.
*/
substring: function( indexA, indexB ) {
// We need the following check due to a Firefox bug
// https://bugzilla.mozilla.org/show_bug.cgi?id=458886
if ( typeof indexB != 'number' )
return this.$.nodeValue.substr( indexA );
else
return this.$.nodeValue.substring( indexA, indexB );
}
} );

View File

@@ -0,0 +1,616 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
( function() {
// This function is to be called under a "walker" instance scope.
function iterate( rtl, breakOnFalse ) {
var range = this.range;
// Return null if we have reached the end.
if ( this._.end )
return null;
// This is the first call. Initialize it.
if ( !this._.start ) {
this._.start = 1;
// A collapsed range must return null at first call.
if ( range.collapsed ) {
this.end();
return null;
}
// Move outside of text node edges.
range.optimize();
}
var node,
startCt = range.startContainer,
endCt = range.endContainer,
startOffset = range.startOffset,
endOffset = range.endOffset,
guard,
userGuard = this.guard,
type = this.type,
getSourceNodeFn = ( rtl ? 'getPreviousSourceNode' : 'getNextSourceNode' );
// Create the LTR guard function, if necessary.
if ( !rtl && !this._.guardLTR ) {
// The node that stops walker from moving up.
var limitLTR = endCt.type == CKEDITOR.NODE_ELEMENT ? endCt : endCt.getParent();
// The node that stops the walker from going to next.
var blockerLTR = endCt.type == CKEDITOR.NODE_ELEMENT ? endCt.getChild( endOffset ) : endCt.getNext();
this._.guardLTR = function( node, movingOut ) {
return ( ( !movingOut || !limitLTR.equals( node ) ) && ( !blockerLTR || !node.equals( blockerLTR ) ) && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || !node.equals( range.root ) ) );
};
}
// Create the RTL guard function, if necessary.
if ( rtl && !this._.guardRTL ) {
// The node that stops walker from moving up.
var limitRTL = startCt.type == CKEDITOR.NODE_ELEMENT ? startCt : startCt.getParent();
// The node that stops the walker from going to next.
var blockerRTL = startCt.type == CKEDITOR.NODE_ELEMENT ? startOffset ? startCt.getChild( startOffset - 1 ) : null : startCt.getPrevious();
this._.guardRTL = function( node, movingOut ) {
return ( ( !movingOut || !limitRTL.equals( node ) ) && ( !blockerRTL || !node.equals( blockerRTL ) ) && ( node.type != CKEDITOR.NODE_ELEMENT || !movingOut || !node.equals( range.root ) ) );
};
}
// Define which guard function to use.
var stopGuard = rtl ? this._.guardRTL : this._.guardLTR;
// Make the user defined guard function participate in the process,
// otherwise simply use the boundary guard.
if ( userGuard ) {
guard = function( node, movingOut ) {
if ( stopGuard( node, movingOut ) === false )
return false;
return userGuard( node, movingOut );
};
} else
guard = stopGuard;
if ( this.current )
node = this.current[ getSourceNodeFn ]( false, type, guard );
else {
// Get the first node to be returned.
if ( rtl ) {
node = endCt;
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
if ( endOffset > 0 )
node = node.getChild( endOffset - 1 );
else
node = ( guard( node, true ) === false ) ? null : node.getPreviousSourceNode( true, type, guard );
}
} else {
node = startCt;
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
if ( !( node = node.getChild( startOffset ) ) )
node = ( guard( startCt, true ) === false ) ? null : startCt.getNextSourceNode( true, type, guard );
}
}
if ( node && guard( node ) === false )
node = null;
}
while ( node && !this._.end ) {
this.current = node;
if ( !this.evaluator || this.evaluator( node ) !== false ) {
if ( !breakOnFalse )
return node;
} else if ( breakOnFalse && this.evaluator )
return false;
node = node[ getSourceNodeFn ]( false, type, guard );
}
this.end();
return this.current = null;
}
function iterateToLast( rtl ) {
var node,
last = null;
while ( ( node = iterate.call( this, rtl ) ) )
last = node;
return last;
}
/**
* Utility class to "walk" the DOM inside a range boundaries. If the
* range starts or ends in the middle of the text node this node will
* be included as a whole. Outside changes to the range may break the walker.
*
* The walker may return nodes that are not totaly included into the
* range boundaires. Let's take the following range representation,
* where the square brackets indicate the boundaries:
*
* [<p>Some <b>sample] text</b>
*
* While walking forward into the above range, the following nodes are
* returned: `<p>`, `"Some "`, `<b>` and `"sample"`. Going
* backwards instead we have: `"sample"` and `"Some "`. So note that the
* walker always returns nodes when "entering" them, but not when
* "leaving" them. The guard function is instead called both when
* entering and leaving nodes.
*
* @class
*/
CKEDITOR.dom.walker = CKEDITOR.tools.createClass( {
/**
* Creates a walker class instance.
*
* @constructor
* @param {CKEDITOR.dom.range} range The range within which walk.
*/
$: function( range ) {
this.range = range;
/**
* A function executed for every matched node, to check whether
* it's to be considered into the walk or not. If not provided, all
* matched nodes are considered good.
*
* If the function returns `false` the node is ignored.
*
* @property {Function} evaluator
*/
// this.evaluator = null;
/**
* A function executed for every node the walk pass by to check
* whether the walk is to be finished. It's called when both
* entering and exiting nodes, as well as for the matched nodes.
*
* If this function returns `false`, the walking ends and no more
* nodes are evaluated.
* @property {Function} guard
*/
// this.guard = null;
/** @private */
this._ = {};
},
// statics :
// {
// /* Creates a CKEDITOR.dom.walker instance to walk inside DOM boundaries set by nodes.
// * @param {CKEDITOR.dom.node} startNode The node from wich the walk
// * will start.
// * @param {CKEDITOR.dom.node} [endNode] The last node to be considered
// * in the walk. No more nodes are retrieved after touching or
// * passing it. If not provided, the walker stops at the
// * &lt;body&gt; closing boundary.
// * @returns {CKEDITOR.dom.walker} A DOM walker for the nodes between the
// * provided nodes.
// */
// createOnNodes : function( startNode, endNode, startInclusive, endInclusive )
// {
// var range = new CKEDITOR.dom.range();
// if ( startNode )
// range.setStartAt( startNode, startInclusive ? CKEDITOR.POSITION_BEFORE_START : CKEDITOR.POSITION_AFTER_END ) ;
// else
// range.setStartAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_AFTER_START ) ;
//
// if ( endNode )
// range.setEndAt( endNode, endInclusive ? CKEDITOR.POSITION_AFTER_END : CKEDITOR.POSITION_BEFORE_START ) ;
// else
// range.setEndAt( startNode.getDocument().getBody(), CKEDITOR.POSITION_BEFORE_END ) ;
//
// return new CKEDITOR.dom.walker( range );
// }
// },
//
proto: {
/**
* Stops walking. No more nodes are retrieved if this function gets called.
*/
end: function() {
this._.end = 1;
},
/**
* Retrieves the next node (at right).
*
* @returns {CKEDITOR.dom.node} The next node or null if no more
* nodes are available.
*/
next: function() {
return iterate.call( this );
},
/**
* Retrieves the previous node (at left).
*
* @returns {CKEDITOR.dom.node} The previous node or null if no more
* nodes are available.
*/
previous: function() {
return iterate.call( this, 1 );
},
/**
* Check all nodes at right, executing the evaluation function.
*
* @returns {Boolean} `false` if the evaluator function returned
* `false` for any of the matched nodes. Otherwise `true`.
*/
checkForward: function() {
return iterate.call( this, 0, 1 ) !== false;
},
/**
* Check all nodes at left, executing the evaluation function.
*
* @returns {Boolean} `false` if the evaluator function returned
* `false` for any of the matched nodes. Otherwise `true`.
*/
checkBackward: function() {
return iterate.call( this, 1, 1 ) !== false;
},
/**
* Executes a full walk forward (to the right), until no more nodes
* are available, returning the last valid node.
*
* @returns {CKEDITOR.dom.node} The last node at the right or null
* if no valid nodes are available.
*/
lastForward: function() {
return iterateToLast.call( this );
},
/**
* Executes a full walk backwards (to the left), until no more nodes
* are available, returning the last valid node.
*
* @returns {CKEDITOR.dom.node} The last node at the left or null
* if no valid nodes are available.
*/
lastBackward: function() {
return iterateToLast.call( this, 1 );
},
/**
* Resets walker.
*/
reset: function() {
delete this.current;
this._ = {};
}
}
} );
// Anything whose display computed style is block, list-item, table,
// table-row-group, table-header-group, table-footer-group, table-row,
// table-column-group, table-column, table-cell, table-caption, or whose node
// name is hr, br (when enterMode is br only) is a block boundary.
var blockBoundaryDisplayMatch = { block: 1, 'list-item': 1, table: 1, 'table-row-group': 1,
'table-header-group': 1, 'table-footer-group': 1, 'table-row': 1, 'table-column-group': 1,
'table-column': 1, 'table-cell': 1, 'table-caption': 1 },
outOfFlowPositions = { absolute: 1, fixed: 1 };
/**
* Checks whether element is displayed as a block.
*
* @member CKEDITOR.dom.element
* @param [customNodeNames] Custom list of nodes which will extend
* default {@link CKEDITOR.dtd#$block} list.
* @returns {Boolean}
*/
CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) {
// Whether element is in normal page flow. Floated or positioned elements are out of page flow.
// Don't consider floated or positioned formatting as block boundary, fall back to dtd check in that case. (#6297)
var inPageFlow = this.getComputedStyle( 'float' ) == 'none' && !( this.getComputedStyle( 'position' ) in outOfFlowPositions );
if ( inPageFlow && blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] )
return true;
// Either in $block or in customNodeNames if defined.
return !!( this.is( CKEDITOR.dtd.$block ) || customNodeNames && this.is( customNodeNames ) );
};
/**
* Returns a function which checks whether the node is a block boundary.
* See {@link CKEDITOR.dom.element#isBlockBoundary}.
*
* @static
* @param customNodeNames
* @returns {Function}
*/
CKEDITOR.dom.walker.blockBoundary = function( customNodeNames ) {
return function( node, type ) {
return !( node.type == CKEDITOR.NODE_ELEMENT && node.isBlockBoundary( customNodeNames ) );
};
};
/**
* @static
* @todo
*/
CKEDITOR.dom.walker.listItemBoundary = function() {
return this.blockBoundary( { br: 1 } );
};
/**
* Returns a function which checks whether the node is a bookmark node OR bookmark node
* inner contents.
*
* @static
* @param {Boolean} [contentOnly=false] Whether only test against the text content of
* bookmark node instead of the element itself (default).
* @param {Boolean} [isReject=false] Whether should return `false` for the bookmark
* node instead of `true` (default).
* @returns {Function}
*/
CKEDITOR.dom.walker.bookmark = function( contentOnly, isReject ) {
function isBookmarkNode( node ) {
return ( node && node.getName && node.getName() == 'span' && node.data( 'cke-bookmark' ) );
}
return function( node ) {
var isBookmark, parent;
// Is bookmark inner text node?
isBookmark = ( node && node.type != CKEDITOR.NODE_ELEMENT && ( parent = node.getParent() ) && isBookmarkNode( parent ) );
// Is bookmark node?
isBookmark = contentOnly ? isBookmark : isBookmark || isBookmarkNode( node );
return !!( isReject ^ isBookmark );
};
};
/**
* Returns a function which checks whether the node is a text node containing only whitespaces characters.
*
* @static
* @param {Boolean} [isReject=false]
* @returns {Function}
*/
CKEDITOR.dom.walker.whitespaces = function( isReject ) {
return function( node ) {
var isWhitespace;
if ( node && node.type == CKEDITOR.NODE_TEXT ) {
// whitespace, as well as the text cursor filler node we used in Webkit. (#9384)
isWhitespace = !CKEDITOR.tools.trim( node.getText() ) ||
CKEDITOR.env.webkit && node.getText() == '\u200b';
}
return !!( isReject ^ isWhitespace );
};
};
/**
* Returns a function which checks whether the node is invisible in wysiwyg mode.
*
* @static
* @param {Boolean} [isReject=false]
* @returns {Function}
*/
CKEDITOR.dom.walker.invisible = function( isReject ) {
var whitespace = CKEDITOR.dom.walker.whitespaces();
return function( node ) {
var invisible;
if ( whitespace( node ) )
invisible = 1;
else {
// Visibility should be checked on element.
if ( node.type == CKEDITOR.NODE_TEXT )
node = node.getParent();
// Nodes that take no spaces in wysiwyg:
// 1. White-spaces but not including NBSP;
// 2. Empty inline elements, e.g. <b></b> we're checking here
// 'offsetHeight' instead of 'offsetWidth' for properly excluding
// all sorts of empty paragraph, e.g. <br />.
invisible = !node.$.offsetHeight;
}
return !!( isReject ^ invisible );
};
};
/**
* Returns a function which checks whether node's type is equal to passed one.
*
* @static
* @param {Number} type
* @param {Boolean} [isReject=false]
* @returns {Function}
*/
CKEDITOR.dom.walker.nodeType = function( type, isReject ) {
return function( node ) {
return !!( isReject ^ ( node.type == type ) );
};
};
/**
* Returns a function which checks whether node is a bogus (filler) node from
* contenteditable element's point of view.
*
* @static
* @param {Boolean} [isReject=false]
* @returns {Function}
*/
CKEDITOR.dom.walker.bogus = function( isReject ) {
function nonEmpty( node ) {
return !isWhitespaces( node ) && !isBookmark( node );
}
return function( node ) {
var isBogus = CKEDITOR.env.needsBrFiller ? node.is && node.is( 'br' ) : node.getText && tailNbspRegex.test( node.getText() );
if ( isBogus ) {
var parent = node.getParent(),
next = node.getNext( nonEmpty );
isBogus = parent.isBlockBoundary() && ( !next || next.type == CKEDITOR.NODE_ELEMENT && next.isBlockBoundary() );
}
return !!( isReject ^ isBogus );
};
};
/**
* Returns a function which checks whether node is a temporary element
* (element with `data-cke-temp` attribute) or its child.
*
* @since 4.3
* @static
* @param {Boolean} [isReject=false] Whether should return `false` for the
* temporary element instead of `true` (default).
* @returns {Function}
*/
CKEDITOR.dom.walker.temp = function( isReject ) {
return function( node ) {
if ( node.type != CKEDITOR.NODE_ELEMENT )
node = node.getParent();
var isTemp = node && node.hasAttribute( 'data-cke-temp' );
return !!( isReject ^ isTemp );
};
};
var tailNbspRegex = /^[\t\r\n ]*(?:&nbsp;|\xa0)$/,
isWhitespaces = CKEDITOR.dom.walker.whitespaces(),
isBookmark = CKEDITOR.dom.walker.bookmark(),
isTemp = CKEDITOR.dom.walker.temp(),
toSkip = function( node ) {
return isBookmark( node ) ||
isWhitespaces( node ) ||
node.type == CKEDITOR.NODE_ELEMENT && node.is( CKEDITOR.dtd.$inline ) && !node.is( CKEDITOR.dtd.$empty );
};
/**
* Returns a function which checks whether node should be ignored in terms of "editability".
*
* This includes:
*
* * whitespaces (see {@link CKEDITOR.dom.walker#whitespaces}),
* * bookmarks (see {@link CKEDITOR.dom.walker#bookmark}),
* * temporary elements (see {@link CKEDITOR.dom.walker#temp}).
*
* @since 4.3
* @static
* @param {Boolean} [isReject=false] Whether should return `false` for the
* ignored element instead of `true` (default).
* @returns {Function}
*/
CKEDITOR.dom.walker.ignored = function( isReject ) {
return function( node ) {
var isIgnored = isWhitespaces( node ) || isBookmark( node ) || isTemp( node );
return !!( isReject ^ isIgnored );
};
};
var isIgnored = CKEDITOR.dom.walker.ignored();
function isEmpty( node ) {
var i = 0,
l = node.getChildCount();
for ( ; i < l; ++i ) {
if ( !isIgnored( node.getChild( i ) ) )
return false;
}
return true;
}
function filterTextContainers( dtd ) {
var hash = {},
name;
for ( name in dtd ) {
if ( CKEDITOR.dtd[ name ][ '#' ] )
hash[ name ] = 1;
}
return hash;
}
// Block elements which can contain text nodes (without ul, ol, dl, etc.).
var dtdTextBlock = filterTextContainers( CKEDITOR.dtd.$block );
function isEditable( node ) {
// Skip temporary elements, bookmarks and whitespaces.
if ( isIgnored( node ) )
return false;
if ( node.type == CKEDITOR.NODE_TEXT )
return true;
if ( node.type == CKEDITOR.NODE_ELEMENT ) {
// All inline and non-editable elements are valid editable places.
// Note: non-editable block has to be treated differently (should be selected entirely).
if ( node.is( CKEDITOR.dtd.$inline ) || node.getAttribute( 'contenteditable' ) == 'false' )
return true;
// Empty blocks are editable on IE.
if ( !CKEDITOR.env.needsBrFiller && node.is( dtdTextBlock ) && isEmpty( node ) )
return true;
}
// Skip all other nodes.
return false;
}
/**
* Returns a function which checks whether node can be a container or a sibling
* of selection end.
*
* This includes:
*
* * text nodes (but not whitespaces),
* * inline elements,
* * non-editable blocks (special case - such blocks cannot be containers nor
* siblings, they need to be selected entirely),
* * empty blocks which can contain text (IE only).
*
* @since 4.3
* @static
* @param {Boolean} [isReject=false] Whether should return `false` for the
* ignored element instead of `true` (default).
* @returns {Function}
*/
CKEDITOR.dom.walker.editable = function( isReject ) {
return function( node ) {
return !!( isReject ^ isEditable( node ) );
};
};
/**
* Checks if there's a filler node at the end of an element, and returns it.
*
* @member CKEDITOR.dom.element
* @returns {CKEDITOR.dom.node/Boolean} Bogus node or `false`.
*/
CKEDITOR.dom.element.prototype.getBogus = function() {
// Bogus are not always at the end, e.g. <p><a>text<br /></a></p> (#7070).
var tail = this;
do {
tail = tail.getPreviousSourceNode();
}
while ( toSkip( tail ) )
if ( tail && ( CKEDITOR.env.needsBrFiller ? tail.is && tail.is( 'br' ) : tail.getText && tailNbspRegex.test( tail.getText() ) ) )
return tail;
return false;
};
} )();

View File

@@ -0,0 +1,95 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dom.document} class, which
* represents a DOM document.
*/
/**
* Represents a DOM window.
*
* var document = new CKEDITOR.dom.window( window );
*
* @class
* @extends CKEDITOR.dom.domObject
* @constructor Creates a window class instance.
* @param {Object} domWindow A native DOM window.
*/
CKEDITOR.dom.window = function( domWindow ) {
CKEDITOR.dom.domObject.call( this, domWindow );
};
CKEDITOR.dom.window.prototype = new CKEDITOR.dom.domObject();
CKEDITOR.tools.extend( CKEDITOR.dom.window.prototype, {
/**
* Moves the selection focus to this window.
*
* var win = new CKEDITOR.dom.window( window );
* win.focus();
*/
focus: function() {
this.$.focus();
},
/**
* Gets the width and height of this window's viewable area.
*
* var win = new CKEDITOR.dom.window( window );
* var size = win.getViewPaneSize();
* alert( size.width );
* alert( size.height );
*
* @returns {Object} An object with the `width` and `height`
* properties containing the size.
*/
getViewPaneSize: function() {
var doc = this.$.document,
stdMode = doc.compatMode == 'CSS1Compat';
return {
width: ( stdMode ? doc.documentElement.clientWidth : doc.body.clientWidth ) || 0,
height: ( stdMode ? doc.documentElement.clientHeight : doc.body.clientHeight ) || 0
};
},
/**
* Gets the current position of the window's scroll.
*
* var win = new CKEDITOR.dom.window( window );
* var pos = win.getScrollPosition();
* alert( pos.x );
* alert( pos.y );
*
* @returns {Object} An object with the `x` and `y` properties
* containing the scroll position.
*/
getScrollPosition: function() {
var $ = this.$;
if ( 'pageXOffset' in $ ) {
return {
x: $.pageXOffset || 0,
y: $.pageYOffset || 0
};
} else {
var doc = $.document;
return {
x: doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,
y: doc.documentElement.scrollTop || doc.body.scrollTop || 0
};
}
},
/**
* Gets the frame element containing this window context.
*
* @returns {CKEDITOR.dom.element} The frame element or `null` if not in a frame context.
*/
getFrame: function() {
var iframe = this.$.frameElement;
return iframe ? new CKEDITOR.dom.element.get( iframe ) : null;
}
} );

View File

@@ -0,0 +1,328 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.dtd} object, which holds the DTD
* mapping for XHTML 1.0 Transitional. This file was automatically
* generated from the file: xhtml1-transitional.dtd.
*/
/**
* Holds and object representation of the HTML DTD to be used by the
* editor in its internal operations.
*
* Each element in the DTD is represented by a property in this object. Each
* property contains the list of elements that can be contained by the element.
* Text is represented by the `#` property.
*
* Several special grouping properties are also available. Their names start
* with the `$` character.
*
* // Check if <div> can be contained in a <p> element.
* alert( !!CKEDITOR.dtd[ 'p' ][ 'div' ] ); // false
*
* // Check if <p> can be contained in a <div> element.
* alert( !!CKEDITOR.dtd[ 'div' ][ 'p' ] ); // true
*
* // Check if <p> is a block element.
* alert( !!CKEDITOR.dtd.$block[ 'p' ] ); // true
*
* @class CKEDITOR.dtd
* @singleton
*/
CKEDITOR.dtd = ( function() {
'use strict';
var X = CKEDITOR.tools.extend,
// Subtraction rest of sets, from the first set.
Y = function( source, removed ) {
var substracted = CKEDITOR.tools.clone( source );
for ( var i = 1; i < arguments.length; i++ ) {
removed = arguments[ i ];
for ( var name in removed )
delete substracted[ name ];
}
return substracted;
};
// Phrasing elements.
// P = { a: 1, em: 1, strong: 1, small: 1, abbr: 1, dfn: 1, i: 1, b: 1, s: 1,
// u: 1, code: 1, 'var': 1, samp: 1, kbd: 1, sup: 1, sub: 1, q: 1, cite: 1,
// span: 1, bdo: 1, bdi: 1, br: 1, wbr: 1, ins: 1, del: 1, img: 1, embed: 1,
// object: 1, iframe: 1, map: 1, area: 1, script: 1, noscript: 1, ruby: 1,
// video: 1, audio: 1, input: 1, textarea: 1, select: 1, button: 1, label: 1,
// output: 1, keygen: 1, progress: 1, command: 1, canvas: 1, time: 1,
// meter: 1, detalist: 1 },
// Flow elements.
// F = { a: 1, p: 1, hr: 1, pre: 1, ul: 1, ol: 1, dl: 1, div: 1, h1: 1, h2: 1,
// h3: 1, h4: 1, h5: 1, h6: 1, hgroup: 1, address: 1, blockquote: 1, ins: 1,
// del: 1, object: 1, map: 1, noscript: 1, section: 1, nav: 1, article: 1,
// aside: 1, header: 1, footer: 1, video: 1, audio: 1, figure: 1, table: 1,
// form: 1, fieldset: 1, menu: 1, canvas: 1, details:1 },
// Text can be everywhere.
// X( P, T );
// Flow elements set consists of phrasing elements set.
// X( F, P );
var P = {}, F = {},
// Intersection of flow elements set and phrasing elements set.
PF = { a: 1, abbr: 1, area: 1, audio: 1, b: 1, bdi: 1, bdo: 1, br: 1, button: 1, canvas: 1, cite: 1,
code: 1, command: 1, datalist: 1, del: 1, dfn: 1, em: 1, embed: 1, i: 1, iframe: 1, img: 1,
input: 1, ins: 1, kbd: 1, keygen: 1, label: 1, map: 1, mark: 1, meter: 1, noscript: 1, object: 1,
output: 1, progress: 1, q: 1, ruby: 1, s: 1, samp: 1, script: 1, select: 1, small: 1, span: 1,
strong: 1, sub: 1, sup: 1, textarea: 1, time: 1, u: 1, 'var': 1, video: 1, wbr: 1 },
// F - PF (Flow Only).
FO = { address: 1, article: 1, aside: 1, blockquote: 1, details: 1, div: 1, dl: 1, fieldset: 1,
figure: 1, footer: 1, form: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, header: 1, hgroup: 1,
hr: 1, menu: 1, nav: 1, ol: 1, p: 1, pre: 1, section: 1, table: 1, ul: 1 },
// Metadata elements.
M = { command: 1, link: 1, meta: 1, noscript: 1, script: 1, style: 1 },
// Empty.
E = {},
// Text.
T = { '#': 1 },
// Deprecated phrasing elements.
DP = { acronym: 1, applet: 1, basefont: 1, big: 1, font: 1, isindex: 1, strike: 1, style: 1, tt: 1 }, // TODO remove "style".
// Deprecated flow only elements.
DFO = { center: 1, dir: 1, noframes: 1 };
// Phrasing elements := PF + T + DP
X( P, PF, T, DP );
// Flow elements := FO + P + DFO
X( F, FO, P, DFO );
var dtd = {
a: Y( P, { a: 1, button: 1 } ), // Treat as normal inline element (not a transparent one).
abbr: P,
address: F,
area: E,
article: X( { style: 1 }, F ),
aside: X( { style: 1 }, F ),
audio: X( { source: 1, track: 1 }, F ),
b: P,
base: E,
bdi: P,
bdo: P,
blockquote: F,
body: F,
br: E,
button: Y( P, { a: 1, button: 1 } ),
canvas: P, // Treat as normal inline element (not a transparent one).
caption: F,
cite: P,
code: P,
col: E,
colgroup: { col: 1 },
command: E,
datalist: X( { option: 1 }, P ),
dd: F,
del: P, // Treat as normal inline element (not a transparent one).
details: X( { summary: 1 }, F ),
dfn: P,
div: X( { style: 1 }, F ),
dl: { dt: 1, dd: 1 },
dt: F,
em: P,
embed: E,
fieldset: X( { legend: 1 }, F ),
figcaption: F,
figure: X( { figcaption: 1 }, F ),
footer: F,
form: F,
h1: P,
h2: P,
h3: P,
h4: P,
h5: P,
h6: P,
head: X( { title: 1, base: 1 }, M ),
header: F,
hgroup: { h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1 },
hr: E,
html: X( { head: 1, body: 1 }, F, M ), // Head and body are optional...
i: P,
iframe: T,
img: E,
input: E,
ins: P, // Treat as normal inline element (not a transparent one).
kbd: P,
keygen: E,
label: P,
legend: P,
li: F,
link: E,
map: F,
mark: P, // Treat as normal inline element (not a transparent one).
menu: X( { li: 1 }, F ),
meta: E,
meter: Y( P, { meter: 1 } ),
nav: F,
noscript: X( { link: 1, meta: 1, style: 1 }, P ), // Treat as normal inline element (not a transparent one).
object: X( { param: 1 }, P ), // Treat as normal inline element (not a transparent one).
ol: { li: 1 },
optgroup: { option: 1 },
option: T,
output: P,
p: P,
param: E,
pre: P,
progress: Y( P, { progress: 1 } ),
q: P,
rp: P,
rt: P,
ruby: X( { rp: 1, rt: 1 }, P ),
s: P,
samp: P,
script: T,
section: X( { style: 1 }, F ),
select: { optgroup: 1, option: 1 },
small: P,
source: E,
span: P,
strong: P,
style: T,
sub: P,
summary: P,
sup: P,
table: { caption: 1, colgroup: 1, thead: 1, tfoot: 1, tbody: 1, tr: 1 },
tbody: { tr: 1 },
td: F,
textarea: T,
tfoot: { tr: 1 },
th: F,
thead: { tr: 1 },
time: Y( P, { time: 1 } ),
title: T,
tr: { th: 1, td: 1 },
track: E,
u: P,
ul: { li: 1 },
'var': P,
video: X( { source: 1, track: 1 }, F ),
wbr: E,
// Deprecated tags.
acronym: P,
applet: X( { param: 1 }, F ),
basefont: E,
big: P,
center: F,
dialog: E,
dir: { li: 1 },
font: P,
isindex: E,
noframes: F,
strike: P,
tt: P
};
X( dtd, {
/**
* List of block elements, like `<p>` or `<div>`.
*/
$block: X( { audio: 1, dd: 1, dt: 1, figcaption: 1, li: 1, video: 1 }, FO, DFO ),
/**
* List of elements that contain other blocks, in which block-level operations should be limited,
* this property is not intended to be checked directly, use {@link CKEDITOR.dom.elementPath#blockLimit} instead.
*
* Some examples of editor behaviors that are impacted by block limits:
*
* * Enter key never split a block-limit element;
* * Style application is constraint by the block limit of the current selection.
* * Pasted html will be inserted into the block limit of the current selection.
*
* **Note:** As an exception `<li>` is not considered as a block limit, as it's generally used as a text block.
*/
$blockLimit: { article: 1, aside: 1, audio: 1, body: 1, caption: 1, details: 1, dir: 1, div: 1, dl: 1,
fieldset: 1, figcaption: 1, figure: 1, footer: 1, form: 1, header: 1, hgroup: 1, menu: 1, nav: 1,
ol: 1, section: 1, table: 1, td: 1, th: 1, tr: 1, ul: 1, video: 1 },
/**
* List of elements that contain character data.
*/
$cdata: { script: 1, style: 1 },
/**
* List of elements that are accepted as inline editing hosts.
*/
$editable: { address: 1, article: 1, aside: 1, blockquote: 1, body: 1, details: 1, div: 1, fieldset: 1,
figcaption: 1, footer: 1, form: 1, h1: 1, h2: 1, h3: 1, h4: 1, h5: 1, h6: 1, header: 1, hgroup: 1,
nav: 1, p: 1, pre: 1, section: 1 },
/**
* List of empty (self-closing) elements, like `<br>` or `<img>`.
*/
$empty: { area: 1, base: 1, basefont: 1, br: 1, col: 1, command: 1, dialog: 1, embed: 1, hr: 1, img: 1,
input: 1, isindex: 1, keygen: 1, link: 1, meta: 1, param: 1, source: 1, track: 1, wbr: 1 },
/**
* List of inline (`<span>` like) elements.
*/
$inline: P,
/**
* List of list root elements.
*/
$list: { dl: 1, ol: 1, ul: 1 },
/**
* List of list item elements, like `<li>` or `<dd>`.
*/
$listItem: { dd: 1, dt: 1, li: 1 },
/**
* List of elements which may live outside body.
*/
$nonBodyContent: X( { body: 1, head: 1, html: 1 }, dtd.head ),
/**
* Elements that accept text nodes, but are not possible to edit into the browser.
*/
$nonEditable: { applet: 1, audio: 1, button: 1, embed: 1, iframe: 1, map: 1, object: 1, option: 1,
param: 1, script: 1, textarea: 1, video: 1 },
/**
* Elements that are considered objects, therefore selected as a whole in the editor.
*/
$object: { applet: 1, audio: 1, button: 1, hr: 1, iframe: 1, img: 1, input: 1, object: 1, select: 1,
table: 1, textarea: 1, video: 1 },
/**
* List of elements that can be ignored if empty, like `<b>` or `<span>`.
*/
$removeEmpty: { abbr: 1, acronym: 1, b: 1, bdi: 1, bdo: 1, big: 1, cite: 1, code: 1, del: 1, dfn: 1,
em: 1, font: 1, i: 1, ins: 1, label: 1, kbd: 1, mark: 1, meter: 1, output: 1, q: 1, ruby: 1, s: 1,
samp: 1, small: 1, span: 1, strike: 1, strong: 1, sub: 1, sup: 1, time: 1, tt: 1, u: 1, 'var': 1 },
/**
* List of elements that have tabindex set to zero by default.
*/
$tabIndex: { a: 1, area: 1, button: 1, input: 1, object: 1, select: 1, textarea: 1 },
/**
* List of elements used inside the `<table>` element, like `<tbody>` or `<td>`.
*/
$tableContent: { caption: 1, col: 1, colgroup: 1, tbody: 1, td: 1, tfoot: 1, th: 1, thead: 1, tr: 1 },
/**
* List of "transparent" elements. See [W3C's definition of "transparent" element](http://dev.w3.org/html5/markup/terminology.html#transparent).
*/
$transparent: { a: 1, audio: 1, canvas: 1, del: 1, ins: 1, map: 1, noscript: 1, object: 1, video: 1 },
/**
* List of elements that are not to exist standalone that must live under it's parent element.
*/
$intermediate: { caption: 1, colgroup: 1, dd: 1, dt: 1, figcaption: 1, legend: 1, li: 1, optgroup: 1,
option: 1, rp: 1, rt: 1, summary: 1, tbody: 1, td: 1, tfoot: 1, th: 1, thead: 1, tr: 1 }
} );
return dtd;
} )();
// PACKAGER_RENAME( CKEDITOR.dtd )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
if ( !CKEDITOR.editor ) {
// Documented at editor.js.
CKEDITOR.editor = function() {
// Push this editor to the pending list. It'll be processed later once
// the full editor code is loaded.
CKEDITOR._.pending.push( [ this, arguments ] );
// Call the CKEDITOR.event constructor to initialize this instance.
CKEDITOR.event.call( this );
};
// Both fire and fireOnce will always pass this editor instance as the
// "editor" param in CKEDITOR.event.fire. So, we override it to do that
// automaticaly.
CKEDITOR.editor.prototype.fire = function( eventName, data ) {
if ( eventName in { instanceReady: 1, loaded: 1 } )
this[ eventName ] = true;
return CKEDITOR.event.prototype.fire.call( this, eventName, data, this );
};
CKEDITOR.editor.prototype.fireOnce = function( eventName, data ) {
if ( eventName in { instanceReady: 1, loaded: 1 } )
this[ eventName ] = true;
return CKEDITOR.event.prototype.fireOnce.call( this, eventName, data, this );
};
// "Inherit" (copy actually) from CKEDITOR.event.
CKEDITOR.event.implementOn( CKEDITOR.editor.prototype );
}

View File

@@ -0,0 +1,359 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.env} object which contains
* environment and browser information.
*/
if ( !CKEDITOR.env ) {
/**
* Environment and browser information.
*
* @class CKEDITOR.env
* @singleton
*/
CKEDITOR.env = ( function() {
var agent = navigator.userAgent.toLowerCase();
var opera = window.opera;
var env = {
/**
* Indicates that CKEditor is running in Internet Explorer.
*
* if ( CKEDITOR.env.ie )
* alert( 'I\'m running in IE!' );
*
* @property {Boolean}
*/
ie: ( agent.indexOf( 'trident/' ) > -1 ),
/**
* Indicates that CKEditor is running in Opera.
*
* if ( CKEDITOR.env.opera )
* alert( 'I\'m running in Opera!' );
*
* @property {Boolean}
*/
opera: ( !!opera && opera.version ),
/**
* Indicates that CKEditor is running in a WebKit-based browser, like Safari.
*
* if ( CKEDITOR.env.webkit )
* alert( 'I\'m running in a WebKit browser!' );
*
* @property {Boolean}
*/
webkit: ( agent.indexOf( ' applewebkit/' ) > -1 ),
/**
* Indicates that CKEditor is running in Adobe AIR.
*
* if ( CKEDITOR.env.air )
* alert( 'I\'m on AIR!' );
*
* @property {Boolean}
*/
air: ( agent.indexOf( ' adobeair/' ) > -1 ),
/**
* Indicates that CKEditor is running on Macintosh.
*
* if ( CKEDITOR.env.mac )
* alert( 'I love apples!'' );
*
* @property {Boolean}
*/
mac: ( agent.indexOf( 'macintosh' ) > -1 ),
/**
* Indicates that CKEditor is running in a Quirks Mode environment.
*
* if ( CKEDITOR.env.quirks )
* alert( 'Nooooo!' );
*
* Internet Explorer 10 introduced the _New Quirks Mode_, which is similar to the _Quirks Mode_
* implemented in other modern browsers and defined in the HTML5 specification. It can be handled
* as the Standards mode, so the value of this property will be set to `false`.
*
* The _Internet Explorer 5 Quirks_ mode which is still available in Internet Explorer 10+
* sets this value to `true` and {@link #version} to `7`.
*
* Read more: [IEBlog](http://blogs.msdn.com/b/ie/archive/2011/12/14/interoperable-html5-quirks-mode-in-ie10.aspx)
*
* @property {Boolean}
*/
quirks: ( document.compatMode == 'BackCompat' && ( !document.documentMode || document.documentMode < 10 ) ),
/**
* Indicates that CKEditor is running in a mobile environemnt.
*
* if ( CKEDITOR.env.mobile )
* alert( 'I\'m running with CKEditor today!' );
*
* @property {Boolean}
*/
mobile: ( agent.indexOf( 'mobile' ) > -1 ),
/**
* Indicates that CKEditor is running on Apple iPhone/iPad/iPod devices.
*
* if ( CKEDITOR.env.iOS )
* alert( 'I like little apples!' );
*
* @property {Boolean}
*/
iOS: /(ipad|iphone|ipod)/.test( agent ),
/**
* Indicates that the browser has a custom domain enabled. This has
* been set with `document.domain`.
*
* if ( CKEDITOR.env.isCustomDomain() )
* alert( 'I\'m in a custom domain!' );
*
* @returns {Boolean} `true` if a custom domain is enabled.
* @deprecated
*/
isCustomDomain: function() {
if ( !this.ie )
return false;
var domain = document.domain,
hostname = window.location.hostname;
return domain != hostname && domain != ( '[' + hostname + ']' ); // IPv6 IP support (#5434)
},
/**
* Indicates that the page is running under an encrypted connection.
*
* if ( CKEDITOR.env.secure )
* alert( 'I\'m on SSL!' );
*
* @returns {Boolean} `true` if the page has an encrypted connection.
*/
secure: location.protocol == 'https:'
};
/**
* Indicates that CKEditor is running in a Gecko-based browser, like
* Firefox.
*
* if ( CKEDITOR.env.gecko )
* alert( 'I\'m riding a gecko!' );
*
* @property {Boolean}
*/
env.gecko = ( navigator.product == 'Gecko' && !env.webkit && !env.opera && !env.ie );
/**
* Indicates that CKEditor is running in Chrome.
*
* if ( CKEDITOR.env.chrome )
* alert( 'I\'m running in Chrome!' );
*
* @property {Boolean} chrome
*/
/**
* Indicates that CKEditor is running in Safari (including the mobile version).
*
* if ( CKEDITOR.env.safari )
* alert( 'I\'m on Safari!' );
*
* @property {Boolean} safari
*/
if ( env.webkit ) {
if ( agent.indexOf( 'chrome' ) > -1 )
env.chrome = true;
else
env.safari = true;
}
var version = 0;
// Internet Explorer 6.0+
if ( env.ie ) {
// We use env.version for feature detection, so set it properly.
if ( env.quirks || !document.documentMode )
version = parseFloat( agent.match( /msie (\d+)/ )[ 1 ] );
else
version = document.documentMode;
// Deprecated features available just for backwards compatibility.
env.ie9Compat = version == 9;
env.ie8Compat = version == 8;
env.ie7Compat = version == 7;
env.ie6Compat = version < 7 || env.quirks;
/**
* Indicates that CKEditor is running in an IE6-like environment, which
* includes IE6 itself as well as IE7, IE8 and IE9 in Quirks Mode.
*
* @deprecated
* @property {Boolean} ie6Compat
*/
/**
* Indicates that CKEditor is running in an IE7-like environment, which
* includes IE7 itself and IE8's IE7 Document Mode.
*
* @deprecated
* @property {Boolean} ie7Compat
*/
/**
* Indicates that CKEditor is running in Internet Explorer 8 on
* Standards Mode.
*
* @deprecated
* @property {Boolean} ie8Compat
*/
/**
* Indicates that CKEditor is running in Internet Explorer 9 on
* Standards Mode.
*
* @deprecated
* @property {Boolean} ie9Compat
*/
}
// Gecko.
if ( env.gecko ) {
var geckoRelease = agent.match( /rv:([\d\.]+)/ );
if ( geckoRelease ) {
geckoRelease = geckoRelease[ 1 ].split( '.' );
version = geckoRelease[ 0 ] * 10000 + ( geckoRelease[ 1 ] || 0 ) * 100 + ( geckoRelease[ 2 ] || 0 ) * 1;
}
}
// Opera 9.50+
if ( env.opera )
version = parseFloat( opera.version() );
// Adobe AIR 1.0+
// Checked before Safari because AIR have the WebKit rich text editor
// features from Safari 3.0.4, but the version reported is 420.
if ( env.air )
version = parseFloat( agent.match( / adobeair\/(\d+)/ )[ 1 ] );
// WebKit 522+ (Safari 3+)
if ( env.webkit )
version = parseFloat( agent.match( / applewebkit\/(\d+)/ )[ 1 ] );
/**
* Contains the browser version.
*
* For Gecko-based browsers (like Firefox) it contains the revision
* number with first three parts concatenated with a padding zero
* (e.g. for revision 1.9.0.2 we have 10900).
*
* For WebKit-based browsers (like Safari and Chrome) it contains the
* WebKit build version (e.g. 522).
*
* For IE browsers, it matches the "Document Mode".
*
* if ( CKEDITOR.env.ie && CKEDITOR.env.version <= 6 )
* alert( 'Ouch!' );
*
* @property {Number}
*/
env.version = version;
/**
* Indicates that CKEditor is running in a compatible browser.
*
* if ( CKEDITOR.env.isCompatible )
* alert( 'Your browser is pretty cool!' );
*
* @property {Boolean}
*/
env.isCompatible =
// White list of mobile devices that CKEditor supports.
env.iOS && version >= 534 ||
!env.mobile && (
( env.ie && version > 6 ) ||
( env.gecko && version >= 10801 ) ||
( env.opera && version >= 9.5 ) ||
( env.air && version >= 1 ) ||
( env.webkit && version >= 522 ) ||
false
);
/**
* Indicates that CKEditor is running in the HiDPI environment.
*
* if ( CKEDITOR.env.hidpi )
* alert( 'You are using a screen with high pixel density.' );
*
* @property {Boolean}
*/
env.hidpi = window.devicePixelRatio >= 2;
/**
* Indicates that CKEditor is running in a browser which uses a bogus
* `<br>` filler in order to correctly display caret in empty blocks.
*
* @since 4.3
* @property {Boolean}
*/
env.needsBrFiller = env.gecko || env.webkit || ( env.ie && version > 10 );
/**
* Indicates that CKEditor is running in a browser which needs a
* non-breaking space filler in order to correctly display caret in empty blocks.
*
* @since 4.3
* @property {Boolean}
*/
env.needsNbspFiller = env.ie && version < 11;
/**
* A CSS class that denotes the browser where CKEditor runs and is appended
* to the HTML element that contains the editor. It makes it easier to apply
* browser-specific styles to editor instances.
*
* myDiv.className = CKEDITOR.env.cssClass;
*
* @property {String}
*/
env.cssClass = 'cke_browser_' + ( env.ie ? 'ie' : env.gecko ? 'gecko' : env.opera ? 'opera' : env.webkit ? 'webkit' : 'unknown' );
if ( env.quirks )
env.cssClass += ' cke_browser_quirks';
if ( env.ie ) {
env.cssClass += ' cke_browser_ie' + ( env.quirks || env.version < 7 ? '6' : env.version );
if ( env.quirks )
env.cssClass += ' cke_browser_iequirks';
}
if ( env.gecko ) {
if ( version < 10900 )
env.cssClass += ' cke_browser_gecko18';
else if ( version <= 11000 )
env.cssClass += ' cke_browser_gecko19';
}
if ( env.air )
env.cssClass += ' cke_browser_air';
if ( env.iOS )
env.cssClass += ' cke_browser_ios';
if ( env.hidpi )
env.cssClass += ' cke_hidpi';
return env;
} )();
}
// PACKAGER_RENAME( CKEDITOR.env )
// PACKAGER_RENAME( CKEDITOR.env.ie )

View File

@@ -0,0 +1,387 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.event} class, which serves as the
* base for classes and objects that require event handling features.
*/
if ( !CKEDITOR.event ) {
/**
* Creates an event class instance. This constructor is rearely used, being
* the {@link #implementOn} function used in class prototypes directly
* instead.
*
* This is a base class for classes and objects that require event
* handling features.
*
* Do not confuse this class with {@link CKEDITOR.dom.event} which is
* instead used for DOM events. The CKEDITOR.event class implements the
* internal event system used by the CKEditor to fire API related events.
*
* @class
* @constructor Creates an event class instance.
*/
CKEDITOR.event = function() {};
/**
* Implements the {@link CKEDITOR.event} features in an object.
*
* var myObject = { message: 'Example' };
* CKEDITOR.event.implementOn( myObject );
*
* myObject.on( 'testEvent', function() {
* alert( this.message );
* } );
* myObject.fire( 'testEvent' ); // 'Example'
*
* @static
* @param {Object} targetObject The object into which implement the features.
*/
CKEDITOR.event.implementOn = function( targetObject ) {
var eventProto = CKEDITOR.event.prototype;
for ( var prop in eventProto ) {
if ( targetObject[ prop ] == undefined )
targetObject[ prop ] = eventProto[ prop ];
}
};
CKEDITOR.event.prototype = ( function() {
// Returns the private events object for a given object.
var getPrivate = function( obj ) {
var _ = ( obj.getPrivate && obj.getPrivate() ) || obj._ || ( obj._ = {} );
return _.events || ( _.events = {} );
};
var eventEntry = function( eventName ) {
this.name = eventName;
this.listeners = [];
};
eventEntry.prototype = {
// Get the listener index for a specified function.
// Returns -1 if not found.
getListenerIndex: function( listenerFunction ) {
for ( var i = 0, listeners = this.listeners; i < listeners.length; i++ ) {
if ( listeners[ i ].fn == listenerFunction )
return i;
}
return -1;
}
};
// Retrieve the event entry on the event host (create it if needed).
function getEntry( name ) {
// Get the event entry (create it if needed).
var events = getPrivate( this );
return events[ name ] || ( events[ name ] = new eventEntry( name ) );
}
return {
/**
* Predefine some intrinsic properties on a specific event name.
*
* @param {String} name The event name
* @param meta
* @param [meta.errorProof=false] Whether the event firing should catch error thrown from a per listener call.
*/
define: function( name, meta ) {
var entry = getEntry.call( this, name );
CKEDITOR.tools.extend( entry, meta, true );
},
/**
* Registers a listener to a specific event in the current object.
*
* someObject.on( 'someEvent', function() {
* alert( this == someObject ); // true
* } );
*
* someObject.on( 'someEvent', function() {
* alert( this == anotherObject ); // true
* }, anotherObject );
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.listenerData ); // 'Example'
* }, null, 'Example' );
*
* someObject.on( 'someEvent', function() { ... } ); // 2nd called
* someObject.on( 'someEvent', function() { ... }, null, null, 100 ); // 3rd called
* someObject.on( 'someEvent', function() { ... }, null, null, 1 ); // 1st called
*
* @param {String} eventName The event name to which listen.
* @param {Function} listenerFunction The function listening to the
* event. A single {@link CKEDITOR.eventInfo} object instanced
* is passed to this function containing all the event data.
* @param {Object} [scopeObj] The object used to scope the listener
* call (the `this` object). If omitted, the current object is used.
* @param {Object} [listenerData] Data to be sent as the
* {@link CKEDITOR.eventInfo#listenerData} when calling the
* listener.
* @param {Number} [priority=10] The listener priority. Lower priority
* listeners are called first. Listeners with the same priority
* value are called in registration order.
* @returns {Object} An object containing the `removeListener`
* function, which can be used to remove the listener at any time.
*/
on: function( eventName, listenerFunction, scopeObj, listenerData, priority ) {
// Create the function to be fired for this listener.
function listenerFirer( editor, publisherData, stopFn, cancelFn ) {
var ev = {
name: eventName,
sender: this,
editor: editor,
data: publisherData,
listenerData: listenerData,
stop: stopFn,
cancel: cancelFn,
removeListener: removeListener
};
var ret = listenerFunction.call( scopeObj, ev );
return ret === false ? false : ev.data;
}
function removeListener() {
me.removeListener( eventName, listenerFunction );
}
var event = getEntry.call( this, eventName );
if ( event.getListenerIndex( listenerFunction ) < 0 ) {
// Get the listeners.
var listeners = event.listeners;
// Fill the scope.
if ( !scopeObj )
scopeObj = this;
// Default the priority, if needed.
if ( isNaN( priority ) )
priority = 10;
var me = this;
listenerFirer.fn = listenerFunction;
listenerFirer.priority = priority;
// Search for the right position for this new listener, based on its
// priority.
for ( var i = listeners.length - 1; i >= 0; i-- ) {
// Find the item which should be before the new one.
if ( listeners[ i ].priority <= priority ) {
// Insert the listener in the array.
listeners.splice( i + 1, 0, listenerFirer );
return { removeListener: removeListener };
}
}
// If no position has been found (or zero length), put it in
// the front of list.
listeners.unshift( listenerFirer );
}
return { removeListener: removeListener };
},
/**
* Similiar with {@link #on} but the listener will be called only once upon the next event firing.
*
* @see CKEDITOR.event#on
*/
once: function() {
var fn = arguments[ 1 ];
arguments[ 1 ] = function( evt ) {
evt.removeListener();
return fn.apply( this, arguments );
};
return this.on.apply( this, arguments );
},
/**
* @static
* @property {Boolean} useCapture
* @todo
*/
/**
* Register event handler under the capturing stage on supported target.
*/
capture: function() {
CKEDITOR.event.useCapture = 1;
var retval = this.on.apply( this, arguments );
CKEDITOR.event.useCapture = 0;
return retval;
},
/**
* Fires an specific event in the object. All registered listeners are
* called at this point.
*
* someObject.on( 'someEvent', function() { ... } );
* someObject.on( 'someEvent', function() { ... } );
* someObject.fire( 'someEvent' ); // Both listeners are called.
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.data ); // 'Example'
* } );
* someObject.fire( 'someEvent', 'Example' );
*
* @method
* @param {String} eventName The event name to fire.
* @param {Object} [data] Data to be sent as the
* {@link CKEDITOR.eventInfo#data} when calling the listeners.
* @param {CKEDITOR.editor} [editor] The editor instance to send as the
* {@link CKEDITOR.eventInfo#editor} when calling the listener.
* @returns {Boolean/Object} A boolean indicating that the event is to be
* canceled, or data returned by one of the listeners.
*/
fire: ( function() {
// Create the function that marks the event as stopped.
var stopped = 0;
var stopEvent = function() {
stopped = 1;
};
// Create the function that marks the event as canceled.
var canceled = 0;
var cancelEvent = function() {
canceled = 1;
};
return function( eventName, data, editor ) {
// Get the event entry.
var event = getPrivate( this )[ eventName ];
// Save the previous stopped and cancelled states. We may
// be nesting fire() calls.
var previousStopped = stopped,
previousCancelled = canceled;
// Reset the stopped and canceled flags.
stopped = canceled = 0;
if ( event ) {
var listeners = event.listeners;
if ( listeners.length ) {
// As some listeners may remove themselves from the
// event, the original array length is dinamic. So,
// let's make a copy of all listeners, so we are
// sure we'll call all of them.
listeners = listeners.slice( 0 );
var retData;
// Loop through all listeners.
for ( var i = 0; i < listeners.length; i++ ) {
// Call the listener, passing the event data.
if ( event.errorProof ) {
try {
retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
} catch ( er ) {}
} else
retData = listeners[ i ].call( this, editor, data, stopEvent, cancelEvent );
if ( retData === false )
canceled = 1;
else if ( typeof retData != 'undefined' )
data = retData;
// No further calls is stopped or canceled.
if ( stopped || canceled )
break;
}
}
}
var ret = canceled ? false : ( typeof data == 'undefined' ? true : data );
// Restore the previous stopped and canceled states.
stopped = previousStopped;
canceled = previousCancelled;
return ret;
};
} )(),
/**
* Fires an specific event in the object, releasing all listeners
* registered to that event. The same listeners are not called again on
* successive calls of it or of {@link #fire}.
*
* someObject.on( 'someEvent', function() { ... } );
* someObject.fire( 'someEvent' ); // Above listener called.
* someObject.fireOnce( 'someEvent' ); // Above listener called.
* someObject.fire( 'someEvent' ); // No listeners called.
*
* @param {String} eventName The event name to fire.
* @param {Object} [data] Data to be sent as the
* {@link CKEDITOR.eventInfo#data} when calling the listeners.
* @param {CKEDITOR.editor} [editor] The editor instance to send as the
* {@link CKEDITOR.eventInfo#editor} when calling the listener.
* @returns {Boolean/Object} A booloan indicating that the event is to be
* canceled, or data returned by one of the listeners.
*/
fireOnce: function( eventName, data, editor ) {
var ret = this.fire( eventName, data, editor );
delete getPrivate( this )[ eventName ];
return ret;
},
/**
* Unregisters a listener function from being called at the specified
* event. No errors are thrown if the listener has not been registered previously.
*
* var myListener = function() { ... };
* someObject.on( 'someEvent', myListener );
* someObject.fire( 'someEvent' ); // myListener called.
* someObject.removeListener( 'someEvent', myListener );
* someObject.fire( 'someEvent' ); // myListener not called.
*
* @param {String} eventName The event name.
* @param {Function} listenerFunction The listener function to unregister.
*/
removeListener: function( eventName, listenerFunction ) {
// Get the event entry.
var event = getPrivate( this )[ eventName ];
if ( event ) {
var index = event.getListenerIndex( listenerFunction );
if ( index >= 0 )
event.listeners.splice( index, 1 );
}
},
/**
* Remove all existing listeners on this object, for cleanup purpose.
*/
removeAllListeners: function() {
var events = getPrivate( this );
for ( var i in events )
delete events[ i ];
},
/**
* Checks if there is any listener registered to a given event.
*
* var myListener = function() { ... };
* someObject.on( 'someEvent', myListener );
* alert( someObject.hasListeners( 'someEvent' ) ); // true
* alert( someObject.hasListeners( 'noEvent' ) ); // false
*
* @param {String} eventName The event name.
* @returns {Boolean}
*/
hasListeners: function( eventName ) {
var event = getPrivate( this )[ eventName ];
return ( event && event.listeners.length > 0 );
}
};
} )();
}

View File

@@ -0,0 +1,115 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the "virtual" {@link CKEDITOR.eventInfo} class, which
* contains the defintions of the event object passed to event listeners.
* This file is for documentation purposes only.
*/
/**
* Virtual class that illustrates the features of the event object to be
* passed to event listeners by a {@link CKEDITOR.event} based object.
*
* This class is not really part of the API.
*
* @class CKEDITOR.eventInfo
* @abstract
*/
/**
* The event name.
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.name ); // 'someEvent'
* } );
* someObject.fire( 'someEvent' );
*
* @property {String} name
*/
/**
* The object that publishes (sends) the event.
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.sender == someObject ); // true
* } );
* someObject.fire( 'someEvent' );
*
* @property sender
*/
/**
* The editor instance that holds the sender. May be the same as sender. May be
* null if the sender is not part of an editor instance, like a component
* running in standalone mode.
*
* myButton.on( 'someEvent', function( event ) {
* alert( event.editor == myEditor ); // true
* } );
* myButton.fire( 'someEvent', null, myEditor );
*
* @property {CKEDITOR.editor} editor
*/
/**
* Any kind of additional data. Its format and usage is event dependent.
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.data ); // 'Example'
* } );
* someObject.fire( 'someEvent', 'Example' );
*
* @property data
*/
/**
* Any extra data appended during the listener registration.
*
* someObject.on( 'someEvent', function( event ) {
* alert( event.listenerData ); // 'Example'
* }, null, 'Example' );
*
* @property listenerData
*/
/**
* Indicates that no further listeners are to be called.
*
* someObject.on( 'someEvent', function( event ) {
* event.stop();
* } );
* someObject.on( 'someEvent', function( event ) {
* // This one will not be called.
* } );
* alert( someObject.fire( 'someEvent' ) ); // false
*
* @method stop
*/
/**
* Indicates that the event is to be cancelled (if cancelable).
*
* someObject.on( 'someEvent', function( event ) {
* event.cancel();
* } );
* someObject.on( 'someEvent', function( event ) {
* // This one will not be called.
* } );
* alert( someObject.fire( 'someEvent' ) ); // true
*
* @method cancel
*/
/**
* Removes the current listener.
*
* someObject.on( 'someEvent', function( event ) {
* event.removeListener();
* // Now this function won't be called again by 'someEvent'.
* } );
*
* @method removeListener
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,271 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.focusManager} class, which is used
* to handle the focus on editor instances..
*/
( function() {
/**
* Manages the focus activity in an editor instance. This class is to be
* used mainly by UI elements coders when adding interface elements that need
* to set the focus state of the editor.
*
* var focusManager = new CKEDITOR.focusManager( editor );
* focusManager.focus();
*
* @class
* @constructor Creates a focusManager class instance.
* @param {CKEDITOR.editor} editor The editor instance.
*/
CKEDITOR.focusManager = function( editor ) {
if ( editor.focusManager )
return editor.focusManager;
/**
* Indicates that the editor instance has focus.
*
* alert( CKEDITOR.instances.editor1.focusManager.hasFocus ); // e.g. true
*/
this.hasFocus = false;
/**
* Indicate the currently focused DOM element that makes the editor activated.
*
* @property {CKEDITOR.dom.domObject}
*/
this.currentActive = null;
/**
* Object used to hold private stuff.
*
* @private
*/
this._ = {
editor: editor
};
return this;
};
var SLOT_NAME = 'focusmanager',
SLOT_NAME_LISTENERS = 'focusmanager_handlers';
/**
* Object used to hold private stuff.
*
* @private
* @class
* @singleton
*/
CKEDITOR.focusManager._ = {
/**
* The delay (in milliseconds) to deactivate the editor when UI dom element has lost focus.
*
* @private
* @property {Number} [blurDelay=200]
* @member CKEDITOR.focusManager._
*/
blurDelay: 200
};
CKEDITOR.focusManager.prototype = {
/**
* Indicate this editor instance is activated (due to DOM focus change),
* the `activated` state is a symbolic indicator of an active user
* interaction session.
*
* **Note:** This method will not introduce UI focus
* impact on DOM, it's here to record editor UI focus state internally.
* If you want to make the cursor blink inside of the editable, use
* {@link CKEDITOR.editor#method-focus} instead.
*
* var editor = CKEDITOR.instances.editor1;
* editor.focusManage.focus( editor.editable() );
*
* @param {CKEDITOR.dom.element} [currentActive] The new value of {@link #currentActive} property.
* @member CKEDITOR.focusManager
*/
focus: function( currentActive ) {
if ( this._.timer )
clearTimeout( this._.timer );
if ( currentActive )
this.currentActive = currentActive;
if ( !( this.hasFocus || this._.locked ) ) {
// If another editor has the current focus, we first "blur" it. In
// this way the events happen in a more logical sequence, like:
// "focus 1" > "blur 1" > "focus 2"
// ... instead of:
// "focus 1" > "focus 2" > "blur 1"
var current = CKEDITOR.currentInstance;
current && current.focusManager.blur( 1 );
this.hasFocus = true;
var ct = this._.editor.container;
ct && ct.addClass( 'cke_focus' );
this._.editor.fire( 'focus' );
}
},
/**
* Prevent from changing the focus manager state until next {@link #unlock} is called.
*
* @member CKEDITOR.focusManager
*/
lock: function() {
this._.locked = 1;
},
/**
* Restore the automatic focus management, if {@link #lock} is called.
*
* @member CKEDITOR.focusManager
*/
unlock: function() {
delete this._.locked;
},
/**
* Used to indicate that the editor instance has been deactivated by the specified
* element which has just lost focus.
*
* **Note:** that this functions acts asynchronously with a delay of 100ms to
* avoid temporary deactivation. Use instead the `noDelay` parameter
* to deactivate immediately.
*
* var editor = CKEDITOR.instances.editor1;
* editor.focusManager.blur();
*
* @param {Boolean} [noDelay=false] Deactivate immediately the editor instance synchronously.
* @member CKEDITOR.focusManager
*/
blur: function( noDelay ) {
if ( this._.locked )
return;
function doBlur() {
if ( this.hasFocus ) {
this.hasFocus = false;
var ct = this._.editor.container;
ct && ct.removeClass( 'cke_focus' );
this._.editor.fire( 'blur' );
}
}
if ( this._.timer )
clearTimeout( this._.timer );
var delay = CKEDITOR.focusManager._.blurDelay;
if ( noDelay || !delay )
doBlur.call( this );
else {
this._.timer = CKEDITOR.tools.setTimeout( function() {
delete this._.timer;
doBlur.call( this );
}, delay, this );
}
},
/**
* Register an UI DOM element to the focus manager, which will make the focus manager "hasFocus"
* once input focus is relieved on the element, it's to be used by plugins to expand the jurisdiction of the editor focus.
*
* @param {CKEDITOR.dom.element} element The container (top most) element of one UI part.
* @param {Boolean} isCapture If specified {@link CKEDITOR.event#useCapture} will be used when listening to the focus event.
* @member CKEDITOR.focusManager
*/
add: function( element, isCapture ) {
var fm = element.getCustomData( SLOT_NAME );
if ( !fm || fm != this ) {
// If this element is already taken by another instance, dismiss it first.
fm && fm.remove( element );
var focusEvent = 'focus',
blurEvent = 'blur';
// Bypass the element's internal DOM focus change.
if ( isCapture ) {
// Use "focusin/focusout" events instead of capture phase in IEs,
// which fires synchronously.
if ( CKEDITOR.env.ie ) {
focusEvent = 'focusin';
blurEvent = 'focusout';
} else
CKEDITOR.event.useCapture = 1;
}
var listeners = {
blur: function() {
if ( element.equals( this.currentActive ) )
this.blur();
},
focus: function() {
this.focus( element );
}
};
element.on( focusEvent, listeners.focus, this );
element.on( blurEvent, listeners.blur, this );
if ( isCapture )
CKEDITOR.event.useCapture = 0;
element.setCustomData( SLOT_NAME, this );
element.setCustomData( SLOT_NAME_LISTENERS, listeners );
}
},
/**
* Dismiss an element from the the focus manager delegations added by {@link #add}.
*
* @param {CKEDITOR.dom.element} element The element to be removed from the focusmanager.
* @member CKEDITOR.focusManager
*/
remove: function( element ) {
element.removeCustomData( SLOT_NAME );
var listeners = element.removeCustomData( SLOT_NAME_LISTENERS );
element.removeListener( 'blur', listeners.blur );
element.removeListener( 'focus', listeners.focus );
}
};
} )();
/**
* Fired when the editor instance receives the input focus.
*
* editor.on( 'focus', function( e ) {
* alert( 'The editor named ' + e.editor.name + ' is now focused' );
* } );
*
* @event focus
* @member CKEDITOR.editor
* @param {CKEDITOR.editor} editor The editor instance.
*/
/**
* Fired when the editor instance loses the input focus.
*
* **Note:** This event will **NOT** be triggered when focus is moved internally, e.g. from
* the editable to other part of the editor UI like dialog.
* If you're interested on only the editable focus state listen to the {@link CKEDITOR.editable#event-focus}
* and {@link CKEDITOR.editable#blur} events instead.
*
* editor.on( 'blur', function( e ) {
* alert( 'The editor named ' + e.editor.name + ' lost the focus' );
* } );
*
* @event blur
* @member CKEDITOR.editor
* @param {CKEDITOR.editor} editor The editor instance.
*/

View File

@@ -0,0 +1,989 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
( function() {
/**
* Represents an HTML data processor, which is responsible for translating and
* transforming the editor data on input and output.
*
* @class
* @extends CKEDITOR.dataProcessor
* @constructor Creates an htmlDataProcessor class instance.
* @param {CKEDITOR.editor} editor
*/
CKEDITOR.htmlDataProcessor = function( editor ) {
var dataFilter, htmlFilter,
that = this;
this.editor = editor;
/**
* Data filter used when processing input by {@link #toHtml}.
*
* @property {CKEDITOR.htmlParser.filter}
*/
this.dataFilter = dataFilter = new CKEDITOR.htmlParser.filter();
/**
* HTML filter used when processing output by {@link #toDataFormat}.
*
* @property {CKEDITOR.htmlParser.filter}
*/
this.htmlFilter = htmlFilter = new CKEDITOR.htmlParser.filter();
/**
* The HTML writer used by this data processor to format the output.
*
* @property {CKEDITOR.htmlParser.basicWriter}
*/
this.writer = new CKEDITOR.htmlParser.basicWriter();
dataFilter.addRules( defaultDataFilterRulesEditableOnly );
dataFilter.addRules( defaultDataFilterRulesForAll, { applyToAll: true } );
dataFilter.addRules( createBogusAndFillerRules( editor, 'data' ), { applyToAll: true } );
htmlFilter.addRules( defaultHtmlFilterRulesEditableOnly );
htmlFilter.addRules( defaultHtmlFilterRulesForAll, { applyToAll: true } );
htmlFilter.addRules( createBogusAndFillerRules( editor, 'html' ), { applyToAll: true } );
editor.on( 'toHtml', function( evt ) {
var evtData = evt.data,
data = evtData.dataValue;
// The source data is already HTML, but we need to clean
// it up and apply the filter.
data = protectSource( data, editor );
// Protect content of textareas. (#9995)
// Do this before protecting attributes to avoid breaking:
// <textarea><img src="..." /></textarea>
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 <pre> 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 <pre>.
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && fixBin == 'pre' ) {
fixBin = 'div';
data = '<pre>' + data + '</pre>';
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>|<\/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 <br> 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 <br> is higher.
if ( evt.data.enterMode != CKEDITOR.ENTER_BR )
data = data.replace( /^<br *\/?>/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:<block> - this filter removes any bogus node, then check
// if it's an empty block that requires a filler.
// 2. elements:<br> - 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 <BR> 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; <BR> 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.
// <BR> 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. <p>foo<br /></p>
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. <hr /><br /><hr />
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 &nbsp; at the end of blocks, which are actually placeholders.
// Safari transforms the &nbsp; to \xa0. (#4172)
var tailNbspRegex = /(?:&nbsp;|\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 <embed> is child of a <object>, 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 <?xml:namespace> 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:
//
// <img data-x="&lt;a href=&quot;X&quot;" />
//
// then the "non-greedy match" returns:
//
// 'href' => '&quot;X&quot;' // It's wrong! Href is not an attribute of <img>.
//
// while greedy match returns:
//
// 'data-x' => '&lt;a href=&quot;X&quot;'
//
// 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 </style> or </textarea>.
var protectElementsRegex = /(?:<style(?=[ >])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,
protectTextareaRegex = /(<textarea(?=[ >])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,
encodedElementsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi;
var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,
unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi;
var protectSelfClosingRegex = /<cke:(param|embed)([^>]*?)\/?>(?!\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( '<textarea' ) === 0 )
match = tag + unprotectRealComments( content ).replace( /</g, '&lt;' ).replace( />/g, '&gt;' ) + '</textarea>';
return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>';
} );
}
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, '<cke:$1$2></cke:$1>' );
}
function protectPreFormatted( html ) {
return CKEDITOR.env.opera ? html : html.replace( /(<pre\b[^>]*>)(\r\n|\n)/g, '$1$2$2' );
}
function protectRealComments( html ) {
return html.replace( /<!--(?!{cke_protected})[\s\S]+?-->/g, function( match ) {
return '<!--' + protectedSourceMarker +
'{C}' +
encodeURIComponent( match ).replace( /--/g, '%2D%2D' ) +
'-->';
} );
}
function unprotectRealComments( html ) {
return html.replace( /<!--\{cke_protected\}\{C\}([\s\S]+?)-->/g, function( match, data ) {
return decodeURIComponent( data );
} );
}
function unprotectSource( html, editor ) {
var store = editor._.dataStore;
return html.replace( /<!--\{cke_protected\}([\s\S]+?)-->/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.
( /<script[\s\S]*?<\/script>/gi ),
// <noscript> tags (get lost in IE and messed up in FF).
/<noscript[\s\S]*?<\/noscript>/gi
].concat( protectRegexes );
// First of any other protection, we must protect all comments
// to avoid loosing them (of course, IE related).
// Note that we use a different tag for comments, as we need to
// transform them when applying filters.
data = data.replace( ( /<!--[\s\S]*?-->/g ), function( match ) {
return '<!--{cke_tempcomment}' + ( protectedHtml.push( match ) - 1 ) + '-->';
} );
for ( var i = 0; i < regexes.length; i++ ) {
data = data.replace( regexes[ i ], function( match ) {
match = match.replace( tempRegex, // There could be protected source inside another one. (#3869).
function( $, isComment, id ) {
return protectedHtml[ id ];
} );
// Avoid protecting over protected, e.g. /\{.*?\}/
return ( /cke_temp(comment)?/ ).test( match ) ? match : '<!--{cke_temp}' + ( protectedHtml.push( match ) - 1 ) + '-->';
} );
}
data = data.replace( tempRegex, function( $, isComment, id ) {
return '<!--' + protectedSourceMarker +
( isComment ? '{C}' : '' ) +
encodeURIComponent( protectedHtml[ id ] ).replace( /--/g, '%2D%2D' ) +
'-->';
} );
// Different protection pattern is used for those that
// live in attributes to avoid from being HTML encoded.
return data.replace( /(['"]).*?\1/g, function( match ) {
return match.replace( /<!--\{cke_protected\}([\s\S]+?)-->/g, function( match, data ) {
store[ store.id ] = decodeURIComponent( data );
return '{cke_protected_' + ( store.id++ ) + '}';
} );
} );
}
} )();
/**
* Whether a filler text (non-breaking space entity &mdash; `&nbsp;`) will be
* inserted into empty block elements in HTML output.
* This is used to render block elements properly with `line-height`.
* When a function is specified instead, it will be passed a {@link CKEDITOR.htmlParser.element}
* to decide whether adding the filler text by expecting a Boolean return value.
*
* config.fillEmptyBlocks = false; // Prevent filler nodes in all empty blocks.
*
* // Prevent filler node only in float cleaners.
* config.fillEmptyBlocks = function( element ) {
* if ( element.attributes[ 'class' ].indexOf( 'clear-both' ) != -1 )
* return false;
* };
*
* @since 3.5
* @cfg {Boolean} [fillEmptyBlocks=true]
* @member CKEDITOR.config
*/
/**
* This event is fired by the {@link CKEDITOR.htmlDataProcessor} when input HTML
* is to be purified by the {@link CKEDITOR.htmlDataProcessor#toHtml} method.
*
* By adding listeners with different priorities it is possible
* to process input HTML on different stages:
*
* * 1-4: Data is available in the original string format.
* * 5: Data is initially filtered with regexp patterns and parsed to
* {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}.
* * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#dataFilter}
* is not applied yet.
* * 6: Data is filtered with the {CKEDITOR.filter content filter}.
* * 10: Data is processed with {@link CKEDITOR.htmlDataProcessor#dataFilter}.
* * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#dataFilter}
* has already been applied.
* * 15: Data is written back to an HTML string.
* * 15-*: Data is available in an HTML string.
*
* For example to be able to process parsed, but not yet filtered data add listener this way:
*
* editor.on( 'toHtml', function( evt) {
* evt.data.dataValue; // -> CKEDITOR.htmlParser.fragment instance
* }, null, null, 7 );
*
* @since 4.1
* @event toHtml
* @member CKEDITOR.editor
* @param {CKEDITOR.editor} editor This editor instance.
* @param data
* @param {String/CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} data.dataValue Input data to be purified.
* @param {String} data.context See {@link CKEDITOR.htmlDataProcessor#toHtml} The `context` argument.
* @param {Boolean} data.fixForBody See {@link CKEDITOR.htmlDataProcessor#toHtml} The `fixForBody` argument.
* @param {Boolean} data.dontFilter See {@link CKEDITOR.htmlDataProcessor#toHtml} The `dontFilter` argument.
* @param {Boolean} data.filter See {@link CKEDITOR.htmlDataProcessor#toHtml} The `filter` argument.
* @param {Boolean} data.enterMode See {@link CKEDITOR.htmlDataProcessor#toHtml} The `enterMode` argument.
*/
/**
* This event is fired when {@link CKEDITOR.htmlDataProcessor} is converting
* internal HTML to output data HTML.
*
* By adding listeners with different priorities it is possible
* to process input HTML on different stages:
*
* * 1-4: Data is available in the original string format.
* * 5: Data is initially filtered with regexp patterns and parsed to
* {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}.
* * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#htmlFilter}
* is not applied yet.
* * 10: Data is filtered with {@link CKEDITOR.htmlDataProcessor#htmlFilter}.
* * 11: Data is filtered with the {CKEDITOR.filter content filter} (on output the content filter makes
* only transformations, without filtering).
* * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#htmlFilter}
* has already been applied.
* * 15: Data is written back to an HTML string.
* * 15-*: Data is available in an HTML string.
*
* For example to be able to process parsed and already processed data add listener this way:
*
* editor.on( 'toDataFormat', function( evt) {
* evt.data.dataValue; // -> CKEDITOR.htmlParser.fragment instance
* }, null, null, 12 );
*
* @since 4.1
* @event toDataFormat
* @member CKEDITOR.editor
* @param {CKEDITOR.editor} editor This editor instance.
* @param data
* @param {String/CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} data.dataValue Output data to be prepared.
* @param {String} data.context See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `context` argument.
* @param {Boolean} data.filter See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `filter` argument.
* @param {Boolean} data.enterMode See {@link CKEDITOR.htmlDataProcessor#toDataFormat} The `enterMode` argument.
*/

View File

@@ -0,0 +1,207 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* Provides an "event like" system to parse strings of HTML data.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onTagOpen = function( tagName, attributes, selfClosing ) {
* alert( tagName );
* };
* parser.parse( '<p>Some <b>text</b>.</p>' ); // Alerts 'p', 'b'.
*
* @class
* @constructor Creates a htmlParser class instance.
*/
CKEDITOR.htmlParser = function() {
this._ = {
htmlPartsRegex: new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:"[^"]*")|(?:\'[^\']*\')|[^"\'>])*)\\/?>))', 'g' )
};
};
( function() {
var attribsRegex = /([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,
emptyAttribs = { checked: 1, compact: 1, declare: 1, defer: 1, disabled: 1, ismap: 1, multiple: 1, nohref: 1, noresize: 1, noshade: 1, nowrap: 1, readonly: 1, selected: 1 };
CKEDITOR.htmlParser.prototype = {
/**
* Function to be fired when a tag opener is found. This function
* should be overriden when using this class.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onTagOpen = function( tagName, attributes, selfClosing ) {
* alert( tagName ); // e.g. 'b'
* } );
* parser.parse( '<!-- Example --><b>Hello</b>' );
*
* @param {String} tagName The tag name. The name is guarantted to be lowercased.
* @param {Object} attributes An object containing all tag attributes. Each
* property in this object represent and attribute name and its value is the attribute value.
* @param {Boolean} selfClosing `true` if the tag closes itself, false if the tag doesn't.
*/
onTagOpen: function() {},
/**
* Function to be fired when a tag closer is found. This function
* should be overriden when using this class.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onTagClose = function( tagName ) {
* alert( tagName ); // 'b'
* } );
* parser.parse( '<!-- Example --><b>Hello</b>' );
*
* @param {String} tagName The tag name. The name is guarantted to be lowercased.
*/
onTagClose: function() {},
/**
* Function to be fired when text is found. This function
* should be overriden when using this class.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onText = function( text ) {
* alert( text ); // 'Hello'
* } );
* parser.parse( '<!-- Example --><b>Hello</b>' );
*
* @param {String} text The text found.
*/
onText: function() {},
/**
* Function to be fired when CDATA section is found. This function
* should be overriden when using this class.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onCDATA = function( cdata ) {
* alert( cdata ); // 'var hello;'
* } );
* parser.parse( '<script>var hello;</script>' );
*
* @param {String} cdata The CDATA been found.
*/
onCDATA: function() {},
/**
* Function to be fired when a commend is found. This function
* should be overriden when using this class.
*
* var parser = new CKEDITOR.htmlParser();
* parser.onComment = function( comment ) {
* alert( comment ); // ' Example '
* } );
* parser.parse( '<!-- Example --><b>Hello</b>' );
*
* @param {String} comment The comment text.
*/
onComment: function() {},
/**
* Parses text, looking for HTML tokens, like tag openers or closers,
* or comments. This function fires the onTagOpen, onTagClose, onText
* and onComment function during its execution.
*
* var parser = new CKEDITOR.htmlParser();
* // The onTagOpen, onTagClose, onText and onComment should be overriden
* // at this point.
* parser.parse( '<!-- Example --><b>Hello</b>' );
*
* @param {String} html The HTML to be parsed.
*/
parse: function( html ) {
var parts, tagName,
nextIndex = 0,
cdata; // The collected data inside a CDATA section.
while ( ( parts = this._.htmlPartsRegex.exec( html ) ) ) {
var tagIndex = parts.index;
if ( tagIndex > nextIndex ) {
var text = html.substring( nextIndex, tagIndex );
if ( cdata )
cdata.push( text );
else
this.onText( text );
}
nextIndex = this._.htmlPartsRegex.lastIndex;
/*
"parts" is an array with the following items:
0 : The entire match for opening/closing tags and comments.
1 : Group filled with the tag name for closing tags.
2 : Group filled with the comment text.
3 : Group filled with the tag name for opening tags.
4 : Group filled with the attributes part of opening tags.
*/
// Closing tag
if ( ( tagName = parts[ 1 ] ) ) {
tagName = tagName.toLowerCase();
if ( cdata && CKEDITOR.dtd.$cdata[ tagName ] ) {
// Send the CDATA data.
this.onCDATA( cdata.join( '' ) );
cdata = null;
}
if ( !cdata ) {
this.onTagClose( tagName );
continue;
}
}
// If CDATA is enabled, just save the raw match.
if ( cdata ) {
cdata.push( parts[ 0 ] );
continue;
}
// Opening tag
if ( ( tagName = parts[ 3 ] ) ) {
tagName = tagName.toLowerCase();
// There are some tag names that can break things, so let's
// simply ignore them when parsing. (#5224)
if ( /="/.test( tagName ) )
continue;
var attribs = {},
attribMatch,
attribsPart = parts[ 4 ],
selfClosing = !!( attribsPart && attribsPart.charAt( attribsPart.length - 1 ) == '/' );
if ( attribsPart ) {
while ( ( attribMatch = attribsRegex.exec( attribsPart ) ) ) {
var attName = attribMatch[ 1 ].toLowerCase(),
attValue = attribMatch[ 2 ] || attribMatch[ 3 ] || attribMatch[ 4 ] || '';
if ( !attValue && emptyAttribs[ attName ] )
attribs[ attName ] = attName;
else
attribs[ attName ] = CKEDITOR.tools.htmlDecodeAttr( attValue );
}
}
this.onTagOpen( tagName, attribs, selfClosing );
// Open CDATA mode when finding the appropriate tags.
if ( !cdata && CKEDITOR.dtd.$cdata[ tagName ] )
cdata = [];
continue;
}
// Comment
if ( ( tagName = parts[ 2 ] ) )
this.onComment( tagName );
}
if ( html.length > nextIndex )
this.onText( html.substring( nextIndex, html.length ) );
}
};
} )();

View File

@@ -0,0 +1,152 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* TODO
*
* @class
* @todo
*/
CKEDITOR.htmlParser.basicWriter = CKEDITOR.tools.createClass( {
/**
* Creates a basicWriter class instance.
*
* @constructor
*/
$: function() {
this._ = {
output: []
};
},
proto: {
/**
* Writes the tag opening part for a opener tag.
*
* // Writes '<p'.
* writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } );
*
* @param {String} tagName The element name for this tag.
* @param {Object} attributes The attributes defined for this tag. The
* attributes could be used to inspect the tag.
*/
openTag: function( tagName, attributes ) {
this._.output.push( '<', tagName );
},
/**
* Writes the tag closing part for a opener tag.
*
* // Writes '>'.
* writer.openTagClose( 'p', false );
*
* // Writes ' />'.
* writer.openTagClose( 'br', true );
*
* @param {String} tagName The element name for this tag.
* @param {Boolean} isSelfClose Indicates that this is a self-closing tag,
* like `<br>` or `<img>`.
*/
openTagClose: function( tagName, isSelfClose ) {
if ( isSelfClose )
this._.output.push( ' />' );
else
this._.output.push( '>' );
},
/**
* Writes an attribute. This function should be called after opening the
* tag with {@link #openTagClose}.
*
* // Writes ' class="MyClass"'.
* writer.attribute( 'class', 'MyClass' );
*
* @param {String} attName The attribute name.
* @param {String} attValue The attribute value.
*/
attribute: function( attName, attValue ) {
// Browsers don't always escape special character in attribute values. (#4683, #4719).
if ( typeof attValue == 'string' )
attValue = CKEDITOR.tools.htmlEncodeAttr( attValue );
this._.output.push( ' ', attName, '="', attValue, '"' );
},
/**
* Writes a closer tag.
*
* // Writes '</p>'.
* writer.closeTag( 'p' );
*
* @param {String} tagName The element name for this tag.
*/
closeTag: function( tagName ) {
this._.output.push( '</', tagName, '>' );
},
/**
* Writes text.
*
* // Writes 'Hello Word'.
* writer.text( 'Hello Word' );
*
* @param {String} text The text value.
*/
text: function( text ) {
this._.output.push( text );
},
/**
* Writes a comment.
*
* // Writes '<!-- My comment -->'.
* writer.comment( ' My comment ' );
*
* @param {String} comment The comment text.
*/
comment: function( comment ) {
this._.output.push( '<!--', comment, '-->' );
},
/**
* Writes any kind of data to the ouput.
*
* writer.write( 'This is an <b>example</b>.' );
*
* @param {String} data
*/
write: function( data ) {
this._.output.push( data );
},
/**
* Empties the current output buffer.
*
* writer.reset();
*/
reset: function() {
this._.output = [];
this._.indent = false;
},
/**
* Empties the current output buffer.
*
* var html = writer.getHtml();
*
* @param {Boolean} reset Indicates that the {@link #reset} method is to
* be automatically called after retrieving the HTML.
* @returns {String} The HTML written to the writer so far.
*/
getHtml: function( reset ) {
var html = this._.output.join( '' );
if ( reset )
this.reset();
return html;
}
}
} );

View File

@@ -0,0 +1,48 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
( function() {
/**
* A lightweight representation of HTML CDATA.
*
* @class
* @extends CKEDITOR.htmlParser.node
* @constructor Creates a cdata class instance.
* @param {String} value The CDATA section value.
*/
CKEDITOR.htmlParser.cdata = function( value ) {
/**
* The CDATA value.
*
* @property {String}
*/
this.value = value;
};
CKEDITOR.htmlParser.cdata.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
/**
* CDATA has the same type as {@link CKEDITOR.htmlParser.text} This is
* a constant value set to {@link CKEDITOR#NODE_TEXT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_TEXT]
*/
type: CKEDITOR.NODE_TEXT,
filter: function() {},
/**
* Writes the CDATA with no special manipulations.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
*/
writeHtml: function( writer ) {
writer.write( this.value );
}
} );
} )();

View File

@@ -0,0 +1,80 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
/**
* A lightweight representation of an HTML comment.
*
* @class
* @extends CKEDITOR.htmlParser.node
* @constructor Creates a comment class instance.
* @param {String} value The comment text value.
*/
CKEDITOR.htmlParser.comment = function( value ) {
/**
* The comment text.
*
* @property {String}
*/
this.value = value;
/** @private */
this._ = {
isBlockLike: false
};
};
CKEDITOR.htmlParser.comment.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_COMMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_COMMENT]
*/
type: CKEDITOR.NODE_COMMENT,
/**
* Filter this comment with given filter.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.filter} filter
* @returns {Boolean} Method returns `false` when this comment has
* been removed or replaced with other node. This is an information for
* {@link CKEDITOR.htmlParser.element#filterChildren} that it has
* to repeat filter on current position in parent's children array.
*/
filter: function( filter, context ) {
var comment = this.value;
if ( !( comment = filter.onComment( context, comment, this ) ) ) {
this.remove();
return false;
}
if ( typeof comment != 'string' ) {
this.replaceWith( comment );
return false;
}
this.value = comment;
return true;
},
/**
* Writes the HTML representation of this comment to a CKEDITOR.htmlWriter.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
* **Note:** it's unsafe to filter offline (not appended) node.
*/
writeHtml: function( writer, filter ) {
if ( filter )
this.filter( filter );
writer.comment( this.value );
}
} );

View File

@@ -0,0 +1,519 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
/**
* A lightweight representation of an HTML element.
*
* @class
* @extends CKEDITOR.htmlParser.node
* @constructor Creates an element class instance.
* @param {String} name The element name.
* @param {Object} attributes And object holding all attributes defined for
* this element.
*/
CKEDITOR.htmlParser.element = function( name, attributes ) {
/**
* The element name.
*
* @property {String}
*/
this.name = name;
/**
* Holds the attributes defined for this element.
*
* @property {Object}
*/
this.attributes = attributes || {};
/**
* The nodes that are direct children of this element.
*/
this.children = [];
// Reveal the real semantic of our internal custom tag name (#6639),
// when resolving whether it's block like.
var realName = name || '',
prefixed = realName.match( /^cke:(.*)/ );
prefixed && ( realName = prefixed[ 1 ] );
var isBlockLike = !!( CKEDITOR.dtd.$nonBodyContent[ realName ] || CKEDITOR.dtd.$block[ realName ] || CKEDITOR.dtd.$listItem[ realName ] || CKEDITOR.dtd.$tableContent[ realName ] || CKEDITOR.dtd.$nonEditable[ realName ] || realName == 'br' );
this.isEmpty = !!CKEDITOR.dtd.$empty[ name ];
this.isUnknown = !CKEDITOR.dtd[ name ];
/** @private */
this._ = {
isBlockLike: isBlockLike,
hasInlineStarted: this.isEmpty || !isBlockLike
};
};
/**
* Object presentation of CSS style declaration text.
*
* @class
* @constructor Creates a cssStyle class instance.
* @param {CKEDITOR.htmlParser.element/String} elementOrStyleText
* A html parser element or the inline style text.
*/
CKEDITOR.htmlParser.cssStyle = function() {
var styleText,
arg = arguments[ 0 ],
rules = {};
styleText = arg instanceof CKEDITOR.htmlParser.element ? arg.attributes.style : arg;
// html-encoded quote might be introduced by 'font-family'
// from MS-Word which confused the following regexp. e.g.
//'font-family: &quot;Lucida, Console&quot;'
// TODO reuse CSS methods from tools.
( styleText || '' ).replace( /&quot;/g, '"' ).replace( /\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) {
name == 'font-family' && ( value = value.replace( /["']/g, '' ) );
rules[ name.toLowerCase() ] = value;
} );
return {
rules: rules,
/**
* Apply the styles onto the specified element or object.
*
* @param {CKEDITOR.htmlParser.element/CKEDITOR.dom.element/Object} obj
*/
populate: function( obj ) {
var style = this.toString();
if ( style )
obj instanceof CKEDITOR.dom.element ? obj.setAttribute( 'style', style ) : obj instanceof CKEDITOR.htmlParser.element ? obj.attributes.style = style : obj.style = style;
},
/**
* Serialize CSS style declaration to string.
*
* @returns {String}
*/
toString: function() {
var output = [];
for ( var i in rules )
rules[ i ] && output.push( i, ':', rules[ i ], ';' );
return output.join( '' );
}
};
};
/** @class CKEDITOR.htmlParser.element */
( function() {
// Used to sort attribute entries in an array, where the first element of
// each object is the attribute name.
var sortAttribs = function( a, b ) {
a = a[ 0 ];
b = b[ 0 ];
return a < b ? -1 : a > b ? 1 : 0;
},
fragProto = CKEDITOR.htmlParser.fragment.prototype;
CKEDITOR.htmlParser.element.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_ELEMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_ELEMENT]
*/
type: CKEDITOR.NODE_ELEMENT,
/**
* Adds a node to the element children list.
*
* @method
* @param {CKEDITOR.htmlParser.node} node The node to be added.
* @param {Number} [index] From where the insertion happens.
*/
add: fragProto.add,
/**
* Clone this element.
*
* @returns {CKEDITOR.htmlParser.element} The element clone.
*/
clone: function() {
return new CKEDITOR.htmlParser.element( this.name, this.attributes );
},
/**
* Filter this element and its children with given filter.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.filter} filter
* @returns {Boolean} Method returns `false` when this element has
* been removed or replaced with other. This is an information for
* {@link #filterChildren} that it has to repeat filter on current
* position in parent's children array.
*/
filter: function( filter, context ) {
var element = this,
originalName, name;
context = element.getFilterContext( context );
// Do not process elements with data-cke-processor attribute set to off.
if ( context.off )
return true;
// Filtering if it's the root node.
if ( !element.parent )
filter.onRoot( context, element );
while ( true ) {
originalName = element.name;
if ( !( name = filter.onElementName( context, originalName ) ) ) {
this.remove();
return false;
}
element.name = name;
if ( !( element = filter.onElement( context, element ) ) ) {
this.remove();
return false;
}
// New element has been returned - replace current one
// and process it (stop processing this and return false, what
// means that element has been removed).
if ( element !== this ) {
this.replaceWith( element );
return false;
}
// If name has been changed - continue loop, so in next iteration
// filters for new name will be applied to this element.
// If name hasn't been changed - stop.
if ( element.name == originalName )
break;
// If element has been replaced with something of a
// different type, then make the replacement filter itself.
if ( element.type != CKEDITOR.NODE_ELEMENT ) {
this.replaceWith( element );
return false;
}
// This indicate that the element has been dropped by
// filter but not the children.
if ( !element.name ) {
this.replaceWithChildren();
return false;
}
}
var attributes = element.attributes,
a, value, newAttrName;
for ( a in attributes ) {
newAttrName = a;
value = attributes[ a ];
// Loop until name isn't modified.
// A little bit senseless, but IE would do that anyway
// because it iterates with for-in loop even over properties
// created during its run.
while ( true ) {
if ( !( newAttrName = filter.onAttributeName( context, a ) ) ) {
delete attributes[ a ];
break;
} else if ( newAttrName != a ) {
delete attributes[ a ];
a = newAttrName;
continue;
} else
break;
}
if ( newAttrName ) {
if ( ( value = filter.onAttribute( context, element, newAttrName, value ) ) === false )
delete attributes[ newAttrName ];
else
attributes[ newAttrName ] = value;
}
}
if ( !element.isEmpty )
this.filterChildren( filter, false, context );
return true;
},
/**
* Filter this element's children with given filter.
*
* Element's children may only be filtered once by one
* instance of filter.
*
* @method filterChildren
* @param {CKEDITOR.htmlParser.filter} filter
*/
filterChildren: fragProto.filterChildren,
/**
* Writes the element HTML to a CKEDITOR.htmlWriter.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
* **Note:** it's unsafe to filter offline (not appended) node.
*/
writeHtml: function( writer, filter ) {
if ( filter )
this.filter( filter );
var name = this.name,
attribsArray = [],
attributes = this.attributes,
attrName,
attr, i, l;
// Open element tag.
writer.openTag( name, attributes );
// Copy all attributes to an array.
for ( attrName in attributes )
attribsArray.push( [ attrName, attributes[ attrName ] ] );
// Sort the attributes by name.
if ( writer.sortAttributes )
attribsArray.sort( sortAttribs );
// Send the attributes.
for ( i = 0, l = attribsArray.length; i < l; i++ ) {
attr = attribsArray[ i ];
writer.attribute( attr[ 0 ], attr[ 1 ] );
}
// Close the tag.
writer.openTagClose( name, this.isEmpty );
this.writeChildrenHtml( writer );
// Close the element.
if ( !this.isEmpty )
writer.closeTag( name );
},
/**
* Send children of this element to the writer.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter]
*/
writeChildrenHtml: fragProto.writeChildrenHtml,
/**
* Replace this element with its children.
*
* @since 4.1
*/
replaceWithChildren: function() {
var children = this.children;
for ( var i = children.length; i; )
children[ --i ].insertAfter( this );
this.remove();
},
/**
* Execute callback on each node (of given type) in this element.
*
* // Create <p> element with foo<b>bar</b>bom as its content.
* var elP = CKEDITOR.htmlParser.fragment.fromHtml( 'foo<b>bar</b>bom', 'p' );
* elP.forEach( function( node ) {
* console.log( node );
* } );
* // Will log:
* // 1. document fragment,
* // 2. <p> element,
* // 3. "foo" text node,
* // 4. <b> element,
* // 5. "bar" text node,
* // 6. "bom" text node.
*
* @since 4.1
* @param {Function} callback Function to be executed on every node.
* **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
* @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
* @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
* @param {Boolean} [skipRoot] Don't execute `callback` on this element.
*/
forEach: fragProto.forEach,
/**
* Gets this element's first child. If `condition` is given returns
* first child which satisfies that condition.
*
* @since 4.3
* @param {String/Object/Function} condition Name of a child, hash of names or validator function.
* @returns {CKEDITOR.htmlParser.node}
*/
getFirst: function( condition ) {
if ( !condition )
return this.children.length ? this.children[ 0 ] : null;
if ( typeof condition != 'function' )
condition = nameCondition( condition );
for ( var i = 0, l = this.children.length; i < l; ++i ) {
if ( condition( this.children[ i ] ) )
return this.children[ i ];
}
return null;
},
/**
* Gets this element's inner HTML.
*
* @since 4.3
* @returns {String}
*/
getHtml: function() {
var writer = new CKEDITOR.htmlParser.basicWriter();
this.writeChildrenHtml( writer );
return writer.getHtml();
},
/**
* Sets this element's inner HTML.
*
* @since 4.3
* @param {String} html
*/
setHtml: function( html ) {
var children = this.children = CKEDITOR.htmlParser.fragment.fromHtml( html ).children;
for ( var i = 0, l = children.length; i < l; ++i )
children[ i ].parent = this;
},
/**
* Gets this element's outer HTML.
*
* @since 4.3
* @returns {String}
*/
getOuterHtml: function() {
var writer = new CKEDITOR.htmlParser.basicWriter();
this.writeHtml( writer );
return writer.getHtml();
},
/**
* Splits this element at given index.
*
* @since 4.3
* @param {Number} index Index at which element will be split &ndash; `0` means beginning,
* `1` after first child node, etc.
* @returns {CKEDITOR.htmlParser.element} New element, following this one.
*/
split: function( index ) {
var cloneChildren = this.children.splice( index, this.children.length - index ),
clone = this.clone();
for ( var i = 0; i < cloneChildren.length; ++i )
cloneChildren[ i ].parent = clone;
clone.children = cloneChildren;
if ( cloneChildren[ 0 ] )
cloneChildren[ 0 ].previous = null;
if ( index > 0 )
this.children[ index - 1 ].next = null;
this.parent.add( clone, this.getIndex() + 1 );
return clone;
},
/**
* Removes class name from classes list.
*
* @since 4.3
* @param {String} className The class name to be removed.
*/
removeClass: function( className ) {
var classes = this.attributes[ 'class' ],
index;
if ( !classes )
return;
// We can safely assume that className won't break regexp.
// http://stackoverflow.com/questions/448981/what-characters-are-valid-in-css-class-names
classes = CKEDITOR.tools.trim( classes.replace( new RegExp( '(?:\\s+|^)' + className + '(?:\\s+|$)' ), ' ' ) );
if ( classes )
this.attributes[ 'class' ] = classes;
else
delete this.attributes[ 'class' ];
},
/**
* Checkes whether this element has a class name.
*
* @since 4.3
* @param {String} className The class name to be checked.
* @returns {Boolean} Whether this element has a `className`.
*/
hasClass: function( className ) {
var classes = this.attributes[ 'class' ];
if ( !classes )
return false;
return ( new RegExp( '(?:^|\\s)' + className + '(?=\\s|$)' ) ).test( classes );
},
getFilterContext: function( ctx ) {
var changes = [];
if ( !ctx ) {
ctx = {
off: false,
nonEditable: false,
nestedEditable: false
};
}
if ( !ctx.off && this.attributes[ 'data-cke-processor' ] == 'off' )
changes.push( 'off', true );
if ( !ctx.nonEditable && this.attributes.contenteditable == 'false' )
changes.push( 'nonEditable', true );
// A context to be given nestedEditable must be nonEditable first (by inheritance) (#11372).
// Never set "nestedEditable" context for a body. If body is processed then it indicates
// a fullPage editor and there's no slightest change of nesting such editable (#11504).
else if ( this.name != 'body' && !ctx.nestedEditable && this.attributes.contenteditable == 'true' )
changes.push( 'nestedEditable', true );
if ( changes.length ) {
ctx = CKEDITOR.tools.copy( ctx );
for ( var i = 0; i < changes.length; i += 2 )
ctx[ changes[ i ] ] = changes[ i + 1 ];
}
return ctx;
}
}, true );
function nameCondition( condition ) {
return function( el ) {
return el.type == CKEDITOR.NODE_ELEMENT &&
( typeof condition == 'string' ? el.name == condition : el.name in condition );
};
}
} )();

View File

@@ -0,0 +1,407 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
( function() {
/**
* Filter is a configurable tool for transforming and filtering {@link CKEDITOR.htmlParser.node nodes}.
* It is mainly used during data processing phase which is done not on real DOM nodes,
* but on their simplified form represented by {@link CKEDITOR.htmlParser.node} class and its subclasses.
*
* var filter = new CKEDITOR.htmlParser.filter( {
* text: function( value ) {
* return '@' + value + '@';
* },
* elements: {
* p: function( element ) {
* element.attributes.foo = '1';
* }
* }
* } );
*
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>Foo<b>bar!</b></p>' ),
* writer = new CKEDITOR.htmlParser.basicWriter();
* filter.applyTo( fragment );
* fragment.writeHtml( writer );
* writer.getHtml(); // '<p foo="1">@Foo@<b>@bar!@</b></p>'
*
* @class
*/
CKEDITOR.htmlParser.filter = CKEDITOR.tools.createClass( {
/**
* @constructor Creates a filter class instance.
* @param {CKEDITOR.htmlParser.filterRulesDefinition} [rules]
*/
$: function( rules ) {
/**
* ID of filter instance, which is used to mark elements
* to which this filter has been already applied.
*
* @property {Number} id
* @readonly
*/
this.id = CKEDITOR.tools.getNextNumber();
/**
* Rules for element names.
*
* @property {CKEDITOR.htmlParser.filterRulesGroup}
* @readonly
*/
this.elementNameRules = new filterRulesGroup();
/**
* Rules for attribute names.
*
* @property {CKEDITOR.htmlParser.filterRulesGroup}
* @readonly
*/
this.attributeNameRules = new filterRulesGroup();
/**
* Hash of elementName => {@link CKEDITOR.htmlParser.filterRulesGroup rules for elements}.
*
* @readonly
*/
this.elementsRules = {};
/**
* Hash of attributeName => {@link CKEDITOR.htmlParser.filterRulesGroup rules for attributes}.
*
* @readonly
*/
this.attributesRules = {};
/**
* Rules for text nodes.
*
* @property {CKEDITOR.htmlParser.filterRulesGroup}
* @readonly
*/
this.textRules = new filterRulesGroup();
/**
* Rules for comment nodes.
*
* @property {CKEDITOR.htmlParser.filterRulesGroup}
* @readonly
*/
this.commentRules = new filterRulesGroup();
/**
* Rules for a root node.
*
* @property {CKEDITOR.htmlParser.filterRulesGroup}
* @readonly
*/
this.rootRules = new filterRulesGroup();
if ( rules )
this.addRules( rules, 10 );
},
proto: {
/**
* Add rules to this filter.
*
* @param {CKEDITOR.htmlParser.filterRulesDefinition} rules Object containing filter rules.
* @param {Object/Number} [options] Object containing rules' options or a priority
* (for a backward compatibility with CKEditor versions up to 4.2.x).
* @param {Number} [options.priority=10] The priority of a rule.
* @param {Boolean} [options.applyToAll=false] Whether to apply rule to non-editable
* elements and their descendants too.
*/
addRules: function( rules, options ) {
var priority;
// Backward compatibility.
if ( typeof options == 'number' )
priority = options;
// New version - try reading from options.
else if ( options && ( 'priority' in options ) )
priority = options.priority;
// Defaults.
if ( typeof priority != 'number' )
priority = 10;
if ( typeof options != 'object' )
options = {};
// Add the elementNames.
if ( rules.elementNames )
this.elementNameRules.addMany( rules.elementNames, priority, options );
// Add the attributeNames.
if ( rules.attributeNames )
this.attributeNameRules.addMany( rules.attributeNames, priority, options );
// Add the elements.
if ( rules.elements )
addNamedRules( this.elementsRules, rules.elements, priority, options );
// Add the attributes.
if ( rules.attributes )
addNamedRules( this.attributesRules, rules.attributes, priority, options );
// Add the text.
if ( rules.text )
this.textRules.add( rules.text, priority, options );
// Add the comment.
if ( rules.comment )
this.commentRules.add( rules.comment, priority, options );
// Add root node rules.
if ( rules.root )
this.rootRules.add( rules.root, priority, options );
},
/**
* Apply this filter to given node.
*
* @param {CKEDITOR.htmlParser.node} node The node to be filtered.
*/
applyTo: function( node ) {
node.filter( this );
},
onElementName: function( context, name ) {
return this.elementNameRules.execOnName( context, name );
},
onAttributeName: function( context, name ) {
return this.attributeNameRules.execOnName( context, name );
},
onText: function( context, text ) {
return this.textRules.exec( context, text );
},
onComment: function( context, commentText, comment ) {
return this.commentRules.exec( context, commentText, comment );
},
onRoot: function( context, element ) {
return this.rootRules.exec( context, element );
},
onElement: function( context, element ) {
// We must apply filters set to the specific element name as
// well as those set to the generic ^/$ name. So, add both to an
// array and process them in a small loop.
var rulesGroups = [ this.elementsRules[ '^' ], this.elementsRules[ element.name ], this.elementsRules.$ ],
rulesGroup, ret;
for ( var i = 0; i < 3; i++ ) {
rulesGroup = rulesGroups[ i ];
if ( rulesGroup ) {
ret = rulesGroup.exec( context, element, this );
if ( ret === false )
return null;
if ( ret && ret != element )
return this.onNode( context, ret );
// The non-root element has been dismissed by one of the filters.
if ( element.parent && !element.name )
break;
}
}
return element;
},
onNode: function( context, node ) {
var type = node.type;
return type == CKEDITOR.NODE_ELEMENT ? this.onElement( context, node ) :
type == CKEDITOR.NODE_TEXT ? new CKEDITOR.htmlParser.text( this.onText( context, node.value ) ) :
type == CKEDITOR.NODE_COMMENT ? new CKEDITOR.htmlParser.comment( this.onComment( context, node.value ) ) : null;
},
onAttribute: function( context, element, name, value ) {
var rulesGroup = this.attributesRules[ name ];
if ( rulesGroup )
return rulesGroup.exec( context, value, element, this );
return value;
}
}
} );
/**
* Class grouping filter rules for one subject (like element or attribute names).
*
* @class CKEDITOR.htmlParser.filterRulesGroup
*/
function filterRulesGroup() {
/**
* Array of objects containing rule, priority and options.
*
* @property {Object[]}
* @readonly
*/
this.rules = [];
}
CKEDITOR.htmlParser.filterRulesGroup = filterRulesGroup;
filterRulesGroup.prototype = {
/**
* Adds specified rule to this group.
*
* @param {Function/Array} rule Function for function based rule or [ pattern, replacement ] array for
* rule applicable to names.
* @param {Number} priority
* @param options
*/
add: function( rule, priority, options ) {
this.rules.splice( this.findIndex( priority ), 0, {
value: rule,
priority: priority,
options: options
} );
},
/**
* Adds specified rules to this group.
*
* @param {Array} rules Array of rules - see {@link #add}.
* @param {Number} priority
* @param options
*/
addMany: function( rules, priority, options ) {
var args = [ this.findIndex( priority ), 0 ];
for ( var i = 0, len = rules.length; i < len; i++ ) {
args.push( {
value: rules[ i ],
priority: priority,
options: options
} );
}
this.rules.splice.apply( this.rules, args );
},
/**
* Finds an index at which rule with given priority should be inserted.
*
* @param {Number} priority
* @returns {Number} Index.
*/
findIndex: function( priority ) {
var rules = this.rules,
len = rules.length,
i = len - 1;
// Search from the end, because usually rules will be added with default priority, so
// we will be able to stop loop quickly.
while ( i >= 0 && priority < rules[ i ].priority )
i--;
return i + 1;
},
/**
* Executes this rules group on given value. Applicable only if function based rules were added.
*
* All arguments passed to this function will be forwarded to rules' functions.
*
* @param {CKEDITOR.htmlParser.node/CKEDITOR.htmlParser.fragment/String} currentValue The value to be filtered.
* @returns {CKEDITOR.htmlParser.node/CKEDITOR.htmlParser.fragment/String} Filtered value.
*/
exec: function( context, currentValue ) {
var isNode = currentValue instanceof CKEDITOR.htmlParser.node || currentValue instanceof CKEDITOR.htmlParser.fragment,
// Splice '1' to remove context, which we don't want to pass to filter rules.
args = Array.prototype.slice.call( arguments, 1 ),
rules = this.rules,
len = rules.length,
orgType, orgName, ret, i, rule;
for ( i = 0; i < len; i++ ) {
// Backup the node info before filtering.
if ( isNode ) {
orgType = currentValue.type;
orgName = currentValue.name;
}
rule = rules[ i ];
if ( isRuleApplicable( context, rule ) ) {
ret = rule.value.apply( null, args );
if ( ret === false )
return ret;
// We're filtering node (element/fragment).
// No further filtering if it's not anymore fitable for the subsequent filters.
if ( isNode && ret && ( ret.name != orgName || ret.type != orgType ) )
return ret;
// Update currentValue and corresponding argument in args array.
// Updated values will be used in next for-loop step.
if ( ret != undefined )
args[ 0 ] = currentValue = ret;
// ret == undefined will continue loop as nothing has happened.
}
}
return currentValue;
},
/**
* Executes this rules group on name. Applicable only if filter rules for names were added.
*
* @param {String} currentName The name to be filtered.
* @returns {String} Filtered name.
*/
execOnName: function( context, currentName ) {
var i = 0,
rules = this.rules,
len = rules.length,
rule;
for ( ; currentName && i < len; i++ ) {
rule = rules[ i ];
if ( isRuleApplicable( context, rule ) )
currentName = currentName.replace( rule.value[ 0 ], rule.value[ 1 ] );
}
return currentName;
}
};
function addNamedRules( rulesGroups, newRules, priority, options ) {
var ruleName, rulesGroup;
for ( ruleName in newRules ) {
rulesGroup = rulesGroups[ ruleName ];
if ( !rulesGroup )
rulesGroup = rulesGroups[ ruleName ] = new filterRulesGroup();
rulesGroup.add( newRules[ ruleName ], priority, options );
}
}
function isRuleApplicable( context, rule ) {
if ( context.nonEditable && !rule.options.applyToAll )
return false;
if ( context.nestedEditable && rule.options.excludeNestedEditable )
return false;
return true;
}
} )();
/**
* @class CKEDITOR.htmlParser.filterRulesDefinition
* @abstract
*/

View File

@@ -0,0 +1,631 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
/**
* A lightweight representation of an HTML DOM structure.
*
* @class
* @constructor Creates a fragment class instance.
*/
CKEDITOR.htmlParser.fragment = function() {
/**
* The nodes contained in the root of this fragment.
*
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
* alert( fragment.children.length ); // 2
*/
this.children = [];
/**
* Get the fragment parent. Should always be null.
*
* @property {Object} [=null]
*/
this.parent = null;
/** @private */
this._ = {
isBlockLike: true,
hasInlineStarted: false
};
};
( function() {
// Block-level elements whose internal structure should be respected during
// parser fixing.
var nonBreakingBlocks = CKEDITOR.tools.extend( { table: 1, ul: 1, ol: 1, dl: 1 }, CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl );
var listBlocks = { ol: 1, ul: 1 };
// Dtd of the fragment element, basically it accept anything except for intermediate structure, e.g. orphan <li>.
var rootDtd = CKEDITOR.tools.extend( {}, { html: 1 }, CKEDITOR.dtd.html, CKEDITOR.dtd.body, CKEDITOR.dtd.head, { style: 1, script: 1 } );
function isRemoveEmpty( node ) {
// Keep marked element event if it is empty.
if ( node.attributes[ 'data-cke-survive' ] )
return false;
// Empty link is to be removed when empty but not anchor. (#7894)
return node.name == 'a' && node.attributes.href || CKEDITOR.dtd.$removeEmpty[ node.name ];
}
/**
* Creates a {@link CKEDITOR.htmlParser.fragment} from an HTML string.
*
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<b>Sample</b> Text' );
* alert( fragment.children[ 0 ].name ); // 'b'
* alert( fragment.children[ 1 ].value ); // ' Text'
*
* @static
* @param {String} fragmentHtml The HTML to be parsed, filling the fragment.
* @param {CKEDITOR.htmlParser.element/String} [parent] Optional contextual
* element which makes the content been parsed as the content of this element and fix
* to match it.
* If not provided, then {@link CKEDITOR.htmlParser.fragment} will be used
* as the parent and it will be returned.
* @param {String/Boolean} [fixingBlock] When `parent` is a block limit element,
* and the param is a string value other than `false`, it is to
* avoid having block-less content as the direct children of parent by wrapping
* the content with a block element of the specified tag, e.g.
* when `fixingBlock` specified as `p`, the content `<body><i>foo</i></body>`
* will be fixed into `<body><p><i>foo</i></p></body>`.
* @returns {CKEDITOR.htmlParser.fragment/CKEDITOR.htmlParser.element} The created fragment or passed `parent`.
*/
CKEDITOR.htmlParser.fragment.fromHtml = function( fragmentHtml, parent, fixingBlock ) {
var parser = new CKEDITOR.htmlParser();
var root = parent instanceof CKEDITOR.htmlParser.element ? parent : typeof parent == 'string' ? new CKEDITOR.htmlParser.element( parent ) : new CKEDITOR.htmlParser.fragment();
var pendingInline = [],
pendingBRs = [],
currentNode = root,
// Indicate we're inside a <textarea> element, spaces should be touched differently.
inTextarea = root.name == 'textarea',
// Indicate we're inside a <pre> element, spaces should be touched differently.
inPre = root.name == 'pre';
function checkPending( newTagName ) {
var pendingBRsSent;
if ( pendingInline.length > 0 ) {
for ( var i = 0; i < pendingInline.length; i++ ) {
var pendingElement = pendingInline[ i ],
pendingName = pendingElement.name,
pendingDtd = CKEDITOR.dtd[ pendingName ],
currentDtd = currentNode.name && CKEDITOR.dtd[ currentNode.name ];
if ( ( !currentDtd || currentDtd[ pendingName ] ) && ( !newTagName || !pendingDtd || pendingDtd[ newTagName ] || !CKEDITOR.dtd[ newTagName ] ) ) {
if ( !pendingBRsSent ) {
sendPendingBRs();
pendingBRsSent = 1;
}
// Get a clone for the pending element.
pendingElement = pendingElement.clone();
// Add it to the current node and make it the current,
// so the new element will be added inside of it.
pendingElement.parent = currentNode;
currentNode = pendingElement;
// Remove the pending element (back the index by one
// to properly process the next entry).
pendingInline.splice( i, 1 );
i--;
} else {
// Some element of the same type cannot be nested, flat them,
// e.g. <a href="#">foo<a href="#">bar</a></a>. (#7894)
if ( pendingName == currentNode.name )
addElement( currentNode, currentNode.parent, 1 ), i--;
}
}
}
}
function sendPendingBRs() {
while ( pendingBRs.length )
addElement( pendingBRs.shift(), currentNode );
}
// Rtrim empty spaces on block end boundary. (#3585)
function removeTailWhitespace( element ) {
if ( element._.isBlockLike && element.name != 'pre' && element.name != 'textarea' ) {
var length = element.children.length,
lastChild = element.children[ length - 1 ],
text;
if ( lastChild && lastChild.type == CKEDITOR.NODE_TEXT ) {
if ( !( text = CKEDITOR.tools.rtrim( lastChild.value ) ) )
element.children.length = length - 1;
else
lastChild.value = text;
}
}
}
// Beside of simply append specified element to target, this function also takes
// care of other dirty lifts like forcing block in body, trimming spaces at
// the block boundaries etc.
//
// @param {Element} element The element to be added as the last child of {@link target}.
// @param {Element} target The parent element to relieve the new node.
// @param {Boolean} [moveCurrent=false] Don't change the "currentNode" global unless
// there's a return point node specified on the element, otherwise move current onto {@link target} node.
//
function addElement( element, target, moveCurrent ) {
target = target || currentNode || root;
// Current element might be mangled by fix body below,
// save it for restore later.
var savedCurrent = currentNode;
// Ignore any element that has already been added.
if ( element.previous === undefined ) {
if ( checkAutoParagraphing( target, element ) ) {
// Create a <p> in the fragment.
currentNode = target;
parser.onTagOpen( fixingBlock, {} );
// The new target now is the <p>.
element.returnPoint = target = currentNode;
}
removeTailWhitespace( element );
// Avoid adding empty inline.
if ( !( isRemoveEmpty( element ) && !element.children.length ) )
target.add( element );
if ( element.name == 'pre' )
inPre = false;
if ( element.name == 'textarea' )
inTextarea = false;
}
if ( element.returnPoint ) {
currentNode = element.returnPoint;
delete element.returnPoint;
} else
currentNode = moveCurrent ? target : savedCurrent;
}
// Auto paragraphing should happen when inline content enters the root element.
function checkAutoParagraphing( parent, node ) {
// Check for parent that can contain block.
if ( ( parent == root || parent.name == 'body' ) && fixingBlock &&
( !parent.name || CKEDITOR.dtd[ parent.name ][ fixingBlock ] ) )
{
var name, realName;
if ( node.attributes && ( realName = node.attributes[ 'data-cke-real-element-type' ] ) )
name = realName;
else
name = node.name;
// Text node, inline elements are subjected, except for <script>/<style>.
return name && name in CKEDITOR.dtd.$inline &&
!( name in CKEDITOR.dtd.head ) &&
!node.isOrphan ||
node.type == CKEDITOR.NODE_TEXT;
}
}
// Judge whether two element tag names are likely the siblings from the same
// structural element.
function possiblySibling( tag1, tag2 ) {
if ( tag1 in CKEDITOR.dtd.$listItem || tag1 in CKEDITOR.dtd.$tableContent )
return tag1 == tag2 || tag1 == 'dt' && tag2 == 'dd' || tag1 == 'dd' && tag2 == 'dt';
return false;
}
parser.onTagOpen = function( tagName, attributes, selfClosing, optionalClose ) {
var element = new CKEDITOR.htmlParser.element( tagName, attributes );
// "isEmpty" will be always "false" for unknown elements, so we
// must force it if the parser has identified it as a selfClosing tag.
if ( element.isUnknown && selfClosing )
element.isEmpty = true;
// Check for optional closed elements, including browser quirks and manually opened blocks.
element.isOptionalClose = optionalClose;
// This is a tag to be removed if empty, so do not add it immediately.
if ( isRemoveEmpty( element ) ) {
pendingInline.push( element );
return;
} else if ( tagName == 'pre' )
inPre = true;
else if ( tagName == 'br' && inPre ) {
currentNode.add( new CKEDITOR.htmlParser.text( '\n' ) );
return;
} else if ( tagName == 'textarea' )
inTextarea = true;
if ( tagName == 'br' ) {
pendingBRs.push( element );
return;
}
while ( 1 ) {
var currentName = currentNode.name;
var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd;
// If the element cannot be child of the current element.
if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) {
// Current node doesn't have a close tag, time for a close
// as this element isn't fit in. (#7497)
if ( currentNode.isOptionalClose )
parser.onTagClose( currentName );
// Fixing malformed nested lists by moving it into a previous list item. (#3828)
else if ( tagName in listBlocks && currentName in listBlocks ) {
var children = currentNode.children,
lastChild = children[ children.length - 1 ];
// Establish the list item if it's not existed.
if ( !( lastChild && lastChild.name == 'li' ) )
addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
!element.returnPoint && ( element.returnPoint = currentNode );
currentNode = lastChild;
}
// Establish new list root for orphan list items, but NOT to create
// new list for the following ones, fix them instead. (#6975)
// <dl><dt>foo<dd>bar</dl>
// <ul><li>foo<li>bar</ul>
else if ( tagName in CKEDITOR.dtd.$listItem &&
!possiblySibling( tagName, currentName ) ) {
parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {}, 0, 1 );
}
// We're inside a structural block like table and list, AND the incoming element
// is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,
// and most importantly, return back to here once this element is added,
// e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>
else if ( currentName in nonBreakingBlocks &&
!possiblySibling( tagName, currentName ) ) {
!element.returnPoint && ( element.returnPoint = currentNode );
currentNode = currentNode.parent;
} else {
// The current element is an inline element, which
// need to be continued even after the close, so put
// it in the pending list.
if ( currentName in CKEDITOR.dtd.$inline )
pendingInline.unshift( currentNode );
// The most common case where we just need to close the
// current one and append the new one to the parent.
if ( currentNode.parent )
addElement( currentNode, currentNode.parent, 1 );
// We've tried our best to fix the embarrassment here, while
// this element still doesn't find it's parent, mark it as
// orphan and show our tolerance to it.
else {
element.isOrphan = 1;
break;
}
}
} else
break;
}
checkPending( tagName );
sendPendingBRs();
element.parent = currentNode;
if ( element.isEmpty )
addElement( element );
else
currentNode = element;
};
parser.onTagClose = function( tagName ) {
// Check if there is any pending tag to be closed.
for ( var i = pendingInline.length - 1; i >= 0; i-- ) {
// If found, just remove it from the list.
if ( tagName == pendingInline[ i ].name ) {
pendingInline.splice( i, 1 );
return;
}
}
var pendingAdd = [],
newPendingInline = [],
candidate = currentNode;
while ( candidate != root && candidate.name != tagName ) {
// If this is an inline element, add it to the pending list, if we're
// really closing one of the parents element later, they will continue
// after it.
if ( !candidate._.isBlockLike )
newPendingInline.unshift( candidate );
// This node should be added to it's parent at this point. But,
// it should happen only if the closing tag is really closing
// one of the nodes. So, for now, we just cache it.
pendingAdd.push( candidate );
// Make sure return point is properly restored.
candidate = candidate.returnPoint || candidate.parent;
}
if ( candidate != root ) {
// Add all elements that have been found in the above loop.
for ( i = 0; i < pendingAdd.length; i++ ) {
var node = pendingAdd[ i ];
addElement( node, node.parent );
}
currentNode = candidate;
if ( candidate._.isBlockLike )
sendPendingBRs();
addElement( candidate, candidate.parent );
// The parent should start receiving new nodes now, except if
// addElement changed the currentNode.
if ( candidate == currentNode )
currentNode = currentNode.parent;
pendingInline = pendingInline.concat( newPendingInline );
}
if ( tagName == 'body' )
fixingBlock = false;
};
parser.onText = function( text ) {
// Trim empty spaces at beginning of text contents except <pre> and <textarea>.
if ( ( !currentNode._.hasInlineStarted || pendingBRs.length ) && !inPre && !inTextarea ) {
text = CKEDITOR.tools.ltrim( text );
if ( text.length === 0 )
return;
}
var currentName = currentNode.name,
currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd;
// Fix orphan text in list/table. (#8540) (#8870)
if ( !inTextarea && !currentDtd[ '#' ] && currentName in nonBreakingBlocks ) {
parser.onTagOpen( currentName in listBlocks ? 'li' : currentName == 'dl' ? 'dd' : currentName == 'table' ? 'tr' : currentName == 'tr' ? 'td' : '' );
parser.onText( text );
return;
}
sendPendingBRs();
checkPending();
// Shrinking consequential spaces into one single for all elements
// text contents.
if ( !inPre && !inTextarea )
text = text.replace( /[\t\r\n ]{2,}|[\t\r\n]/g, ' ' );
text = new CKEDITOR.htmlParser.text( text );
if ( checkAutoParagraphing( currentNode, text ) )
this.onTagOpen( fixingBlock, {}, 0, 1 );
currentNode.add( text );
};
parser.onCDATA = function( cdata ) {
currentNode.add( new CKEDITOR.htmlParser.cdata( cdata ) );
};
parser.onComment = function( comment ) {
sendPendingBRs();
checkPending();
currentNode.add( new CKEDITOR.htmlParser.comment( comment ) );
};
// Parse it.
parser.parse( fragmentHtml );
sendPendingBRs();
// Close all pending nodes, make sure return point is properly restored.
while ( currentNode != root )
addElement( currentNode, currentNode.parent, 1 );
removeTailWhitespace( root );
return root;
};
CKEDITOR.htmlParser.fragment.prototype = {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_DOCUMENT_FRAGMENT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_DOCUMENT_FRAGMENT]
*/
type: CKEDITOR.NODE_DOCUMENT_FRAGMENT,
/**
* Adds a node to this fragment.
*
* @param {CKEDITOR.htmlParser.node} node The node to be added.
* @param {Number} [index] From where the insertion happens.
*/
add: function( node, index ) {
isNaN( index ) && ( index = this.children.length );
var previous = index > 0 ? this.children[ index - 1 ] : null;
if ( previous ) {
// If the block to be appended is following text, trim spaces at
// the right of it.
if ( node._.isBlockLike && previous.type == CKEDITOR.NODE_TEXT ) {
previous.value = CKEDITOR.tools.rtrim( previous.value );
// If we have completely cleared the previous node.
if ( previous.value.length === 0 ) {
// Remove it from the list and add the node again.
this.children.pop();
this.add( node );
return;
}
}
previous.next = node;
}
node.previous = previous;
node.parent = this;
this.children.splice( index, 0, node );
if ( !this._.hasInlineStarted )
this._.hasInlineStarted = node.type == CKEDITOR.NODE_TEXT || ( node.type == CKEDITOR.NODE_ELEMENT && !node._.isBlockLike );
},
/**
* Filter this fragment's content with given filter.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.filter} filter
*/
filter: function( filter, context ) {
context = this.getFilterContext( context );
// Apply the root filter.
filter.onRoot( context, this );
this.filterChildren( filter, false, context );
},
/**
* Filter this fragment's children with given filter.
*
* Element's children may only be filtered once by one
* instance of filter.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.filter} filter
* @param {Boolean} [filterRoot] Whether to apply the "root" filter rule specified in the `filter`.
*/
filterChildren: function( filter, filterRoot, context ) {
// If this element's children were already filtered
// by current filter, don't filter them 2nd time.
// This situation may occur when filtering bottom-up
// (filterChildren() called manually in element's filter),
// or in unpredictable edge cases when filter
// is manipulating DOM structure.
if ( this.childrenFilteredBy == filter.id )
return;
context = this.getFilterContext( context );
// Filtering root if enforced.
if ( filterRoot && !this.parent )
filter.onRoot( context, this );
this.childrenFilteredBy = filter.id;
// Don't cache anything, children array may be modified by filter rule.
for ( var i = 0; i < this.children.length; i++ ) {
// Stay in place if filter returned false, what means
// that node has been removed.
if ( this.children[ i ].filter( filter, context ) === false )
i--;
}
},
/**
* Writes the fragment HTML to a {@link CKEDITOR.htmlParser.basicWriter}.
*
* var writer = new CKEDITOR.htmlWriter();
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<P><B>Example' );
* fragment.writeHtml( writer );
* alert( writer.getHtml() ); // '<p><b>Example</b></p>'
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to use when writing the HTML.
*/
writeHtml: function( writer, filter ) {
if ( filter )
this.filter( filter );
this.writeChildrenHtml( writer );
},
/**
* Write and filtering the child nodes of this fragment.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to use when writing the HTML.
* @param {Boolean} [filterRoot] Whether to apply the "root" filter rule specified in the `filter`.
*/
writeChildrenHtml: function( writer, filter, filterRoot ) {
var context = this.getFilterContext();
// Filtering root if enforced.
if ( filterRoot && !this.parent && filter )
filter.onRoot( context, this );
if ( filter )
this.filterChildren( filter, false, context );
for ( var i = 0, children = this.children, l = children.length; i < l; i++ )
children[ i ].writeHtml( writer );
},
/**
* Execute callback on each node (of given type) in this document fragment.
*
* var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>foo<b>bar</b>bom</p>' );
* fragment.forEach( function( node ) {
* console.log( node );
* } );
* // Will log:
* // 1. document fragment,
* // 2. <p> element,
* // 3. "foo" text node,
* // 4. <b> element,
* // 5. "bar" text node,
* // 6. "bom" text node.
*
* @since 4.1
* @param {Function} callback Function to be executed on every node.
* **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
* @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
* @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
* @param {Boolean} [skipRoot] Don't execute `callback` on this fragment.
*/
forEach: function( callback, type, skipRoot ) {
if ( !skipRoot && ( !type || this.type == type ) )
var ret = callback( this );
// Do not filter children if callback returned false.
if ( ret === false )
return;
var children = this.children,
node,
i = 0;
// We do not cache the size, because the list of nodes may be changed by the callback.
for ( ; i < children.length; i++ ) {
node = children[ i ];
if ( node.type == CKEDITOR.NODE_ELEMENT )
node.forEach( callback, type );
else if ( !type || node.type == type )
callback( node );
}
},
getFilterContext: function( context ) {
return context || {};
}
};
} )();

View File

@@ -0,0 +1,150 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
( function() {
/**
* A lightweight representation of HTML node.
*
* @since 4.1
* @class
* @constructor Creates a node class instance.
*/
CKEDITOR.htmlParser.node = function() {};
CKEDITOR.htmlParser.node.prototype = {
/**
* Remove this node from a tree.
*
* @since 4.1
*/
remove: function() {
var children = this.parent.children,
index = CKEDITOR.tools.indexOf( children, this ),
previous = this.previous,
next = this.next;
previous && ( previous.next = next );
next && ( next.previous = previous );
children.splice( index, 1 );
this.parent = null;
},
/**
* Replace this node with given one.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.node} node The node that will replace this one.
*/
replaceWith: function( node ) {
var children = this.parent.children,
index = CKEDITOR.tools.indexOf( children, this ),
previous = node.previous = this.previous,
next = node.next = this.next;
previous && ( previous.next = node );
next && ( next.previous = node );
children[ index ] = node;
node.parent = this.parent;
this.parent = null;
},
/**
* Insert this node after given one.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.node} node The node that will precede this element.
*/
insertAfter: function( node ) {
var children = node.parent.children,
index = CKEDITOR.tools.indexOf( children, node ),
next = node.next;
children.splice( index + 1, 0, this );
this.next = node.next;
this.previous = node;
node.next = this;
next && ( next.previous = this );
this.parent = node.parent;
},
/**
* Insert this node before given one.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.node} node The node that will follow this element.
*/
insertBefore: function( node ) {
var children = node.parent.children,
index = CKEDITOR.tools.indexOf( children, node );
children.splice( index, 0, this );
this.next = node;
this.previous = node.previous;
node.previous && ( node.previous.next = this );
node.previous = this;
this.parent = node.parent;
},
/**
* Gets the closest ancestor element of this element which satisfies given condition
*
* @since 4.3
* @param {String/Object/Function} condition Name of an ancestor, hash of names or validator function.
* @returns {CKEDITOR.htmlParser.element} The closest ancestor which satisfies given condition or `null`.
*/
getAscendant: function( condition ) {
var checkFn =
typeof condition == 'function' ? condition :
typeof condition == 'string' ? function( el ) { return el.name == condition; } :
function( el ) { return el.name in condition; };
var parent = this.parent;
// Parent has to be an element - don't check doc fragment.
while ( parent && parent.type == CKEDITOR.NODE_ELEMENT ) {
if ( checkFn( parent ) )
return parent;
parent = parent.parent;
}
return null;
},
/**
* Wraps this element with given `wrapper`.
*
* @since 4.3
* @param {CKEDITOR.htmlParser.element} wrapper The element which will be this element's new parent.
* @returns {CKEDITOR.htmlParser.element} Wrapper.
*/
wrapWith: function( wrapper ) {
this.replaceWith( wrapper );
wrapper.add( this );
return wrapper;
},
/**
* Gets this node's index in its parent's children array.
*
* @since 4.3
* @returns {Number}
*/
getIndex: function() {
return CKEDITOR.tools.indexOf( this.parent.children, this );
},
getFilterContext: function( context ) {
return context || {};
}
};
} )();

View File

@@ -0,0 +1,70 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
'use strict';
( function() {
/**
* A lightweight representation of HTML text.
*
* @class
* @extends CKEDITOR.htmlParser.node
* @constructor Creates a text class instance.
* @param {String} value The text node value.
*/
CKEDITOR.htmlParser.text = function( value ) {
/**
* The text value.
*
* @property {String}
*/
this.value = value;
/** @private */
this._ = {
isBlockLike: false
};
};
CKEDITOR.htmlParser.text.prototype = CKEDITOR.tools.extend( new CKEDITOR.htmlParser.node(), {
/**
* The node type. This is a constant value set to {@link CKEDITOR#NODE_TEXT}.
*
* @readonly
* @property {Number} [=CKEDITOR.NODE_TEXT]
*/
type: CKEDITOR.NODE_TEXT,
/**
* Filter this text node with given filter.
*
* @since 4.1
* @param {CKEDITOR.htmlParser.filter} filter
* @returns {Boolean} Method returns `false` when this text node has
* been removed. This is an information for {@link CKEDITOR.htmlParser.element#filterChildren}
* that it has to repeat filter on current position in parent's children array.
*/
filter: function( filter, context ) {
if ( !( this.value = filter.onText( context, this.value, this ) ) ) {
this.remove();
return false;
}
},
/**
* Writes the HTML representation of this text to a {CKEDITOR.htmlParser.basicWriter}.
*
* @param {CKEDITOR.htmlParser.basicWriter} writer The writer to which write the HTML.
* @param {CKEDITOR.htmlParser.filter} [filter] The filter to be applied to this node.
* **Note:** it's unsafe to filter offline (not appended) node.
*/
writeHtml: function( writer, filter ) {
if ( filter )
this.filter( filter );
writer.text( this.value );
}
} );
} )();

View File

@@ -0,0 +1,153 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* Controls keystrokes typing in an editor instance.
*
* @class
* @constructor Creates a keystrokeHandler class instance.
* @param {CKEDITOR.editor} editor The editor instance.
*/
CKEDITOR.keystrokeHandler = function( editor ) {
if ( editor.keystrokeHandler )
return editor.keystrokeHandler;
/**
* List of keystrokes associated to commands. Each entry points to the
* command to be executed.
*
* Since CKEditor 4 there's no need to modify this property directly during the runtime.
* Use {@link CKEDITOR.editor#setKeystroke} instead.
*/
this.keystrokes = {};
/**
* List of keystrokes that should be blocked if not defined at
* {@link #keystrokes}. In this way it is possible to block the default
* browser behavior for those keystrokes.
*/
this.blockedKeystrokes = {};
this._ = {
editor: editor
};
return this;
};
( function() {
var cancel;
var onKeyDown = function( event ) {
// The DOM event object is passed by the "data" property.
event = event.data;
var keyCombination = event.getKeystroke();
var command = this.keystrokes[ keyCombination ];
var editor = this._.editor;
cancel = ( editor.fire( 'key', { keyCode: keyCombination } ) === false );
if ( !cancel ) {
if ( command ) {
var data = { from: 'keystrokeHandler' };
cancel = ( editor.execCommand( command, data ) !== false );
}
if ( !cancel )
cancel = !!this.blockedKeystrokes[ keyCombination ];
}
if ( cancel )
event.preventDefault( true );
return !cancel;
};
var onKeyPress = function( event ) {
if ( cancel ) {
cancel = false;
event.data.preventDefault( true );
}
};
CKEDITOR.keystrokeHandler.prototype = {
/**
* Attaches this keystroke handle to a DOM object. Keystrokes typed
* over this object will get handled by this keystrokeHandler.
*
* @param {CKEDITOR.dom.domObject} domObject The DOM object to attach to.
*/
attach: function( domObject ) {
// For most browsers, it is enough to listen to the keydown event
// only.
domObject.on( 'keydown', onKeyDown, this );
// Some browsers instead, don't cancel key events in the keydown, but in the
// keypress. So we must do a longer trip in those cases.
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
domObject.on( 'keypress', onKeyPress, this );
}
};
} )();
/**
* A list associating keystrokes to editor commands. Each element in the list
* is an array where the first item is the keystroke, and the second is the
* name of the command to be executed.
*
* This setting should be used to define (as well as to overwrite or remove) keystrokes
* set by plugins (like `link` and `basicstyles`). If you want to set a keystroke
* for your plugin or during the runtime, use {@link CKEDITOR.editor#setKeystroke} instead.
*
* Since default keystrokes are set by {@link CKEDITOR.editor#setKeystroke}
* method, by default `config.keystrokes` is an empty array.
*
* See {@link CKEDITOR.editor#setKeystroke} documentation for more details
* regarding the start up order.
*
* // Change default CTRL + L keystroke for 'link' command to CTRL + SHIFT + L.
* config.keystrokes = [
* ...
* [ CKEDITOR.CTRL + CKEDITOR.SHIFT + 76, 'link' ], // CTRL + SHIFT + L
* ...
* ];
*
* To reset a particular keystroke, the following approach can be used:
*
* // Disable default CTRL + L keystroke which executes link command by default.
* config.keystrokes = [
* ...
* [ CKEDITOR.CTRL + 76, null ], // CTRL + L
* ...
* ];
*
* To reset all default keystrokes an {@link CKEDITOR#instanceReady} callback should be
* used. This is since editor defaults are merged rather than overwritten by
* user keystrokes.
*
* **Note**: This can be potentially harmful for an editor. Avoid this unless you're
* aware of the consequences.
*
* // Reset all default keystrokes.
* config.on.instanceReady = function() {
* this.keystrokeHandler.keystrokes = [];
* };
*
* @cfg {Array} [keystrokes=[]]
* @member CKEDITOR.config
*/
/**
* Fired when any keyboard key (or combination) is pressed into the editing area.
*
* @event key
* @member CKEDITOR.editor
* @param data
* @param {Number} data.keyCode A number representing the key code (or combination).
* It is the sum of the current key code and the {@link CKEDITOR#CTRL}, {@link CKEDITOR#SHIFT}
* and {@link CKEDITOR#ALT} constants, if those are pressed.
* @param {CKEDITOR.editor} editor This editor instance.
*/

View File

@@ -0,0 +1,100 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
( function() {
var loadedLangs = {};
/**
* Stores language-related functions.
*
* @class
* @singleton
*/
CKEDITOR.lang = {
/**
* The list of languages available in the editor core.
*
* alert( CKEDITOR.lang.en ); // 1
*/
languages: { af: 1, ar: 1, bg: 1, bn: 1, bs: 1, ca: 1, cs: 1, cy: 1, da: 1, de: 1, el: 1,
'en-au': 1, 'en-ca': 1, 'en-gb': 1, en: 1, eo: 1, es: 1, et: 1, eu: 1, fa: 1, fi: 1, fo: 1,
'fr-ca': 1, fr: 1, gl: 1, gu: 1, he: 1, hi: 1, hr: 1, hu: 1, id: 1, is: 1, it: 1, ja: 1, ka: 1,
km: 1, ko: 1, ku: 1, lt: 1, lv: 1, mk: 1, mn: 1, ms: 1, nb: 1, nl: 1, no: 1, pl: 1, 'pt-br': 1,
pt: 1, ro: 1, ru: 1, si: 1, sk: 1, sl: 1, sq: 1, 'sr-latn': 1, sr: 1, sv: 1, th: 1, tr: 1, ug: 1,
uk: 1, vi: 1, 'zh-cn': 1, zh: 1 },
/**
* The list of languages that are written Right-To-Left (RTL) and are supported by the editor.
*/
rtl: { ar: 1, fa: 1, he: 1, ku: 1, ug: 1 },
/**
* Loads a specific language file, or auto detects it. A callback is
* then called when the file gets loaded.
*
* @param {String} languageCode The code of the language file to be
* loaded. If null or empty, autodetection will be performed. The
* same happens if the language is not supported.
* @param {String} defaultLanguage The language to be used if
* `languageCode` is not supported or if the autodetection fails.
* @param {Function} callback A function to be called once the
* language file is loaded. Two parameters are passed to this
* function: the language code and the loaded language entries.
*/
load: function( languageCode, defaultLanguage, callback ) {
// If no languageCode - fallback to browser or default.
// If languageCode - fallback to no-localized version or default.
if ( !languageCode || !CKEDITOR.lang.languages[ languageCode ] )
languageCode = this.detect( defaultLanguage, languageCode );
if ( !this[ languageCode ] ) {
CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( 'lang/' + languageCode + '.js' ), function() {
this[ languageCode ].dir = this.rtl[ languageCode ] ? 'rtl' : 'ltr';
callback( languageCode, this[ languageCode ] );
}, this );
} else
callback( languageCode, this[ languageCode ] );
},
/**
* Returns the language that best fits the user language. For example,
* suppose that the user language is "pt-br". If this language is
* supported by the editor, it is returned. Otherwise, if only "pt" is
* supported, it is returned instead. If none of the previous are
* supported, a default language is then returned.
*
* alert( CKEDITOR.lang.detect( 'en' ) ); // e.g., in a German browser: 'de'
*
* @param {String} defaultLanguage The default language to be returned
* if the user language is not supported.
* @param {String} [probeLanguage] A language code to try to use,
* instead of the browser-based autodetection.
* @returns {String} The detected language code.
*/
detect: function( defaultLanguage, probeLanguage ) {
var languages = this.languages;
probeLanguage = probeLanguage || navigator.userLanguage || navigator.language || defaultLanguage;
var parts = probeLanguage.toLowerCase().match( /([a-z]+)(?:-([a-z]+))?/ ),
lang = parts[ 1 ],
locale = parts[ 2 ];
if ( languages[ lang + '-' + locale ] )
lang = lang + '-' + locale;
else if ( !languages[ lang ] )
lang = null;
CKEDITOR.lang.detect = lang ?
function() {
return lang;
} : function( defaultLanguage ) {
return defaultLanguage;
};
return lang || defaultLanguage;
}
};
} )();

View File

@@ -0,0 +1,247 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.loader} objects, which is used to
* load core scripts and their dependencies from _source.
*/
if ( typeof CKEDITOR == 'undefined' )
CKEDITOR = {};
if ( !CKEDITOR.loader ) {
/**
* Load core scripts and their dependencies from _source.
*
* @class
* @singleton
*/
CKEDITOR.loader = ( function() {
// Table of script names and their dependencies.
var scripts = {
'_bootstrap': [ 'config', 'creators/inline', 'creators/themedui', 'editable', 'ckeditor', 'plugins', 'scriptloader', 'style', 'tools', /* The following are entries that we want to force loading at the end to avoid dependence recursion */ 'dom/comment', 'dom/elementpath', 'dom/text', 'dom/rangelist', 'skin' ],
'ckeditor': [ 'ckeditor_basic', 'dom', 'dtd', 'dom/document', 'dom/element', 'dom/iterator', 'editor', 'event', 'htmldataprocessor', 'htmlparser', 'htmlparser/element', 'htmlparser/fragment', 'htmlparser/filter', 'htmlparser/basicwriter', 'template', 'tools' ],
'ckeditor_base': [],
'ckeditor_basic': [ 'editor_basic', 'env', 'event' ],
'command': [],
'config': [ 'ckeditor_base' ],
'dom': [],
'dom/comment': [ 'dom/node' ],
'dom/document': [ 'dom/node', 'dom/window' ],
'dom/documentfragment': [ 'dom/element' ],
'dom/element': [ 'dom', 'dom/document', 'dom/domobject', 'dom/node', 'dom/nodelist', 'tools' ],
'dom/elementpath': [ 'dom/element' ],
'dom/event': [],
'dom/iterator': [ 'dom/range' ],
'dom/node': [ 'dom/domobject', 'tools' ],
'dom/nodelist': [ 'dom/node' ],
'dom/domobject': [ 'dom/event' ],
'dom/range': [ 'dom/document', 'dom/documentfragment', 'dom/element', 'dom/walker' ],
'dom/rangelist': [ 'dom/range' ],
'dom/text': [ 'dom/node', 'dom/domobject' ],
'dom/walker': [ 'dom/node' ],
'dom/window': [ 'dom/domobject' ],
'dtd': [ 'tools' ],
'editable': [ 'editor', 'tools' ],
'editor': [ 'command', 'config', 'editor_basic', 'filter', 'focusmanager', 'keystrokehandler', 'lang', 'plugins', 'tools', 'ui' ],
'editor_basic': [ 'event' ],
'env': [],
'event': [],
'filter': [ 'dtd', 'tools' ],
'focusmanager': [],
'htmldataprocessor': [ 'htmlparser', 'htmlparser/basicwriter', 'htmlparser/fragment', 'htmlparser/filter' ],
'htmlparser': [],
'htmlparser/comment': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/element': [ 'htmlparser', 'htmlparser/fragment', 'htmlparser/node' ],
'htmlparser/fragment': [ 'htmlparser', 'htmlparser/comment', 'htmlparser/text', 'htmlparser/cdata' ],
'htmlparser/text': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/cdata': [ 'htmlparser', 'htmlparser/node' ],
'htmlparser/filter': [ 'htmlparser' ],
'htmlparser/basicwriter': [ 'htmlparser' ],
'htmlparser/node': [ 'htmlparser' ],
'keystrokehandler': [ 'event' ],
'lang': [],
'plugins': [ 'resourcemanager' ],
'resourcemanager': [ 'scriptloader', 'tools' ],
'scriptloader': [ 'dom/element', 'env' ],
'selection': [ 'dom/range', 'dom/walker' ],
'skin': [],
'style': [ 'selection' ],
'template': [],
'tools': [ 'env' ],
'ui': [],
'creators/themedui': [],
'creators/inline': []
};
var basePath = ( function() {
// This is a copy of CKEDITOR.basePath, but requires the script having
// "_source/loader.js".
if ( CKEDITOR && CKEDITOR.basePath )
return CKEDITOR.basePath;
// Find out the editor directory path, based on its <script> tag.
var path = '';
var scripts = document.getElementsByTagName( 'script' );
for ( var i = 0; i < scripts.length; i++ ) {
var match = scripts[ i ].src.match( /(^|.*?[\\\/])(?:_source\/)?core\/loader.js(?:\?.*)?$/i );
if ( match ) {
path = match[ 1 ];
break;
}
}
// In IE (only) the script.src string is the raw valued entered in the
// HTML. Other browsers return the full resolved URL instead.
if ( path.indexOf( '://' ) == -1 ) {
// Absolute path.
if ( path.indexOf( '/' ) === 0 )
path = location.href.match( /^.*?:\/\/[^\/]*/ )[ 0 ] + path;
// Relative path.
else
path = location.href.match( /^[^\?]*\// )[ 0 ] + path;
}
return path;
} )();
var timestamp = ( CKEDITOR && CKEDITOR.timestamp ) || ( new Date() ).valueOf(); // %REMOVE_LINE%
/* // %REMOVE_LINE%
* The production implementation contains a fixed timestamp // %REMOVE_LINE%
* generated by the releaser // %REMOVE_LINE%
var timestamp = '%TIMESTAMP%';
*/ // %REMOVE_LINE%
var getUrl = function( resource ) {
if ( CKEDITOR && CKEDITOR.getUrl )
return CKEDITOR.getUrl( resource );
return basePath + resource + ( resource.indexOf( '?' ) >= 0 ? '&' : '?' ) + 't=' + timestamp;
};
var pendingLoad = [];
return {
/**
* The list of loaded scripts in their loading order.
*
* // Alert the loaded script names.
* alert( CKEDITOR.loader.loadedScripts );
*/
loadedScripts: [],
/**
* Table of script names and their dependencies.
*
* @property {Array}
*/
scripts: scripts,
/**
* @todo
*/
loadPending: function() {
var scriptName = pendingLoad.shift();
if ( !scriptName )
return;
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
var script = document.createElement( 'script' );
script.type = 'text/javascript';
script.src = scriptSrc;
function onScriptLoaded() {
// Append this script to the list of loaded scripts.
CKEDITOR.loader.loadedScripts.push( scriptName );
// Load the next.
CKEDITOR.loader.loadPending();
}
// We must guarantee the execution order of the scripts, so we
// need to load them one by one. (#4145)
// The following if/else block has been taken from the scriptloader core code.
if ( typeof( script.onreadystatechange ) !== "undefined" ) {
/** @ignore */
script.onreadystatechange = function() {
if ( script.readyState == 'loaded' || script.readyState == 'complete' ) {
script.onreadystatechange = null;
onScriptLoaded();
}
};
} else {
/** @ignore */
script.onload = function() {
// Some browsers, such as Safari, may call the onLoad function
// immediately. Which will break the loading sequence. (#3661)
setTimeout( function() {
onScriptLoaded( scriptName );
}, 0 );
};
}
document.body.appendChild( script );
},
/**
* Loads a specific script, including its dependencies. This is not a
* synchronous loading, which means that the code to be loaded will
* not necessarily be available after this call.
*
* CKEDITOR.loader.load( 'dom/element' );
*
* @param {String} scriptName
* @param {Boolean} [defer=false]
* @todo params
*/
load: function( scriptName, defer ) {
// Check if the script has already been loaded.
if ( ( 's:' + scriptName ) in this.loadedScripts )
return;
// Get the script dependencies list.
var dependencies = scripts[ scriptName ];
if ( !dependencies )
throw 'The script name"' + scriptName + '" is not defined.';
// Mark the script as loaded, even before really loading it, to
// avoid cross references recursion.
// Prepend script name with 's:' to avoid conflict with Array's methods.
this.loadedScripts[ 's:' + scriptName ] = true;
// Load all dependencies first.
for ( var i = 0; i < dependencies.length; i++ )
this.load( dependencies[ i ], true );
var scriptSrc = getUrl( 'core/' + scriptName + '.js' );
// Append the <script> element to the DOM.
// If the page is fully loaded, we can't use document.write
// but if the script is run while the body is loading then it's safe to use it
// Unfortunately, Firefox <3.6 doesn't support document.readyState, so it won't get this improvement
if ( document.body && ( !document.readyState || document.readyState == 'complete' ) ) {
pendingLoad.push( scriptName );
if ( !defer )
this.loadPending();
} else {
// Append this script to the list of loaded scripts.
this.loadedScripts.push( scriptName );
document.write( '<script src="' + scriptSrc + '" type="text/javascript"><\/script>' );
}
}
};
} )();
}
// Check if any script has been defined for autoload.
if ( CKEDITOR._autoLoad ) {
CKEDITOR.loader.load( CKEDITOR._autoLoad );
delete CKEDITOR._autoLoad;
}

View File

@@ -0,0 +1,96 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the "virtual" {@link CKEDITOR.pluginDefinition} class which
* contains the defintion of a plugin. This file is for documentation
* purposes only.
*/
/**
* Virtual class which just illustrates the features of plugin objects to be
* passed to the {@link CKEDITOR.plugins#add} method.
*
* This class is not really part of the API, so its constructor should not be called.
*
* @class CKEDITOR.pluginDefinition
* @abstract
*/
/**
* A list of plugins that are required by this plugin. Note that this property
* does not determine the loading order of the plugins.
*
* CKEDITOR.plugins.add( 'sample', {
* requires: [ 'button', 'selection' ]
* } );
*
* @property {Array} requires
*/
/**
* A list of language files available for this plugin. These files are stored inside
* the `lang` directory inside the plugin directory, follow the name
* pattern of `langCode.js`, and contain the language definition created with
* {@link CKEDITOR.plugins#setLang}.
*
* When the plugin is being loaded, the editor checks this list to see if
* a language file of the current editor language ({@link CKEDITOR.editor#langCode})
* is available, and if so, loads it. Otherwise, the file represented by the first item
* in the list is loaded.
*
* CKEDITOR.plugins.add( 'sample', {
* lang: [ 'en', 'fr' ]
* } );
*
* @property {Array} lang
*/
/**
* A function called on initialization of every editor instance created in the
* page before the {@link #init} call task. The `beforeInit` function will be called for
* all plugins, after that the `init` function is called for all of them. This
* feature makes it possible to initialize things that could be used in the
* `init` function of other plugins.
*
* CKEDITOR.plugins.add( 'sample', {
* beforeInit: function( editor ) {
* alert( 'Editor "' + editor.name + '" is to be initialized!' );
* }
* } );
*
* @method beforeInit
* @param {CKEDITOR.editor} editor The editor instance being initialized.
*/
/**
* Function called on initialization of every editor instance created in the page.
*
* CKEDITOR.plugins.add( 'sample', {
* init: function( editor ) {
* alert( 'Editor "' + editor.name + '" is being initialized!' );
* }
* } );
*
* @method init
* @param {CKEDITOR.editor} editor The editor instance being initialized.
*/
/**
* Announces the plugin as HiDPI-ready (optimized for high pixel density screens, e.g. *Retina*)
* by providing high-resolution icons and images. HiDPI icons must be twice as big
* (defaults are `16px x 16px`) and stored under `plugin_name/icons/hidpi/` directory.
*
* The common place for additional HiDPI images used by the plugin (**but not icons**)
* is `plugin_name/images/hidpi/` directory.
*
* This property is optional and only makes sense if `32px x 32px` icons
* and high-resolution images actually exist. If this flag is set `true`, the editor
* will automatically detect the HiDPI environment and attempt to load the
* high-resolution resources.
*
* @since 4.2
* @property {Boolean} hidpi
*/

View File

@@ -0,0 +1,119 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.plugins} object, which is used to
* manage plugins registration and loading.
*/
/**
* Manages plugins registration and loading.
*
* @class
* @extends CKEDITOR.resourceManager
* @singleton
*/
CKEDITOR.plugins = new CKEDITOR.resourceManager( 'plugins/', 'plugin' );
// PACKAGER_RENAME( CKEDITOR.plugins )
CKEDITOR.plugins.load = CKEDITOR.tools.override( CKEDITOR.plugins.load, function( originalLoad ) {
var initialized = {};
return function( name, callback, scope ) {
var allPlugins = {};
var loadPlugins = function( names ) {
originalLoad.call( this, names, function( plugins ) {
CKEDITOR.tools.extend( allPlugins, plugins );
var requiredPlugins = [];
for ( var pluginName in plugins ) {
var plugin = plugins[ pluginName ],
requires = plugin && plugin.requires;
if ( !initialized[ pluginName ] ) {
// Register all icons eventually defined by this plugin.
if ( plugin.icons ) {
var icons = plugin.icons.split( ',' );
for ( var ic = icons.length; ic--; ) {
CKEDITOR.skin.addIcon( icons[ ic ],
plugin.path +
'icons/' +
( CKEDITOR.env.hidpi && plugin.hidpi ? 'hidpi/' : '' ) +
icons[ ic ] +
'.png' );
}
}
initialized[ pluginName ] = 1;
}
if ( requires ) {
// Trasnform it into an array, if it's not one.
if ( requires.split )
requires = requires.split( ',' );
for ( var i = 0; i < requires.length; i++ ) {
if ( !allPlugins[ requires[ i ] ] )
requiredPlugins.push( requires[ i ] );
}
}
}
if ( requiredPlugins.length )
loadPlugins.call( this, requiredPlugins );
else {
// Call the "onLoad" function for all plugins.
for ( pluginName in allPlugins ) {
plugin = allPlugins[ pluginName ];
if ( plugin.onLoad && !plugin.onLoad._called ) {
// Make it possible to return false from plugin::onLoad to disable it.
if ( plugin.onLoad() === false )
delete allPlugins[ pluginName ];
plugin.onLoad._called = 1;
}
}
// Call the callback.
if ( callback )
callback.call( scope || window, allPlugins );
}
}, this );
};
loadPlugins.call( this, name );
};
} );
/**
* Loads a specific language file, or auto detect it. A callback is
* then called when the file gets loaded.
*
* CKEDITOR.plugins.setLang( 'myPlugin', 'en', {
* title: 'My plugin',
* selectOption: 'Please select an option'
* } );
*
* @param {String} pluginName The name of the plugin to which the provided translation
* should be attached.
* @param {String} languageCode The code of the language translation provided.
* @param {Object} languageEntries An object that contains pairs of label and
* the respective translation.
*/
CKEDITOR.plugins.setLang = function( pluginName, languageCode, languageEntries ) {
var plugin = this.get( pluginName ),
pluginLangEntries = plugin.langEntries || ( plugin.langEntries = {} ),
pluginLang = plugin.lang || ( plugin.lang = [] );
if ( pluginLang.split )
pluginLang = pluginLang.split( ',' );
if ( CKEDITOR.tools.indexOf( pluginLang, languageCode ) == -1 )
pluginLang.push( languageCode );
pluginLangEntries[ languageCode ] = languageEntries;
};

View File

@@ -0,0 +1,227 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.resourceManager} class, which is
* the base for resource managers, like plugins.
*/
/**
* Base class for resource managers, like plugins. This class is not
* intended to be used out of the CKEditor core code.
*
* @class
* @constructor Creates a resourceManager class instance.
* @param {String} basePath The path for the resources folder.
* @param {String} fileName The name used for resource files.
*/
CKEDITOR.resourceManager = function( basePath, fileName ) {
/**
* The base directory containing all resources.
*
* @property {String}
*/
this.basePath = basePath;
/**
* The name used for resource files.
*
* @property {String}
*/
this.fileName = fileName;
/**
* Contains references to all resources that have already been registered
* with {@link #add}.
*/
this.registered = {};
/**
* Contains references to all resources that have already been loaded
* with {@link #load}.
*/
this.loaded = {};
/**
* Contains references to all resources that have already been registered
* with {@link #addExternal}.
*/
this.externals = {};
/**
* @private
*/
this._ = {
// List of callbacks waiting for plugins to be loaded.
waitingList: {}
};
};
CKEDITOR.resourceManager.prototype = {
/**
* Registers a resource.
*
* CKEDITOR.plugins.add( 'sample', { ... plugin definition ... } );
*
* @param {String} name The resource name.
* @param {Object} [definition] The resource definition.
* @see CKEDITOR.pluginDefinition
*/
add: function( name, definition ) {
if ( this.registered[ name ] )
throw '[CKEDITOR.resourceManager.add] The resource name "' + name + '" is already registered.';
var resource = this.registered[ name ] = definition || {};
resource.name = name;
resource.path = this.getPath( name );
CKEDITOR.fire( name + CKEDITOR.tools.capitalize( this.fileName ) + 'Ready', resource );
return this.get( name );
},
/**
* Gets the definition of a specific resource.
*
* var definition = CKEDITOR.plugins.get( 'sample' );
*
* @param {String} name The resource name.
* @returns {Object} The registered object.
*/
get: function( name ) {
return this.registered[ name ] || null;
},
/**
* Get the folder path for a specific loaded resource.
*
* alert( CKEDITOR.plugins.getPath( 'sample' ) ); // '<editor path>/plugins/sample/'
*
* @param {String} name The resource name.
* @returns {String}
*/
getPath: function( name ) {
var external = this.externals[ name ];
return CKEDITOR.getUrl( ( external && external.dir ) || this.basePath + name + '/' );
},
/**
* Get the file path for a specific loaded resource.
*
* alert( CKEDITOR.plugins.getFilePath( 'sample' ) ); // '<editor path>/plugins/sample/plugin.js'
*
* @param {String} name The resource name.
* @returns {String}
*/
getFilePath: function( name ) {
var external = this.externals[ name ];
return CKEDITOR.getUrl( this.getPath( name ) + ( external ? external.file : this.fileName + '.js' ) );
},
/**
* Registers one or more resources to be loaded from an external path
* instead of the core base path.
*
* // Loads a plugin from '/myplugin/samples/plugin.js'.
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' );
*
* // Loads a plugin from '/myplugin/samples/my_plugin.js'.
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/', 'my_plugin.js' );
*
* // Loads a plugin from '/myplugin/samples/my_plugin.js'.
* CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/my_plugin.js', '' );
*
* @param {String} names The resource names, separated by commas.
* @param {String} path The path of the folder containing the resource.
* @param {String} [fileName] The resource file name. If not provided, the
* default name is used. If provided with a empty string, will implicitly indicates that `path` argument
* is already the full path.
*/
addExternal: function( names, path, fileName ) {
names = names.split( ',' );
for ( var i = 0; i < names.length; i++ ) {
var name = names[ i ];
// If "fileName" is not provided, we assume that it may be available
// in "path". Try to extract it in this case.
if ( !fileName ) {
path = path.replace( /[^\/]+$/, function( match ) {
fileName = match;
return '';
} );
}
this.externals[ name ] = {
dir: path,
// Use the default file name if there is no "fileName" and it
// was not found in "path".
file: fileName || ( this.fileName + '.js' )
};
}
},
/**
* Loads one or more resources.
*
* CKEDITOR.plugins.load( 'myplugin', function( plugins ) {
* alert( plugins[ 'myplugin' ] ); // object
* } );
*
* @param {String/Array} name The name of the resource to load. It may be a
* string with a single resource name, or an array with several names.
* @param {Function} callback A function to be called when all resources
* are loaded. The callback will receive an array containing all loaded names.
* @param {Object} [scope] The scope object to be used for the callback call.
*/
load: function( names, callback, scope ) {
// Ensure that we have an array of names.
if ( !CKEDITOR.tools.isArray( names ) )
names = names ? [ names ] : [];
var loaded = this.loaded,
registered = this.registered,
urls = [],
urlsNames = {},
resources = {};
// Loop through all names.
for ( var i = 0; i < names.length; i++ ) {
var name = names[ i ];
if ( !name )
continue;
// If not available yet.
if ( !loaded[ name ] && !registered[ name ] ) {
var url = this.getFilePath( name );
urls.push( url );
if ( !( url in urlsNames ) )
urlsNames[ url ] = [];
urlsNames[ url ].push( name );
} else
resources[ name ] = this.get( name );
}
CKEDITOR.scriptLoader.load( urls, function( completed, failed ) {
if ( failed.length ) {
throw '[CKEDITOR.resourceManager.load] Resource name "' + urlsNames[ failed[ 0 ] ].join( ',' )
+ '" was not found at "' + failed[ 0 ] + '".';
}
for ( var i = 0; i < completed.length; i++ ) {
var nameList = urlsNames[ completed[ i ] ];
for ( var j = 0; j < nameList.length; j++ ) {
var name = nameList[ j ];
resources[ name ] = this.get( name );
loaded[ name ] = 1;
}
}
callback.call( scope, resources );
}, this );
}
};

View File

@@ -0,0 +1,202 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.scriptLoader} object, used to load scripts
* asynchronously.
*/
/**
* Load scripts asynchronously.
*
* @class
* @singleton
*/
CKEDITOR.scriptLoader = ( function() {
var uniqueScripts = {},
waitingList = {};
return {
/**
* Loads one or more external script checking if not already loaded
* previously by this function.
*
* CKEDITOR.scriptLoader.load( '/myscript.js' );
*
* CKEDITOR.scriptLoader.load( '/myscript.js', function( success ) {
* // Alerts true if the script has been properly loaded.
* // HTTP error 404 should return false.
* alert( success );
* } );
*
* CKEDITOR.scriptLoader.load( [ '/myscript1.js', '/myscript2.js' ], function( completed, failed ) {
* alert( 'Number of scripts loaded: ' + completed.length );
* alert( 'Number of failures: ' + failed.length );
* } );
*
* @param {String/Array} scriptUrl One or more URLs pointing to the
* scripts to be loaded.
* @param {Function} [callback] A function to be called when the script
* is loaded and executed. If a string is passed to `scriptUrl`, a
* boolean parameter is passed to the callback, indicating the
* success of the load. If an array is passed instead, two arrays
* parameters are passed to the callback - the first contains the
* URLs that have been properly loaded and the second the failed ones.
* @param {Object} [scope] The scope (`this` reference) to be used for
* the callback call. Defaults to {@link CKEDITOR}.
* @param {Boolean} [showBusy] Changes the cursor of the document while
* the script is loaded.
*/
load: function( scriptUrl, callback, scope, showBusy ) {
var isString = ( typeof scriptUrl == 'string' );
if ( isString )
scriptUrl = [ scriptUrl ];
if ( !scope )
scope = CKEDITOR;
var scriptCount = scriptUrl.length,
completed = [],
failed = [];
var doCallback = function( success ) {
if ( callback ) {
if ( isString )
callback.call( scope, success );
else
callback.call( scope, completed, failed );
}
};
if ( scriptCount === 0 ) {
doCallback( true );
return;
}
var checkLoaded = function( url, success ) {
( success ? completed : failed ).push( url );
if ( --scriptCount <= 0 ) {
showBusy && CKEDITOR.document.getDocumentElement().removeStyle( 'cursor' );
doCallback( success );
}
};
var onLoad = function( url, success ) {
// Mark this script as loaded.
uniqueScripts[ url ] = 1;
// Get the list of callback checks waiting for this file.
var waitingInfo = waitingList[ url ];
delete waitingList[ url ];
// Check all callbacks waiting for this file.
for ( var i = 0; i < waitingInfo.length; i++ )
waitingInfo[ i ]( url, success );
};
var loadScript = function( url ) {
if ( uniqueScripts[ url ] ) {
checkLoaded( url, true );
return;
}
var waitingInfo = waitingList[ url ] || ( waitingList[ url ] = [] );
waitingInfo.push( checkLoaded );
// Load it only for the first request.
if ( waitingInfo.length > 1 )
return;
// Create the <script> element.
var script = new CKEDITOR.dom.element( 'script' );
script.setAttributes( {
type: 'text/javascript',
src: url } );
if ( callback ) {
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 ) {
// FIXME: For IE, we are not able to return false on error (like 404).
script.$.onreadystatechange = function() {
if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' ) {
script.$.onreadystatechange = null;
onLoad( url, true );
}
};
} else {
script.$.onload = function() {
// Some browsers, such as Safari, may call the onLoad function
// immediately. Which will break the loading sequence. (#3661)
setTimeout( function() {
onLoad( url, true );
}, 0 );
};
// FIXME: Opera and Safari will not fire onerror.
script.$.onerror = function() {
onLoad( url, false );
};
}
}
// Append it to <head>.
script.appendTo( CKEDITOR.document.getHead() );
CKEDITOR.fire( 'download', url ); // %REMOVE_LINE%
};
showBusy && CKEDITOR.document.getDocumentElement().setStyle( 'cursor', 'wait' );
for ( var i = 0; i < scriptCount; i++ ) {
loadScript( scriptUrl[ i ] );
}
},
/**
* Loads a script in a queue, so only one is loaded at the same time.
*
* @since 4.1.2
* @param {String} scriptUrl URL pointing to the script to be loaded.
* @param {Function} [callback] A function to be called when the script
* is loaded and executed. A boolean parameter is passed to the callback,
* indicating the success of the load.
*
* @see CKEDITOR.scriptLoader#load
*/
queue: ( function() {
var pending = [];
// Loads the very first script from queue and removes it.
function loadNext() {
var script;
if ( ( script = pending[ 0 ] ) )
this.load( script.scriptUrl, script.callback, CKEDITOR, 0 );
}
return function( scriptUrl, callback ) {
var that = this;
// This callback calls the standard callback for the script
// and loads the very next script from pending list.
function callbackWrapper() {
callback && callback.apply( this, arguments );
// Removed the just loaded script from the queue.
pending.shift();
loadNext.call( that );
}
// Let's add this script to the queue
pending.push( { scriptUrl: scriptUrl, callback: callbackWrapper } );
// If the queue was empty, then start loading.
if ( pending.length == 1 )
loadNext.call( this );
};
} )()
};
} )();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,335 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.skin} class that is used to manage skin parts.
*/
( function() {
var cssLoaded = {};
function getName() {
return CKEDITOR.skinName.split( ',' )[ 0 ];
}
function getConfigPath() {
return CKEDITOR.getUrl( CKEDITOR.skinName.split( ',' )[ 1 ] || ( 'skins/' + getName() + '/' ) );
}
/**
* Manages the loading of skin parts among all editor instances.
*
* @class
* @singleton
*/
CKEDITOR.skin = {
/**
* Returns the root path to the skin directory.
*
* @method
* @todo
*/
path: getConfigPath,
/**
* Loads a skin part into the page. Does nothing if the part has already been loaded.
*
* **Note:** The "editor" part is always auto loaded upon instance creation,
* thus this function is mainly used to **lazy load** other parts of the skin
* that do not have to be displayed until requested.
*
* // Load the dialog part.
* editor.skin.loadPart( 'dialog' );
*
* @param {String} part The name of the skin part CSS file that resides in the skin directory.
* @param {Function} fn The provided callback function which is invoked after the part is loaded.
*/
loadPart: function( part, fn ) {
if ( CKEDITOR.skin.name != getName() ) {
CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( getConfigPath() + 'skin.js' ), function() {
loadCss( part, fn );
} );
} else
loadCss( part, fn );
},
/**
* Retrieves the real URL of a (CSS) skin part.
*
* @param {String} part
*/
getPath: function( part ) {
return CKEDITOR.getUrl( getCssPath( part ) );
},
/**
* The list of registered icons. To add new icons to this list, use {@link #addIcon}.
*/
icons: {},
/**
* Registers an icon.
*
* @param {String} name The icon name.
* @param {String} path The path to the icon image file.
* @param {Number} [offset] The vertical offset position of the icon, if
* available inside a strip image.
* @param {String} [bgsize] The value of the CSS "background-size" property to
* use for this icon
*/
addIcon: function( name, path, offset, bgsize ) {
name = name.toLowerCase();
if ( !this.icons[ name ] ) {
this.icons[ name ] = {
path: path,
offset: offset || 0,
bgsize : bgsize || '16px'
};
}
},
/**
* Gets the CSS background styles to be used to render a specific icon.
*
* @param {String} name The icon name, as registered with {@link #addIcon}.
* @param {Boolean} [rtl] Indicates that the RTL version of the icon is
* to be used, if available.
* @param {String} [overridePath] The path to the icon image file. It
* overrides the path defined by the named icon, if available, and is
* used if the named icon was not registered.
* @param {Number} [overrideOffset] The vertical offset position of the
* icon. It overrides the offset defined by the named icon, if
* available, and is used if the named icon was not registered.
* @param {String} [overrideBgsize] The value of the CSS "background-size" property
* to use for the icon. It overrides the value defined by the named icon,
* if available, and is used if the named icon was not registered.
*/
getIconStyle: function( name, rtl, overridePath, overrideOffset, overrideBgsize ) {
var icon, path, offset, bgsize;
if ( name ) {
name = name.toLowerCase();
// If we're in RTL, try to get the RTL version of the icon.
if ( rtl )
icon = this.icons[ name + '-rtl' ];
// If not in LTR or no RTL version available, get the generic one.
if ( !icon )
icon = this.icons[ name ];
}
path = overridePath || ( icon && icon.path ) || '';
offset = overrideOffset || ( icon && icon.offset );
bgsize = overrideBgsize || ( icon && icon.bgsize ) || '16px';
return path &&
( 'background-image:url(' + CKEDITOR.getUrl( path ) + ');background-position:0 ' + offset + 'px;background-size:' + bgsize + ';' );
}
};
function getCssPath( part ) {
// Check for ua-specific version of skin part.
var uas = CKEDITOR.skin[ 'ua_' + part ], env = CKEDITOR.env;
if ( uas ) {
// Having versioned UA checked first.
uas = uas.split( ',' ).sort( function( a, b ) { return a > b ? -1 : 1; } );
// Loop through all ua entries, checking is any of them match the current ua.
for ( var i = 0, ua; i < uas.length; i++ ) {
ua = uas[ i ];
if ( env.ie ) {
if ( ( ua.replace( /^ie/, '' ) == env.version ) || ( env.quirks && ua == 'iequirks' ) )
ua = 'ie';
}
if ( env[ ua ] ) {
part += '_' + uas[ i ];
break;
}
}
}
return CKEDITOR.getUrl( getConfigPath() + part + '.css' );
}
function loadCss( part, callback ) {
// Avoid reload.
if ( !cssLoaded[ part ] ) {
CKEDITOR.document.appendStyleSheet( getCssPath( part ) );
cssLoaded[ part ] = 1;
}
// CSS loading should not be blocking.
callback && callback();
}
CKEDITOR.tools.extend( CKEDITOR.editor.prototype, {
/** Gets the color of the editor user interface.
*
* CKEDITOR.instances.editor1.getUiColor();
*
* @method
* @member CKEDITOR.editor
* @returns {String} uiColor The editor UI color or `undefined` if the UI color is not set.
*/
getUiColor: function() {
return this.uiColor;
},
/** Sets the color of the editor user interface. This method accepts a color value in
* hexadecimal notation, with a `#` character (e.g. #ffffff).
*
* CKEDITOR.instances.editor1.setUiColor( '#ff00ff' );
*
* @method
* @member CKEDITOR.editor
* @param {String} color The desired editor UI color in hexadecimal notation.
*/
setUiColor: function( color ) {
var uiStyle = getStylesheet( CKEDITOR.document );
return ( this.setUiColor = function( color ) {
var chameleon = CKEDITOR.skin.chameleon;
var replace = [ [ uiColorRegexp, color ] ];
this.uiColor = color;
// Update general style.
updateStylesheets( [ uiStyle ], chameleon( this, 'editor' ), replace );
// Update panel styles.
updateStylesheets( uiColorMenus, chameleon( this, 'panel' ), replace );
} ).call( this, color );
}
} );
var uiColorStylesheetId = 'cke_ui_color',
uiColorMenus = [],
uiColorRegexp = /\$color/g;
function getStylesheet( document ) {
var node = document.getById( uiColorStylesheetId );
if ( !node ) {
node = document.getHead().append( 'style' );
node.setAttribute( "id", uiColorStylesheetId );
node.setAttribute( "type", "text/css" );
}
return node;
}
function updateStylesheets( styleNodes, styleContent, replace ) {
var r, i, content;
// We have to split CSS declarations for webkit.
if ( CKEDITOR.env.webkit ) {
styleContent = styleContent.split( '}' ).slice( 0, -1 );
for ( i = 0; i < styleContent.length; i++ )
styleContent[ i ] = styleContent[ i ].split( '{' );
}
for ( var id = 0; id < styleNodes.length; id++ ) {
if ( CKEDITOR.env.webkit ) {
for ( i = 0; i < styleContent.length; i++ ) {
content = styleContent[ i ][ 1 ];
for ( r = 0; r < replace.length; r++ )
content = content.replace( replace[ r ][ 0 ], replace[ r ][ 1 ] );
styleNodes[ id ].$.sheet.addRule( styleContent[ i ][ 0 ], content );
}
} else {
content = styleContent;
for ( r = 0; r < replace.length; r++ )
content = content.replace( replace[ r ][ 0 ], replace[ r ][ 1 ] );
if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 )
styleNodes[ id ].$.styleSheet.cssText += content;
else
styleNodes[ id ].$.innerHTML += content;
}
}
}
CKEDITOR.on( 'instanceLoaded', function( evt ) {
// The chameleon feature is not for IE quirks.
if ( CKEDITOR.env.ie && CKEDITOR.env.quirks )
return;
var editor = evt.editor,
showCallback = function( event ) {
var panel = event.data[ 0 ] || event.data;
var iframe = panel.element.getElementsByTag( 'iframe' ).getItem( 0 ).getFrameDocument();
// Add stylesheet if missing.
if ( !iframe.getById( 'cke_ui_color' ) ) {
var node = getStylesheet( iframe );
uiColorMenus.push( node );
var color = editor.getUiColor();
// Set uiColor for new panel.
if ( color )
updateStylesheets( [ node ], CKEDITOR.skin.chameleon( editor, 'panel' ), [ [ uiColorRegexp, color ] ] );
}
};
editor.on( 'panelShow', showCallback );
editor.on( 'menuShow', showCallback );
// Apply UI color if specified in config.
if ( editor.config.uiColor )
editor.setUiColor( editor.config.uiColor );
} );
} )();
/**
* The list of file names matching the browser user agent string from
* {@link CKEDITOR.env}. This is used to load the skin part file in addition
* to the "main" skin file for a particular browser.
*
* **Note:** For each of the defined skin parts the corresponding
* CSS file with the same name as the user agent must exist inside
* the skin directory.
*
* @property ua
* @todo type?
*/
/**
* The name of the skin that is currently used.
*
* @property {String} name
* @todo
*/
/**
* The editor skin name. Note that it is not possible to have editors with
* different skin settings in the same page. In such case just one of the
* skins will be used for all editors.
*
* This is a shortcut to {@link CKEDITOR#skinName}.
*
* It is possible to install skins outside the default `skin` folder in the
* editor installation. In that case, the absolute URL path to that folder
* should be provided, separated by a comma (`'skin_name,skin_path'`).
*
* config.skin = 'moono';
*
* config.skin = 'myskin,/customstuff/myskin/';
*
* @cfg {String} skin
* @member CKEDITOR.config
*/
/**
* A function that supports the chameleon (skin color switch) feature, providing
* the skin color style updates to be applied in runtime.
*
* **Note:** The embedded `$color` variable is to be substituted with a specific UI color.
*
* @method chameleon
* @param {String} editor The editor instance that the color changes apply to.
* @param {String} part The name of the skin part where the color changes take place.
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.template} class, which represents
* an UI template for an editor instance.
*/
( function() {
var cache = {},
rePlaceholder = /{([^}]+)}/g,
reQuote = /'/g,
reEscapableChars = /([\\'])/g,
reNewLine = /\n/g,
reCarriageReturn = /\r/g;
/**
* Lightweight template used to build the output string from variables.
*
* // HTML template for presenting a label UI.
* var tpl = new CKEDITOR.template( '<div class="{cls}">{label}</div>' );
* alert( tpl.output( { cls: 'cke-label', label: 'foo'} ) ); // '<div class="cke-label">foo</div>'
*
* @class
* @constructor Creates a template class instance.
* @param {String} source The template source.
*/
CKEDITOR.template = function( source ) {
// Builds an optimized function body for the output() method, focused on performance.
// For example, if we have this "source":
// '<div style="{style}">{editorName}</div>'
// ... the resulting function body will be (apart from the "buffer" handling):
// return [ '<div style="', data['style'] == undefined ? '{style}' : data['style'], '">', data['editorName'] == undefined ? '{editorName}' : data['editorName'], '</div>' ].join('');
// Try to read from the cache.
if ( cache[ source ] )
this.output = cache[ source ];
else {
var fn = source
// Escape chars like slash "\" or single quote "'".
.replace( reEscapableChars, '\\$1' )
.replace( reNewLine, '\\n' )
.replace( reCarriageReturn, '\\r' )
// Inject the template keys replacement.
.replace( rePlaceholder, function( m, key ) {
return "',data['" + key + "']==undefined?'{" + key + "}':data['" + key + "'],'";
} );
fn = "return buffer?buffer.push('" + fn + "'):['" + fn + "'].join('');";
this.output = cache[ source ] = Function( 'data', 'buffer', fn );
}
};
} )();
/**
* Processes the template, filling its variables with the provided data.
*
* @method output
* @param {Object} data An object containing properties which values will be
* used to fill the template variables. The property names must match the
* template variables names. Variables without matching properties will be
* kept untouched.
* @param {Array} [buffer] An array into which the output data will be pushed into.
* The number of entries appended to the array is unknown.
* @returns {String/Number} If `buffer` has not been provided, the processed
* template output data, otherwise the new length of `buffer`.
*/

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,168 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* Contains UI features related to an editor instance.
*
* @class
* @mixins CKEDITOR.event
* @constructor Creates an ui class instance.
* @param {CKEDITOR.editor} editor The editor instance.
*/
CKEDITOR.ui = function( editor ) {
if ( editor.ui )
return editor.ui;
this.items = {};
this.instances = {};
this.editor = editor;
/**
* Object used to hold private stuff.
*
* @private
*/
this._ = {
handlers: {}
};
return this;
};
// PACKAGER_RENAME( CKEDITOR.ui )
CKEDITOR.ui.prototype = {
/**
* Adds a UI item to the items collection. These items can be later used in
* the interface.
*
* // Add a new button named 'MyBold'.
* editorInstance.ui.add( 'MyBold', CKEDITOR.UI_BUTTON, {
* label: 'My Bold',
* command: 'bold'
* } );
*
* @param {String} name The UI item name.
* @param {Object} type The item type.
* @param {Object} definition The item definition. The properties of this
* object depend on the item type.
*/
add: function( name, type, definition ) {
// Compensate the unique name of this ui item to definition.
definition.name = name.toLowerCase();
var item = this.items[ name ] = {
type: type,
// The name of {@link CKEDITOR.command} which associate with this UI.
command: definition.command || null,
args: Array.prototype.slice.call( arguments, 2 )
};
CKEDITOR.tools.extend( item, definition );
},
/**
* Retrieve the created ui objects by name.
*
* @param {String} name The name of the UI definition.
*/
get: function( name ) {
return this.instances[ name ];
},
/**
* Gets a UI object.
*
* @param {String} name The UI item hame.
* @returns {Object} The UI element.
*/
create: function( name ) {
var item = this.items[ name ],
handler = item && this._.handlers[ item.type ],
command = item && item.command && this.editor.getCommand( item.command );
var result = handler && handler.create.apply( this, item.args );
this.instances[ name ] = result;
// Add reference inside command object.
if ( command )
command.uiItems.push( result );
if ( result && !result.type )
result.type = item.type;
return result;
},
/**
* Adds a handler for a UI item type. The handler is responsible for
* transforming UI item definitions in UI objects.
*
* @param {Object} type The item type.
* @param {Object} handler The handler definition.
*/
addHandler: function( type, handler ) {
this._.handlers[ type ] = handler;
},
/**
* Returns the unique DOM element that represents one editor's UI part, as
* the editor UI is made completely decoupled from DOM (no DOM reference hold),
* this method is mainly used to retrieve the rendered DOM part by name.
*
* // Hide the bottom space in the UI.
* var bottom = editor.ui.getSpace( 'bottom' );
* bottom.setStyle( 'display', 'none' );
*
* @param {String} name The space name.
* @returns {CKEDITOR.dom.element} The element that represents the space.
*/
space: function( name ) {
return CKEDITOR.document.getById( this.spaceId( name ) );
},
/**
* Generate the HTML ID from a specific UI space name.
*
* @param name
* @todo param and return types?
*/
spaceId: function( name ) {
return this.editor.id + '_' + name;
}
};
CKEDITOR.event.implementOn( CKEDITOR.ui );
/**
* Internal event fired when a new UI element is ready.
*
* @event ready
* @param {Object} data The new element.
*/
/**
* Virtual class which just illustrates the features of handler objects to be
* passed to the {@link CKEDITOR.ui#addHandler} function.
* This class is not really part of the API, so don't call its constructor.
*
* @class CKEDITOR.ui.handlerDefinition
*/
/**
* Transforms an item definition into an UI item object.
*
* editorInstance.ui.addHandler( CKEDITOR.UI_BUTTON, {
* create: function( definition ) {
* return new CKEDITOR.ui.button( definition );
* }
* } );
*
* @method create
* @param {Object} definition The item definition.
* @returns {Object} The UI element.
* @todo We lack the "UI element" abstract super class.
*/

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Afrikaans language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'af' ] = {
// ARIA description.
editor: 'Teksverwerker',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Druk op ALT 0 vir hulp',
browseServer: 'Blaai op bediener',
url: 'URL',
protocol: 'Protokol',
upload: 'Oplaai',
uploadSubmit: 'Stuur na bediener',
image: 'Afbeelding',
flash: 'Flash',
form: 'Vorm',
checkbox: 'Merkhokkie',
radio: 'Radioknoppie',
textField: 'Teksveld',
textarea: 'Teks-area',
hiddenField: 'Blinde veld',
button: 'Knop',
select: 'Keuseveld',
imageButton: 'Afbeeldingsknop',
notSet: '<geen instelling>',
id: 'Id',
name: 'Naam',
langDir: 'Skryfrigting',
langDirLtr: 'Links na regs (LTR)',
langDirRtl: 'Regs na links (RTL)',
langCode: 'Taalkode',
longDescr: 'Lang beskrywing URL',
cssClass: 'CSS klasse',
advisoryTitle: 'Aanbevole titel',
cssStyle: 'Styl',
ok: 'OK',
cancel: 'Kanselleer',
close: 'Sluit',
preview: 'Voorbeeld',
resize: 'Sleep om te herskaal',
generalTab: 'Algemeen',
advancedTab: 'Gevorderd',
validateNumberFailed: 'Hierdie waarde is nie \'n getal nie.',
confirmNewPage: 'Alle wysiginge sal verlore gaan. Is u seker dat u \'n nuwe bladsy wil laai?',
confirmCancel: 'Sommige opsies is gewysig. Is u seker dat u hierdie dialoogvenster wil sluit?',
options: 'Opsies',
target: 'Doel',
targetNew: 'Nuwe venster (_blank)',
targetTop: 'Boonste venster (_top)',
targetSelf: 'Selfde venster (_self)',
targetParent: 'Oorspronklike venster (_parent)',
langDirLTR: 'Links na Regs (LTR)',
langDirRTL: 'Regs na Links (RTL)',
styles: 'Styl',
cssClasses: 'CSS klasse',
width: 'Breedte',
height: 'Hoogte',
align: 'Oplyn',
alignLeft: 'Links',
alignRight: 'Regs',
alignCenter: 'Sentreer',
alignTop: 'Bo',
alignMiddle: 'Middel',
alignBottom: 'Onder',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Hoogte moet \'n getal wees',
invalidWidth: 'Breedte moet \'n getal wees.',
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nie beskikbaar nie</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Arabic language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'ar' ] = {
// ARIA description.
editor: 'محرر النص الغني',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'إضغط على ALT + 0 للحصول على المساعدة.',
browseServer: 'تصفح',
url: 'الرابط',
protocol: 'البروتوكول',
upload: 'رفع',
uploadSubmit: 'أرسل',
image: 'صورة',
flash: 'فلاش',
form: 'نموذج',
checkbox: 'خانة إختيار',
radio: 'زر اختيار',
textField: 'مربع نص',
textarea: 'مساحة نصية',
hiddenField: 'إدراج حقل خفي',
button: 'زر ضغط',
select: 'اختار',
imageButton: 'زر صورة',
notSet: '<بدون تحديد>',
id: 'الرقم',
name: 'إسم',
langDir: 'إتجاه النص',
langDirLtr: 'اليسار لليمين (LTR)',
langDirRtl: 'اليمين لليسار (RTL)',
langCode: 'رمز اللغة',
longDescr: 'الوصف التفصيلى',
cssClass: 'فئات التنسيق',
advisoryTitle: 'عنوان التقرير',
cssStyle: 'نمط',
ok: 'موافق',
cancel: 'إلغاء الأمر',
close: 'أغلق',
preview: 'استعراض',
resize: 'تغيير الحجم',
generalTab: 'عام',
advancedTab: 'متقدم',
validateNumberFailed: 'لايوجد نتيجة',
confirmNewPage: 'ستفقد أي متغييرات اذا لم تقم بحفظها اولا. هل أنت متأكد أنك تريد صفحة جديدة؟',
confirmCancel: 'بعض الخيارات قد تغيرت. هل أنت متأكد من إغلاق مربع النص؟',
options: 'خيارات',
target: 'هدف الرابط',
targetNew: 'نافذة جديدة',
targetTop: 'النافذة الأعلى',
targetSelf: 'داخل النافذة',
targetParent: 'النافذة الأم',
langDirLTR: 'اليسار لليمين (LTR)',
langDirRTL: 'اليمين لليسار (RTL)',
styles: 'نمط',
cssClasses: 'فئات التنسيق',
width: 'العرض',
height: 'الإرتفاع',
align: 'محاذاة',
alignLeft: 'يسار',
alignRight: 'يمين',
alignCenter: 'وسط',
alignTop: 'أعلى',
alignMiddle: 'وسط',
alignBottom: 'أسفل',
invalidValue : 'قيمة غير مفبولة.',
invalidHeight: 'الارتفاع يجب أن يكون عدداً.',
invalidWidth: 'العرض يجب أن يكون عدداً.',
invalidCssLength: 'قيمة الخانة المخصصة لـ "%1" يجب أن تكون رقما موجبا، باستخدام أو من غير استخدام وحدة CSS قياس مقبولة (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'قيمة الخانة المخصصة لـ "%1" يجب أن تكون رقما موجبا، باستخدام أو من غير استخدام وحدة HTML قياس مقبولة (px or %).',
invalidInlineStyle: 'قيمة الخانة المخصصة لـ Inline Style يجب أن تختوي على مجموع واحد أو أكثر بالشكل التالي: "name : value", مفصولة بفاصلة منقزطة.',
cssLengthTooltip: 'أدخل رقما للقيمة بالبكسل أو رقما بوحدة CSS مقبولة (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, غير متاح</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Bulgarian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'bg' ] = {
// ARIA description.
editor: 'Текстов редактор за форматиран текст',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'натиснете ALT 0 за помощ',
browseServer: 'Избор от сървъра',
url: 'URL',
protocol: 'Протокол',
upload: 'Качване',
uploadSubmit: 'Изпращане към сървъра',
image: 'Снимка',
flash: 'Флаш',
form: 'Форма',
checkbox: 'Поле за избор',
radio: 'Радио бутон',
textField: 'Текстово поле',
textarea: 'Текстова зона',
hiddenField: 'Скрито поле',
button: 'Бутон',
select: 'Поле за избор',
imageButton: 'Бутон за снимка',
notSet: '<не е избрано>',
id: 'ID',
name: 'Име',
langDir: 'Посока на езика',
langDirLtr: 'Ляво на дясно (ЛнД)',
langDirRtl: 'Дясно на ляво (ДнЛ)',
langCode: 'Код на езика',
longDescr: 'Уеб адрес за дълго описание',
cssClass: 'Класове за CSS',
advisoryTitle: 'Препоръчително заглавие',
cssStyle: 'Стил',
ok: 'ОК',
cancel: 'Отказ',
close: 'Затвори',
preview: 'Преглед',
resize: 'Влачете за да оразмерите',
generalTab: 'Общи',
advancedTab: 'Разширено',
validateNumberFailed: 'Тази стойност не е число',
confirmNewPage: 'Всички незапазени промени ще бъдат изгубени. Сигурни ли сте, че желаете да заредите нова страница?',
confirmCancel: 'Някои от опциите са променени. Сигурни ли сте, че желаете да затворите прозореца?',
options: 'Опции',
target: 'Цел',
targetNew: 'Нов прозорец (_blank)',
targetTop: 'Горна позиция (_top)',
targetSelf: 'Текущия прозорец (_self)',
targetParent: 'Основен прозорец (_parent)',
langDirLTR: 'Ляво на дясно (ЛнД)',
langDirRTL: 'Дясно на ляво (ДнЛ)',
styles: 'Стил',
cssClasses: 'Класове за CSS',
width: 'Ширина',
height: 'Височина',
align: 'Подравняване',
alignLeft: 'Ляво',
alignRight: 'Дясно',
alignCenter: 'Център',
alignTop: 'Горе',
alignMiddle: 'По средата',
alignBottom: 'Долу',
invalidValue : 'Невалидна стойност.',
invalidHeight: 'Височината трябва да е число.',
invalidWidth: 'Ширина требе да е число.',
invalidCssLength: 'Стойността на полето "%1" трябва да бъде положително число с или без валидна CSS измервателна единица (px, %, in, cm, mm, em, ex, pt, или pc).',
invalidHtmlLength: 'Стойността на полето "%1" трябва да бъде положително число с или без валидна HTML измервателна единица (px или %).',
invalidInlineStyle: 'Стойността на стилa трябва да съдържат една или повече двойки във формат "name : value", разделени с двоеточие.',
cssLengthTooltip: 'Въведете числена стойност в пиксели или друга валидна CSS единица (px, %, in, cm, mm, em, ex, pt, или pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, недостъпно</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Bengali/Bangla language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'bn' ] = {
// ARIA description.
editor: 'Rich Text Editor', // MISSING
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help', // MISSING
browseServer: 'ব্রাউজ সার্ভার',
url: 'URL',
protocol: 'প্রোটোকল',
upload: 'আপলোড',
uploadSubmit: 'ইহাকে সার্ভারে প্রেরন কর',
image: 'ছবির লেবেল যুক্ত কর',
flash: 'ফ্লাশ লেবেল যুক্ত কর',
form: 'ফর্ম',
checkbox: 'চেক বাক্স',
radio: 'রেডিও বাটন',
textField: 'টেক্সট ফীল্ড',
textarea: 'টেক্সট এরিয়া',
hiddenField: 'গুপ্ত ফীল্ড',
button: 'বাটন',
select: 'বাছাই ফীল্ড',
imageButton: 'ছবির বাটন',
notSet: '<সেট নেই>',
id: 'আইডি',
name: 'নাম',
langDir: 'ভাষা লেখার দিক',
langDirLtr: 'বাম থেকে ডান (LTR)',
langDirRtl: 'ডান থেকে বাম (RTL)',
langCode: 'ভাষা কোড',
longDescr: 'URL এর লম্বা বর্ণনা',
cssClass: 'স্টাইল-শীট ক্লাস',
advisoryTitle: 'পরামর্শ শীর্ষক',
cssStyle: 'স্টাইল',
ok: 'ওকে',
cancel: 'বাতিল',
close: 'Close', // MISSING
preview: 'প্রিভিউ',
resize: 'Resize', // MISSING
generalTab: 'General', // MISSING
advancedTab: 'এডভান্সড',
validateNumberFailed: 'This value is not a number.', // MISSING
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?', // MISSING
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?', // MISSING
options: 'Options', // MISSING
target: 'টার্গেট',
targetNew: 'New Window (_blank)', // MISSING
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Same Window (_self)', // MISSING
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'বাম থেকে ডান (LTR)',
langDirRTL: 'ডান থেকে বাম (RTL)',
styles: 'স্টাইল',
cssClasses: 'স্টাইল-শীট ক্লাস',
width: 'প্রস্থ',
height: 'দৈর্ঘ্য',
align: 'এলাইন',
alignLeft: 'বামে',
alignRight: 'ডানে',
alignCenter: 'মাঝখানে',
alignTop: 'উপর',
alignMiddle: 'মধ্য',
alignBottom: 'নীচে',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Height must be a number.', // MISSING
invalidWidth: 'Width must be a number.', // MISSING
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>' // MISSING
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Bosnian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'bs' ] = {
// ARIA description.
editor: 'Rich Text Editor', // MISSING
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help', // MISSING
browseServer: 'Browse Server', // MISSING
url: 'URL',
protocol: 'Protokol',
upload: 'Šalji',
uploadSubmit: 'Šalji na server',
image: 'Slika',
flash: 'Flash', // MISSING
form: 'Form', // MISSING
checkbox: 'Checkbox', // MISSING
radio: 'Radio Button', // MISSING
textField: 'Text Field', // MISSING
textarea: 'Textarea', // MISSING
hiddenField: 'Hidden Field', // MISSING
button: 'Button',
select: 'Selection Field', // MISSING
imageButton: 'Image Button', // MISSING
notSet: '<nije podešeno>',
id: 'Id',
name: 'Naziv',
langDir: 'Smjer pisanja',
langDirLtr: 'S lijeva na desno (LTR)',
langDirRtl: 'S desna na lijevo (RTL)',
langCode: 'Jezièni kôd',
longDescr: 'Dugaèki opis URL-a',
cssClass: 'Klase CSS stilova',
advisoryTitle: 'Advisory title',
cssStyle: 'Stil',
ok: 'OK',
cancel: 'Odustani',
close: 'Close', // MISSING
preview: 'Prikaži',
resize: 'Resize', // MISSING
generalTab: 'General', // MISSING
advancedTab: 'Naprednije',
validateNumberFailed: 'This value is not a number.', // MISSING
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?', // MISSING
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?', // MISSING
options: 'Options', // MISSING
target: 'Prozor',
targetNew: 'New Window (_blank)', // MISSING
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Same Window (_self)', // MISSING
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'S lijeva na desno (LTR)',
langDirRTL: 'S desna na lijevo (RTL)',
styles: 'Stil',
cssClasses: 'Klase CSS stilova',
width: 'Širina',
height: 'Visina',
align: 'Poravnanje',
alignLeft: 'Lijevo',
alignRight: 'Desno',
alignCenter: 'Centar',
alignTop: 'Vrh',
alignMiddle: 'Sredina',
alignBottom: 'Dno',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Height must be a number.', // MISSING
invalidWidth: 'Width must be a number.', // MISSING
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>' // MISSING
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Catalan language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'ca' ] = {
// ARIA description.
editor: 'Editor de text enriquit',
editorPanel: 'Panell de l\'editor de text enriquit',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Premeu ALT 0 per ajuda',
browseServer: 'Veure servidor',
url: 'URL',
protocol: 'Protocol',
upload: 'Puja',
uploadSubmit: 'Envia-la al servidor',
image: 'Imatge',
flash: 'Flash',
form: 'Formulari',
checkbox: 'Casella de verificació',
radio: 'Botó d\'opció',
textField: 'Camp de text',
textarea: 'Àrea de text',
hiddenField: 'Camp ocult',
button: 'Botó',
select: 'Camp de selecció',
imageButton: 'Botó d\'imatge',
notSet: '<no definit>',
id: 'Id',
name: 'Nom',
langDir: 'Direcció de l\'idioma',
langDirLtr: 'D\'esquerra a dreta (LTR)',
langDirRtl: 'De dreta a esquerra (RTL)',
langCode: 'Codi d\'idioma',
longDescr: 'Descripció llarga de la URL',
cssClass: 'Classes del full d\'estil',
advisoryTitle: 'Títol consultiu',
cssStyle: 'Estil',
ok: 'D\'acord',
cancel: 'Cancel·la',
close: 'Tanca',
preview: 'Previsualitza',
resize: 'Arrossegueu per redimensionar',
generalTab: 'General',
advancedTab: 'Avançat',
validateNumberFailed: 'Aquest valor no és un número.',
confirmNewPage: 'Els canvis en aquest contingut que no es desin es perdran. Esteu segur que voleu carregar una pàgina nova?',
confirmCancel: 'Algunes opcions s\'han canviat. Esteu segur que voleu tancar el quadre de diàleg?',
options: 'Opcions',
target: 'Destí',
targetNew: 'Nova finestra (_blank)',
targetTop: 'Finestra superior (_top)',
targetSelf: 'Mateixa finestra (_self)',
targetParent: 'Finestra pare (_parent)',
langDirLTR: 'D\'esquerra a dreta (LTR)',
langDirRTL: 'De dreta a esquerra (RTL)',
styles: 'Estil',
cssClasses: 'Classes del full d\'estil',
width: 'Amplada',
height: 'Alçada',
align: 'Alineació',
alignLeft: 'Ajusta a l\'esquerra',
alignRight: 'Ajusta a la dreta',
alignCenter: 'Centre',
alignTop: 'Superior',
alignMiddle: 'Centre',
alignBottom: 'Inferior',
invalidValue : 'Valor no vàlid.',
invalidHeight: 'L\'alçada ha de ser un número.',
invalidWidth: 'L\'amplada ha de ser un número.',
invalidCssLength: 'El valor especificat per als "%1" camps ha de ser un número positiu amb o sense unitat de mesura vàlida de CSS (px, %, in, cm, mm, em, ex, pt o pc).',
invalidHtmlLength: 'El valor especificat per als "%1" camps ha de ser un número positiu amb o sense unitat de mesura vàlida d\'HTML (px o %).',
invalidInlineStyle: 'El valor especificat per l\'estil en línia ha de constar d\'una o més tuples amb el format "name: value", separats per punt i coma.',
cssLengthTooltip: 'Introduïu un número per un valor en píxels o un número amb una unitat vàlida de CSS (px, %, in, cm, mm, em, ex, pt o pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, no disponible</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Czech language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'cs' ] = {
// ARIA description.
editor: 'Textový editor',
editorPanel: 'Panel textového editoru',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Stiskněte ALT 0 pro nápovědu',
browseServer: 'Vybrat na serveru',
url: 'URL',
protocol: 'Protokol',
upload: 'Odeslat',
uploadSubmit: 'Odeslat na server',
image: 'Obrázek',
flash: 'Flash',
form: 'Formulář',
checkbox: 'Zaškrtávací políčko',
radio: 'Přepínač',
textField: 'Textové pole',
textarea: 'Textová oblast',
hiddenField: 'Skryté pole',
button: 'Tlačítko',
select: 'Seznam',
imageButton: 'Obrázkové tlačítko',
notSet: '<nenastaveno>',
id: 'Id',
name: 'Jméno',
langDir: 'Směr jazyka',
langDirLtr: 'Zleva doprava (LTR)',
langDirRtl: 'Zprava doleva (RTL)',
langCode: 'Kód jazyka',
longDescr: 'Dlouhý popis URL',
cssClass: 'Třída stylu',
advisoryTitle: 'Pomocný titulek',
cssStyle: 'Styl',
ok: 'OK',
cancel: 'Zrušit',
close: 'Zavřít',
preview: 'Náhled',
resize: 'Uchopit pro změnu velikosti',
generalTab: 'Obecné',
advancedTab: 'Rozšířené',
validateNumberFailed: 'Zadaná hodnota není číselná.',
confirmNewPage: 'Jakékoliv neuložené změny obsahu budou ztraceny. Skutečně chcete otevřít novou stránku?',
confirmCancel: 'Některá z nastavení byla změněna. Skutečně chcete zavřít dialogové okno?',
options: 'Nastavení',
target: 'Cíl',
targetNew: 'Nové okno (_blank)',
targetTop: 'Okno nejvyšší úrovně (_top)',
targetSelf: 'Stejné okno (_self)',
targetParent: 'Rodičovské okno (_parent)',
langDirLTR: 'Zleva doprava (LTR)',
langDirRTL: 'Zprava doleva (RTL)',
styles: 'Styly',
cssClasses: 'Třídy stylů',
width: 'Šířka',
height: 'Výška',
align: 'Zarovnání',
alignLeft: 'Vlevo',
alignRight: 'Vpravo',
alignCenter: 'Na střed',
alignTop: 'Nahoru',
alignMiddle: 'Na střed',
alignBottom: 'Dolů',
invalidValue : 'Neplatná hodnota.',
invalidHeight: 'Zadaná výška musí být číslo.',
invalidWidth: 'Šířka musí být číslo.',
invalidCssLength: 'Hodnota určená pro pole "%1" musí být kladné číslo bez nebo s platnou jednotkou míry CSS (px, %, in, cm, mm, em, ex, pt, nebo pc).',
invalidHtmlLength: 'Hodnota určená pro pole "%1" musí být kladné číslo bez nebo s platnou jednotkou míry HTML (px nebo %).',
invalidInlineStyle: 'Hodnota určená pro řádkový styl se musí skládat z jedné nebo více n-tic ve formátu "název : hodnota", oddělené středníky',
cssLengthTooltip: 'Zadejte číslo jako hodnotu v pixelech nebo číslo s platnou jednotkou CSS (px, %, v cm, mm, em, ex, pt, nebo pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nedostupné</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Welsh language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'cy' ] = {
// ARIA description.
editor: 'Golygydd Testun Cyfoethog',
editorPanel: 'Panel Golygydd Testun Cyfoethog',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Gwasgwch ALT 0 am gymorth',
browseServer: 'Pori\'r Gweinydd',
url: 'URL',
protocol: 'Protocol',
upload: 'Lanlwytho',
uploadSubmit: 'Anfon i\'r Gweinydd',
image: 'Delwedd',
flash: 'Flash',
form: 'Ffurflen',
checkbox: 'Blwch ticio',
radio: 'Botwm Radio',
textField: 'Maes Testun',
textarea: 'Ardal Testun',
hiddenField: 'Maes Cudd',
button: 'Botwm',
select: 'Maes Dewis',
imageButton: 'Botwm Delwedd',
notSet: '<heb osod>',
id: 'Id',
name: 'Name',
langDir: 'Cyfeiriad Iaith',
langDirLtr: 'Chwith i\'r Dde (LTR)',
langDirRtl: 'Dde i\'r Chwith (RTL)',
langCode: 'Cod Iaith',
longDescr: 'URL Disgrifiad Hir',
cssClass: 'Dosbarthiadau Dalen Arddull',
advisoryTitle: 'Teitl Cynghorol',
cssStyle: 'Arddull',
ok: 'Iawn',
cancel: 'Diddymu',
close: 'Cau',
preview: 'Rhagolwg',
resize: 'Ailfeintio',
generalTab: 'Cyffredinol',
advancedTab: 'Uwch',
validateNumberFailed: '\'Dyw\'r gwerth hwn ddim yn rhif.',
confirmNewPage: 'Byddwch chi\'n colli unrhyw newidiadau i\'r cynnwys sydd heb eu cadw. Ydych am barhau i lwytho tudalen newydd?',
confirmCancel: 'Cafodd rhai o\'r opsiynau eu newid. Ydych chi wir am gau\'r deialog?',
options: 'Opsiynau',
target: 'Targed',
targetNew: 'Ffenest Newydd (_blank)',
targetTop: 'Ffenest ar y Brig (_top)',
targetSelf: 'Yr un Ffenest (_self)',
targetParent: 'Ffenest y Rhiant (_parent)',
langDirLTR: 'Chwith i\'r Dde (LTR)',
langDirRTL: 'Dde i\'r Chwith (RTL)',
styles: 'Arddull',
cssClasses: 'Dosbarthiadau Dalen Arddull',
width: 'Lled',
height: 'Uchder',
align: 'Alinio',
alignLeft: 'Chwith',
alignRight: 'Dde',
alignCenter: 'Canol',
alignTop: 'Brig',
alignMiddle: 'Canol',
alignBottom: 'Gwaelod',
invalidValue : 'Gwerth annilys.',
invalidHeight: 'Mae\'n rhaid i\'r uchder fod yn rhif.',
invalidWidth: 'Mae\'n rhaid i\'r lled fod yn rhif.',
invalidCssLength: 'Mae\'n rhaid i\'r gwerth ar gyfer maes "%1" fod yn rhif positif gyda neu heb uned fesuriad CSS dilys (px, %, in, cm, mm, em, ex, pt, neu pc).',
invalidHtmlLength: 'Mae\'n rhaid i\'r gwerth ar gyfer maes "%1" fod yn rhif positif gyda neu heb uned fesuriad HTML dilys (px neu %).',
invalidInlineStyle: 'Mae\'n rhaid i\'r gwerth ar gyfer arddull mewn-llinell gynnwys un set neu fwy ar y fformat "enw : gwerth", wedi\'u gwahanu gyda hanner colon.',
cssLengthTooltip: 'Rhowch rif am werth mewn picsel neu rhif gydag uned CSS dilys (px, %, in, cm, mm, em, pt neu pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, ddim ar gael</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Danish language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'da' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Tryk ALT 0 for hjælp',
browseServer: 'Gennemse...',
url: 'URL',
protocol: 'Protokol',
upload: 'Upload',
uploadSubmit: 'Upload',
image: 'Indsæt billede',
flash: 'Indsæt Flash',
form: 'Indsæt formular',
checkbox: 'Indsæt afkrydsningsfelt',
radio: 'Indsæt alternativknap',
textField: 'Indsæt tekstfelt',
textarea: 'Indsæt tekstboks',
hiddenField: 'Indsæt skjult felt',
button: 'Indsæt knap',
select: 'Indsæt liste',
imageButton: 'Indsæt billedknap',
notSet: '<intet valgt>',
id: 'Id',
name: 'Navn',
langDir: 'Tekstretning',
langDirLtr: 'Fra venstre mod højre (LTR)',
langDirRtl: 'Fra højre mod venstre (RTL)',
langCode: 'Sprogkode',
longDescr: 'Udvidet beskrivelse',
cssClass: 'Typografiark (CSS)',
advisoryTitle: 'Titel',
cssStyle: 'Typografi (CSS)',
ok: 'OK',
cancel: 'Annullér',
close: 'Luk',
preview: 'Forhåndsvisning',
resize: 'Træk for at skalere',
generalTab: 'Generelt',
advancedTab: 'Avanceret',
validateNumberFailed: 'Værdien er ikke et tal.',
confirmNewPage: 'Alt indhold, der ikke er blevet gemt, vil gå tabt. Er du sikker på, at du vil indlæse en ny side?',
confirmCancel: 'Nogle af indstillingerne er blevet ændret. Er du sikker på, at du vil lukke vinduet?',
options: 'Vis muligheder',
target: 'Mål',
targetNew: 'Nyt vindue (_blank)',
targetTop: 'Øverste vindue (_top)',
targetSelf: 'Samme vindue (_self)',
targetParent: 'Samme vindue (_parent)',
langDirLTR: 'Venstre til højre (LTR)',
langDirRTL: 'Højre til venstre (RTL)',
styles: 'Style',
cssClasses: 'Stylesheetklasser',
width: 'Bredde',
height: 'Højde',
align: 'Justering',
alignLeft: 'Venstre',
alignRight: 'Højre',
alignCenter: 'Centreret',
alignTop: 'Øverst',
alignMiddle: 'Centreret',
alignBottom: 'Nederst',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Højde skal være et tal.',
invalidWidth: 'Bredde skal være et tal.',
invalidCssLength: 'Værdien specificeret for "%1" feltet skal være et positivt nummer med eller uden en CSS måleenhed (px, %, in, cm, mm, em, ex, pt, eller pc).',
invalidHtmlLength: 'Værdien specificeret for "%1" feltet skal være et positivt nummer med eller uden en CSS måleenhed (px eller %).',
invalidInlineStyle: 'Værdien specificeret for inline style skal indeholde en eller flere elementer med et format som "name:value", separeret af semikoloner',
cssLengthTooltip: 'Indsæt en numerisk værdi i pixel eller nummer med en gyldig CSS værdi (px, %, in, cm, mm, em, ex, pt, eller pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, ikke tilgængelig</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* German language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'de' ] = {
// ARIA description.
editor: 'WYSIWYG-Editor',
editorPanel: 'WYSIWYG-Editor-Leiste',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Drücken Sie ALT 0 für Hilfe',
browseServer: 'Server durchsuchen',
url: 'URL',
protocol: 'Protokoll',
upload: 'Hochladen',
uploadSubmit: 'Zum Server senden',
image: 'Bild',
flash: 'Flash',
form: 'Formular',
checkbox: 'Checkbox',
radio: 'Radiobutton',
textField: 'Textfeld einzeilig',
textarea: 'Textfeld mehrzeilig',
hiddenField: 'Verstecktes Feld',
button: 'Klickbutton',
select: 'Auswahlfeld',
imageButton: 'Bildbutton',
notSet: '<nichts>',
id: 'ID',
name: 'Name',
langDir: 'Schreibrichtung',
langDirLtr: 'Links nach Rechts (LTR)',
langDirRtl: 'Rechts nach Links (RTL)',
langCode: 'Sprachenkürzel',
longDescr: 'Langform URL',
cssClass: 'Stylesheet Klasse',
advisoryTitle: 'Titel Beschreibung',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Abbrechen',
close: 'Schließen',
preview: 'Vorschau',
resize: 'Zum Vergrößern ziehen',
generalTab: 'Allgemein',
advancedTab: 'Erweitert',
validateNumberFailed: 'Dieser Wert ist keine Nummer.',
confirmNewPage: 'Alle nicht gespeicherten Änderungen gehen verlohren. Sind Sie sicher die neue Seite zu laden?',
confirmCancel: 'Einige Optionen wurden geändert. Wollen Sie den Dialog dennoch schließen?',
options: 'Optionen',
target: 'Zielseite',
targetNew: 'Neues Fenster (_blank)',
targetTop: 'Oberstes Fenster (_top)',
targetSelf: 'Gleiches Fenster (_self)',
targetParent: 'Oberes Fenster (_parent)',
langDirLTR: 'Links nach Rechts (LNR)',
langDirRTL: 'Rechts nach Links (RNL)',
styles: 'Style',
cssClasses: 'Stylesheet Klasse',
width: 'Breite',
height: 'Höhe',
align: 'Ausrichtung',
alignLeft: 'Links',
alignRight: 'Rechts',
alignCenter: 'Zentriert',
alignTop: 'Oben',
alignMiddle: 'Mitte',
alignBottom: 'Unten',
invalidValue : 'Ungültiger Wert.',
invalidHeight: 'Höhe muss eine Zahl sein.',
invalidWidth: 'Breite muss eine Zahl sein.',
invalidCssLength: 'Wert spezifiziert für "%1" Feld muss ein positiver numerischer Wert sein mit oder ohne korrekte CSS Messeinheit (px, %, in, cm, mm, em, ex, pt oder pc).',
invalidHtmlLength: 'Wert spezifiziert für "%1" Feld muss ein positiver numerischer Wert sein mit oder ohne korrekte HTML Messeinheit (px oder %).',
invalidInlineStyle: 'Wert spezifiziert für inline Stilart muss enthalten ein oder mehr Tupels mit dem Format "Name : Wert" getrennt mit Semikolons.',
cssLengthTooltip: 'Gebe eine Zahl ein für ein Wert in pixels oder eine Zahl mit einer korrekten CSS Messeinheit (px, %, in, cm, mm, em, ex, pt oder pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nicht verfügbar</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Greek language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'el' ] = {
// ARIA description.
editor: 'Επεξεργαστής Πλούσιου Κειμένου',
editorPanel: 'Πίνακας Επεξεργαστή Πλούσιου Κειμένου',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Πατήστε το ALT 0 για βοήθεια',
browseServer: 'Εξερεύνηση Διακομιστή',
url: 'URL',
protocol: 'Πρωτόκολλο',
upload: 'Αποστολή',
uploadSubmit: 'Αποστολή στον Διακομιστή',
image: 'Εικόνα',
flash: 'Flash',
form: 'Φόρμα',
checkbox: 'Κουτί Επιλογής',
radio: 'Κουμπί Επιλογής',
textField: 'Πεδίο Κειμένου',
textarea: 'Περιοχή Κειμένου',
hiddenField: 'Κρυφό Πεδίο',
button: 'Κουμπί',
select: 'Πεδίο Επιλογής',
imageButton: 'Κουμπί Εικόνας',
notSet: '<δεν έχει ρυθμιστεί>',
id: 'Id',
name: 'Όνομα',
langDir: 'Κατεύθυνση Κειμένου',
langDirLtr: 'Αριστερά προς Δεξιά (LTR)',
langDirRtl: 'Δεξιά προς Αριστερά (RTL)',
langCode: 'Κωδικός Γλώσσας',
longDescr: 'Αναλυτική Περιγραφή URL',
cssClass: 'Κλάσεις Φύλλων Στυλ',
advisoryTitle: 'Ενδεικτικός Τίτλος',
cssStyle: 'Μορφή Κειμένου',
ok: 'OK',
cancel: 'Ακύρωση',
close: 'Κλείσιμο',
preview: 'Προεπισκόπηση',
resize: 'Αλλαγή Μεγέθους',
generalTab: 'Γενικά',
advancedTab: 'Για Προχωρημένους',
validateNumberFailed: 'Αυτή η τιμή δεν είναι αριθμός.',
confirmNewPage: 'Οι όποιες αλλαγές στο περιεχόμενο θα χαθούν. Είσαστε σίγουροι ότι θέλετε να φορτώσετε μια νέα σελίδα;',
confirmCancel: 'Μερικές επιλογές έχουν αλλάξει. Είσαστε σίγουροι ότι θέλετε να κλείσετε το παράθυρο διαλόγου;',
options: 'Επιλογές',
target: 'Προορισμός',
targetNew: 'Νέο Παράθυρο (_blank)',
targetTop: 'Αρχική Περιοχή (_top)',
targetSelf: 'Ίδιο Παράθυρο (_self)',
targetParent: 'Γονεϊκό Παράθυρο (_parent)',
langDirLTR: 'Αριστερά προς Δεξιά (LTR)',
langDirRTL: 'Δεξιά προς Αριστερά (RTL)',
styles: 'Μορφή',
cssClasses: 'Κλάσεις Φύλλων Στυλ',
width: 'Πλάτος',
height: 'Ύψος',
align: 'Στοίχιση',
alignLeft: 'Αριστερά',
alignRight: 'Δεξιά',
alignCenter: 'Κέντρο',
alignTop: 'Πάνω',
alignMiddle: 'Μέση',
alignBottom: 'Κάτω',
invalidValue : 'Μη έγκυρη τιμή.',
invalidHeight: 'Το ύψος πρέπει να είναι ένας αριθμός.',
invalidWidth: 'Το πλάτος πρέπει να είναι ένας αριθμός.',
invalidCssLength: 'Η τιμή που ορίζεται για το πεδίο "%1" πρέπει να είναι ένας θετικός αριθμός με ή χωρίς μια έγκυρη μονάδα μέτρησης CSS (px, %, in, cm, mm, em, ex, pt, ή pc).',
invalidHtmlLength: 'Η τιμή που ορίζεται για το πεδίο "%1" πρέπει να είναι ένας θετικός αριθμός με ή χωρίς μια έγκυρη μονάδα μέτρησης HTML (px ή %).',
invalidInlineStyle: 'Η τιμή για το εν σειρά στυλ πρέπει να περιέχει ένα ή περισσότερα ζεύγη με την μορφή "όνομα: τιμή" διαχωρισμένα με Ελληνικό ερωτηματικό.',
cssLengthTooltip: 'Εισάγεται μια τιμή σε pixel ή έναν αριθμό μαζί με μια έγκυρη μονάδα μέτρησης CSS (px, %, in, cm, mm, em, ex, pt, ή pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, δεν είναι διαθέσιμο</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* English (Australia) language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'en-au' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help', // MISSING
browseServer: 'Browse Server',
url: 'URL',
protocol: 'Protocol',
upload: 'Upload',
uploadSubmit: 'Send it to the Server',
image: 'Image',
flash: 'Flash',
form: 'Form',
checkbox: 'Checkbox',
radio: 'Radio Button',
textField: 'Text Field',
textarea: 'Textarea',
hiddenField: 'Hidden Field',
button: 'Button',
select: 'Selection Field',
imageButton: 'Image Button',
notSet: '<not set>',
id: 'Id',
name: 'Name',
langDir: 'Language Direction',
langDirLtr: 'Left to Right (LTR)',
langDirRtl: 'Right to Left (RTL)',
langCode: 'Language Code',
longDescr: 'Long Description URL',
cssClass: 'Stylesheet Classes',
advisoryTitle: 'Advisory Title',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Cancel',
close: 'Close', // MISSING
preview: 'Preview',
resize: 'Resize', // MISSING
generalTab: 'General',
advancedTab: 'Advanced',
validateNumberFailed: 'This value is not a number.',
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?',
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?',
options: 'Options', // MISSING
target: 'Target',
targetNew: 'New Window (_blank)', // MISSING
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Same Window (_self)', // MISSING
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'Left to Right (LTR)',
langDirRTL: 'Right to Left (RTL)',
styles: 'Style',
cssClasses: 'Stylesheet Classes',
width: 'Width', // MISSING
height: 'Height', // MISSING
align: 'Align',
alignLeft: 'Left', // MISSING
alignRight: 'Right', // MISSING
alignCenter: 'Centre',
alignTop: 'Top', // MISSING
alignMiddle: 'Middle', // MISSING
alignBottom: 'Bottom', // MISSING
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Height must be a number.', // MISSING
invalidWidth: 'Width must be a number.', // MISSING
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>' // MISSING
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* English (Canadian) language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'en-ca' ] = {
// ARIA description.
editor: 'Rich Text Editor', // MISSING
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help', // MISSING
browseServer: 'Browse Server',
url: 'URL',
protocol: 'Protocol',
upload: 'Upload',
uploadSubmit: 'Send it to the Server',
image: 'Image',
flash: 'Flash',
form: 'Form',
checkbox: 'Checkbox',
radio: 'Radio Button',
textField: 'Text Field',
textarea: 'Textarea',
hiddenField: 'Hidden Field',
button: 'Button',
select: 'Selection Field',
imageButton: 'Image Button',
notSet: '<not set>',
id: 'Id',
name: 'Name',
langDir: 'Language Direction',
langDirLtr: 'Left to Right (LTR)',
langDirRtl: 'Right to Left (RTL)',
langCode: 'Language Code',
longDescr: 'Long Description URL',
cssClass: 'Stylesheet Classes',
advisoryTitle: 'Advisory Title',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Cancel',
close: 'Close', // MISSING
preview: 'Preview',
resize: 'Resize', // MISSING
generalTab: 'General',
advancedTab: 'Advanced',
validateNumberFailed: 'This value is not a number.',
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?',
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?',
options: 'Options', // MISSING
target: 'Target',
targetNew: 'New Window (_blank)', // MISSING
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Same Window (_self)', // MISSING
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'Left to Right (LTR)',
langDirRTL: 'Right to Left (RTL)',
styles: 'Style',
cssClasses: 'Stylesheet Classes',
width: 'Width', // MISSING
height: 'Height', // MISSING
align: 'Align',
alignLeft: 'Left', // MISSING
alignRight: 'Right', // MISSING
alignCenter: 'Centre',
alignTop: 'Top', // MISSING
alignMiddle: 'Middle', // MISSING
alignBottom: 'Bottom', // MISSING
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Height must be a number.', // MISSING
invalidWidth: 'Width must be a number.', // MISSING
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>' // MISSING
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* English (United Kingdom) language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'en-gb' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help',
browseServer: 'Browse Server',
url: 'URL',
protocol: 'Protocol',
upload: 'Upload',
uploadSubmit: 'Send it to the Server',
image: 'Image',
flash: 'Flash',
form: 'Form',
checkbox: 'Checkbox',
radio: 'Radio Button',
textField: 'Text Field',
textarea: 'Textarea',
hiddenField: 'Hidden Field',
button: 'Button',
select: 'Selection Field',
imageButton: 'Image Button',
notSet: '<not set>',
id: 'Id',
name: 'Name',
langDir: 'Language Direction',
langDirLtr: 'Left to Right (LTR)',
langDirRtl: 'Right to Left (RTL)',
langCode: 'Language Code',
longDescr: 'Long Description URL',
cssClass: 'Stylesheet Classes',
advisoryTitle: 'Advisory Title',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Cancel',
close: 'Close',
preview: 'Preview',
resize: 'Drag to resize',
generalTab: 'General',
advancedTab: 'Advanced',
validateNumberFailed: 'This value is not a number.',
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?',
confirmCancel: 'You have changed some options. Are you sure you want to close the dialogue window?',
options: 'Options',
target: 'Target',
targetNew: 'New Window (_blank)',
targetTop: 'Topmost Window (_top)',
targetSelf: 'Same Window (_self)',
targetParent: 'Parent Window (_parent)',
langDirLTR: 'Left to Right (LTR)',
langDirRTL: 'Right to Left (RTL)',
styles: 'Style',
cssClasses: 'Stylesheet Classes',
width: 'Width',
height: 'Height',
align: 'Align',
alignLeft: 'Left',
alignRight: 'Right',
alignCenter: 'Centre',
alignTop: 'Top',
alignMiddle: 'Middle',
alignBottom: 'Bottom',
invalidValue : 'Invalid value.',
invalidHeight: 'Height must be a number.',
invalidWidth: 'Width must be a number.',
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).',
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.',
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object for the English
* language. This is the base file for all translations.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'en' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Press ALT 0 for help',
browseServer: 'Browse Server',
url: 'URL',
protocol: 'Protocol',
upload: 'Upload',
uploadSubmit: 'Send it to the Server',
image: 'Image',
flash: 'Flash',
form: 'Form',
checkbox: 'Checkbox',
radio: 'Radio Button',
textField: 'Text Field',
textarea: 'Textarea',
hiddenField: 'Hidden Field',
button: 'Button',
select: 'Selection Field',
imageButton: 'Image Button',
notSet: '<not set>',
id: 'Id',
name: 'Name',
langDir: 'Language Direction',
langDirLtr: 'Left to Right (LTR)',
langDirRtl: 'Right to Left (RTL)',
langCode: 'Language Code',
longDescr: 'Long Description URL',
cssClass: 'Stylesheet Classes',
advisoryTitle: 'Advisory Title',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Cancel',
close: 'Close',
preview: 'Preview',
resize: 'Resize',
generalTab: 'General',
advancedTab: 'Advanced',
validateNumberFailed: 'This value is not a number.',
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?',
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?',
options: 'Options',
target: 'Target',
targetNew: 'New Window (_blank)',
targetTop: 'Topmost Window (_top)',
targetSelf: 'Same Window (_self)',
targetParent: 'Parent Window (_parent)',
langDirLTR: 'Left to Right (LTR)',
langDirRTL: 'Right to Left (RTL)',
styles: 'Style',
cssClasses: 'Stylesheet Classes',
width: 'Width',
height: 'Height',
align: 'Alignment',
alignLeft: 'Left',
alignRight: 'Right',
alignCenter: 'Center',
alignTop: 'Top',
alignMiddle: 'Middle',
alignBottom: 'Bottom',
invalidValue : 'Invalid value.',
invalidHeight: 'Height must be a number.',
invalidWidth: 'Width must be a number.',
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).',
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.',
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Esperanto language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'eo' ] = {
// ARIA description.
editor: 'Redaktilo por Riĉiga Teksto',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Premu ALT 0 por helpilo',
browseServer: 'Foliumi en la Servilo',
url: 'URL',
protocol: 'Protokolo',
upload: 'Alŝuti',
uploadSubmit: 'Sendu al Servilo',
image: 'Bildo',
flash: 'Flaŝo',
form: 'Formularo',
checkbox: 'Markobutono',
radio: 'Radiobutono',
textField: 'Teksta kampo',
textarea: 'Teksta Areo',
hiddenField: 'Kaŝita Kampo',
button: 'Butono',
select: 'Elekta Kampo',
imageButton: 'Bildbutono',
notSet: '<Defaŭlta>',
id: 'Id',
name: 'Nomo',
langDir: 'Skribdirekto',
langDirLtr: 'De maldekstro dekstren (LTR)',
langDirRtl: 'De dekstro maldekstren (RTL)',
langCode: 'Lingva Kodo',
longDescr: 'URL de Longa Priskribo',
cssClass: 'Klasoj de Stilfolioj',
advisoryTitle: 'Priskriba Titolo',
cssStyle: 'Stilo',
ok: 'Akcepti',
cancel: 'Rezigni',
close: 'Fermi',
preview: 'Vidigi Aspekton',
resize: 'Movigi por ŝanĝi la grandon',
generalTab: 'Ĝenerala',
advancedTab: 'Speciala',
validateNumberFailed: 'Tiu valoro ne estas nombro.',
confirmNewPage: 'La neregistritaj ŝanĝoj estas perdotaj. Ĉu vi certas, ke vi volas ŝargi novan paĝon?',
confirmCancel: 'Iuj opcioj esta ŝanĝitaj. Ĉu vi certas, ke vi volas fermi la dialogon?',
options: 'Opcioj',
target: 'Celo',
targetNew: 'Nova Fenestro (_blank)',
targetTop: 'Supra Fenestro (_top)',
targetSelf: 'Sama Fenestro (_self)',
targetParent: 'Patra Fenestro (_parent)',
langDirLTR: 'De maldekstro dekstren (LTR)',
langDirRTL: 'De dekstro maldekstren (RTL)',
styles: 'Stilo',
cssClasses: 'Stilfoliaj Klasoj',
width: 'Larĝo',
height: 'Alto',
align: 'Ĝisrandigo',
alignLeft: 'Maldekstre',
alignRight: 'Dekstre',
alignCenter: 'Centre',
alignTop: 'Supre',
alignMiddle: 'Centre',
alignBottom: 'Malsupre',
invalidValue : 'Nevalida Valoro',
invalidHeight: 'Alto devas esti nombro.',
invalidWidth: 'Larĝo devas esti nombro.',
invalidCssLength: 'La valoro indikita por la "%1" kampo devas esti pozitiva nombro kun aŭ sen valida CSSmezurunuo (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'La valoro indikita por la "%1" kampo devas esti pozitiva nombro kun aŭ sen valida HTMLmezurunuo (px or %).',
invalidInlineStyle: 'La valoro indikita por la enlinia stilo devas konsisti el unu aŭ pluraj elementoj kun la formato de "nomo : valoro", apartigitaj per punktokomoj.',
cssLengthTooltip: 'Entajpu nombron por rastrumera valoro aŭ nombron kun valida CSSunuo (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nehavebla</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Spanish language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'es' ] = {
// ARIA description.
editor: 'Editor de texto enriquecido',
editorPanel: 'Panel del Editor de Texto Enriquecido',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Pulse ALT 0 para ayuda',
browseServer: 'Ver Servidor',
url: 'URL',
protocol: 'Protocolo',
upload: 'Cargar',
uploadSubmit: 'Enviar al Servidor',
image: 'Imagen',
flash: 'Flash',
form: 'Formulario',
checkbox: 'Casilla de Verificación',
radio: 'Botones de Radio',
textField: 'Campo de Texto',
textarea: 'Area de Texto',
hiddenField: 'Campo Oculto',
button: 'Botón',
select: 'Campo de Selección',
imageButton: 'Botón Imagen',
notSet: '<No definido>',
id: 'Id',
name: 'Nombre',
langDir: 'Orientación',
langDirLtr: 'Izquierda a Derecha (LTR)',
langDirRtl: 'Derecha a Izquierda (RTL)',
langCode: 'Cód. de idioma',
longDescr: 'Descripción larga URL',
cssClass: 'Clases de hojas de estilo',
advisoryTitle: 'Título',
cssStyle: 'Estilo',
ok: 'Aceptar',
cancel: 'Cancelar',
close: 'Cerrar',
preview: 'Previsualización',
resize: 'Arrastre para redimensionar',
generalTab: 'General',
advancedTab: 'Avanzado',
validateNumberFailed: 'El valor no es un número.',
confirmNewPage: 'Cualquier cambio que no se haya guardado se perderá.\r\n¿Está seguro de querer crear una nueva página?',
confirmCancel: 'Algunas de las opciones se han cambiado.\r\n¿Está seguro de querer cerrar el diálogo?',
options: 'Opciones',
target: 'Destino',
targetNew: 'Nueva ventana (_blank)',
targetTop: 'Ventana principal (_top)',
targetSelf: 'Misma ventana (_self)',
targetParent: 'Ventana padre (_parent)',
langDirLTR: 'Izquierda a derecha (LTR)',
langDirRTL: 'Derecha a izquierda (RTL)',
styles: 'Estilos',
cssClasses: 'Clase de la hoja de estilos',
width: 'Anchura',
height: 'Altura',
align: 'Alineación',
alignLeft: 'Izquierda',
alignRight: 'Derecha',
alignCenter: 'Centrado',
alignTop: 'Tope',
alignMiddle: 'Centro',
alignBottom: 'Pie',
invalidValue : 'Valor no válido',
invalidHeight: 'Altura debe ser un número.',
invalidWidth: 'Anchura debe ser un número.',
invalidCssLength: 'El valor especificado para el campo "%1" debe ser un número positivo, incluyendo optionalmente una unidad de medida CSS válida (px, %, in, cm, mm, em, ex, pt, o pc).',
invalidHtmlLength: 'El valor especificado para el campo "%1" debe ser un número positivo, incluyendo optionalmente una unidad de medida HTML válida (px o %).',
invalidInlineStyle: 'El valor especificado para el estilo debe consistir en uno o más pares con el formato "nombre: valor", separados por punto y coma.',
cssLengthTooltip: 'Introduca un número para el valor en pixels o un número con una unidad de medida CSS válida (px, %, in, cm, mm, em, ex, pt, o pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, no disponible</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Estonian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'et' ] = {
// ARIA description.
editor: 'Rikkalik tekstiredaktor',
editorPanel: 'Rikkaliku tekstiredaktori paneel',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Abi saamiseks vajuta ALT 0',
browseServer: 'Serveri sirvimine',
url: 'URL',
protocol: 'Protokoll',
upload: 'Laadi üles',
uploadSubmit: 'Saada serverisse',
image: 'Pilt',
flash: 'Flash',
form: 'Vorm',
checkbox: 'Märkeruut',
radio: 'Raadionupp',
textField: 'Tekstilahter',
textarea: 'Tekstiala',
hiddenField: 'Varjatud lahter',
button: 'Nupp',
select: 'Valiklahter',
imageButton: 'Piltnupp',
notSet: '<määramata>',
id: 'ID',
name: 'Nimi',
langDir: 'Keele suund',
langDirLtr: 'Vasakult paremale (LTR)',
langDirRtl: 'Paremalt vasakule (RTL)',
langCode: 'Keele kood',
longDescr: 'Pikk kirjeldus URL',
cssClass: 'Stiilistiku klassid',
advisoryTitle: 'Soovituslik pealkiri',
cssStyle: 'Laad',
ok: 'Olgu',
cancel: 'Loobu',
close: 'Sulge',
preview: 'Eelvaade',
resize: 'Suuruse muutmiseks lohista',
generalTab: 'Üldine',
advancedTab: 'Täpsemalt',
validateNumberFailed: 'See väärtus pole number.',
confirmNewPage: 'Kõik salvestamata muudatused lähevad kaotsi. Kas oled kindel, et tahad laadida uue lehe?',
confirmCancel: 'Mõned valikud on muudetud. Kas oled kindel, et tahad dialoogi sulgeda?',
options: 'Valikud',
target: 'Sihtkoht',
targetNew: 'Uus aken (_blank)',
targetTop: 'Kõige ülemine aken (_top)',
targetSelf: 'Sama aken (_self)',
targetParent: 'Vanemaken (_parent)',
langDirLTR: 'Vasakult paremale (LTR)',
langDirRTL: 'Paremalt vasakule (RTL)',
styles: 'Stiili',
cssClasses: 'Stiililehe klassid',
width: 'Laius',
height: 'Kõrgus',
align: 'Joondus',
alignLeft: 'Vasak',
alignRight: 'Paremale',
alignCenter: 'Kesk',
alignTop: 'Üles',
alignMiddle: 'Keskele',
alignBottom: 'Alla',
invalidValue : 'Vigane väärtus.',
invalidHeight: 'Kõrgus peab olema number.',
invalidWidth: 'Laius peab olema number.',
invalidCssLength: '"%1" välja jaoks määratud väärtus peab olema positiivne täisarv CSS ühikuga (px, %, in, cm, mm, em, ex, pt või pc) või ilma.',
invalidHtmlLength: '"%1" välja jaoks määratud väärtus peab olema positiivne täisarv HTML ühikuga (px või %) või ilma.',
invalidInlineStyle: 'Reasisese stiili määrangud peavad koosnema paarisväärtustest (tuples), mis on semikoolonitega eraldatult järgnevas vormingus: "nimi : väärtus".',
cssLengthTooltip: 'Sisesta väärtus pikslites või number koos sobiva CSS-i ühikuga (px, %, in, cm, mm, em, ex, pt või pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, pole saadaval</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Basque language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'eu' ] = {
// ARIA description.
editor: 'Testu Aberastuko Editorea',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'ALT 0 sakatu laguntza jasotzeko',
browseServer: 'Zerbitzaria arakatu',
url: 'URL',
protocol: 'Protokoloa',
upload: 'Gora kargatu',
uploadSubmit: 'Zerbitzarira bidali',
image: 'Irudia',
flash: 'Flasha',
form: 'Formularioa',
checkbox: 'Kontrol-laukia',
radio: 'Aukera-botoia',
textField: 'Testu Eremua',
textarea: 'Testu-area',
hiddenField: 'Ezkutuko Eremua',
button: 'Botoia',
select: 'Hautespen Eremua',
imageButton: 'Irudi Botoia',
notSet: '<Ezarri gabe>',
id: 'Id',
name: 'Izena',
langDir: 'Hizkuntzaren Norabidea',
langDirLtr: 'Ezkerretik Eskumara(LTR)',
langDirRtl: 'Eskumatik Ezkerrera (RTL)',
langCode: 'Hizkuntza Kodea',
longDescr: 'URL Deskribapen Luzea',
cssClass: 'Estilo-orriko Klaseak',
advisoryTitle: 'Izenburua',
cssStyle: 'Estiloa',
ok: 'Ados',
cancel: 'Utzi',
close: 'Itxi',
preview: 'Aurrebista',
resize: 'Arrastatu tamaina aldatzeko',
generalTab: 'Orokorra',
advancedTab: 'Aurreratua',
validateNumberFailed: 'Balio hau ez da zenbaki bat.',
confirmNewPage: 'Eduki honetan gorde gabe dauden aldaketak galduko dira. Ziur zaude orri berri bat kargatu nahi duzula?',
confirmCancel: 'Aukera batzuk aldatu egin dira. Ziur zaude elkarrizketa-koadroa itxi nahi duzula?',
options: 'Aukerak',
target: 'Target (Helburua)',
targetNew: 'Leiho Berria (_blank)',
targetTop: 'Goieneko Leihoan (_top)',
targetSelf: 'Leiho Berdinean (_self)',
targetParent: 'Leiho Gurasoan (_parent)',
langDirLTR: 'Ezkerretik Eskumara(LTR)',
langDirRTL: 'Eskumatik Ezkerrera (RTL)',
styles: 'Estiloa',
cssClasses: 'Estilo-orriko Klaseak',
width: 'Zabalera',
height: 'Altuera',
align: 'Lerrokatu',
alignLeft: 'Ezkerrera',
alignRight: 'Eskuman',
alignCenter: 'Erdian',
alignTop: 'Goian',
alignMiddle: 'Erdian',
alignBottom: 'Behean',
invalidValue : 'Balio ezegokia.',
invalidHeight: 'Altuera zenbaki bat izan behar da.',
invalidWidth: 'Zabalera zenbaki bat izan behar da.',
invalidCssLength: '"%1" eremurako zehaztutako balioa zenbaki positibo bat izan behar du, aukeran CSS neurri unitate batekin (px, %, in, cm, mm, em, ex, pt edo pc).',
invalidHtmlLength: '"%1" eremurako zehaztutako balioa zenbaki positibo bat izan behar du, aukeran HTML neurri unitate batekin (px edo %).',
invalidInlineStyle: 'Lerroko estiloan zehazten dena tupla "name : value" formatuko eta puntu eta komaz bereiztutako tupla bat edo gehiago izan behar dira.',
cssLengthTooltip: 'Zenbakia bakarrik zehazten bada pixeletan egongo da. CSS neurri unitatea ere zehaztu ahal da (px, %, in, cm, mm, em, ex, pt, edo pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, erabilezina</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object for the
* Persian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'fa' ] = {
// ARIA description.
editor: 'ویرایشگر متن کامل',
editorPanel: 'پنل ویرایشگر متن غنی',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'کلید Alt+0 را برای راهنمایی بفشارید',
browseServer: 'فهرست​نمایی سرور',
url: 'URL',
protocol: 'پروتکل',
upload: 'آپلود',
uploadSubmit: 'به سرور بفرست',
image: 'تصویر',
flash: 'فلش',
form: 'فرم',
checkbox: 'چک‌باکس',
radio: 'دکمه‌ی رادیویی',
textField: 'فیلد متنی',
textarea: 'ناحیهٴ متنی',
hiddenField: 'فیلد پنهان',
button: 'دکمه',
select: 'فیلد انتخاب چند گزینه​ای',
imageButton: 'دکمه‌ی تصویری',
notSet: '<تعین نشده>',
id: 'شناسه',
name: 'نام',
langDir: 'جهت​نمای زبان',
langDirLtr: 'چپ به راست',
langDirRtl: 'راست به چپ',
langCode: 'کد زبان',
longDescr: 'URL توصیف طولانی',
cssClass: 'کلاس​های شیوه​نامه (Stylesheet)',
advisoryTitle: 'عنوان کمکی',
cssStyle: 'شیوه (style)',
ok: 'پذیرش',
cancel: 'انصراف',
close: 'بستن',
preview: 'پیش‌نمایش',
resize: 'تغییر اندازه',
generalTab: 'عمومی',
advancedTab: 'پیشرفته',
validateNumberFailed: 'این مقدار یک عدد نیست.',
confirmNewPage: 'هر تغییر ایجاد شده​ی ذخیره نشده از بین خواهد رفت. آیا اطمینان دارید که قصد بارگیری صفحه جدیدی را دارید؟',
confirmCancel: 'برخی از گزینه‌ها تغییر کرده‌اند. آیا واقعا قصد بستن این پنجره را دارید؟',
options: 'گزینه​ها',
target: 'نحوه باز کردن',
targetNew: 'پنجره جدید',
targetTop: 'بالاترین پنجره',
targetSelf: 'همان پنجره',
targetParent: 'پنجره والد',
langDirLTR: 'چپ به راست',
langDirRTL: 'راست به چپ',
styles: 'سبک',
cssClasses: 'کلاس‌های شیوه‌نامه',
width: 'عرض',
height: 'طول',
align: 'چینش',
alignLeft: 'چپ',
alignRight: 'راست',
alignCenter: 'مرکز',
alignTop: 'بالا',
alignMiddle: 'وسط',
alignBottom: 'پائین',
invalidValue : 'مقدار نامعتبر.',
invalidHeight: 'ارتفاع باید یک عدد باشد.',
invalidWidth: 'عرض باید یک عدد باشد.',
invalidCssLength: 'عدد تعیین شده برای فیلد "%1" باید یک عدد مثبت با یا بدون یک واحد اندازه گیری CSS معتبر باشد (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'عدد تعیین شده برای فیلد "%1" باید یک عدد مثبت با یا بدون یک واحد اندازه گیری HTML معتبر باشد (px or %).',
invalidInlineStyle: 'عدد تعیین شده برای سبک درون​خطی -Inline Style- باید دارای یک یا چند چندتایی با شکلی شبیه "name : value" که باید با یک ";" از هم جدا شوند.',
cssLengthTooltip: 'یک عدد برای یک مقدار بر حسب پیکسل و یا یک عدد با یک واحد CSS معتبر وارد کنید (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">، غیر قابل دسترس</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object for the
* Finnish language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'fi' ] = {
// ARIA description.
editor: 'Rikastekstieditori',
editorPanel: 'Rikastekstieditoripaneeli',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Paina ALT 0 nähdäksesi ohjeen',
browseServer: 'Selaa palvelinta',
url: 'Osoite',
protocol: 'Protokolla',
upload: 'Lisää tiedosto',
uploadSubmit: 'Lähetä palvelimelle',
image: 'Kuva',
flash: 'Flash-animaatio',
form: 'Lomake',
checkbox: 'Valintaruutu',
radio: 'Radiopainike',
textField: 'Tekstikenttä',
textarea: 'Tekstilaatikko',
hiddenField: 'Piilokenttä',
button: 'Painike',
select: 'Valintakenttä',
imageButton: 'Kuvapainike',
notSet: '<ei asetettu>',
id: 'Tunniste',
name: 'Nimi',
langDir: 'Kielen suunta',
langDirLtr: 'Vasemmalta oikealle (LTR)',
langDirRtl: 'Oikealta vasemmalle (RTL)',
langCode: 'Kielikoodi',
longDescr: 'Pitkän kuvauksen URL',
cssClass: 'Tyyliluokat',
advisoryTitle: 'Avustava otsikko',
cssStyle: 'Tyyli',
ok: 'OK',
cancel: 'Peruuta',
close: 'Sulje',
preview: 'Esikatselu',
resize: 'Raahaa muuttaaksesi kokoa',
generalTab: 'Yleinen',
advancedTab: 'Lisäominaisuudet',
validateNumberFailed: 'Arvon pitää olla numero.',
confirmNewPage: 'Kaikki tallentamattomat muutokset tähän sisältöön menetetään. Oletko varma, että haluat ladata uuden sivun?',
confirmCancel: 'Jotkut asetuksista on muuttuneet. Oletko varma, että haluat sulkea valintaikkunan?',
options: 'Asetukset',
target: 'Kohde',
targetNew: 'Uusi ikkuna (_blank)',
targetTop: 'Päällimmäinen ikkuna (_top)',
targetSelf: 'Sama ikkuna (_self)',
targetParent: 'Ylemmän tason ikkuna (_parent)',
langDirLTR: 'Vasemmalta oikealle (LTR)',
langDirRTL: 'Oikealta vasemmalle (RTL)',
styles: 'Tyyli',
cssClasses: 'Tyylitiedoston luokat',
width: 'Leveys',
height: 'Korkeus',
align: 'Kohdistus',
alignLeft: 'Vasemmalle',
alignRight: 'Oikealle',
alignCenter: 'Keskelle',
alignTop: 'Ylös',
alignMiddle: 'Keskelle',
alignBottom: 'Alas',
invalidValue : 'Virheellinen arvo.',
invalidHeight: 'Korkeuden täytyy olla numero.',
invalidWidth: 'Leveyden täytyy olla numero.',
invalidCssLength: 'Kentän "%1" arvon täytyy olla positiivinen luku CSS mittayksikön (px, %, in, cm, mm, em, ex, pt tai pc) kanssa tai ilman.',
invalidHtmlLength: 'Kentän "%1" arvon täytyy olla positiivinen luku HTML mittayksikön (px tai %) kanssa tai ilman.',
invalidInlineStyle: 'Tyylille annetun arvon täytyy koostua yhdestä tai useammasta "nimi : arvo" parista, jotka ovat eroteltuna toisistaan puolipisteillä.',
cssLengthTooltip: 'Anna numeroarvo pikseleinä tai numeroarvo CSS mittayksikön kanssa (px, %, in, cm, mm, em, ex, pt, tai pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, ei saatavissa</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Faroese language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'fo' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Trýst ALT og 0 fyri vegleiðing',
browseServer: 'Ambætarakagi',
url: 'URL',
protocol: 'Protokoll',
upload: 'Send til ambætaran',
uploadSubmit: 'Send til ambætaran',
image: 'Myndir',
flash: 'Flash',
form: 'Formur',
checkbox: 'Flugubein',
radio: 'Radioknøttur',
textField: 'Tekstteigur',
textarea: 'Tekstumráði',
hiddenField: 'Fjaldur teigur',
button: 'Knøttur',
select: 'Valskrá',
imageButton: 'Myndaknøttur',
notSet: '<ikki sett>',
id: 'Id',
name: 'Navn',
langDir: 'Tekstkós',
langDirLtr: 'Frá vinstru til høgru (LTR)',
langDirRtl: 'Frá høgru til vinstru (RTL)',
langCode: 'Málkoda',
longDescr: 'Víðkað URL frágreiðing',
cssClass: 'Typografi klassar',
advisoryTitle: 'Vegleiðandi heiti',
cssStyle: 'Typografi',
ok: 'Góðkent',
cancel: 'Avlýst',
close: 'Lat aftur',
preview: 'Frumsýn',
resize: 'Drag fyri at broyta stødd',
generalTab: 'Generelt',
advancedTab: 'Fjølbroytt',
validateNumberFailed: 'Hetta er ikki eitt tal.',
confirmNewPage: 'Allar ikki goymdar broytingar í hesum innihaldið hvørva. Skal nýggj síða lesast kortini?',
confirmCancel: 'Nakrir valmøguleikar eru broyttir. Ert tú vísur í, at dialogurin skal latast aftur?',
options: 'Options',
target: 'Target',
targetNew: 'Nýtt vindeyga (_blank)',
targetTop: 'Vindeyga ovast (_top)',
targetSelf: 'Sama vindeyga (_self)',
targetParent: 'Upphavligt vindeyga (_parent)',
langDirLTR: 'Frá vinstru til høgru (LTR)',
langDirRTL: 'Frá høgru til vinstru (RTL)',
styles: 'Style',
cssClasses: 'Stylesheet Classes',
width: 'Breidd',
height: 'Hædd',
align: 'Justering',
alignLeft: 'Vinstra',
alignRight: 'Høgra',
alignCenter: 'Miðsett',
alignTop: 'Ovast',
alignMiddle: 'Miðja',
alignBottom: 'Botnur',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Hædd má vera eitt tal.',
invalidWidth: 'Breidd má vera eitt tal.',
invalidCssLength: 'Virðið sett í "%1" feltið má vera eitt positivt tal, við ella uttan gyldugum CSS mátieind (px, %, in, cm, mm, em, ex, pt, ella pc).',
invalidHtmlLength: 'Virðið sett í "%1" feltiðield má vera eitt positivt tal, við ella uttan gyldugum CSS mátieind (px ella %).',
invalidInlineStyle: 'Virði specifiserað fyri inline style má hava eitt ella fleiri pør (tuples) skrivað sum "name : value", hvørt parið sundurskilt við semi-colon.',
cssLengthTooltip: 'Skriva eitt tal fyri eitt virði í pixels ella eitt tal við gyldigum CSS eind (px, %, in, cm, mm, em, ex, pt, ella pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, ikki tøkt</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Canadian French language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'fr-ca' ] = {
// ARIA description.
editor: 'Éditeur de texte enrichi',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Appuyez sur 0 pour de l\'aide',
browseServer: 'Parcourir le serveur',
url: 'URL',
protocol: 'Protocole',
upload: 'Envoyer',
uploadSubmit: 'Envoyer au serveur',
image: 'Image',
flash: 'Animation Flash',
form: 'Formulaire',
checkbox: 'Case à cocher',
radio: 'Bouton radio',
textField: 'Champ texte',
textarea: 'Zone de texte',
hiddenField: 'Champ caché',
button: 'Bouton',
select: 'Liste déroulante',
imageButton: 'Bouton image',
notSet: '<Par défaut>',
id: 'Id',
name: 'Nom',
langDir: 'Sens d\'écriture',
langDirLtr: 'De gauche à droite (LTR)',
langDirRtl: 'De droite à gauche (RTL)',
langCode: 'Code langue',
longDescr: 'URL de description longue',
cssClass: 'Classes CSS',
advisoryTitle: 'Titre',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Annuler',
close: 'Fermer',
preview: 'Aperçu',
resize: 'Redimensionner',
generalTab: 'Général',
advancedTab: 'Avancé',
validateNumberFailed: 'Cette valeur n\'est pas un nombre.',
confirmNewPage: 'Les changements non sauvegardés seront perdus. Êtes-vous certain de vouloir charger une nouvelle page?',
confirmCancel: 'Certaines options ont été modifiées. Êtes-vous certain de vouloir fermer?',
options: 'Options',
target: 'Cible',
targetNew: 'Nouvelle fenêtre (_blank)',
targetTop: 'Fenêtre supérieur (_top)',
targetSelf: 'Cette fenêtre (_self)',
targetParent: 'Fenêtre parent (_parent)',
langDirLTR: 'De gauche à droite (LTR)',
langDirRTL: 'De droite à gauche (RTL)',
styles: 'Style',
cssClasses: 'Classe CSS',
width: 'Largeur',
height: 'Hauteur',
align: 'Alignement',
alignLeft: 'Gauche',
alignRight: 'Droite',
alignCenter: 'Centré',
alignTop: 'Haut',
alignMiddle: 'Milieu',
alignBottom: 'Bas',
invalidValue : 'Valeur invalide.',
invalidHeight: 'La hauteur doit être un nombre.',
invalidWidth: 'La largeur doit être un nombre.',
invalidCssLength: 'La valeur spécifiée pour le champ "%1" doit être un nombre positif avec ou sans unité de mesure CSS valide (px, %, in, cm, mm, em, ex, pt, ou pc).',
invalidHtmlLength: 'La valeur spécifiée pour le champ "%1" doit être un nombre positif avec ou sans unité de mesure HTML valide (px ou %).',
invalidInlineStyle: 'La valeur spécifiée pour le style intégré doit être composée d\'un ou plusieurs couples de valeur au format "nom : valeur", separés par des points-virgules.',
cssLengthTooltip: 'Entrer un nombre pour la valeur en pixel ou un nombre avec une unité CSS valide (px, %, in, cm, mm, em, ex, pt, ou pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, indisponible</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* French language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'fr' ] = {
// ARIA description.
editor: 'Éditeur de Texte Enrichi',
editorPanel: 'Tableau de bord de l\'éditeur de texte enrichi',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Appuyez sur ALT-0 pour l\'aide',
browseServer: 'Explorer le serveur',
url: 'URL',
protocol: 'Protocole',
upload: 'Envoyer',
uploadSubmit: 'Envoyer sur le serveur',
image: 'Image',
flash: 'Flash',
form: 'Formulaire',
checkbox: 'Case à cocher',
radio: 'Bouton Radio',
textField: 'Champ texte',
textarea: 'Zone de texte',
hiddenField: 'Champ caché',
button: 'Bouton',
select: 'Liste déroulante',
imageButton: 'Bouton image',
notSet: '<non défini>',
id: 'Id',
name: 'Nom',
langDir: 'Sens d\'écriture',
langDirLtr: 'Gauche à droite (LTR)',
langDirRtl: 'Droite à gauche (RTL)',
langCode: 'Code de langue',
longDescr: 'URL de description longue (longdesc => malvoyant)',
cssClass: 'Classe CSS',
advisoryTitle: 'Description (title)',
cssStyle: 'Style',
ok: 'OK',
cancel: 'Annuler',
close: 'Fermer',
preview: 'Aperçu',
resize: 'Déplacer pour modifier la taille',
generalTab: 'Général',
advancedTab: 'Avancé',
validateNumberFailed: 'Cette valeur n\'est pas un nombre.',
confirmNewPage: 'Les changements non sauvegardés seront perdus. Êtes-vous sûr de vouloir charger une nouvelle page?',
confirmCancel: 'Certaines options ont été modifiées. Êtes-vous sûr de vouloir fermer?',
options: 'Options',
target: 'Cible (Target)',
targetNew: 'Nouvelle fenêtre (_blank)',
targetTop: 'Fenêtre supérieure (_top)',
targetSelf: 'Même fenêtre (_self)',
targetParent: 'Fenêtre parent (_parent)',
langDirLTR: 'Gauche à Droite (LTR)',
langDirRTL: 'Droite à Gauche (RTL)',
styles: 'Style',
cssClasses: 'Classes de style',
width: 'Largeur',
height: 'Hauteur',
align: 'Alignement',
alignLeft: 'Gauche',
alignRight: 'Droite',
alignCenter: 'Centré',
alignTop: 'Haut',
alignMiddle: 'Milieu',
alignBottom: 'Bas',
invalidValue : 'Valeur incorrecte.',
invalidHeight: 'La hauteur doit être un nombre.',
invalidWidth: 'La largeur doit être un nombre.',
invalidCssLength: 'La valeur spécifiée pour le champ "%1" doit être un nombre positif avec ou sans unité de mesure CSS valide (px, %, in, cm, mm, em, ex, pt, ou pc).',
invalidHtmlLength: 'La valeur spécifiée pour le champ "%1" doit être un nombre positif avec ou sans unité de mesure HTML valide (px ou %).',
invalidInlineStyle: 'La valeur spécifiée pour le style inline doit être composée d\'un ou plusieurs couples de valeur au format "nom : valeur", separés par des points-virgules.',
cssLengthTooltip: 'Entrer un nombre pour une valeur en pixels ou un nombre avec une unité de mesure CSS valide (px, %, in, cm, mm, em, ex, pt, ou pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, Indisponible</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Galician language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'gl' ] = {
// ARIA description.
editor: 'Editor de texto mellorado',
editorPanel: 'Panel do editor de texto mellorado',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Prema ALT 0 para obter axuda',
browseServer: 'Examinar o servidor',
url: 'URL',
protocol: 'Protocolo',
upload: 'Enviar',
uploadSubmit: 'Enviar ao servidor',
image: 'Imaxe',
flash: 'Flash',
form: 'Formulario',
checkbox: 'Caixa de selección',
radio: 'Botón de opción',
textField: 'Campo de texto',
textarea: 'Área de texto',
hiddenField: 'Campo agochado',
button: 'Botón',
select: 'Campo de selección',
imageButton: 'Botón de imaxe',
notSet: '<sen estabelecer>',
id: 'ID',
name: 'Nome',
langDir: 'Dirección de escritura do idioma',
langDirLtr: 'Esquerda a dereita (LTR)',
langDirRtl: 'Dereita a esquerda (RTL)',
langCode: 'Código do idioma',
longDescr: 'Descrición completa do URL',
cssClass: 'Clases da folla de estilos',
advisoryTitle: 'Título',
cssStyle: 'Estilo',
ok: 'Aceptar',
cancel: 'Cancelar',
close: 'Pechar',
preview: 'Vista previa',
resize: 'Redimensionar',
generalTab: 'Xeral',
advancedTab: 'Avanzado',
validateNumberFailed: 'Este valor non é un número.',
confirmNewPage: 'Calquera cambio que non gardara neste contido perderase.\r\nConfirma que quere cargar unha páxina nova?',
confirmCancel: 'Algunhas das opcións foron cambiadas.\r\nConfirma que quere pechar o diálogo?',
options: 'Opcións',
target: 'Destino',
targetNew: 'Nova xanela (_blank)',
targetTop: 'Xanela principal (_top)',
targetSelf: 'Mesma xanela (_self)',
targetParent: 'Xanela superior (_parent)',
langDirLTR: 'Esquerda a dereita (LTR)',
langDirRTL: 'Dereita a esquerda (RTL)',
styles: 'Estilo',
cssClasses: 'Clases da folla de estilos',
width: 'Largo',
height: 'Alto',
align: 'Aliñamento',
alignLeft: 'Esquerda',
alignRight: 'Dereita',
alignCenter: 'Centro',
alignTop: 'Arriba',
alignMiddle: 'Centro',
alignBottom: 'Abaixo',
invalidValue : 'Valor incorrecto.',
invalidHeight: 'O alto debe ser un número.',
invalidWidth: 'O largo debe ser un número.',
invalidCssLength: 'O valor especificado para o campo «%1» debe ser un número positivo con ou sen unha unidade de medida CSS correcta (px, %, in, cm, mm, em, ex, pt, ou pc).',
invalidHtmlLength: 'O valor especificado para o campo «%1» debe ser un número positivo con ou sen unha unidade de medida HTML correcta (px ou %).',
invalidInlineStyle: 'O valor especificado no estilo en liña debe consistir nunha ou máis tuplas co formato «nome : valor», separadas por punto e coma.',
cssLengthTooltip: 'Escriba un número para o valor en píxeles ou un número cunha unidade CSS correcta (px, %, in, cm, mm, em, ex, pt, ou pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, non dispoñíbel</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Gujarati language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'gu' ] = {
// ARIA description.
editor: 'રીચ ટેક્ષ્ત્ એડીટર',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'પ્રેસ ALT 0 મદદ માટ',
browseServer: 'સર્વર બ્રાઉઝ કરો',
url: 'URL',
protocol: 'પ્રોટોકૉલ',
upload: 'અપલોડ',
uploadSubmit: 'આ સર્વરને મોકલવું',
image: 'ચિત્ર',
flash: 'ફ્લૅશ',
form: 'ફૉર્મ/પત્રક',
checkbox: 'ચેક બોક્સ',
radio: 'રેડિઓ બટન',
textField: 'ટેક્સ્ટ ફીલ્ડ, શબ્દ ક્ષેત્ર',
textarea: 'ટેક્સ્ટ એરિઆ, શબ્દ વિસ્તાર',
hiddenField: 'ગુપ્ત ક્ષેત્ર',
button: 'બટન',
select: 'પસંદગી ક્ષેત્ર',
imageButton: 'ચિત્ર બટન',
notSet: '<સેટ નથી>',
id: 'Id',
name: 'નામ',
langDir: 'ભાષા લેખવાની પદ્ધતિ',
langDirLtr: 'ડાબે થી જમણે (LTR)',
langDirRtl: 'જમણે થી ડાબે (RTL)',
langCode: 'ભાષા કોડ',
longDescr: 'વધારે માહિતી માટે URL',
cssClass: 'સ્ટાઇલ-શીટ ક્લાસ',
advisoryTitle: 'મુખ્ય મથાળું',
cssStyle: 'સ્ટાઇલ',
ok: 'ઠીક છે',
cancel: 'રદ કરવું',
close: 'બંધ કરવું',
preview: 'જોવું',
resize: 'ખેંચી ને યોગ્ય કરવું',
generalTab: 'જનરલ',
advancedTab: 'અડ્વાન્સડ',
validateNumberFailed: 'આ રકમ આકડો નથી.',
confirmNewPage: 'સવે કાર્ય વગરનું ફકરો ખોવાઈ જશે. તમને ખાતરી છે કે તમને નવું પાનું ખોલવું છે?',
confirmCancel: 'ઘણા વિકલ્પો બદલાયા છે. તમારે આ બોક્ષ્ બંધ કરવું છે?',
options: 'વિકલ્પો',
target: 'લક્ષ્ય',
targetNew: 'નવી વિન્ડો (_blank)',
targetTop: 'ઉપરની વિન્ડો (_top)',
targetSelf: 'એજ વિન્ડો (_self)',
targetParent: 'પેરનટ વિન્ડો (_parent)',
langDirLTR: 'ડાબે થી જમણે (LTR)',
langDirRTL: 'જમણે થી ડાબે (RTL)',
styles: 'શૈલી',
cssClasses: 'શૈલી કલાસીસ',
width: 'પહોળાઈ',
height: 'ઊંચાઈ',
align: 'લાઇનદોરીમાં ગોઠવવું',
alignLeft: 'ડાબી બાજુ ગોઠવવું',
alignRight: 'જમણી',
alignCenter: 'મધ્ય સેન્ટર',
alignTop: 'ઉપર',
alignMiddle: 'વચ્ચે',
alignBottom: 'નીચે',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'ઉંચાઈ એક આંકડો હોવો જોઈએ.',
invalidWidth: 'પોહળ ઈ એક આંકડો હોવો જોઈએ.',
invalidCssLength: '"%1" ની વેલ્યુ એક પોસીટીવ આંકડો હોવો જોઈએ અથવા CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc) વગર.',
invalidHtmlLength: '"%1" ની વેલ્યુ એક પોસીટીવ આંકડો હોવો જોઈએ અથવા HTML measurement unit (px or %) વગર.',
invalidInlineStyle: 'ઈનલાઈન સ્ટાઈલ ની વેલ્યુ "name : value" ના ફોર્મેટ માં હોવી જોઈએ, વચ્ચે સેમી-કોલોન જોઈએ.',
cssLengthTooltip: 'પિક્ષ્લ્ નો આંકડો CSS unit (px, %, in, cm, mm, em, ex, pt, or pc) માં નાખો.',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, નથી મળતું</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Hebrew language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'he' ] = {
// ARIA description.
editor: 'עורך טקסט עשיר',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'לחץ אלט ALT + 0 לעזרה',
browseServer: 'סייר השרת',
url: 'כתובת (URL)',
protocol: 'פרוטוקול',
upload: 'העלאה',
uploadSubmit: 'שליחה לשרת',
image: 'תמונה',
flash: 'פלאש',
form: 'טופס',
checkbox: 'תיבת סימון',
radio: 'לחצן אפשרויות',
textField: 'שדה טקסט',
textarea: 'איזור טקסט',
hiddenField: 'שדה חבוי',
button: 'כפתור',
select: 'שדה בחירה',
imageButton: 'כפתור תמונה',
notSet: '<לא נקבע>',
id: 'זיהוי (ID)',
name: 'שם',
langDir: 'כיוון שפה',
langDirLtr: 'שמאל לימין (LTR)',
langDirRtl: 'ימין לשמאל (RTL)',
langCode: 'קוד שפה',
longDescr: 'קישור לתיאור מפורט',
cssClass: 'מחלקת עיצוב (CSS Class)',
advisoryTitle: 'כותרת מוצעת',
cssStyle: 'סגנון',
ok: 'אישור',
cancel: 'ביטול',
close: 'סגירה',
preview: 'תצוגה מקדימה',
resize: 'יש לגרור בכדי לשנות את הגודל',
generalTab: 'כללי',
advancedTab: 'אפשרויות מתקדמות',
validateNumberFailed: 'הערך חייב להיות מספרי.',
confirmNewPage: 'כל השינויים שלא נשמרו יאבדו. האם להעלות דף חדש?',
confirmCancel: 'חלק מהאפשרויות שונו, האם לסגור את הדיאלוג?',
options: 'אפשרויות',
target: 'מטרה',
targetNew: 'חלון חדש (_blank)',
targetTop: 'החלון העליון ביותר (_top)',
targetSelf: 'אותו חלון (_self)',
targetParent: 'חלון האב (_parent)',
langDirLTR: 'שמאל לימין (LTR)',
langDirRTL: 'ימין לשמאל (RTL)',
styles: 'סגנון',
cssClasses: 'מחלקות גליונות סגנון',
width: 'רוחב',
height: 'גובה',
align: 'יישור',
alignLeft: 'לשמאל',
alignRight: 'לימין',
alignCenter: 'מרכז',
alignTop: 'למעלה',
alignMiddle: 'לאמצע',
alignBottom: 'לתחתית',
invalidValue : 'ערך לא חוקי.',
invalidHeight: 'הגובה חייב להיות מספר.',
invalidWidth: 'הרוחב חייב להיות מספר.',
invalidCssLength: 'הערך שצוין לשדה "%1" חייב להיות מספר חיובי עם או ללא יחידת מידה חוקית של CSS (px, %, in, cm, mm, em, ex, pt, או pc).',
invalidHtmlLength: 'הערך שצוין לשדה "%1" חייב להיות מספר חיובי עם או ללא יחידת מידה חוקית של HTML (px או %).',
invalidInlineStyle: 'הערך שצויין לשדה הסגנון חייב להכיל זוג ערכים אחד או יותר בפורמט "שם : ערך", מופרדים על ידי נקודה-פסיק.',
cssLengthTooltip: 'יש להכניס מספר המייצג פיקסלים או מספר עם יחידת גליונות סגנון תקינה (px, %, in, cm, mm, em, ex, pt, או pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, לא זמין</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Hindi language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'hi' ] = {
// ARIA description.
editor: 'रिच टेक्स्ट एडिटर',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'मदद के लिये ALT 0 दबाए',
browseServer: 'सर्वर ब्राउज़ करें',
url: 'URL',
protocol: 'प्रोटोकॉल',
upload: 'अपलोड',
uploadSubmit: 'इसे सर्वर को भेजें',
image: 'तस्वीर',
flash: 'फ़्लैश',
form: 'फ़ॉर्म',
checkbox: 'चॅक बॉक्स',
radio: 'रेडिओ बटन',
textField: 'टेक्स्ट फ़ील्ड',
textarea: 'टेक्स्ट एरिया',
hiddenField: 'गुप्त फ़ील्ड',
button: 'बटन',
select: 'चुनाव फ़ील्ड',
imageButton: 'तस्वीर बटन',
notSet: '<सॅट नहीं>',
id: 'Id',
name: 'नाम',
langDir: 'भाषा लिखने की दिशा',
langDirLtr: 'बायें से दायें (LTR)',
langDirRtl: 'दायें से बायें (RTL)',
langCode: 'भाषा कोड',
longDescr: 'अधिक विवरण के लिए URL',
cssClass: 'स्टाइल-शीट क्लास',
advisoryTitle: 'परामर्श शीर्शक',
cssStyle: 'स्टाइल',
ok: 'ठीक है',
cancel: 'रद्द करें',
close: 'Close', // MISSING
preview: 'प्रीव्यू',
resize: 'Resize', // MISSING
generalTab: 'सामान्य',
advancedTab: 'ऍड्वान्स्ड',
validateNumberFailed: 'This value is not a number.', // MISSING
confirmNewPage: 'Any unsaved changes to this content will be lost. Are you sure you want to load new page?', // MISSING
confirmCancel: 'You have changed some options. Are you sure you want to close the dialog window?', // MISSING
options: 'Options', // MISSING
target: 'टार्गेट',
targetNew: 'New Window (_blank)', // MISSING
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Same Window (_self)', // MISSING
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'बायें से दायें (LTR)',
langDirRTL: 'दायें से बायें (RTL)',
styles: 'स्टाइल',
cssClasses: 'स्टाइल-शीट क्लास',
width: 'चौड़ाई',
height: 'ऊँचाई',
align: 'ऍलाइन',
alignLeft: 'दायें',
alignRight: 'दायें',
alignCenter: 'बीच में',
alignTop: 'ऊपर',
alignMiddle: 'मध्य',
alignBottom: 'नीचे',
invalidValue : 'Invalid value.', // MISSING
invalidHeight: 'Height must be a number.', // MISSING
invalidWidth: 'Width must be a number.', // MISSING
invalidCssLength: 'Value specified for the "%1" field must be a positive number with or without a valid CSS measurement unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
invalidHtmlLength: 'Value specified for the "%1" field must be a positive number with or without a valid HTML measurement unit (px or %).', // MISSING
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Enter a number for a value in pixels or a number with a valid CSS unit (px, %, in, cm, mm, em, ex, pt, or pc).', // MISSING
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, unavailable</span>' // MISSING
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Croatian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'hr' ] = {
// ARIA description.
editor: 'Bogati uređivač teksta',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Pritisni ALT 0 za pomoć',
browseServer: 'Pretraži server',
url: 'URL',
protocol: 'Protokol',
upload: 'Pošalji',
uploadSubmit: 'Pošalji na server',
image: 'Slika',
flash: 'Flash',
form: 'Forma',
checkbox: 'Checkbox',
radio: 'Radio Button',
textField: 'Text Field',
textarea: 'Textarea',
hiddenField: 'Hidden Field',
button: 'Button',
select: 'Selection Field',
imageButton: 'Image Button',
notSet: '<nije postavljeno>',
id: 'Id',
name: 'Naziv',
langDir: 'Smjer jezika',
langDirLtr: 'S lijeva na desno (LTR)',
langDirRtl: 'S desna na lijevo (RTL)',
langCode: 'Kôd jezika',
longDescr: 'Dugački opis URL',
cssClass: 'Klase stilova',
advisoryTitle: 'Advisory naslov',
cssStyle: 'Stil',
ok: 'OK',
cancel: 'Poništi',
close: 'Zatvori',
preview: 'Pregledaj',
resize: 'Povuci za promjenu veličine',
generalTab: 'Općenito',
advancedTab: 'Napredno',
validateNumberFailed: 'Ova vrijednost nije broj.',
confirmNewPage: 'Sve napravljene promjene će biti izgubljene ukoliko ih niste snimili. Sigurno želite učitati novu stranicu?',
confirmCancel: 'Neke od opcija su promjenjene. Sigurno želite zatvoriti ovaj prozor?',
options: 'Opcije',
target: 'Odredište',
targetNew: 'Novi prozor (_blank)',
targetTop: 'Vršni prozor (_top)',
targetSelf: 'Isti prozor (_self)',
targetParent: 'Roditeljski prozor (_parent)',
langDirLTR: 'S lijeva na desno (LTR)',
langDirRTL: 'S desna na lijevo (RTL)',
styles: 'Stil',
cssClasses: 'Klase stilova',
width: 'Širina',
height: 'Visina',
align: 'Poravnanje',
alignLeft: 'Lijevo',
alignRight: 'Desno',
alignCenter: 'Središnje',
alignTop: 'Vrh',
alignMiddle: 'Sredina',
alignBottom: 'Dolje',
invalidValue : 'Neispravna vrijednost.',
invalidHeight: 'Visina mora biti broj.',
invalidWidth: 'Širina mora biti broj.',
invalidCssLength: 'Vrijednost određena za "%1" polje mora biti pozitivni broj sa ili bez važećih CSS mjernih jedinica (px, %, in, cm, mm, em, ex, pt ili pc).',
invalidHtmlLength: 'Vrijednost određena za "%1" polje mora biti pozitivni broj sa ili bez važećih HTML mjernih jedinica (px ili %).',
invalidInlineStyle: 'Vrijednost za linijski stil mora sadržavati jednu ili više definicija s formatom "naziv:vrijednost", odvojenih točka-zarezom.',
cssLengthTooltip: 'Unesite broj za vrijednost u pikselima ili broj s važećim CSS mjernim jedinicama (px, %, in, cm, mm, em, ex, pt ili pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nedostupno</span>'
}
};

View File

@@ -0,0 +1,98 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview Defines the {@link CKEDITOR.lang} object, for the
* Hungarian language.
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'hu' ] = {
// ARIA description.
editor: 'HTML szerkesztő',
editorPanel: 'Rich Text szerkesztő panel',
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Segítségért nyomjon ALT 0',
browseServer: 'Böngészés a szerveren',
url: 'Hivatkozás',
protocol: 'Protokoll',
upload: 'Feltöltés',
uploadSubmit: 'Küldés a szerverre',
image: 'Kép',
flash: 'Flash',
form: 'Űrlap',
checkbox: 'Jelölőnégyzet',
radio: 'Választógomb',
textField: 'Szövegmező',
textarea: 'Szövegterület',
hiddenField: 'Rejtettmező',
button: 'Gomb',
select: 'Legördülő lista',
imageButton: 'Képgomb',
notSet: '<nincs beállítva>',
id: 'Azonosító',
name: 'Név',
langDir: 'Írás iránya',
langDirLtr: 'Balról jobbra',
langDirRtl: 'Jobbról balra',
langCode: 'Nyelv kódja',
longDescr: 'Részletes leírás webcíme',
cssClass: 'Stíluskészlet',
advisoryTitle: 'Súgócimke',
cssStyle: 'Stílus',
ok: 'Rendben',
cancel: 'Mégsem',
close: 'Bezárás',
preview: 'Előnézet',
resize: 'Húzza az átméretezéshez',
generalTab: 'Általános',
advancedTab: 'További opciók',
validateNumberFailed: 'A mezőbe csak számokat írhat.',
confirmNewPage: 'Minden nem mentett változás el fog veszni! Biztosan be szeretné tölteni az oldalt?',
confirmCancel: 'Az űrlap tartalma megváltozott, ám a változásokat nem rögzítette. Biztosan be szeretné zárni az űrlapot?',
options: 'Beállítások',
target: 'Cél',
targetNew: 'Új ablak (_blank)',
targetTop: 'Legfelső ablak (_top)',
targetSelf: 'Aktuális ablakban (_self)',
targetParent: 'Szülő ablak (_parent)',
langDirLTR: 'Balról jobbra (LTR)',
langDirRTL: 'Jobbról balra (RTL)',
styles: 'Stílus',
cssClasses: 'Stíluslap osztály',
width: 'Szélesség',
height: 'Magasság',
align: 'Igazítás',
alignLeft: 'Bal',
alignRight: 'Jobbra',
alignCenter: 'Középre',
alignTop: 'Tetejére',
alignMiddle: 'Középre',
alignBottom: 'Aljára',
invalidValue : 'Érvénytelen érték.',
invalidHeight: 'A magasság mezőbe csak számokat írhat.',
invalidWidth: 'A szélesség mezőbe csak számokat írhat.',
invalidCssLength: '"%1"-hez megadott érték csakis egy pozitív szám lehet, esetleg egy érvényes CSS egységgel megjelölve(px, %, in, cm, mm, em, ex, pt vagy pc).',
invalidHtmlLength: '"%1"-hez megadott érték csakis egy pozitív szám lehet, esetleg egy érvényes HTML egységgel megjelölve(px vagy %).',
invalidInlineStyle: 'Az inline stílusnak megadott értéknek tartalmaznia kell egy vagy több rekordot a "name : value" formátumban, pontosvesszővel elválasztva.',
cssLengthTooltip: 'Adjon meg egy számot értéknek pixelekben vagy egy számot érvényes CSS mértékegységben (px, %, in, cm, mm, em, ex, pt, vagy pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, nem elérhető</span>'
}
};

View File

@@ -0,0 +1,97 @@
/**
* @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or http://ckeditor.com/license
*/
/**
* @fileOverview
*/
/**#@+
@type String
@example
*/
/**
* Contains the dictionary of language entries.
* @namespace
*/
CKEDITOR.lang[ 'id' ] = {
// ARIA description.
editor: 'Rich Text Editor',
editorPanel: 'Rich Text Editor panel', // MISSING
// Common messages and labels.
common: {
// Screenreader titles. Please note that screenreaders are not always capable
// of reading non-English words. So be careful while translating it.
editorHelp: 'Tekan ALT 0 untuk bantuan.',
browseServer: 'Jelajah Server',
url: 'URL',
protocol: 'Protokol',
upload: 'Unggah',
uploadSubmit: 'Kirim ke Server',
image: 'Gambar',
flash: 'Flash',
form: 'Formulir',
checkbox: 'Kotak Cek',
radio: 'Tombol Radio',
textField: 'Kolom Teks',
textarea: 'Area Teks',
hiddenField: 'Kolom Tersembunyi',
button: 'Tombol',
select: 'Kolom Seleksi',
imageButton: 'Tombol Gambar',
notSet: '<tidak diatur>',
id: 'Id',
name: 'Nama',
langDir: 'Arah Bahasa',
langDirLtr: 'Kiri ke Kanan (LTR)',
langDirRtl: 'Kanan ke Kiri',
langCode: 'Kode Bahasa',
longDescr: 'Deskripsi URL Panjang',
cssClass: 'Kelas Stylesheet',
advisoryTitle: 'Penasehat Judul',
cssStyle: 'Gaya',
ok: 'OK',
cancel: 'Batal',
close: 'Tutup',
preview: 'Pratinjau',
resize: 'Ubah ukuran',
generalTab: 'Umum',
advancedTab: 'Advanced', // MISSING
validateNumberFailed: 'Nilai ini tidak sebuah angka',
confirmNewPage: 'Semua perubahan yang tidak disimpan di konten ini akan hilang. Apakah anda yakin ingin memuat halaman baru?',
confirmCancel: 'Beberapa opsi telah berubah. Apakah anda yakin ingin menutup dialog?',
options: 'Opsi',
target: 'Sasaran',
targetNew: 'Jendela Baru (_blank)',
targetTop: 'Topmost Window (_top)', // MISSING
targetSelf: 'Jendela yang Sama (_self)',
targetParent: 'Parent Window (_parent)', // MISSING
langDirLTR: 'Kiri ke Kanan (LTR)',
langDirRTL: 'Kanan ke Kiri (RTL)',
styles: 'Gaya',
cssClasses: 'Kelas Stylesheet',
width: 'Lebar',
height: 'Tinggi',
align: 'Penjajaran',
alignLeft: 'Kiri',
alignRight: 'Kanan',
alignCenter: 'Tengah',
alignTop: 'Atas',
alignMiddle: 'Tengah',
alignBottom: 'Bawah',
invalidValue : 'Nilai tidak sah.',
invalidHeight: 'Tinggi harus sebuah angka.',
invalidWidth: 'Lebar harus sebuah angka.',
invalidCssLength: 'Nilai untuk "%1" harus sebuah angkat positif dengan atau tanpa pengukuran unit CSS yang sah (px, %, in, cm, mm, em, ex, pt, or pc).',
invalidHtmlLength: 'Nilai yang dispesifikasian untuk kolom "%1" harus sebuah angka positif dengan atau tanpa sebuah unit pengukuran HTML (px atau %) yang valid.',
invalidInlineStyle: 'Value specified for the inline style must consist of one or more tuples with the format of "name : value", separated by semi-colons.', // MISSING
cssLengthTooltip: 'Masukkan sebuah angka untuk sebuah nilai dalam pixel atau sebuah angka dengan unit CSS yang sah (px, %, in, cm, mm, em, ex, pt, or pc).',
// Put the voice-only part of the label in the span.
unavailable: '%1<span class="cke_accessibility">, tidak tersedia</span>'
}
};

Some files were not shown because too many files have changed in this diff Show More