diff options
Diffstat (limited to 'sources/core')
59 files changed, 2188 insertions, 506 deletions
diff --git a/sources/core/_bootstrap.js b/sources/core/_bootstrap.js index 9fcbe25..cc7fb38 100644 --- a/sources/core/_bootstrap.js +++ b/sources/core/_bootstrap.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -8,7 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | ( function() { | 10 | ( function() { |
11 | // Disable HC detection in WebKit. (#5429) | 11 | // Disable HC detection in WebKit. (http://dev.ckeditor.com/ticket/5429) |
12 | if ( CKEDITOR.env.webkit ) | 12 | if ( CKEDITOR.env.webkit ) |
13 | CKEDITOR.env.hc = false; | 13 | CKEDITOR.env.hc = false; |
14 | else { | 14 | else { |
@@ -19,13 +19,13 @@ | |||
19 | hcDetect.appendTo( CKEDITOR.document.getHead() ); | 19 | hcDetect.appendTo( CKEDITOR.document.getHead() ); |
20 | 20 | ||
21 | // Update CKEDITOR.env. | 21 | // Update CKEDITOR.env. |
22 | // Catch exception needed sometimes for FF. (#4230) | 22 | // Catch exception needed sometimes for FF. (http://dev.ckeditor.com/ticket/4230) |
23 | try { | 23 | try { |
24 | var top = hcDetect.getComputedStyle( 'border-top-color' ), | 24 | var top = hcDetect.getComputedStyle( 'border-top-color' ), |
25 | right = hcDetect.getComputedStyle( 'border-right-color' ); | 25 | right = hcDetect.getComputedStyle( 'border-right-color' ); |
26 | 26 | ||
27 | // We need to check if getComputedStyle returned any value, because on FF | 27 | // We need to check if getComputedStyle returned any value, because on FF |
28 | // it returnes empty string if CKEditor is loaded in hidden iframe. (#11121) | 28 | // it returnes empty string if CKEditor is loaded in hidden iframe. (http://dev.ckeditor.com/ticket/11121) |
29 | CKEDITOR.env.hc = !!( top && top == right ); | 29 | CKEDITOR.env.hc = !!( top && top == right ); |
30 | } catch ( e ) { | 30 | } catch ( e ) { |
31 | CKEDITOR.env.hc = false; | 31 | CKEDITOR.env.hc = false; |
diff --git a/sources/core/ckeditor.js b/sources/core/ckeditor.js index 2b3e5cd..95dd67a 100644 --- a/sources/core/ckeditor.js +++ b/sources/core/ckeditor.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -126,7 +126,7 @@ CKEDITOR.remove = function( editor ) { | |||
126 | }; | 126 | }; |
127 | 127 | ||
128 | /** | 128 | /** |
129 | * Returns a string will all CSS rules passed to the {@link CKEDITOR#addCss} method. | 129 | * Returns a string with all CSS rules passed to the {@link CKEDITOR#addCss} method. |
130 | * | 130 | * |
131 | * @returns {String} A string containing CSS rules. | 131 | * @returns {String} A string containing CSS rules. |
132 | */ | 132 | */ |
diff --git a/sources/core/ckeditor_base.js b/sources/core/ckeditor_base.js index 1e90d72..8c36b11 100644 --- a/sources/core/ckeditor_base.js +++ b/sources/core/ckeditor_base.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -33,6 +33,10 @@ if ( !window.CKEDITOR ) { | |||
33 | * by the editor code, guaranteeing clean cache results when | 33 | * by the editor code, guaranteeing clean cache results when |
34 | * upgrading. | 34 | * upgrading. |
35 | * | 35 | * |
36 | * **Note:** There is [a known issue where "icons.png" does not include | ||
37 | * timestamp](http://dev.ckeditor.com/ticket/10685) and might get cached. | ||
38 | * We are working on having it fixed. | ||
39 | * | ||
36 | * alert( CKEDITOR.timestamp ); // e.g. '87dm' | 40 | * alert( CKEDITOR.timestamp ); // e.g. '87dm' |
37 | */ | 41 | */ |
38 | timestamp: '', // %REMOVE_LINE% | 42 | timestamp: '', // %REMOVE_LINE% |
diff --git a/sources/core/ckeditor_basic.js b/sources/core/ckeditor_basic.js index 847d661..c07e4d9 100644 --- a/sources/core/ckeditor_basic.js +++ b/sources/core/ckeditor_basic.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/command.js b/sources/core/command.js index a0e07b5..128cea4 100644 --- a/sources/core/command.js +++ b/sources/core/command.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -47,7 +47,7 @@ CKEDITOR.command = function( editor, commandDefinition ) { | |||
47 | if ( this.state == CKEDITOR.TRISTATE_DISABLED || !this.checkAllowed() ) | 47 | if ( this.state == CKEDITOR.TRISTATE_DISABLED || !this.checkAllowed() ) |
48 | return false; | 48 | return false; |
49 | 49 | ||
50 | if ( this.editorFocus ) // Give editor focus if necessary (#4355). | 50 | if ( this.editorFocus ) // Give editor focus if necessary (http://dev.ckeditor.com/ticket/4355). |
51 | editor.focus(); | 51 | editor.focus(); |
52 | 52 | ||
53 | if ( this.fire( 'exec' ) === false ) | 53 | if ( this.fire( 'exec' ) === false ) |
diff --git a/sources/core/commanddefinition.js b/sources/core/commanddefinition.js index 68b2253..10a040f 100644 --- a/sources/core/commanddefinition.js +++ b/sources/core/commanddefinition.js | |||
@@ -1,10 +1,10 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
6 | /** | 6 | /** |
7 | * @fileOverview Defines the "virtual" {@link CKEDITOR.commandDefinition} class, | 7 | * @fileOverview Defines the "virtual" {@link CKEDITOR.commandDefinition} class |
8 | * which contains the defintion of a command. This file is for | 8 | * which contains the defintion of a command. This file is for |
9 | * documentation purposes only. | 9 | * documentation purposes only. |
10 | */ | 10 | */ |
@@ -27,14 +27,14 @@ | |||
27 | * } ); | 27 | * } ); |
28 | * | 28 | * |
29 | * @method exec | 29 | * @method exec |
30 | * @param {CKEDITOR.editor} editor The editor within which run the command. | 30 | * @param {CKEDITOR.editor} editor The editor within which to run the command. |
31 | * @param {Object} [data] Additional data to be used to execute the command. | 31 | * @param {Object} [data] Additional data to be used to execute the command. |
32 | * @returns {Boolean} Whether the command has been successfully executed. | 32 | * @returns {Boolean} Whether the command has been successfully executed. |
33 | * Defaults to `true`, if nothing is returned. | 33 | * Defaults to `true` if nothing is returned. |
34 | */ | 34 | */ |
35 | 35 | ||
36 | /** | 36 | /** |
37 | * Whether the command need to be hooked into the redo/undo system. | 37 | * Whether the command needs to be hooked into the redo/undo system. |
38 | * | 38 | * |
39 | * editorInstance.addCommand( 'alertName', { | 39 | * editorInstance.addCommand( 'alertName', { |
40 | * exec: function( editor ) { | 40 | * exec: function( editor ) { |
@@ -52,14 +52,18 @@ | |||
52 | * command itself manually, and that the return value of this command is not to | 52 | * command itself manually, and that the return value of this command is not to |
53 | * be returned by the {@link #exec} function. | 53 | * be returned by the {@link #exec} function. |
54 | * | 54 | * |
55 | * editorInstance.addCommand( 'loadOptions', { | 55 | * editorInstance.addCommand( 'loadoptions', { |
56 | * exec: function( editor ) { | 56 | * exec: function( editor ) { |
57 | * var cmd = this; | ||
57 | * // Asynchronous operation below. | 58 | * // Asynchronous operation below. |
58 | * CKEDITOR.ajax.loadXml( 'data.xml', function() { | 59 | * CKEDITOR.ajax.loadXml( 'data.xml', function() { |
59 | * editor.fire( 'afterCommandExec' ); | 60 | * editor.fire( 'afterCommandExec', { |
61 | * name: 'loadoptions', | ||
62 | * command: cmd | ||
63 | * } ); | ||
60 | * } ); | 64 | * } ); |
61 | * }, | 65 | * }, |
62 | * async: true // The command need some time to complete after exec function returns. | 66 | * async: true // The command needs some time to complete after the exec function returns. |
63 | * } ); | 67 | * } ); |
64 | * | 68 | * |
65 | * @property {Boolean} [async=false] | 69 | * @property {Boolean} [async=false] |
@@ -72,7 +76,7 @@ | |||
72 | * exec: function( editor ) { | 76 | * exec: function( editor ) { |
73 | * // ... | 77 | * // ... |
74 | * }, | 78 | * }, |
75 | * editorFocus: false // The command doesn't require focusing the editing document. | 79 | * editorFocus: false // The command does not require focusing the editing document. |
76 | * } ); | 80 | * } ); |
77 | * | 81 | * |
78 | * See also {@link CKEDITOR.command#editorFocus}. | 82 | * See also {@link CKEDITOR.command#editorFocus}. |
@@ -88,14 +92,14 @@ | |||
88 | * exec: function( editor ) { | 92 | * exec: function( editor ) { |
89 | * // ... | 93 | * // ... |
90 | * }, | 94 | * }, |
91 | * startDisabled: true // Command is unavailable until selection is inside a link. | 95 | * startDisabled: true // The command is unavailable until the selection is inside a link. |
92 | * } ); | 96 | * } ); |
93 | * | 97 | * |
94 | * @property {Boolean} [startDisabled=false] | 98 | * @property {Boolean} [startDisabled=false] |
95 | */ | 99 | */ |
96 | 100 | ||
97 | /** | 101 | /** |
98 | * Indicates that this command is sensible to the selection context. | 102 | * Indicates that this command is sensitive to the selection context. |
99 | * If `true`, the {@link CKEDITOR.command#method-refresh} method will be | 103 | * If `true`, the {@link CKEDITOR.command#method-refresh} method will be |
100 | * called for this command on selection changes, with a single parameter | 104 | * called for this command on selection changes, with a single parameter |
101 | * representing the current elements path. | 105 | * representing the current elements path. |
@@ -104,10 +108,10 @@ | |||
104 | */ | 108 | */ |
105 | 109 | ||
106 | /** | 110 | /** |
107 | * Defined by command definition a function to determinate the command state, it will be invoked | 111 | * Defined by the command definition, a function to determine the command state. It will be invoked |
108 | * when editor has it's `states` or `selection` changed. | 112 | * when the editor has its `states` or `selection` changed. |
109 | * | 113 | * |
110 | * **Note:** The function provided must be calling {@link CKEDITOR.command#setState} in all circumstance, | 114 | * **Note:** The function provided must be calling {@link CKEDITOR.command#setState} in all circumstances |
111 | * if it is intended to update the command state. | 115 | * if it is intended to update the command state. |
112 | * | 116 | * |
113 | * @method refresh | 117 | * @method refresh |
@@ -132,7 +136,7 @@ | |||
132 | * exec: function( editor ) { | 136 | * exec: function( editor ) { |
133 | * // ... | 137 | * // ... |
134 | * }, | 138 | * }, |
135 | * modes: { wysiwyg:1 } // Command is available in wysiwyg mode only. | 139 | * modes: { wysiwyg:1 } // The command is available in wysiwyg mode only. |
136 | * } ); | 140 | * } ); |
137 | * | 141 | * |
138 | * See also {@link CKEDITOR.command#modes}. | 142 | * See also {@link CKEDITOR.command#modes}. |
@@ -146,3 +150,13 @@ | |||
146 | * @since 4.0 | 150 | * @since 4.0 |
147 | * @property {Boolean} [readOnly=false] | 151 | * @property {Boolean} [readOnly=false] |
148 | */ | 152 | */ |
153 | |||
154 | /** | ||
155 | * A property that should be set when a command has no keystroke assigned by {@link CKEDITOR.editor#setKeystroke}, but | ||
156 | * the keystroke is still supported. For example: `cut`, `copy` and `paste` commands are handled that way. | ||
157 | * This property is used when displaying keystroke information in tooltips and context menus. It is used by | ||
158 | * {@link CKEDITOR.editor#getCommandKeystroke}. | ||
159 | * | ||
160 | * @since 4.6.0 | ||
161 | * @property {Number} fakeKeystroke | ||
162 | */ | ||
diff --git a/sources/core/config.js b/sources/core/config.js index 4ce98d7..f0f4aec 100644 --- a/sources/core/config.js +++ b/sources/core/config.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/creators/inline.js b/sources/core/creators/inline.js index a1a653c..adb402f 100644 --- a/sources/core/creators/inline.js +++ b/sources/core/creators/inline.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/creators/themedui.js b/sources/core/creators/themedui.js index c87d079..4e7a93e 100644 --- a/sources/core/creators/themedui.js +++ b/sources/core/creators/themedui.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -20,7 +20,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
20 | * Replaces a `<textarea>` or a DOM element (`<div>`) with a CKEditor | 20 | * Replaces a `<textarea>` or a DOM element (`<div>`) with a CKEditor |
21 | * instance. For textareas, the initial value in the editor will be the | 21 | * instance. For textareas, the initial value in the editor will be the |
22 | * textarea value. For DOM elements, their `innerHTML` will be used | 22 | * textarea value. For DOM elements, their `innerHTML` will be used |
23 | * instead. We recommend using `<textarea>` and `<div>` elements only. | 23 | * instead. It is recommended to use `<textarea>` and `<div>` elements only. |
24 | * | 24 | * |
25 | * <textarea id="myfield" name="myfield"></textarea> | 25 | * <textarea id="myfield" name="myfield"></textarea> |
26 | * ... | 26 | * ... |
@@ -79,10 +79,10 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
79 | * // Replace all <textarea class="myClassName"> elements in the page. | 79 | * // Replace all <textarea class="myClassName"> elements in the page. |
80 | * CKEDITOR.replaceAll( 'myClassName' ); | 80 | * CKEDITOR.replaceAll( 'myClassName' ); |
81 | * | 81 | * |
82 | * // Selectively replace <textarea> elements, based on custom assertions. | 82 | * // Selectively replace <textarea> elements, based on a custom evaluation function. |
83 | * CKEDITOR.replaceAll( function( textarea, config ) { | 83 | * CKEDITOR.replaceAll( function( textarea, config ) { |
84 | * // An assertion function that needs to be evaluated for the <textarea> | 84 | * // A function that needs to be evaluated for the <textarea> |
85 | * // to be replaced. It must explicitely return "false" to ignore a | 85 | * // to be replaced. It must explicitly return "false" to ignore a |
86 | * // specific <textarea>. | 86 | * // specific <textarea>. |
87 | * // You can also customize the editor instance by having the function | 87 | * // You can also customize the editor instance by having the function |
88 | * // modify the "config" parameter. | 88 | * // modify the "config" parameter. |
@@ -109,7 +109,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
109 | * </html> | 109 | * </html> |
110 | * | 110 | * |
111 | * @param {String} [className] The `<textarea>` class name. | 111 | * @param {String} [className] The `<textarea>` class name. |
112 | * @param {Function} [function] An assertion function that must return `true` for a `<textarea>` | 112 | * @param {Function} [evaluator] An evaluation function that must return `true` for a `<textarea>` |
113 | * to be replaced with the editor. If the function returns `false`, the `<textarea>` element | 113 | * to be replaced with the editor. If the function returns `false`, the `<textarea>` element |
114 | * will not be replaced. | 114 | * will not be replaced. |
115 | */ | 115 | */ |
@@ -133,7 +133,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
133 | if ( !classRegex.test( textarea.className ) ) | 133 | if ( !classRegex.test( textarea.className ) ) |
134 | continue; | 134 | continue; |
135 | } else if ( typeof arguments[ 0 ] == 'function' ) { | 135 | } else if ( typeof arguments[ 0 ] == 'function' ) { |
136 | // An assertion function could be passed as the function parameter. | 136 | // An evaluation function could be passed as the function parameter. |
137 | // It must explicitly return "false" to ignore a specific <textarea>. | 137 | // It must explicitly return "false" to ignore a specific <textarea>. |
138 | config = {}; | 138 | config = {}; |
139 | if ( arguments[ 0 ]( textarea, config ) === false ) | 139 | if ( arguments[ 0 ]( textarea, config ) === false ) |
@@ -280,10 +280,10 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
280 | outer = container; | 280 | outer = container; |
281 | } | 281 | } |
282 | 282 | ||
283 | // Set as border box width. (#5353) | 283 | // Set as border box width. (http://dev.ckeditor.com/ticket/5353) |
284 | outer.setSize( 'width', width, true ); | 284 | outer.setSize( 'width', width, true ); |
285 | 285 | ||
286 | // WebKit needs to refresh the iframe size to avoid rendering issues. (1/2) (#8348) | 286 | // WebKit needs to refresh the iframe size to avoid rendering issues. (1/2) (http://dev.ckeditor.com/ticket/8348) |
287 | contentsFrame && ( contentsFrame.style.width = '1%' ); | 287 | contentsFrame && ( contentsFrame.style.width = '1%' ); |
288 | 288 | ||
289 | // Get the height delta between the outer table and the content area. | 289 | // Get the height delta between the outer table and the content area. |
@@ -295,7 +295,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
295 | 295 | ||
296 | contents.setStyle( 'height', resultContentsHeight + 'px' ); | 296 | contents.setStyle( 'height', resultContentsHeight + 'px' ); |
297 | 297 | ||
298 | // WebKit needs to refresh the iframe size to avoid rendering issues. (2/2) (#8348) | 298 | // WebKit needs to refresh the iframe size to avoid rendering issues. (2/2) (http://dev.ckeditor.com/ticket/8348) |
299 | contentsFrame && ( contentsFrame.style.width = '100%' ); | 299 | contentsFrame && ( contentsFrame.style.width = '100%' ); |
300 | 300 | ||
301 | // Emit a resize event. | 301 | // Emit a resize event. |
@@ -309,8 +309,8 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
309 | 309 | ||
310 | /** | 310 | /** |
311 | * Gets the element that can be used to check the editor size. This method | 311 | * Gets the element that can be used to check the editor size. This method |
312 | * is mainly used by the `resize` plugin, which adds a UI handle that can be used | 312 | * is mainly used by the [Editor Resize](http://ckeditor.com/addon/resize) plugin, which adds |
313 | * to resize the editor. | 313 | * a UI handle that can be used to resize the editor. |
314 | * | 314 | * |
315 | * @param {Boolean} forContents Whether to return the "contents" part of the theme instead of the container. | 315 | * @param {Boolean} forContents Whether to return the "contents" part of the theme instead of the container. |
316 | * @returns {CKEDITOR.dom.element} The resizable element. | 316 | * @returns {CKEDITOR.dom.element} The resizable element. |
@@ -337,7 +337,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
337 | // replacement will be done later in the editor creation lifecycle. | 337 | // replacement will be done later in the editor creation lifecycle. |
338 | element.setStyle( 'visibility', 'hidden' ); | 338 | element.setStyle( 'visibility', 'hidden' ); |
339 | 339 | ||
340 | // #8031 Remember if textarea was required and remove the attribute. | 340 | // http://dev.ckeditor.com/ticket/8031 Remember if textarea was required and remove the attribute. |
341 | editor._.required = element.hasAttribute( 'required' ); | 341 | editor._.required = element.hasAttribute( 'required' ); |
342 | element.removeAttribute( 'required' ); | 342 | element.removeAttribute( 'required' ); |
343 | } | 343 | } |
@@ -422,7 +422,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
422 | topHtml: topHtml ? '<span id="' + editor.ui.spaceId( 'top' ) + '" class="cke_top cke_reset_all" role="presentation" style="height:auto">' + topHtml + '</span>' : '', | 422 | topHtml: topHtml ? '<span id="' + editor.ui.spaceId( 'top' ) + '" class="cke_top cke_reset_all" role="presentation" style="height:auto">' + topHtml + '</span>' : '', |
423 | contentId: editor.ui.spaceId( 'contents' ), | 423 | contentId: editor.ui.spaceId( 'contents' ), |
424 | bottomHtml: bottomHtml ? '<span id="' + editor.ui.spaceId( 'bottom' ) + '" class="cke_bottom cke_reset_all" role="presentation">' + bottomHtml + '</span>' : '', | 424 | bottomHtml: bottomHtml ? '<span id="' + editor.ui.spaceId( 'bottom' ) + '" class="cke_bottom cke_reset_all" role="presentation">' + bottomHtml + '</span>' : '', |
425 | outerEl: CKEDITOR.env.ie ? 'span' : 'div' // #9571 | 425 | outerEl: CKEDITOR.env.ie ? 'span' : 'div' // http://dev.ckeditor.com/ticket/9571 |
426 | } ) ); | 426 | } ) ); |
427 | 427 | ||
428 | if ( elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) { | 428 | if ( elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) { |
@@ -451,7 +451,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
451 | // Disable browser context menu for editor's chrome. | 451 | // Disable browser context menu for editor's chrome. |
452 | container.disableContextMenu(); | 452 | container.disableContextMenu(); |
453 | 453 | ||
454 | // Redirect the focus into editor for webkit. (#5713) | 454 | // Redirect the focus into editor for webkit. (http://dev.ckeditor.com/ticket/5713) |
455 | CKEDITOR.env.webkit && container.on( 'focus', function() { | 455 | CKEDITOR.env.webkit && container.on( 'focus', function() { |
456 | editor.focus(); | 456 | editor.focus(); |
457 | } ); | 457 | } ); |
@@ -467,7 +467,7 @@ CKEDITOR.replaceClass = 'ckeditor'; | |||
467 | 467 | ||
468 | /** | 468 | /** |
469 | * The current editing mode. An editing mode basically provides | 469 | * The current editing mode. An editing mode basically provides |
470 | * different ways of editing or viewing the contents. | 470 | * different ways of editing or viewing the editor content. |
471 | * | 471 | * |
472 | * alert( CKEDITOR.instances.editor1.mode ); // (e.g.) 'wysiwyg' | 472 | * alert( CKEDITOR.instances.editor1.mode ); // (e.g.) 'wysiwyg' |
473 | * | 473 | * |
diff --git a/sources/core/dataprocessor.js b/sources/core/dataprocessor.js index ebc4d1c..c42eab4 100644 --- a/sources/core/dataprocessor.js +++ b/sources/core/dataprocessor.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dom.js b/sources/core/dom.js index a806a74..84aa21d 100644 --- a/sources/core/dom.js +++ b/sources/core/dom.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dom/comment.js b/sources/core/dom/comment.js index 69828c2..4abb453 100644 --- a/sources/core/dom/comment.js +++ b/sources/core/dom/comment.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dom/document.js b/sources/core/dom/document.js index f287245..ebf0bab 100644 --- a/sources/core/dom/document.js +++ b/sources/core/dom/document.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -262,7 +262,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.document.prototype, { | |||
262 | * @param {String} html The HTML defining the document content. | 262 | * @param {String} html The HTML defining the document content. |
263 | */ | 263 | */ |
264 | write: function( html ) { | 264 | write: function( html ) { |
265 | // Don't leave any history log in IE. (#5657) | 265 | // Don't leave any history log in IE. (http://dev.ckeditor.com/ticket/5657) |
266 | this.$.open( 'text/html', 'replace' ); | 266 | this.$.open( 'text/html', 'replace' ); |
267 | 267 | ||
268 | // Support for custom document.domain in IE. | 268 | // Support for custom document.domain in IE. |
diff --git a/sources/core/dom/documentfragment.js b/sources/core/dom/documentfragment.js index ffca9e5..1058144 100644 --- a/sources/core/dom/documentfragment.js +++ b/sources/core/dom/documentfragment.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dom/domobject.js b/sources/core/dom/domobject.js index 607e9f3..f4e258a 100644 --- a/sources/core/dom/domobject.js +++ b/sources/core/dom/domobject.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -41,7 +41,7 @@ CKEDITOR.dom.domObject.prototype = ( function() { | |||
41 | return function( domEvent ) { | 41 | return function( domEvent ) { |
42 | // In FF, when reloading the page with the editor focused, it may | 42 | // In FF, when reloading the page with the editor focused, it may |
43 | // throw an error because the CKEDITOR global is not anymore | 43 | // throw an error because the CKEDITOR global is not anymore |
44 | // available. So, we check it here first. (#2923) | 44 | // available. So, we check it here first. (http://dev.ckeditor.com/ticket/2923) |
45 | if ( typeof CKEDITOR != 'undefined' ) | 45 | if ( typeof CKEDITOR != 'undefined' ) |
46 | domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) ); | 46 | domObject.fire( eventName, new CKEDITOR.dom.event( domEvent ) ); |
47 | }; | 47 | }; |
@@ -138,7 +138,7 @@ CKEDITOR.dom.domObject.prototype = ( function() { | |||
138 | } | 138 | } |
139 | 139 | ||
140 | // Remove events from events object so fire() method will not call | 140 | // Remove events from events object so fire() method will not call |
141 | // listeners (#11400). | 141 | // listeners (http://dev.ckeditor.com/ticket/11400). |
142 | CKEDITOR.event.prototype.removeAllListeners.call( this ); | 142 | CKEDITOR.event.prototype.removeAllListeners.call( this ); |
143 | } | 143 | } |
144 | }; | 144 | }; |
diff --git a/sources/core/dom/element.js b/sources/core/dom/element.js index b586b02..31451f9 100644 --- a/sources/core/dom/element.js +++ b/sources/core/dom/element.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -308,7 +308,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
308 | */ | 308 | */ |
309 | appendText: function( text ) { | 309 | appendText: function( text ) { |
310 | // On IE8 it is impossible to append node to script tag, so we use its text. | 310 | // On IE8 it is impossible to append node to script tag, so we use its text. |
311 | // On the contrary, on Safari the text property is unpredictable in links. (#13232) | 311 | // On the contrary, on Safari the text property is unpredictable in links. (http://dev.ckeditor.com/ticket/13232) |
312 | if ( this.$.text != null && CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) | 312 | if ( this.$.text != null && CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) |
313 | this.$.text += text; | 313 | this.$.text += text; |
314 | else | 314 | else |
@@ -368,13 +368,36 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
368 | range.setEndAfter( parent ); | 368 | range.setEndAfter( parent ); |
369 | 369 | ||
370 | // Extract it. | 370 | // Extract it. |
371 | var docFrag = range.extractContents( false, cloneId || false ); | 371 | var docFrag = range.extractContents( false, cloneId || false ), |
372 | tmpElement, | ||
373 | current; | ||
372 | 374 | ||
373 | // Move the element outside the broken element. | 375 | // Move the element outside the broken element. |
374 | range.insertNode( this.remove() ); | 376 | range.insertNode( this.remove() ); |
375 | 377 | ||
376 | // Re-insert the extracted piece after the element. | 378 | // In case of Internet Explorer, we must check if there is no background-color |
377 | docFrag.insertAfterNode( this ); | 379 | // added to the element. In such case, we have to overwrite it to prevent "switching it off" |
380 | // by a browser (http://dev.ckeditor.com/ticket/14667). | ||
381 | if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) { | ||
382 | tmpElement = new CKEDITOR.dom.element( 'div' ); | ||
383 | |||
384 | while ( current = docFrag.getFirst() ) { | ||
385 | if ( current.$.style.backgroundColor ) { | ||
386 | // This is a necessary hack to make sure that IE will track backgroundColor CSS property, see | ||
387 | // http://dev.ckeditor.com/ticket/14667#comment:8 for more details. | ||
388 | current.$.style.backgroundColor = current.$.style.backgroundColor; | ||
389 | } | ||
390 | |||
391 | tmpElement.append( current ); | ||
392 | } | ||
393 | |||
394 | // Re-insert the extracted piece after the element. | ||
395 | tmpElement.insertAfter( this ); | ||
396 | tmpElement.remove( true ); | ||
397 | } else { | ||
398 | // Re-insert the extracted piece after the element. | ||
399 | docFrag.insertAfterNode( this ); | ||
400 | } | ||
378 | }, | 401 | }, |
379 | 402 | ||
380 | /** | 403 | /** |
@@ -429,7 +452,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
429 | */ | 452 | */ |
430 | getHtml: function() { | 453 | getHtml: function() { |
431 | var retval = this.$.innerHTML; | 454 | var retval = this.$.innerHTML; |
432 | // Strip <?xml:namespace> tags in IE. (#3341). | 455 | // Strip <?xml:namespace> tags in IE. (http://dev.ckeditor.com/ticket/3341). |
433 | return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval; | 456 | return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval; |
434 | }, | 457 | }, |
435 | 458 | ||
@@ -444,7 +467,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
444 | getOuterHtml: function() { | 467 | getOuterHtml: function() { |
445 | if ( this.$.outerHTML ) { | 468 | if ( this.$.outerHTML ) { |
446 | // IE includes the <?xml:namespace> tag in the outerHTML of | 469 | // IE includes the <?xml:namespace> tag in the outerHTML of |
447 | // namespaced element. So, we must strip it here. (#3341) | 470 | // namespaced element. So, we must strip it here. (http://dev.ckeditor.com/ticket/3341) |
448 | return this.$.outerHTML.replace( /<\?[^>]*>/, '' ); | 471 | return this.$.outerHTML.replace( /<\?[^>]*>/, '' ); |
449 | } | 472 | } |
450 | 473 | ||
@@ -595,7 +618,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
595 | return this.$[ name ]; | 618 | return this.$[ name ]; |
596 | 619 | ||
597 | case 'style': | 620 | case 'style': |
598 | // IE does not return inline styles via getAttribute(). See #2947. | 621 | // IE does not return inline styles via getAttribute(). See http://dev.ckeditor.com/ticket/2947. |
599 | return this.$.style.cssText; | 622 | return this.$.style.cssText; |
600 | 623 | ||
601 | case 'contenteditable': | 624 | case 'contenteditable': |
@@ -611,6 +634,28 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
611 | } )(), | 634 | } )(), |
612 | 635 | ||
613 | /** | 636 | /** |
637 | * Gets the values of all element attributes. | ||
638 | * | ||
639 | * @param {Array} exclude The names of attributes to be excluded from the returned object. | ||
640 | * @return {Object} An object containing all element attributes with their values. | ||
641 | */ | ||
642 | getAttributes: function( exclude ) { | ||
643 | var attributes = {}, | ||
644 | attrDefs = this.$.attributes, | ||
645 | i; | ||
646 | |||
647 | exclude = CKEDITOR.tools.isArray( exclude ) ? exclude : []; | ||
648 | |||
649 | for ( i = 0; i < attrDefs.length; i++ ) { | ||
650 | if ( CKEDITOR.tools.indexOf( exclude, attrDefs[ i ].name ) === -1 ) { | ||
651 | attributes[ attrDefs[ i ].name ] = attrDefs[ i ].value; | ||
652 | } | ||
653 | } | ||
654 | |||
655 | return attributes; | ||
656 | }, | ||
657 | |||
658 | /** | ||
614 | * Gets the nodes list containing all children of this element. | 659 | * Gets the nodes list containing all children of this element. |
615 | * | 660 | * |
616 | * @returns {CKEDITOR.dom.nodeList} | 661 | * @returns {CKEDITOR.dom.nodeList} |
@@ -634,7 +679,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
634 | function( propertyName ) { | 679 | function( propertyName ) { |
635 | var style = this.getWindow().$.getComputedStyle( this.$, null ); | 680 | var style = this.getWindow().$.getComputedStyle( this.$, null ); |
636 | 681 | ||
637 | // Firefox may return null if we call the above on a hidden iframe. (#9117) | 682 | // Firefox may return null if we call the above on a hidden iframe. (http://dev.ckeditor.com/ticket/9117) |
638 | return style ? style.getPropertyValue( propertyName ) : ''; | 683 | return style ? style.getPropertyValue( propertyName ) : ''; |
639 | } : function( propertyName ) { | 684 | } : function( propertyName ) { |
640 | return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ]; | 685 | return this.$.currentStyle[ CKEDITOR.tools.cssStyleToDomStyle( propertyName ) ]; |
@@ -927,7 +972,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
927 | elementWindow, elementWindowFrame; | 972 | elementWindow, elementWindowFrame; |
928 | 973 | ||
929 | // Webkit and Opera report non-zero offsetHeight despite that | 974 | // Webkit and Opera report non-zero offsetHeight despite that |
930 | // element is inside an invisible iframe. (#4542) | 975 | // element is inside an invisible iframe. (http://dev.ckeditor.com/ticket/4542) |
931 | if ( isVisible && CKEDITOR.env.webkit ) { | 976 | if ( isVisible && CKEDITOR.env.webkit ) { |
932 | elementWindow = this.getWindow(); | 977 | elementWindow = this.getWindow(); |
933 | 978 | ||
@@ -988,7 +1033,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
988 | // attribute, which will be marked as "specified", even if the | 1033 | // attribute, which will be marked as "specified", even if the |
989 | // outerHTML of the element is not displaying the class attribute. | 1034 | // outerHTML of the element is not displaying the class attribute. |
990 | // Note : I was not able to reproduce it outside the editor, | 1035 | // Note : I was not able to reproduce it outside the editor, |
991 | // but I've faced it while working on the TC of #1391. | 1036 | // but I've faced it while working on the TC of http://dev.ckeditor.com/ticket/1391. |
992 | if ( this.getAttribute( 'class' ) ) { | 1037 | if ( this.getAttribute( 'class' ) ) { |
993 | return true; | 1038 | return true; |
994 | } | 1039 | } |
@@ -1012,7 +1057,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1012 | var attrs = this.$.attributes, | 1057 | var attrs = this.$.attributes, |
1013 | attrsNum = attrs.length; | 1058 | attrsNum = attrs.length; |
1014 | 1059 | ||
1015 | // The _moz_dirty attribute might get into the element after pasting (#5455) | 1060 | // The _moz_dirty attribute might get into the element after pasting (http://dev.ckeditor.com/ticket/5455) |
1016 | var execludeAttrs = { 'data-cke-expando': 1, _moz_dirty: 1 }; | 1061 | var execludeAttrs = { 'data-cke-expando': 1, _moz_dirty: 1 }; |
1017 | 1062 | ||
1018 | return attrsNum > 0 && ( attrsNum > 2 || !execludeAttrs[ attrs[ 0 ].nodeName ] || ( attrsNum == 2 && !execludeAttrs[ attrs[ 1 ].nodeName ] ) ); | 1063 | return attrsNum > 0 && ( attrsNum > 2 || !execludeAttrs[ attrs[ 0 ].nodeName ] || ( attrsNum == 2 && !execludeAttrs[ attrs[ 1 ].nodeName ] ) ); |
@@ -1119,7 +1164,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1119 | function mergeElements( element, sibling, isNext ) { | 1164 | function mergeElements( element, sibling, isNext ) { |
1120 | if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) { | 1165 | if ( sibling && sibling.type == CKEDITOR.NODE_ELEMENT ) { |
1121 | // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>, | 1166 | // Jumping over bookmark nodes and empty inline elements, e.g. <b><i></i></b>, |
1122 | // queuing them to be moved later. (#5567) | 1167 | // queuing them to be moved later. (http://dev.ckeditor.com/ticket/5567) |
1123 | var pendingNodes = []; | 1168 | var pendingNodes = []; |
1124 | 1169 | ||
1125 | while ( sibling.data( 'cke-bookmark' ) || sibling.isEmptyInlineRemoveable() ) { | 1170 | while ( sibling.data( 'cke-bookmark' ) || sibling.isEmptyInlineRemoveable() ) { |
@@ -1149,7 +1194,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1149 | } | 1194 | } |
1150 | 1195 | ||
1151 | return function( inlineOnly ) { | 1196 | return function( inlineOnly ) { |
1152 | // Merge empty links and anchors also. (#5567) | 1197 | // Merge empty links and anchors also. (http://dev.ckeditor.com/ticket/5567) |
1153 | if ( !( inlineOnly === false || CKEDITOR.dtd.$removeEmpty[ this.getName() ] || this.is( 'a' ) ) ) { | 1198 | if ( !( inlineOnly === false || CKEDITOR.dtd.$removeEmpty[ this.getName() ] || this.is( 'a' ) ) ) { |
1154 | return; | 1199 | return; |
1155 | } | 1200 | } |
@@ -1208,7 +1253,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1208 | }; | 1253 | }; |
1209 | } else if ( CKEDITOR.env.ie8Compat && CKEDITOR.env.secure ) { | 1254 | } else if ( CKEDITOR.env.ie8Compat && CKEDITOR.env.secure ) { |
1210 | return function( name, value ) { | 1255 | return function( name, value ) { |
1211 | // IE8 throws error when setting src attribute to non-ssl value. (#7847) | 1256 | // IE8 throws error when setting src attribute to non-ssl value. (http://dev.ckeditor.com/ticket/7847) |
1212 | if ( name == 'src' && value.match( /^http:\/\// ) ) { | 1257 | if ( name == 'src' && value.match( /^http:\/\// ) ) { |
1213 | try { | 1258 | try { |
1214 | standard.apply( this, arguments ); | 1259 | standard.apply( this, arguments ); |
@@ -1292,11 +1337,15 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1292 | */ | 1337 | */ |
1293 | removeAttributes: function( attributes ) { | 1338 | removeAttributes: function( attributes ) { |
1294 | if ( CKEDITOR.tools.isArray( attributes ) ) { | 1339 | if ( CKEDITOR.tools.isArray( attributes ) ) { |
1295 | for ( var i = 0; i < attributes.length; i++ ) | 1340 | for ( var i = 0; i < attributes.length; i++ ) { |
1296 | this.removeAttribute( attributes[ i ] ); | 1341 | this.removeAttribute( attributes[ i ] ); |
1342 | } | ||
1297 | } else { | 1343 | } else { |
1298 | for ( var attr in attributes ) | 1344 | attributes = attributes || this.getAttributes(); |
1345 | |||
1346 | for ( var attr in attributes ) { | ||
1299 | attributes.hasOwnProperty( attr ) && this.removeAttribute( attr ); | 1347 | attributes.hasOwnProperty( attr ) && this.removeAttribute( attr ); |
1348 | } | ||
1300 | } | 1349 | } |
1301 | }, | 1350 | }, |
1302 | 1351 | ||
@@ -1442,7 +1491,8 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1442 | body = doc.getBody(), | 1491 | body = doc.getBody(), |
1443 | quirks = doc.$.compatMode == 'BackCompat'; | 1492 | quirks = doc.$.compatMode == 'BackCompat'; |
1444 | 1493 | ||
1445 | if ( document.documentElement.getBoundingClientRect ) { | 1494 | if ( document.documentElement.getBoundingClientRect && |
1495 | ( CKEDITOR.env.ie ? CKEDITOR.env.version !== 8 : true ) ) { | ||
1446 | var box = this.$.getBoundingClientRect(), | 1496 | var box = this.$.getBoundingClientRect(), |
1447 | $doc = doc.$, | 1497 | $doc = doc.$, |
1448 | $docElem = $doc.documentElement; | 1498 | $docElem = $doc.documentElement; |
@@ -1451,7 +1501,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1451 | clientLeft = $docElem.clientLeft || body.$.clientLeft || 0, | 1501 | clientLeft = $docElem.clientLeft || body.$.clientLeft || 0, |
1452 | needAdjustScrollAndBorders = true; | 1502 | needAdjustScrollAndBorders = true; |
1453 | 1503 | ||
1454 | // #3804: getBoundingClientRect() works differently on IE and non-IE | 1504 | // http://dev.ckeditor.com/ticket/3804: getBoundingClientRect() works differently on IE and non-IE |
1455 | // browsers, regarding scroll positions. | 1505 | // browsers, regarding scroll positions. |
1456 | // | 1506 | // |
1457 | // On IE, the top position of the <html> element is always 0, no matter | 1507 | // On IE, the top position of the <html> element is always 0, no matter |
@@ -1466,12 +1516,12 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1466 | needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem ); | 1516 | needAdjustScrollAndBorders = ( quirks && inBody ) || ( !quirks && inDocElem ); |
1467 | } | 1517 | } |
1468 | 1518 | ||
1469 | // #12747. | 1519 | // http://dev.ckeditor.com/ticket/12747. |
1470 | if ( needAdjustScrollAndBorders ) { | 1520 | if ( needAdjustScrollAndBorders ) { |
1471 | var scrollRelativeLeft, | 1521 | var scrollRelativeLeft, |
1472 | scrollRelativeTop; | 1522 | scrollRelativeTop; |
1473 | 1523 | ||
1474 | // See #12758 to know more about document.(documentElement|body).scroll(Left|Top) in Webkit. | 1524 | // See http://dev.ckeditor.com/ticket/12758 to know more about document.(documentElement|body).scroll(Left|Top) in Webkit. |
1475 | if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version >= 12 ) ) { | 1525 | if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version >= 12 ) ) { |
1476 | scrollRelativeLeft = body.$.scrollLeft || $docElem.scrollLeft; | 1526 | scrollRelativeLeft = body.$.scrollLeft || $docElem.scrollLeft; |
1477 | scrollRelativeTop = body.$.scrollTop || $docElem.scrollTop; | 1527 | scrollRelativeTop = body.$.scrollTop || $docElem.scrollTop; |
@@ -1553,7 +1603,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1553 | parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight; | 1603 | parent.$.clientHeight && parent.$.clientHeight < parent.$.scrollHeight; |
1554 | 1604 | ||
1555 | // Skip body element, which will report wrong clientHeight when containing | 1605 | // Skip body element, which will report wrong clientHeight when containing |
1556 | // floated content. (#9523) | 1606 | // floated content. (http://dev.ckeditor.com/ticket/9523) |
1557 | if ( overflowed && !parent.is( 'body' ) ) | 1607 | if ( overflowed && !parent.is( 'body' ) ) |
1558 | this.scrollIntoParent( parent, alignToTop, 1 ); | 1608 | this.scrollIntoParent( parent, alignToTop, 1 ); |
1559 | 1609 | ||
@@ -1626,6 +1676,16 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1626 | return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0; | 1676 | return parseInt( element.getComputedStyle( 'margin-' + side ) || 0, 10 ) || 0; |
1627 | } | 1677 | } |
1628 | 1678 | ||
1679 | // [WebKit] Reset stored scrollTop value to not break scrollIntoView() method flow. | ||
1680 | // Scrolling breaks when range.select() is used right after element.scrollIntoView(). (http://dev.ckeditor.com/ticket/14659) | ||
1681 | if ( CKEDITOR.env.webkit ) { | ||
1682 | var editor = this.getEditor( false ); | ||
1683 | |||
1684 | if ( editor ) { | ||
1685 | editor._.previousScrollTop = null; | ||
1686 | } | ||
1687 | } | ||
1688 | |||
1629 | var win = parent.getWindow(); | 1689 | var win = parent.getWindow(); |
1630 | 1690 | ||
1631 | var thisPos = screenPos( this, win ), | 1691 | var thisPos = screenPos( this, win ), |
@@ -1797,7 +1857,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1797 | this.getParent( true ) && this.$.parentNode.replaceChild( newNode.$, this.$ ); | 1857 | this.getParent( true ) && this.$.parentNode.replaceChild( newNode.$, this.$ ); |
1798 | newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ]; | 1858 | newNode.$[ 'data-cke-expando' ] = this.$[ 'data-cke-expando' ]; |
1799 | this.$ = newNode.$; | 1859 | this.$ = newNode.$; |
1800 | // Bust getName's cache. (#8663) | 1860 | // Bust getName's cache. (http://dev.ckeditor.com/ticket/8663) |
1801 | delete this.getName; | 1861 | delete this.getName; |
1802 | }, | 1862 | }, |
1803 | 1863 | ||
@@ -1905,17 +1965,32 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
1905 | * CKEDITOR.replace( element ); | 1965 | * CKEDITOR.replace( element ); |
1906 | * alert( element.getEditor().name ); // 'editor1' | 1966 | * alert( element.getEditor().name ); // 'editor1' |
1907 | * | 1967 | * |
1968 | * By default this method considers only original DOM elements upon which the editor | ||
1969 | * was created. Setting `optimized` parameter to `false` will consider editor editable | ||
1970 | * and its children. | ||
1971 | * | ||
1972 | * @param {Boolean} [optimized=true] If set to `false` it will scan every editor editable. | ||
1908 | * @returns {CKEDITOR.editor} An editor instance or null if nothing has been found. | 1973 | * @returns {CKEDITOR.editor} An editor instance or null if nothing has been found. |
1909 | */ | 1974 | */ |
1910 | getEditor: function() { | 1975 | getEditor: function( optimized ) { |
1911 | var instances = CKEDITOR.instances, | 1976 | var instances = CKEDITOR.instances, |
1912 | name, instance; | 1977 | name, instance, editable; |
1978 | |||
1979 | optimized = optimized || optimized === undefined; | ||
1913 | 1980 | ||
1914 | for ( name in instances ) { | 1981 | for ( name in instances ) { |
1915 | instance = instances[ name ]; | 1982 | instance = instances[ name ]; |
1916 | 1983 | ||
1917 | if ( instance.element.equals( this ) && instance.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) | 1984 | if ( instance.element.equals( this ) && instance.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) |
1918 | return instance; | 1985 | return instance; |
1986 | |||
1987 | if ( !optimized ) { | ||
1988 | editable = instance.editable(); | ||
1989 | |||
1990 | if ( editable && ( editable.equals( this ) || editable.contains( this ) ) ) { | ||
1991 | return instance; | ||
1992 | } | ||
1993 | } | ||
1919 | } | 1994 | } |
1920 | 1995 | ||
1921 | return null; | 1996 | return null; |
@@ -2038,7 +2113,8 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
2038 | } | 2113 | } |
2039 | 2114 | ||
2040 | function getContextualizedSelector( element, selector ) { | 2115 | function getContextualizedSelector( element, selector ) { |
2041 | return '#' + element.$.id + ' ' + selector.split( /,\s*/ ).join( ', #' + element.$.id + ' ' ); | 2116 | var id = CKEDITOR.tools.escapeCss( element.$.id ); |
2117 | return '#' + id + ' ' + selector.split( /,\s*/ ).join( ', #' + id + ' ' ); | ||
2042 | } | 2118 | } |
2043 | 2119 | ||
2044 | var sides = { | 2120 | var sides = { |
@@ -2070,7 +2146,7 @@ CKEDITOR.dom.element.clearMarkers = function( database, element, removeFromDatab | |||
2070 | function marginAndPaddingSize( type ) { | 2146 | function marginAndPaddingSize( type ) { |
2071 | var adjustment = 0; | 2147 | var adjustment = 0; |
2072 | for ( var i = 0, len = sides[ type ].length; i < len; i++ ) | 2148 | for ( var i = 0, len = sides[ type ].length; i < len; i++ ) |
2073 | adjustment += parseInt( this.getComputedStyle( sides[ type ][ i ] ) || 0, 10 ) || 0; | 2149 | adjustment += parseFloat( this.getComputedStyle( sides[ type ][ i ] ) || 0, 10 ) || 0; |
2074 | return adjustment; | 2150 | return adjustment; |
2075 | } | 2151 | } |
2076 | 2152 | ||
diff --git a/sources/core/dom/elementpath.js b/sources/core/dom/elementpath.js index 1ee551b..dd50f10 100644 --- a/sources/core/dom/elementpath.js +++ b/sources/core/dom/elementpath.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -57,6 +57,11 @@ | |||
57 | // Backward compact. | 57 | // Backward compact. |
58 | root = root || startNode.getDocument().getBody(); | 58 | root = root || startNode.getDocument().getBody(); |
59 | 59 | ||
60 | // Assign root value if startNode is null (#424)(https://dev.ckeditor.com/ticket/17028). | ||
61 | if ( !e ) { | ||
62 | e = root; | ||
63 | } | ||
64 | |||
60 | do { | 65 | do { |
61 | if ( e.type == CKEDITOR.NODE_ELEMENT ) { | 66 | if ( e.type == CKEDITOR.NODE_ELEMENT ) { |
62 | elements.push( e ); | 67 | elements.push( e ); |
@@ -84,7 +89,7 @@ | |||
84 | block = e; | 89 | block = e; |
85 | 90 | ||
86 | if ( pathBlockLimitElements[ elementName ] ) { | 91 | if ( pathBlockLimitElements[ elementName ] ) { |
87 | // End level DIV is considered as the block, if no block is available. (#525) | 92 | // End level DIV is considered as the block, if no block is available. (http://dev.ckeditor.com/ticket/525) |
88 | // But it must NOT be the root element (checked above). | 93 | // But it must NOT be the root element (checked above). |
89 | if ( !block && elementName == 'div' && !checkHasBlock( e ) ) | 94 | if ( !block && elementName == 'div' && !checkHasBlock( e ) ) |
90 | block = e; | 95 | block = e; |
@@ -181,7 +186,9 @@ CKEDITOR.dom.elementPath.prototype = { | |||
181 | * @returns {CKEDITOR.dom.element} The first matched dom element or `null`. | 186 | * @returns {CKEDITOR.dom.element} The first matched dom element or `null`. |
182 | */ | 187 | */ |
183 | contains: function( query, excludeRoot, fromTop ) { | 188 | contains: function( query, excludeRoot, fromTop ) { |
184 | var evaluator; | 189 | var i = 0, |
190 | evaluator; | ||
191 | |||
185 | if ( typeof query == 'string' ) | 192 | if ( typeof query == 'string' ) |
186 | evaluator = function( node ) { | 193 | evaluator = function( node ) { |
187 | return node.getName() == query; | 194 | return node.getName() == query; |
@@ -203,14 +210,21 @@ CKEDITOR.dom.elementPath.prototype = { | |||
203 | 210 | ||
204 | var elements = this.elements, | 211 | var elements = this.elements, |
205 | length = elements.length; | 212 | length = elements.length; |
206 | excludeRoot && length--; | 213 | |
214 | if ( excludeRoot ) { | ||
215 | if ( !fromTop ) { | ||
216 | length -= 1; | ||
217 | } else { | ||
218 | i += 1; | ||
219 | } | ||
220 | } | ||
207 | 221 | ||
208 | if ( fromTop ) { | 222 | if ( fromTop ) { |
209 | elements = Array.prototype.slice.call( elements, 0 ); | 223 | elements = Array.prototype.slice.call( elements, 0 ); |
210 | elements.reverse(); | 224 | elements.reverse(); |
211 | } | 225 | } |
212 | 226 | ||
213 | for ( var i = 0; i < length; i++ ) { | 227 | for ( ; i < length; i++ ) { |
214 | if ( evaluator( elements[ i ] ) ) | 228 | if ( evaluator( elements[ i ] ) ) |
215 | return elements[ i ]; | 229 | return elements[ i ]; |
216 | } | 230 | } |
diff --git a/sources/core/dom/event.js b/sources/core/dom/event.js index 7cc1bd8..8b1193a 100644 --- a/sources/core/dom/event.js +++ b/sources/core/dom/event.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dom/iterator.js b/sources/core/dom/iterator.js index 1e1b180..9176e33 100644 --- a/sources/core/dom/iterator.js +++ b/sources/core/dom/iterator.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -45,7 +45,7 @@ | |||
45 | */ | 45 | */ |
46 | this.forceBrBreak = 0; | 46 | this.forceBrBreak = 0; |
47 | 47 | ||
48 | // (#3730). | 48 | // (http://dev.ckeditor.com/ticket/3730). |
49 | /** | 49 | /** |
50 | * Whether to include `<br>` elements in the enlarged range. Should be | 50 | * Whether to include `<br>` elements in the enlarged range. Should be |
51 | * set to `false` when using the iterator in the {@link CKEDITOR#ENTER_BR} mode. | 51 | * set to `false` when using the iterator in the {@link CKEDITOR#ENTER_BR} mode. |
@@ -85,7 +85,7 @@ | |||
85 | */ | 85 | */ |
86 | 86 | ||
87 | var beginWhitespaceRegex = /^[\r\n\t ]+$/, | 87 | var beginWhitespaceRegex = /^[\r\n\t ]+$/, |
88 | // Ignore bookmark nodes.(#3783) | 88 | // Ignore bookmark nodes.(http://dev.ckeditor.com/ticket/3783) |
89 | bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ), | 89 | bookmarkGuard = CKEDITOR.dom.walker.bookmark( false, true ), |
90 | whitespacesGuard = CKEDITOR.dom.walker.whitespaces( true ), | 90 | whitespacesGuard = CKEDITOR.dom.walker.whitespaces( true ), |
91 | skipGuard = function( node ) { | 91 | skipGuard = function( node ) { |
@@ -191,12 +191,12 @@ | |||
191 | } | 191 | } |
192 | 192 | ||
193 | // The range must finish right before the boundary, | 193 | // The range must finish right before the boundary, |
194 | // including possibly skipped empty spaces. (#1603) | 194 | // including possibly skipped empty spaces. (http://dev.ckeditor.com/ticket/1603) |
195 | if ( range ) { | 195 | if ( range ) { |
196 | range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START ); | 196 | range.setEndAt( currentNode, CKEDITOR.POSITION_BEFORE_START ); |
197 | 197 | ||
198 | // The found boundary must be set as the next one at this | 198 | // The found boundary must be set as the next one at this |
199 | // point. (#1717) | 199 | // point. (http://dev.ckeditor.com/ticket/1717) |
200 | if ( nodeName != 'br' ) { | 200 | if ( nodeName != 'br' ) { |
201 | this._.nextNode = currentNode; | 201 | this._.nextNode = currentNode; |
202 | } | 202 | } |
@@ -244,7 +244,7 @@ | |||
244 | closeRange = 1; | 244 | closeRange = 1; |
245 | includeNode = 0; | 245 | includeNode = 0; |
246 | isLast = isLast || ( parentNode.equals( lastNode ) ); | 246 | isLast = isLast || ( parentNode.equals( lastNode ) ); |
247 | // Make sure range includes bookmarks at the end of the block. (#7359) | 247 | // Make sure range includes bookmarks at the end of the block. (http://dev.ckeditor.com/ticket/7359) |
248 | range.setEndAt( parentNode, CKEDITOR.POSITION_BEFORE_END ); | 248 | range.setEndAt( parentNode, CKEDITOR.POSITION_BEFORE_END ); |
249 | break; | 249 | break; |
250 | } | 250 | } |
@@ -377,7 +377,7 @@ | |||
377 | // Here we are checking in guard function whether current element | 377 | // Here we are checking in guard function whether current element |
378 | // reach lastNode(default behaviour) and root node to prevent against | 378 | // reach lastNode(default behaviour) and root node to prevent against |
379 | // getting out of editor instance root DOM object. | 379 | // getting out of editor instance root DOM object. |
380 | // #12484 | 380 | // http://dev.ckeditor.com/ticket/12484 |
381 | function guardFunction( node ) { | 381 | function guardFunction( node ) { |
382 | return !( node.equals( lastNode ) || node.equals( rootNode ) ); | 382 | return !( node.equals( lastNode ) || node.equals( rootNode ) ); |
383 | } | 383 | } |
@@ -397,7 +397,7 @@ | |||
397 | // Indicate at least one of the range boundaries is inside a preformat block. | 397 | // Indicate at least one of the range boundaries is inside a preformat block. |
398 | touchPre, | 398 | touchPre, |
399 | 399 | ||
400 | // (#12178) | 400 | // (http://dev.ckeditor.com/ticket/12178) |
401 | // Remember if following situation takes place: | 401 | // Remember if following situation takes place: |
402 | // * startAtInnerBoundary: <p>foo[</p>... | 402 | // * startAtInnerBoundary: <p>foo[</p>... |
403 | // * endAtInnerBoundary: ...<p>]bar</p> | 403 | // * endAtInnerBoundary: ...<p>]bar</p> |
@@ -405,13 +405,13 @@ | |||
405 | // Note that we test only if path block exist, because we must properly shrink | 405 | // Note that we test only if path block exist, because we must properly shrink |
406 | // range containing table and/or table cells. | 406 | // range containing table and/or table cells. |
407 | // Note: When range is collapsed there's no way it can be shrinked. | 407 | // Note: When range is collapsed there's no way it can be shrinked. |
408 | // By checking if range is collapsed we also prevent #12308. | 408 | // By checking if range is collapsed we also prevent http://dev.ckeditor.com/ticket/12308. |
409 | startPath = range.startPath(), | 409 | startPath = range.startPath(), |
410 | endPath = range.endPath(), | 410 | endPath = range.endPath(), |
411 | startAtInnerBoundary = !range.collapsed && rangeAtInnerBlockBoundary( range, startPath.block ), | 411 | startAtInnerBoundary = !range.collapsed && rangeAtInnerBlockBoundary( range, startPath.block ), |
412 | endAtInnerBoundary = !range.collapsed && rangeAtInnerBlockBoundary( range, endPath.block, 1 ); | 412 | endAtInnerBoundary = !range.collapsed && rangeAtInnerBlockBoundary( range, endPath.block, 1 ); |
413 | 413 | ||
414 | // Shrink the range to exclude harmful "noises" (#4087, #4450, #5435). | 414 | // Shrink the range to exclude harmful "noises" (http://dev.ckeditor.com/ticket/4087, http://dev.ckeditor.com/ticket/4450, http://dev.ckeditor.com/ticket/5435). |
415 | range.shrink( CKEDITOR.SHRINK_ELEMENT, true ); | 415 | range.shrink( CKEDITOR.SHRINK_ELEMENT, true ); |
416 | 416 | ||
417 | if ( startAtInnerBoundary ) | 417 | if ( startAtInnerBoundary ) |
@@ -437,7 +437,7 @@ | |||
437 | 437 | ||
438 | // We may have an empty text node at the end of block due to [3770]. | 438 | // We may have an empty text node at the end of block due to [3770]. |
439 | // If that node is the lastNode, it would cause our logic to leak to the | 439 | // If that node is the lastNode, it would cause our logic to leak to the |
440 | // next block.(#3887) | 440 | // next block.(http://dev.ckeditor.com/ticket/3887) |
441 | if ( this._.lastNode && this._.lastNode.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( this._.lastNode.getText() ) && this._.lastNode.getParent().isBlockBoundary() ) { | 441 | if ( this._.lastNode && this._.lastNode.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( this._.lastNode.getText() ) && this._.lastNode.getParent().isBlockBoundary() ) { |
442 | var testRange = this.range.clone(); | 442 | var testRange = this.range.clone(); |
443 | testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END ); | 443 | testRange.moveToPosition( this._.lastNode, CKEDITOR.POSITION_AFTER_END ); |
diff --git a/sources/core/dom/node.js b/sources/core/dom/node.js index 7818b07..69b223e 100644 --- a/sources/core/dom/node.js +++ b/sources/core/dom/node.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -200,7 +200,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { | |||
200 | } | 200 | } |
201 | 201 | ||
202 | // IE8 rename HTML5 nodes by adding `:` at the begging of the tag name when the node is cloned, | 202 | // IE8 rename HTML5 nodes by adding `:` at the begging of the tag name when the node is cloned, |
203 | // so `<figure>` will be `<:figure>` after 'cloneNode'. We need to fix it (#13101). | 203 | // so `<figure>` will be `<:figure>` after 'cloneNode'. We need to fix it (http://dev.ckeditor.com/ticket/13101). |
204 | function renameNodes( node ) { | 204 | function renameNodes( node ) { |
205 | if ( node.type != CKEDITOR.NODE_ELEMENT && node.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT ) | 205 | if ( node.type != CKEDITOR.NODE_ELEMENT && node.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT ) |
206 | return; | 206 | return; |
@@ -804,7 +804,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { | |||
804 | } else if ( trimmed.length < originalLength ) { | 804 | } else if ( trimmed.length < originalLength ) { |
805 | child.split( originalLength - trimmed.length ); | 805 | child.split( originalLength - trimmed.length ); |
806 | 806 | ||
807 | // IE BUG: child.remove() may raise JavaScript errors here. (#81) | 807 | // IE BUG: child.remove() may raise JavaScript errors here. (http://dev.ckeditor.com/ticket/81) |
808 | this.$.removeChild( this.$.firstChild ); | 808 | this.$.removeChild( this.$.firstChild ); |
809 | } | 809 | } |
810 | } | 810 | } |
@@ -829,7 +829,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { | |||
829 | child.split( trimmed.length ); | 829 | child.split( trimmed.length ); |
830 | 830 | ||
831 | // IE BUG: child.getNext().remove() may raise JavaScript errors here. | 831 | // IE BUG: child.getNext().remove() may raise JavaScript errors here. |
832 | // (#81) | 832 | // (http://dev.ckeditor.com/ticket/81) |
833 | this.$.lastChild.parentNode.removeChild( this.$.lastChild ); | 833 | this.$.lastChild.parentNode.removeChild( this.$.lastChild ); |
834 | } | 834 | } |
835 | } | 835 | } |
@@ -840,7 +840,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { | |||
840 | child = this.$.lastChild; | 840 | child = this.$.lastChild; |
841 | 841 | ||
842 | if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) { | 842 | if ( child && child.type == 1 && child.nodeName.toLowerCase() == 'br' ) { |
843 | // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (#324). | 843 | // Use "eChildNode.parentNode" instead of "node" to avoid IE bug (http://dev.ckeditor.com/ticket/324). |
844 | child.parentNode.removeChild( child ); | 844 | child.parentNode.removeChild( child ); |
845 | } | 845 | } |
846 | } | 846 | } |
@@ -874,7 +874,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.node.prototype, { | |||
874 | if ( this.type != CKEDITOR.NODE_ELEMENT ) | 874 | if ( this.type != CKEDITOR.NODE_ELEMENT ) |
875 | element = this.getParent(); | 875 | element = this.getParent(); |
876 | 876 | ||
877 | // Prevent Edge crash (#13609, #13919). | 877 | // Prevent Edge crash (http://dev.ckeditor.com/ticket/13609, http://dev.ckeditor.com/ticket/13919). |
878 | if ( CKEDITOR.env.edge && element && element.is( 'textarea', 'input' ) ) { | 878 | if ( CKEDITOR.env.edge && element && element.is( 'textarea', 'input' ) ) { |
879 | checkOnlyAttributes = true; | 879 | checkOnlyAttributes = true; |
880 | } | 880 | } |
diff --git a/sources/core/dom/nodelist.js b/sources/core/dom/nodelist.js index 0f91eaa..07af314 100644 --- a/sources/core/dom/nodelist.js +++ b/sources/core/dom/nodelist.js | |||
@@ -1,11 +1,11 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
6 | /** | 6 | /** |
7 | * Represents a list of {@link CKEDITOR.dom.node} objects. | 7 | * Represents a list of {@link CKEDITOR.dom.node} objects. |
8 | * It's a wrapper for native nodes list. | 8 | * It is a wrapper for a native nodes list. |
9 | * | 9 | * |
10 | * var nodeList = CKEDITOR.document.getBody().getChildren(); | 10 | * var nodeList = CKEDITOR.document.getBody().getChildren(); |
11 | * alert( nodeList.count() ); // number [0;N] | 11 | * alert( nodeList.count() ); // number [0;N] |
@@ -20,7 +20,7 @@ CKEDITOR.dom.nodeList = function( nativeList ) { | |||
20 | 20 | ||
21 | CKEDITOR.dom.nodeList.prototype = { | 21 | CKEDITOR.dom.nodeList.prototype = { |
22 | /** | 22 | /** |
23 | * Get count of nodes in this list. | 23 | * Gets the count of nodes in this list. |
24 | * | 24 | * |
25 | * @returns {Number} | 25 | * @returns {Number} |
26 | */ | 26 | */ |
@@ -29,7 +29,7 @@ CKEDITOR.dom.nodeList.prototype = { | |||
29 | }, | 29 | }, |
30 | 30 | ||
31 | /** | 31 | /** |
32 | * Get node from the list. | 32 | * Gets the node from the list. |
33 | * | 33 | * |
34 | * @returns {CKEDITOR.dom.node} | 34 | * @returns {CKEDITOR.dom.node} |
35 | */ | 35 | */ |
@@ -39,5 +39,16 @@ CKEDITOR.dom.nodeList.prototype = { | |||
39 | 39 | ||
40 | var $node = this.$[ index ]; | 40 | var $node = this.$[ index ]; |
41 | return $node ? new CKEDITOR.dom.node( $node ) : null; | 41 | return $node ? new CKEDITOR.dom.node( $node ) : null; |
42 | }, | ||
43 | |||
44 | /** | ||
45 | * Returns a node list as an array. | ||
46 | * | ||
47 | * @returns {CKEDITOR.dom.node[]} | ||
48 | */ | ||
49 | toArray: function() { | ||
50 | return CKEDITOR.tools.array.map( this.$, function( nativeEl ) { | ||
51 | return new CKEDITOR.dom.node( nativeEl ); | ||
52 | } ); | ||
42 | } | 53 | } |
43 | }; | 54 | }; |
diff --git a/sources/core/dom/range.js b/sources/core/dom/range.js index b5e8736..742c24c 100644 --- a/sources/core/dom/range.js +++ b/sources/core/dom/range.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -204,9 +204,14 @@ CKEDITOR.dom.range = function( root ) { | |||
204 | // This allows us to not think about startNode == endNode case later on. | 204 | // This allows us to not think about startNode == endNode case later on. |
205 | // We do that only when cloning, because in other cases we can safely split this text node | 205 | // We do that only when cloning, because in other cases we can safely split this text node |
206 | // and hence we can easily handle this case as many others. | 206 | // and hence we can easily handle this case as many others. |
207 | if ( isClone && endNode.type == CKEDITOR.NODE_TEXT && startNode.equals( endNode ) ) { | 207 | |
208 | startNode = range.document.createText( startNode.substring( startOffset, endOffset ) ); | 208 | // We need to handle situation when selection startNode is type of NODE_ELEMENT (#426). |
209 | docFrag.append( startNode ); | 209 | if ( isClone && |
210 | endNode.type == CKEDITOR.NODE_TEXT && | ||
211 | ( startNode.equals( endNode ) || ( startNode.type === CKEDITOR.NODE_ELEMENT && startNode.getFirst().equals( endNode ) ) ) ) { | ||
212 | |||
213 | // Here we should always be inside one text node. | ||
214 | docFrag.append( range.document.createText( endNode.substring( startOffset, endOffset ) ) ); | ||
210 | return; | 215 | return; |
211 | } | 216 | } |
212 | 217 | ||
@@ -214,7 +219,7 @@ CKEDITOR.dom.range = function( root ) { | |||
214 | // second part. The removal will be handled by the rest of the code. | 219 | // second part. The removal will be handled by the rest of the code. |
215 | if ( endNode.type == CKEDITOR.NODE_TEXT ) { | 220 | if ( endNode.type == CKEDITOR.NODE_TEXT ) { |
216 | // If Extract or Delete we can split the text node, | 221 | // If Extract or Delete we can split the text node, |
217 | // but if Clone (2), then we cannot modify the DOM (#11586) so we mark the text node for cloning. | 222 | // but if Clone (2), then we cannot modify the DOM (http://dev.ckeditor.com/ticket/11586) so we mark the text node for cloning. |
218 | if ( !isClone ) { | 223 | if ( !isClone ) { |
219 | endNode = endNode.split( endOffset ); | 224 | endNode = endNode.split( endOffset ); |
220 | } else { | 225 | } else { |
@@ -243,7 +248,7 @@ CKEDITOR.dom.range = function( root ) { | |||
243 | // be handled by the rest of the code . | 248 | // be handled by the rest of the code . |
244 | if ( startNode.type == CKEDITOR.NODE_TEXT ) { | 249 | if ( startNode.type == CKEDITOR.NODE_TEXT ) { |
245 | // If Extract or Delete we can split the text node, | 250 | // If Extract or Delete we can split the text node, |
246 | // but if Clone (2), then we cannot modify the DOM (#11586) so we mark | 251 | // but if Clone (2), then we cannot modify the DOM (http://dev.ckeditor.com/ticket/11586) so we mark |
247 | // the text node for cloning. | 252 | // the text node for cloning. |
248 | if ( !isClone ) { | 253 | if ( !isClone ) { |
249 | startNode.split( startOffset ); | 254 | startNode.split( startOffset ); |
@@ -373,7 +378,7 @@ CKEDITOR.dom.range = function( root ) { | |||
373 | // If we don't do that, in next iterations nodes will be appended to wrong parent. | 378 | // If we don't do that, in next iterations nodes will be appended to wrong parent. |
374 | // | 379 | // |
375 | // We can just take first child because the algorithm guarantees | 380 | // We can just take first child because the algorithm guarantees |
376 | // that this will be the only child on this level. (#13568) | 381 | // that this will be the only child on this level. (http://dev.ckeditor.com/ticket/13568) |
377 | levelParent = levelParent.getChild( 0 ); | 382 | levelParent = levelParent.getChild( 0 ); |
378 | } | 383 | } |
379 | } | 384 | } |
@@ -405,7 +410,7 @@ CKEDITOR.dom.range = function( root ) { | |||
405 | 410 | ||
406 | // When Extracting, move the removed node to the docFrag. | 411 | // When Extracting, move the removed node to the docFrag. |
407 | if ( isExtract ) { | 412 | if ( isExtract ) { |
408 | newParent.append( node ); | 413 | newParent.append( node, toStart ); |
409 | } | 414 | } |
410 | } | 415 | } |
411 | 416 | ||
@@ -574,7 +579,7 @@ CKEDITOR.dom.range = function( root ) { | |||
574 | // Tolerant bogus br when checking at the end of block. | 579 | // Tolerant bogus br when checking at the end of block. |
575 | // Reject any text node unless it's being bookmark | 580 | // Reject any text node unless it's being bookmark |
576 | // OR it's spaces. | 581 | // OR it's spaces. |
577 | // Reject any element unless it's being invisible empty. (#3883) | 582 | // Reject any element unless it's being invisible empty. (http://dev.ckeditor.com/ticket/3883) |
578 | return !checkStart && isBogus( node ) || | 583 | return !checkStart && isBogus( node ) || |
579 | node.type == CKEDITOR.NODE_ELEMENT && | 584 | node.type == CKEDITOR.NODE_ELEMENT && |
580 | node.is( CKEDITOR.dtd.$removeEmpty ); | 585 | node.is( CKEDITOR.dtd.$removeEmpty ); |
@@ -1052,7 +1057,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1052 | } | 1057 | } |
1053 | 1058 | ||
1054 | // Sometimes the endNode will come right before startNode for collapsed | 1059 | // Sometimes the endNode will come right before startNode for collapsed |
1055 | // ranges. Fix it. (#3780) | 1060 | // ranges. Fix it. (http://dev.ckeditor.com/ticket/3780) |
1056 | if ( startNode.getPosition( endNode ) & CKEDITOR.POSITION_FOLLOWING ) | 1061 | if ( startNode.getPosition( endNode ) & CKEDITOR.POSITION_FOLLOWING ) |
1057 | startNode = endNode; | 1062 | startNode = endNode; |
1058 | 1063 | ||
@@ -1201,7 +1206,8 @@ CKEDITOR.dom.range = function( root ) { | |||
1201 | /** | 1206 | /** |
1202 | * Expands the range so that partial units are completely contained. | 1207 | * Expands the range so that partial units are completely contained. |
1203 | * | 1208 | * |
1204 | * @param unit {Number} The unit type to expand with. | 1209 | * @param {Number} unit The unit type to expand with. Use one of following values: {@link CKEDITOR#ENLARGE_BLOCK_CONTENTS}, |
1210 | * {@link CKEDITOR#ENLARGE_ELEMENT}, {@link CKEDITOR#ENLARGE_INLINE}, {@link CKEDITOR#ENLARGE_LIST_ITEM_CONTENTS}. | ||
1205 | * @param {Boolean} [excludeBrs=false] Whether include line-breaks when expanding. | 1211 | * @param {Boolean} [excludeBrs=false] Whether include line-breaks when expanding. |
1206 | */ | 1212 | */ |
1207 | enlarge: function( unit, excludeBrs ) { | 1213 | enlarge: function( unit, excludeBrs ) { |
@@ -1323,13 +1329,13 @@ CKEDITOR.dom.range = function( root ) { | |||
1323 | 1329 | ||
1324 | isWhiteSpace = /[\s\ufeff]$/.test( siblingText ); | 1330 | isWhiteSpace = /[\s\ufeff]$/.test( siblingText ); |
1325 | } else { | 1331 | } else { |
1326 | // #12221 (Chrome) plus #11111 (Safari). | 1332 | // http://dev.ckeditor.com/ticket/12221 (Chrome) plus http://dev.ckeditor.com/ticket/11111 (Safari). |
1327 | var offsetWidth0 = CKEDITOR.env.webkit ? 1 : 0; | 1333 | var offsetWidth0 = CKEDITOR.env.webkit ? 1 : 0; |
1328 | 1334 | ||
1329 | // If this is a visible element. | 1335 | // If this is a visible element. |
1330 | // We need to check for the bookmark attribute because IE insists on | 1336 | // We need to check for the bookmark attribute because IE insists on |
1331 | // rendering the display:none nodes we use for bookmarks. (#3363) | 1337 | // rendering the display:none nodes we use for bookmarks. (http://dev.ckeditor.com/ticket/3363) |
1332 | // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041) | 1338 | // Line-breaks (br) are rendered with zero width, which we don't want to include. (http://dev.ckeditor.com/ticket/7041) |
1333 | if ( ( sibling.$.offsetWidth > offsetWidth0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) { | 1339 | if ( ( sibling.$.offsetWidth > offsetWidth0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) { |
1334 | // We'll accept it only if we need | 1340 | // We'll accept it only if we need |
1335 | // whitespace, and this is an inline | 1341 | // whitespace, and this is an inline |
@@ -1398,7 +1404,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1398 | 1404 | ||
1399 | // Process the end boundary. This is basically the same | 1405 | // Process the end boundary. This is basically the same |
1400 | // code used for the start boundary, with small changes to | 1406 | // code used for the start boundary, with small changes to |
1401 | // make it work in the oposite side (to the right). This | 1407 | // make it work in the opposite side (to the right). This |
1402 | // makes it difficult to reuse the code here. So, fixes to | 1408 | // makes it difficult to reuse the code here. So, fixes to |
1403 | // the above code are likely to be replicated here. | 1409 | // the above code are likely to be replicated here. |
1404 | 1410 | ||
@@ -1481,7 +1487,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1481 | } | 1487 | } |
1482 | } | 1488 | } |
1483 | } else { | 1489 | } else { |
1484 | // Get the node right after the boudary to be checked | 1490 | // Get the node right after the boundary to be checked |
1485 | // first. | 1491 | // first. |
1486 | sibling = container.getChild( offset ); | 1492 | sibling = container.getChild( offset ); |
1487 | 1493 | ||
@@ -1524,8 +1530,8 @@ CKEDITOR.dom.range = function( root ) { | |||
1524 | } else if ( sibling.type == CKEDITOR.NODE_ELEMENT ) { | 1530 | } else if ( sibling.type == CKEDITOR.NODE_ELEMENT ) { |
1525 | // If this is a visible element. | 1531 | // If this is a visible element. |
1526 | // We need to check for the bookmark attribute because IE insists on | 1532 | // We need to check for the bookmark attribute because IE insists on |
1527 | // rendering the display:none nodes we use for bookmarks. (#3363) | 1533 | // rendering the display:none nodes we use for bookmarks. (http://dev.ckeditor.com/ticket/3363) |
1528 | // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041) | 1534 | // Line-breaks (br) are rendered with zero width, which we don't want to include. (http://dev.ckeditor.com/ticket/7041) |
1529 | if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) { | 1535 | if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) { |
1530 | // We'll accept it only if we need | 1536 | // We'll accept it only if we need |
1531 | // whitespace, and this is an inline | 1537 | // whitespace, and this is an inline |
@@ -1616,7 +1622,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1616 | boundaryGuard = function( node ) { | 1622 | boundaryGuard = function( node ) { |
1617 | // We should not check contents of non-editable elements. It may happen | 1623 | // We should not check contents of non-editable elements. It may happen |
1618 | // that inline widget has display:table child which should not block range#enlarge. | 1624 | // that inline widget has display:table child which should not block range#enlarge. |
1619 | // When encoutered non-editable element... | 1625 | // When encountered non-editable element... |
1620 | if ( node.type == CKEDITOR.NODE_ELEMENT && node.getAttribute( 'contenteditable' ) == 'false' ) { | 1626 | if ( node.type == CKEDITOR.NODE_ELEMENT && node.getAttribute( 'contenteditable' ) == 'false' ) { |
1621 | if ( inNonEditable ) { | 1627 | if ( inNonEditable ) { |
1622 | // ... in which we already were, reset it (because we're leaving it) and return. | 1628 | // ... in which we already were, reset it (because we're leaving it) and return. |
@@ -1638,7 +1644,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1638 | blockBoundary = node; | 1644 | blockBoundary = node; |
1639 | return retval; | 1645 | return retval; |
1640 | }, | 1646 | }, |
1641 | // Record the encounted 'tailBr' for later use. | 1647 | // Record the encountered 'tailBr' for later use. |
1642 | tailBrGuard = function( node ) { | 1648 | tailBrGuard = function( node ) { |
1643 | var retval = boundaryGuard( node ); | 1649 | var retval = boundaryGuard( node ); |
1644 | if ( !retval && node.is && node.is( 'br' ) ) | 1650 | if ( !retval && node.is && node.is( 'br' ) ) |
@@ -1659,7 +1665,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1659 | this.setStartAt( blockBoundary, !blockBoundary.is( 'br' ) && ( !enlargeable && this.checkStartOfBlock() || | 1665 | this.setStartAt( blockBoundary, !blockBoundary.is( 'br' ) && ( !enlargeable && this.checkStartOfBlock() || |
1660 | enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END ); | 1666 | enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END ); |
1661 | 1667 | ||
1662 | // Avoid enlarging the range further when end boundary spans right after the BR. (#7490) | 1668 | // Avoid enlarging the range further when end boundary spans right after the BR. (http://dev.ckeditor.com/ticket/7490) |
1663 | if ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) { | 1669 | if ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) { |
1664 | var theRange = this.clone(); | 1670 | var theRange = this.clone(); |
1665 | walker = new CKEDITOR.dom.walker( theRange ); | 1671 | walker = new CKEDITOR.dom.walker( theRange ); |
@@ -1714,18 +1720,28 @@ CKEDITOR.dom.range = function( root ) { | |||
1714 | }, | 1720 | }, |
1715 | 1721 | ||
1716 | /** | 1722 | /** |
1717 | * Descrease the range to make sure that boundaries | 1723 | * Decreases the range to make sure that boundaries |
1718 | * always anchor beside text nodes or innermost element. | 1724 | * always anchor beside text nodes or the innermost element. |
1719 | * | 1725 | * |
1720 | * @param {Number} mode The shrinking mode ({@link CKEDITOR#SHRINK_ELEMENT} or {@link CKEDITOR#SHRINK_TEXT}). | 1726 | * @param {Number} mode The shrinking mode ({@link CKEDITOR#SHRINK_ELEMENT} or {@link CKEDITOR#SHRINK_TEXT}). |
1721 | * | 1727 | * |
1722 | * * {@link CKEDITOR#SHRINK_ELEMENT} - Shrink the range boundaries to the edge of the innermost element. | 1728 | * * {@link CKEDITOR#SHRINK_ELEMENT} – Shrinks the range boundaries to the edge of the innermost element. |
1723 | * * {@link CKEDITOR#SHRINK_TEXT} - Shrink the range boudaries to anchor by the side of enclosed text | 1729 | * * {@link CKEDITOR#SHRINK_TEXT} – Shrinks the range boundaries to anchor by the side of enclosed text |
1724 | * node, range remains if there's no text nodes on boundaries at all. | 1730 | * node. The range remains if there are no text nodes available on boundaries. |
1725 | * | 1731 | * |
1726 | * @param {Boolean} selectContents Whether result range anchors at the inner OR outer boundary of the node. | 1732 | * @param {Boolean} [selectContents=false] Whether the resulting range anchors at the inner OR outer boundary of the node. |
1733 | * @param {Boolean/Object} [options=true] If this parameter is of a Boolean type, it is treated as | ||
1734 | * `options.shrinkOnBlockBoundary`. This parameter was added in 4.7.0. | ||
1735 | * @param {Boolean} [options.shrinkOnBlockBoundary=true] Whether the block boundary should be included in | ||
1736 | * the shrunk range. | ||
1737 | * @param {Boolean} [options.skipBogus=false] Whether bogus `<br>` elements should be ignored while | ||
1738 | * `mode` is set to {@link CKEDITOR#SHRINK_TEXT}. This option was added in 4.7.0. | ||
1727 | */ | 1739 | */ |
1728 | shrink: function( mode, selectContents, shrinkOnBlockBoundary ) { | 1740 | shrink: function( mode, selectContents, options ) { |
1741 | var shrinkOnBlockBoundary = typeof options === 'boolean' ? options : | ||
1742 | ( options && typeof options.shrinkOnBlockBoundary === 'boolean' ? options.shrinkOnBlockBoundary : true ), | ||
1743 | skipBogus = options && options.skipBogus; | ||
1744 | |||
1729 | // Unable to shrink a collapsed range. | 1745 | // Unable to shrink a collapsed range. |
1730 | if ( !this.collapsed ) { | 1746 | if ( !this.collapsed ) { |
1731 | mode = mode || CKEDITOR.SHRINK_TEXT; | 1747 | mode = mode || CKEDITOR.SHRINK_TEXT; |
@@ -1748,7 +1764,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1748 | walkerRange.setStartAfter( startContainer ); | 1764 | walkerRange.setStartAfter( startContainer ); |
1749 | else { | 1765 | else { |
1750 | // Enlarge the range properly to avoid walker making | 1766 | // Enlarge the range properly to avoid walker making |
1751 | // DOM changes caused by triming the text nodes later. | 1767 | // DOM changes caused by trimming the text nodes later. |
1752 | walkerRange.setStartBefore( startContainer ); | 1768 | walkerRange.setStartBefore( startContainer ); |
1753 | moveStart = 0; | 1769 | moveStart = 0; |
1754 | } | 1770 | } |
@@ -1766,7 +1782,8 @@ CKEDITOR.dom.range = function( root ) { | |||
1766 | } | 1782 | } |
1767 | 1783 | ||
1768 | var walker = new CKEDITOR.dom.walker( walkerRange ), | 1784 | var walker = new CKEDITOR.dom.walker( walkerRange ), |
1769 | isBookmark = CKEDITOR.dom.walker.bookmark(); | 1785 | isBookmark = CKEDITOR.dom.walker.bookmark(), |
1786 | isBogus = CKEDITOR.dom.walker.bogus(); | ||
1770 | 1787 | ||
1771 | walker.evaluator = function( node ) { | 1788 | walker.evaluator = function( node ) { |
1772 | return node.type == ( mode == CKEDITOR.SHRINK_ELEMENT ? CKEDITOR.NODE_ELEMENT : CKEDITOR.NODE_TEXT ); | 1789 | return node.type == ( mode == CKEDITOR.SHRINK_ELEMENT ? CKEDITOR.NODE_ELEMENT : CKEDITOR.NODE_TEXT ); |
@@ -1774,6 +1791,11 @@ CKEDITOR.dom.range = function( root ) { | |||
1774 | 1791 | ||
1775 | var currentElement; | 1792 | var currentElement; |
1776 | walker.guard = function( node, movingOut ) { | 1793 | walker.guard = function( node, movingOut ) { |
1794 | // Skipping bogus before other cases (http://dev.ckeditor.com/ticket/17010). | ||
1795 | if ( skipBogus && isBogus( node ) ) { | ||
1796 | return true; | ||
1797 | } | ||
1798 | |||
1777 | if ( isBookmark( node ) ) | 1799 | if ( isBookmark( node ) ) |
1778 | return true; | 1800 | return true; |
1779 | 1801 | ||
@@ -1815,7 +1837,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1815 | 1837 | ||
1816 | /** | 1838 | /** |
1817 | * Inserts a node at the start of the range. The range will be expanded | 1839 | * Inserts a node at the start of the range. The range will be expanded |
1818 | * the contain the node. | 1840 | * to contain the node. |
1819 | * | 1841 | * |
1820 | * @param {CKEDITOR.dom.node} node | 1842 | * @param {CKEDITOR.dom.node} node |
1821 | */ | 1843 | */ |
@@ -1842,7 +1864,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1842 | }, | 1864 | }, |
1843 | 1865 | ||
1844 | /** | 1866 | /** |
1845 | * Moves the range to given position according to specified node. | 1867 | * Moves the range to a given position according to the specified node. |
1846 | * | 1868 | * |
1847 | * // HTML: <p>Foo <b>bar</b></p> | 1869 | * // HTML: <p>Foo <b>bar</b></p> |
1848 | * range.moveToPosition( elB, CKEDITOR.POSITION_BEFORE_START ); | 1870 | * range.moveToPosition( elB, CKEDITOR.POSITION_BEFORE_START ); |
@@ -1850,7 +1872,7 @@ CKEDITOR.dom.range = function( root ) { | |||
1850 | * | 1872 | * |
1851 | * See also {@link #setStartAt} and {@link #setEndAt}. | 1873 | * See also {@link #setStartAt} and {@link #setEndAt}. |
1852 | * | 1874 | * |
1853 | * @param {CKEDITOR.dom.node} node The node according to which position will be set. | 1875 | * @param {CKEDITOR.dom.node} node The node according to which the position will be set. |
1854 | * @param {Number} position One of {@link CKEDITOR#POSITION_BEFORE_START}, | 1876 | * @param {Number} position One of {@link CKEDITOR#POSITION_BEFORE_START}, |
1855 | * {@link CKEDITOR#POSITION_AFTER_START}, {@link CKEDITOR#POSITION_BEFORE_END}, | 1877 | * {@link CKEDITOR#POSITION_AFTER_START}, {@link CKEDITOR#POSITION_BEFORE_END}, |
1856 | * {@link CKEDITOR#POSITION_AFTER_END}. | 1878 | * {@link CKEDITOR#POSITION_AFTER_END}. |
@@ -2117,7 +2139,7 @@ CKEDITOR.dom.range = function( root ) { | |||
2117 | // So even if the initial range was placed before the bogus <br>, after creating the bookmark it | 2139 | // So even if the initial range was placed before the bogus <br>, after creating the bookmark it |
2118 | // is placed before the bookmark. | 2140 | // is placed before the bookmark. |
2119 | // Fortunately, getBogus() is able to skip the bookmark so it finds the bogus <br> in this case. | 2141 | // Fortunately, getBogus() is able to skip the bookmark so it finds the bogus <br> in this case. |
2120 | // We remove incorrectly placed one and add a brand new one. (#13001) | 2142 | // We remove incorrectly placed one and add a brand new one. (http://dev.ckeditor.com/ticket/13001) |
2121 | var bogus = fixedBlock.getBogus(); | 2143 | var bogus = fixedBlock.getBogus(); |
2122 | if ( bogus ) { | 2144 | if ( bogus ) { |
2123 | bogus.remove(); | 2145 | bogus.remove(); |
@@ -2339,7 +2361,7 @@ CKEDITOR.dom.range = function( root ) { | |||
2339 | this.trim( 0, 1 ); | 2361 | this.trim( 0, 1 ); |
2340 | } | 2362 | } |
2341 | 2363 | ||
2342 | // Antecipate the trim() call here, so the walker will not make | 2364 | // Anticipate the trim() call here, so the walker will not make |
2343 | // changes to the DOM, which would not get reflected into this | 2365 | // changes to the DOM, which would not get reflected into this |
2344 | // range otherwise. | 2366 | // range otherwise. |
2345 | this.trim(); | 2367 | this.trim(); |
@@ -2378,7 +2400,7 @@ CKEDITOR.dom.range = function( root ) { | |||
2378 | this.trim( 1, 0 ); | 2400 | this.trim( 1, 0 ); |
2379 | } | 2401 | } |
2380 | 2402 | ||
2381 | // Antecipate the trim() call here, so the walker will not make | 2403 | // Anticipate the trim() call here, so the walker will not make |
2382 | // changes to the DOM, which would not get reflected into this | 2404 | // changes to the DOM, which would not get reflected into this |
2383 | // range otherwise. | 2405 | // range otherwise. |
2384 | this.trim(); | 2406 | this.trim(); |
@@ -2635,7 +2657,7 @@ CKEDITOR.dom.range = function( root ) { | |||
2635 | getEnclosedNode: function() { | 2657 | getEnclosedNode: function() { |
2636 | var walkerRange = this.clone(); | 2658 | var walkerRange = this.clone(); |
2637 | 2659 | ||
2638 | // Optimize and analyze the range to avoid DOM destructive nature of walker. (#5780) | 2660 | // Optimize and analyze the range to avoid DOM destructive nature of walker. (http://dev.ckeditor.com/ticket/5780) |
2639 | walkerRange.optimize(); | 2661 | walkerRange.optimize(); |
2640 | if ( walkerRange.startContainer.type != CKEDITOR.NODE_ELEMENT || walkerRange.endContainer.type != CKEDITOR.NODE_ELEMENT ) | 2662 | if ( walkerRange.startContainer.type != CKEDITOR.NODE_ELEMENT || walkerRange.endContainer.type != CKEDITOR.NODE_ELEMENT ) |
2641 | return null; | 2663 | return null; |
@@ -2702,6 +2724,53 @@ CKEDITOR.dom.range = function( root ) { | |||
2702 | getPreviousEditableNode: getNextEditableNode( 1 ), | 2724 | getPreviousEditableNode: getNextEditableNode( 1 ), |
2703 | 2725 | ||
2704 | /** | 2726 | /** |
2727 | * Returns any table element, like `td`, `tbody`, `table` etc. from a given range. The element | ||
2728 | * is returned only if the range is contained within one table (might be a nested | ||
2729 | * table, but it cannot be two different tables on the same DOM level). | ||
2730 | * | ||
2731 | * @private | ||
2732 | * @since 4.7 | ||
2733 | * @param {Object} [tableElements] Mapping of element names that should be considered. | ||
2734 | * @returns {CKEDITOR.dom.element/null} | ||
2735 | */ | ||
2736 | _getTableElement: function( tableElements ) { | ||
2737 | tableElements = tableElements || { | ||
2738 | td: 1, | ||
2739 | th: 1, | ||
2740 | tr: 1, | ||
2741 | tbody: 1, | ||
2742 | thead: 1, | ||
2743 | tfoot: 1, | ||
2744 | table: 1 | ||
2745 | }; | ||
2746 | |||
2747 | var start = this.startContainer, | ||
2748 | end = this.endContainer, | ||
2749 | startTable = start.getAscendant( 'table', true ), | ||
2750 | endTable = end.getAscendant( 'table', true ); | ||
2751 | |||
2752 | // Super weird edge case in Safari: if there is a table with only one cell inside and that cell | ||
2753 | // is selected, then the end boundary of the table is moved into editor's editable. | ||
2754 | // That case is also present when selecting the last cell inside nested table. | ||
2755 | if ( CKEDITOR.env.safari && startTable && end.equals( this.root ) ) { | ||
2756 | return start.getAscendant( tableElements, true ); | ||
2757 | } | ||
2758 | |||
2759 | if ( this.getEnclosedNode() ) { | ||
2760 | return this.getEnclosedNode().getAscendant( tableElements, true ); | ||
2761 | } | ||
2762 | |||
2763 | // Ensure that selection starts and ends in the same table or one of the table is inside the other. | ||
2764 | if ( startTable && endTable && ( startTable.equals( endTable ) || startTable.contains( endTable ) || | ||
2765 | endTable.contains( startTable ) ) ) { | ||
2766 | |||
2767 | return start.getAscendant( tableElements, true ); | ||
2768 | } | ||
2769 | |||
2770 | return null; | ||
2771 | }, | ||
2772 | |||
2773 | /** | ||
2705 | * Scrolls the start of current range into view. | 2774 | * Scrolls the start of current range into view. |
2706 | */ | 2775 | */ |
2707 | scrollIntoView: function() { | 2776 | scrollIntoView: function() { |
@@ -2783,9 +2852,112 @@ CKEDITOR.dom.range = function( root ) { | |||
2783 | } | 2852 | } |
2784 | // %REMOVE_END% | 2853 | // %REMOVE_END% |
2785 | this.endContainer = endContainer; | 2854 | this.endContainer = endContainer; |
2855 | }, | ||
2856 | |||
2857 | /** | ||
2858 | * Looks for elements matching the `query` selector within a range. | ||
2859 | * | ||
2860 | * @since 4.5.11 | ||
2861 | * @private | ||
2862 | * @param {String} query | ||
2863 | * @param {Boolean} [includeNonEditables=false] Whether elements with `contenteditable` set to `false` should | ||
2864 | * be included. | ||
2865 | * @returns {CKEDITOR.dom.element[]} | ||
2866 | */ | ||
2867 | _find: function( query, includeNonEditables ) { | ||
2868 | var ancestor = this.getCommonAncestor(), | ||
2869 | boundaries = this.getBoundaryNodes(), | ||
2870 | // Contrary to CKEDITOR.dom.element#find we're returning array, that's because NodeList is immutable, and we need | ||
2871 | // to do some filtering in returned list. | ||
2872 | ret = [], | ||
2873 | curItem, | ||
2874 | i, | ||
2875 | initialMatches, | ||
2876 | isStartGood, | ||
2877 | isEndGood; | ||
2878 | |||
2879 | if ( ancestor && ancestor.find ) { | ||
2880 | initialMatches = ancestor.find( query ); | ||
2881 | |||
2882 | for ( i = 0; i < initialMatches.count(); i++ ) { | ||
2883 | curItem = initialMatches.getItem( i ); | ||
2884 | |||
2885 | // Using isReadOnly() method to filterout non editables. It checks isContentEditable including all browser quirks. | ||
2886 | if ( !includeNonEditables && curItem.isReadOnly() ) { | ||
2887 | continue; | ||
2888 | } | ||
2889 | |||
2890 | // It's not enough to get elements from common ancestor, because it might contain too many matches. | ||
2891 | // We need to ensure that returned items are between boundary points. | ||
2892 | isStartGood = ( curItem.getPosition( boundaries.startNode ) & CKEDITOR.POSITION_FOLLOWING ) || boundaries.startNode.equals( curItem ); | ||
2893 | isEndGood = ( curItem.getPosition( boundaries.endNode ) & ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IS_CONTAINED ) ) || boundaries.endNode.equals( curItem ); | ||
2894 | |||
2895 | if ( isStartGood && isEndGood ) { | ||
2896 | ret.push( curItem ); | ||
2897 | } | ||
2898 | } | ||
2899 | } | ||
2900 | |||
2901 | return ret; | ||
2786 | } | 2902 | } |
2787 | }; | 2903 | }; |
2788 | 2904 | ||
2905 | /** | ||
2906 | * Merges every subsequent range in given set, returning a smaller array of ranges. | ||
2907 | * | ||
2908 | * Note that each range in the returned value will be enlarged with `CKEDITOR.ENLARGE_ELEMENT` value. | ||
2909 | * | ||
2910 | * @since 4.7.0 | ||
2911 | * @static | ||
2912 | * @param {CKEDITOR.dom.range[]} ranges | ||
2913 | * @returns {CKEDITOR.dom.range[]} Set of merged ranges. | ||
2914 | * @member CKEDITOR.dom.range | ||
2915 | */ | ||
2916 | CKEDITOR.dom.range.mergeRanges = function( ranges ) { | ||
2917 | return CKEDITOR.tools.array.reduce( ranges, function( ret, rng ) { | ||
2918 | // Last range ATM. | ||
2919 | var lastRange = ret[ ret.length - 1 ], | ||
2920 | isContinuation = false; | ||
2921 | |||
2922 | // Make a clone, we don't want to modify input. | ||
2923 | rng = rng.clone(); | ||
2924 | rng.enlarge( CKEDITOR.ENLARGE_ELEMENT ); | ||
2925 | |||
2926 | if ( lastRange ) { | ||
2927 | // The trick is to create a range spanning the gap between the two ranges. Then iterate over | ||
2928 | // each node found in this gap. If it contains anything other than whitespace, then it means it | ||
2929 | // is not a continuation. | ||
2930 | var gapRange = new CKEDITOR.dom.range( rng.root ), | ||
2931 | walker = new CKEDITOR.dom.walker( gapRange ), | ||
2932 | isWhitespace = CKEDITOR.dom.walker.whitespaces(), | ||
2933 | nodeInBetween; | ||
2934 | |||
2935 | gapRange.setStart( lastRange.endContainer, lastRange.endOffset ); | ||
2936 | gapRange.setEnd( rng.startContainer, rng.startOffset ); | ||
2937 | |||
2938 | nodeInBetween = walker.next(); | ||
2939 | |||
2940 | while ( isWhitespace( nodeInBetween ) || rng.endContainer.equals( nodeInBetween ) ) { | ||
2941 | // We don't care about whitespaces, and range container. Also we skip the endContainer, | ||
2942 | // as it will also be provided by the iterator (as it visits it's opening tag). | ||
2943 | nodeInBetween = walker.next(); | ||
2944 | } | ||
2945 | |||
2946 | // Simply, if anything has been found there's a content in between the two. | ||
2947 | isContinuation = !nodeInBetween; | ||
2948 | } | ||
2949 | |||
2950 | if ( isContinuation ) { | ||
2951 | // If last range ends, where the current range starts, then let's merge it. | ||
2952 | lastRange.setEnd( rng.endContainer, rng.endOffset ); | ||
2953 | } else { | ||
2954 | // In other case just push cur range into the stack. | ||
2955 | ret.push( rng ); | ||
2956 | } | ||
2957 | |||
2958 | return ret; | ||
2959 | }, [] ); | ||
2960 | }; | ||
2789 | 2961 | ||
2790 | } )(); | 2962 | } )(); |
2791 | 2963 | ||
@@ -2861,9 +3033,32 @@ CKEDITOR.POSITION_BEFORE_START = 3; | |||
2861 | */ | 3033 | */ |
2862 | CKEDITOR.POSITION_AFTER_END = 4; | 3034 | CKEDITOR.POSITION_AFTER_END = 4; |
2863 | 3035 | ||
3036 | /** | ||
3037 | * @readonly | ||
3038 | * @member CKEDITOR | ||
3039 | * @property {Number} [=1] | ||
3040 | */ | ||
2864 | CKEDITOR.ENLARGE_ELEMENT = 1; | 3041 | CKEDITOR.ENLARGE_ELEMENT = 1; |
3042 | |||
3043 | /** | ||
3044 | * @readonly | ||
3045 | * @member CKEDITOR | ||
3046 | * @property {Number} [=2] | ||
3047 | */ | ||
2865 | CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2; | 3048 | CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2; |
3049 | |||
3050 | /** | ||
3051 | * @readonly | ||
3052 | * @member CKEDITOR | ||
3053 | * @property {Number} [=3] | ||
3054 | */ | ||
2866 | CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3; | 3055 | CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3; |
3056 | |||
3057 | /** | ||
3058 | * @readonly | ||
3059 | * @member CKEDITOR | ||
3060 | * @property {Number} [=4] | ||
3061 | */ | ||
2867 | CKEDITOR.ENLARGE_INLINE = 4; | 3062 | CKEDITOR.ENLARGE_INLINE = 4; |
2868 | 3063 | ||
2869 | // Check boundary types. | 3064 | // Check boundary types. |
diff --git a/sources/core/dom/rangelist.js b/sources/core/dom/rangelist.js index d02fc03..15ccb88 100644 --- a/sources/core/dom/rangelist.js +++ b/sources/core/dom/rangelist.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -164,7 +164,7 @@ | |||
164 | }; | 164 | }; |
165 | 165 | ||
166 | // Update the specified range which has been mangled by previous insertion of | 166 | // Update the specified range which has been mangled by previous insertion of |
167 | // range bookmark nodes.(#3256) | 167 | // range bookmark nodes.(http://dev.ckeditor.com/ticket/3256) |
168 | function updateDirtyRange( bookmark, dirtyRange, checkEnd ) { | 168 | function updateDirtyRange( bookmark, dirtyRange, checkEnd ) { |
169 | var serializable = bookmark.serializable, | 169 | var serializable = bookmark.serializable, |
170 | container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ], | 170 | container = dirtyRange[ checkEnd ? 'endContainer' : 'startContainer' ], |
diff --git a/sources/core/dom/text.js b/sources/core/dom/text.js index c313259..e77a3d9 100644 --- a/sources/core/dom/text.js +++ b/sources/core/dom/text.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -106,7 +106,7 @@ CKEDITOR.tools.extend( CKEDITOR.dom.text.prototype, { | |||
106 | retval.insertAfter( this ); | 106 | retval.insertAfter( this ); |
107 | } else { | 107 | } else { |
108 | // IE BUG: IE8+ does not update the childNodes array in DOM after splitText(), | 108 | // IE BUG: IE8+ does not update the childNodes array in DOM after splitText(), |
109 | // we need to make some DOM changes to make it update. (#3436) | 109 | // we need to make some DOM changes to make it update. (http://dev.ckeditor.com/ticket/3436) |
110 | var workaround = doc.createText( '' ); | 110 | var workaround = doc.createText( '' ); |
111 | workaround.insertAfter( retval ); | 111 | workaround.insertAfter( retval ); |
112 | workaround.remove(); | 112 | workaround.remove(); |
diff --git a/sources/core/dom/walker.js b/sources/core/dom/walker.js index 746b406..8665909 100644 --- a/sources/core/dom/walker.js +++ b/sources/core/dom/walker.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -319,7 +319,7 @@ | |||
319 | */ | 319 | */ |
320 | CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) { | 320 | CKEDITOR.dom.element.prototype.isBlockBoundary = function( customNodeNames ) { |
321 | // Whether element is in normal page flow. Floated or positioned elements are out of page flow. | 321 | // Whether element is in normal page flow. Floated or positioned elements are out of page flow. |
322 | // Don't consider floated or positioned formatting as block boundary, fall back to dtd check in that case. (#6297) | 322 | // Don't consider floated or positioned formatting as block boundary, fall back to dtd check in that case. (http://dev.ckeditor.com/ticket/6297) |
323 | var inPageFlow = this.getComputedStyle( 'float' ) == 'none' && !( this.getComputedStyle( 'position' ) in outOfFlowPositions ); | 323 | var inPageFlow = this.getComputedStyle( 'float' ) == 'none' && !( this.getComputedStyle( 'position' ) in outOfFlowPositions ); |
324 | 324 | ||
325 | if ( inPageFlow && blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] ) | 325 | if ( inPageFlow && blockBoundaryDisplayMatch[ this.getComputedStyle( 'display' ) ] ) |
@@ -388,7 +388,7 @@ | |||
388 | return function( node ) { | 388 | return function( node ) { |
389 | var isWhitespace; | 389 | var isWhitespace; |
390 | if ( node && node.type == CKEDITOR.NODE_TEXT ) { | 390 | if ( node && node.type == CKEDITOR.NODE_TEXT ) { |
391 | // Whitespace, as well as the Filling Char Sequence text node used in Webkit. (#9384, #13816) | 391 | // Whitespace, as well as the Filling Char Sequence text node used in Webkit. (http://dev.ckeditor.com/ticket/9384, http://dev.ckeditor.com/ticket/13816) |
392 | isWhitespace = !CKEDITOR.tools.trim( node.getText() ) || | 392 | isWhitespace = !CKEDITOR.tools.trim( node.getText() ) || |
393 | CKEDITOR.env.webkit && node.getText() == CKEDITOR.dom.selection.FILLING_CHAR_SEQUENCE; | 393 | CKEDITOR.env.webkit && node.getText() == CKEDITOR.dom.selection.FILLING_CHAR_SEQUENCE; |
394 | } | 394 | } |
@@ -406,7 +406,7 @@ | |||
406 | */ | 406 | */ |
407 | CKEDITOR.dom.walker.invisible = function( isReject ) { | 407 | CKEDITOR.dom.walker.invisible = function( isReject ) { |
408 | var whitespace = CKEDITOR.dom.walker.whitespaces(), | 408 | var whitespace = CKEDITOR.dom.walker.whitespaces(), |
409 | // #12221 (Chrome) plus #11111 (Safari). | 409 | // http://dev.ckeditor.com/ticket/12221 (Chrome) plus http://dev.ckeditor.com/ticket/11111 (Safari). |
410 | offsetWidth0 = CKEDITOR.env.webkit ? 1 : 0; | 410 | offsetWidth0 = CKEDITOR.env.webkit ? 1 : 0; |
411 | 411 | ||
412 | return function( node ) { | 412 | return function( node ) { |
@@ -422,7 +422,7 @@ | |||
422 | // Nodes that take no spaces in wysiwyg: | 422 | // Nodes that take no spaces in wysiwyg: |
423 | // 1. White-spaces but not including NBSP. | 423 | // 1. White-spaces but not including NBSP. |
424 | // 2. Empty inline elements, e.g. <b></b>. | 424 | // 2. Empty inline elements, e.g. <b></b>. |
425 | // 3. <br> elements (bogus, surrounded by text) (#12423). | 425 | // 3. <br> elements (bogus, surrounded by text) (http://dev.ckeditor.com/ticket/12423). |
426 | invisible = node.$.offsetWidth <= offsetWidth0; | 426 | invisible = node.$.offsetWidth <= offsetWidth0; |
427 | } | 427 | } |
428 | 428 | ||
@@ -636,7 +636,7 @@ | |||
636 | * @returns {CKEDITOR.dom.node/Boolean} Bogus node or `false`. | 636 | * @returns {CKEDITOR.dom.node/Boolean} Bogus node or `false`. |
637 | */ | 637 | */ |
638 | CKEDITOR.dom.element.prototype.getBogus = function() { | 638 | CKEDITOR.dom.element.prototype.getBogus = function() { |
639 | // Bogus are not always at the end, e.g. <p><a>text<br /></a></p> (#7070). | 639 | // Bogus are not always at the end, e.g. <p><a>text<br /></a></p> (http://dev.ckeditor.com/ticket/7070). |
640 | var tail = this; | 640 | var tail = this; |
641 | do { | 641 | do { |
642 | tail = tail.getPreviousSourceNode(); | 642 | tail = tail.getPreviousSourceNode(); |
diff --git a/sources/core/dom/window.js b/sources/core/dom/window.js index edfeb84..ceeaeff 100644 --- a/sources/core/dom/window.js +++ b/sources/core/dom/window.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/dtd.js b/sources/core/dtd.js index d6dc5a6..6059d48 100644 --- a/sources/core/dtd.js +++ b/sources/core/dtd.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/editable.js b/sources/core/editable.js index b9b0270..6b3fa9f 100644 --- a/sources/core/editable.js +++ b/sources/core/editable.js | |||
@@ -1,9 +1,12 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
6 | ( function() { | 6 | ( function() { |
7 | var isNotWhitespace, isNotBookmark, isEmpty, isBogus, emptyParagraphRegexp, | ||
8 | insert, fixTableAfterContentsDeletion, fixListAfterContentsDelete, getHtmlFromRangeHelpers, extractHtmlFromRangeHelpers; | ||
9 | |||
7 | /** | 10 | /** |
8 | * Editable class which provides all editing related activities by | 11 | * Editable class which provides all editing related activities by |
9 | * the `contenteditable` element, dynamically get attached to editor instance. | 12 | * the `contenteditable` element, dynamically get attached to editor instance. |
@@ -71,17 +74,36 @@ | |||
71 | } | 74 | } |
72 | } | 75 | } |
73 | 76 | ||
74 | // [IE] Use instead "setActive" method to focus the editable if it belongs to | 77 | // [Edge] Starting from EdgeHTML 14.14393, it does not support `setActive`. We need to use focus which |
75 | // the host page document, to avoid bringing an unexpected scroll. | 78 | // causes unexpected scroll. Store scrollTop value so it can be restored after focusing editor. |
79 | // Scroll only happens if the editor is focused for the first time. (http://dev.ckeditor.com/ticket/14825) | ||
80 | if ( CKEDITOR.env.edge && CKEDITOR.env.version > 14 && !this.hasFocus && this.getDocument().equals( CKEDITOR.document ) ) { | ||
81 | this.editor._.previousScrollTop = this.$.scrollTop; | ||
82 | } | ||
83 | |||
84 | // [IE] Use instead "setActive" method to focus the editable if it belongs to the host page document, | ||
85 | // to avoid bringing an unexpected scroll. | ||
76 | try { | 86 | try { |
77 | this.$[ CKEDITOR.env.ie && this.getDocument().equals( CKEDITOR.document ) ? 'setActive' : 'focus' ](); | 87 | if ( CKEDITOR.env.ie && !( CKEDITOR.env.edge && CKEDITOR.env.version > 14 ) && this.getDocument().equals( CKEDITOR.document ) ) { |
88 | this.$.setActive(); | ||
89 | } else { | ||
90 | // We have no control over exactly what happens when the native `focus` method is called, | ||
91 | // so save the scroll position and restore it later. | ||
92 | if ( CKEDITOR.env.chrome ) { | ||
93 | var scrollPos = this.$.scrollTop; | ||
94 | this.$.focus(); | ||
95 | this.$.scrollTop = scrollPos; | ||
96 | } else { | ||
97 | this.$.focus(); | ||
98 | } | ||
99 | } | ||
78 | } catch ( e ) { | 100 | } catch ( e ) { |
79 | // IE throws unspecified error when focusing editable after closing dialog opened on nested editable. | 101 | // IE throws unspecified error when focusing editable after closing dialog opened on nested editable. |
80 | if ( !CKEDITOR.env.ie ) | 102 | if ( !CKEDITOR.env.ie ) |
81 | throw e; | 103 | throw e; |
82 | } | 104 | } |
83 | 105 | ||
84 | // Remedy if Safari doens't applies focus properly. (#279) | 106 | // Remedy if Safari doens't applies focus properly. (http://dev.ckeditor.com/ticket/279) |
85 | if ( CKEDITOR.env.safari && !this.isInline() ) { | 107 | if ( CKEDITOR.env.safari && !this.isInline() ) { |
86 | active = CKEDITOR.document.getActive(); | 108 | active = CKEDITOR.document.getActive(); |
87 | if ( !active.equals( this.getWindow().getFrame() ) ) | 109 | if ( !active.equals( this.getWindow().getFrame() ) ) |
@@ -103,7 +125,7 @@ | |||
103 | 125 | ||
104 | // The "focusin/focusout" events bubbled, e.g. If there are elements with layout | 126 | // The "focusin/focusout" events bubbled, e.g. If there are elements with layout |
105 | // they fire this event when clicking in to edit them but it must be ignored | 127 | // they fire this event when clicking in to edit them but it must be ignored |
106 | // to allow edit their contents. (#4682) | 128 | // to allow edit their contents. (http://dev.ckeditor.com/ticket/4682) |
107 | fn = isNotBubbling( fn, this ); | 129 | fn = isNotBubbling( fn, this ); |
108 | args[ 0 ] = name; | 130 | args[ 0 ] = name; |
109 | args[ 1 ] = fn; | 131 | args[ 1 ] = fn; |
@@ -238,7 +260,7 @@ | |||
238 | * @param {String} text | 260 | * @param {String} text |
239 | */ | 261 | */ |
240 | insertText: function( text ) { | 262 | insertText: function( text ) { |
241 | // Focus the editor before calling transformPlainTextToHtml. (#12726) | 263 | // Focus the editor before calling transformPlainTextToHtml. (http://dev.ckeditor.com/ticket/12726) |
242 | this.editor.focus(); | 264 | this.editor.focus(); |
243 | this.insertHtml( this.transformPlainTextToHtml( text ), 'text' ); | 265 | this.insertHtml( this.transformPlainTextToHtml( text ), 'text' ); |
244 | }, | 266 | }, |
@@ -336,7 +358,7 @@ | |||
336 | insertElement: function( element, range ) { | 358 | insertElement: function( element, range ) { |
337 | var editor = this.editor; | 359 | var editor = this.editor; |
338 | 360 | ||
339 | // Prepare for the insertion. For example - focus editor (#11848). | 361 | // Prepare for the insertion. For example - focus editor (http://dev.ckeditor.com/ticket/11848). |
340 | editor.focus(); | 362 | editor.focus(); |
341 | editor.fire( 'saveSnapshot' ); | 363 | editor.fire( 'saveSnapshot' ); |
342 | 364 | ||
@@ -349,12 +371,12 @@ | |||
349 | range = selection.getRanges()[ 0 ]; | 371 | range = selection.getRanges()[ 0 ]; |
350 | } | 372 | } |
351 | 373 | ||
352 | // Insert element into first range only and ignore the rest (#11183). | 374 | // Insert element into first range only and ignore the rest (http://dev.ckeditor.com/ticket/11183). |
353 | if ( this.insertElementIntoRange( element, range ) ) { | 375 | if ( this.insertElementIntoRange( element, range ) ) { |
354 | range.moveToPosition( element, CKEDITOR.POSITION_AFTER_END ); | 376 | range.moveToPosition( element, CKEDITOR.POSITION_AFTER_END ); |
355 | 377 | ||
356 | // If we're inserting a block element, the new cursor position must be | 378 | // If we're inserting a block element, the new cursor position must be |
357 | // optimized. (#3100,#5436,#8950) | 379 | // optimized. (http://dev.ckeditor.com/ticket/3100,http://dev.ckeditor.com/ticket/5436,http://dev.ckeditor.com/ticket/8950) |
358 | if ( isBlock ) { | 380 | if ( isBlock ) { |
359 | // Find next, meaningful element. | 381 | // Find next, meaningful element. |
360 | var next = element.getNext( function( node ) { | 382 | var next = element.getNext( function( node ) { |
@@ -415,12 +437,19 @@ | |||
415 | // Remove the original contents, merge split nodes. | 437 | // Remove the original contents, merge split nodes. |
416 | range.deleteContents( 1 ); | 438 | range.deleteContents( 1 ); |
417 | 439 | ||
418 | // If range is placed in inermediate element (not td or th), we need to do three things: | 440 | if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT ) { |
419 | // * fill emptied <td/th>s with if browser needs them, | 441 | // If range is placed in intermediate element (not td or th), we need to do three things: |
420 | // * remove empty text nodes so IE8 won't crash (http://dev.ckeditor.com/ticket/11183#comment:8), | 442 | // * fill emptied <td/th>s with if browser needs them, |
421 | // * fix structure and move range into the <td/th> element. | 443 | // * remove empty text nodes so IE8 won't crash |
422 | if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT && range.startContainer.is( { tr: 1, table: 1, tbody: 1, thead: 1, tfoot: 1 } ) ) | 444 | // (http://dev.ckeditor.com/ticket/11183#comment:8), |
423 | fixTableAfterContentsDeletion( range ); | 445 | // * fix structure and move range into the <td/th> element. |
446 | if ( range.startContainer.is( { tr: 1, table: 1, tbody: 1, thead: 1, tfoot: 1 } ) ) { | ||
447 | fixTableAfterContentsDeletion( range ); | ||
448 | } else if ( range.startContainer.is( CKEDITOR.dtd.$list ) ) { | ||
449 | // Similarly there's a need for lists. | ||
450 | fixListAfterContentsDelete( range ); | ||
451 | } | ||
452 | } | ||
424 | 453 | ||
425 | // If we're inserting a block at dtd-violated position, split | 454 | // If we're inserting a block at dtd-violated position, split |
426 | // the parent blocks until we reach blockLimit. | 455 | // the parent blocks until we reach blockLimit. |
@@ -435,7 +464,7 @@ | |||
435 | range.splitElement( current ); | 464 | range.splitElement( current ); |
436 | 465 | ||
437 | // If we're in an empty block which indicate a new paragraph, | 466 | // If we're in an empty block which indicate a new paragraph, |
438 | // simply replace it with the inserting block.(#3664) | 467 | // simply replace it with the inserting block.(http://dev.ckeditor.com/ticket/3664) |
439 | else if ( range.checkStartOfBlock() && range.checkEndOfBlock() ) { | 468 | else if ( range.checkStartOfBlock() && range.checkEndOfBlock() ) { |
440 | range.setStartBefore( current ); | 469 | range.setStartBefore( current ); |
441 | range.collapse( true ); | 470 | range.collapse( true ); |
@@ -749,7 +778,7 @@ | |||
749 | range.checkEndOfBlock() && | 778 | range.checkEndOfBlock() && |
750 | path.block && | 779 | path.block && |
751 | !range.root.equals( path.block ) && | 780 | !range.root.equals( path.block ) && |
752 | // Do not remove a block with bookmarks. (#13465) | 781 | // Do not remove a block with bookmarks. (http://dev.ckeditor.com/ticket/13465) |
753 | !hasBookmarks( path.block ) ) { | 782 | !hasBookmarks( path.block ) ) { |
754 | range.moveToPosition( path.block, CKEDITOR.POSITION_BEFORE_START ); | 783 | range.moveToPosition( path.block, CKEDITOR.POSITION_BEFORE_START ); |
755 | path.block.remove(); | 784 | path.block.remove(); |
@@ -811,7 +840,7 @@ | |||
811 | 840 | ||
812 | // IE considers control-type element as separate | 841 | // IE considers control-type element as separate |
813 | // focus host when selected, avoid destroying the | 842 | // focus host when selected, avoid destroying the |
814 | // selection in such case. (#5812) (#8949) | 843 | // selection in such case. (http://dev.ckeditor.com/ticket/5812) (http://dev.ckeditor.com/ticket/8949) |
815 | if ( ieSel && ieSel.type == 'Control' ) | 844 | if ( ieSel && ieSel.type == 'Control' ) |
816 | return; | 845 | return; |
817 | 846 | ||
@@ -862,6 +891,30 @@ | |||
862 | this.hasFocus = true; | 891 | this.hasFocus = true; |
863 | }, null, null, -1 ); | 892 | }, null, null, -1 ); |
864 | 893 | ||
894 | if ( CKEDITOR.env.webkit ) { | ||
895 | // [WebKit] Save scrollTop value so it can be used when restoring locked selection. (http://dev.ckeditor.com/ticket/14659) | ||
896 | this.on( 'scroll', function() { | ||
897 | editor._.previousScrollTop = editor.editable().$.scrollTop; | ||
898 | }, null, null, -1 ); | ||
899 | } | ||
900 | |||
901 | // [Edge] This is the other part of the workaround for Edge which restores saved | ||
902 | // scrollTop value and removes listener which is not needed anymore. (http://dev.ckeditor.com/ticket/14825) | ||
903 | if ( CKEDITOR.env.edge && CKEDITOR.env.version > 14 ) { | ||
904 | |||
905 | var fixScrollOnFocus = function() { | ||
906 | var editable = editor.editable(); | ||
907 | |||
908 | if ( editor._.previousScrollTop != null && editable.getDocument().equals( CKEDITOR.document ) ) { | ||
909 | editable.$.scrollTop = editor._.previousScrollTop; | ||
910 | editor._.previousScrollTop = null; | ||
911 | this.removeListener( 'scroll', fixScrollOnFocus ); | ||
912 | } | ||
913 | }; | ||
914 | |||
915 | this.on( 'scroll', fixScrollOnFocus ); | ||
916 | } | ||
917 | |||
865 | // Register to focus manager. | 918 | // Register to focus manager. |
866 | editor.focusManager.add( this ); | 919 | editor.focusManager.add( this ); |
867 | 920 | ||
@@ -902,12 +955,16 @@ | |||
902 | // Create the content stylesheet for this document. | 955 | // Create the content stylesheet for this document. |
903 | var styles = CKEDITOR.getCss(); | 956 | var styles = CKEDITOR.getCss(); |
904 | if ( styles ) { | 957 | if ( styles ) { |
905 | var head = doc.getHead(); | 958 | var head = doc.getHead(), |
906 | if ( !head.getCustomData( 'stylesheet' ) ) { | 959 | stylesElement = head.getCustomData( 'stylesheet' ); |
960 | |||
961 | if ( !stylesElement ) { | ||
907 | var sheet = doc.appendStyleText( styles ); | 962 | var sheet = doc.appendStyleText( styles ); |
908 | sheet = new CKEDITOR.dom.element( sheet.ownerNode || sheet.owningElement ); | 963 | sheet = new CKEDITOR.dom.element( sheet.ownerNode || sheet.owningElement ); |
909 | head.setCustomData( 'stylesheet', sheet ); | 964 | head.setCustomData( 'stylesheet', sheet ); |
910 | sheet.data( 'cke-temp', 1 ); | 965 | sheet.data( 'cke-temp', 1 ); |
966 | } else if ( styles != stylesElement.getText() ) { | ||
967 | CKEDITOR.env.ie && CKEDITOR.env.version < 9 ? stylesElement.$.styleSheet.cssText = styles : stylesElement.setText( styles ); | ||
911 | } | 968 | } |
912 | } | 969 | } |
913 | 970 | ||
@@ -918,7 +975,7 @@ | |||
918 | // Pass this configuration to styles system. | 975 | // Pass this configuration to styles system. |
919 | this.setCustomData( 'cke_includeReadonly', !editor.config.disableReadonlyStyling ); | 976 | this.setCustomData( 'cke_includeReadonly', !editor.config.disableReadonlyStyling ); |
920 | 977 | ||
921 | // Prevent the browser opening read-only links. (#6032 & #10912) | 978 | // Prevent the browser opening read-only links. (http://dev.ckeditor.com/ticket/6032 & http://dev.ckeditor.com/ticket/10912) |
922 | this.attachListener( this, 'click', function( evt ) { | 979 | this.attachListener( this, 'click', function( evt ) { |
923 | evt = evt.data; | 980 | evt = evt.data; |
924 | 981 | ||
@@ -931,7 +988,7 @@ | |||
931 | var backspaceOrDelete = { 8: 1, 46: 1 }; | 988 | var backspaceOrDelete = { 8: 1, 46: 1 }; |
932 | 989 | ||
933 | // Override keystrokes which should have deletion behavior | 990 | // Override keystrokes which should have deletion behavior |
934 | // on fully selected element . (#4047) (#7645) | 991 | // on fully selected element . (http://dev.ckeditor.com/ticket/4047) (http://dev.ckeditor.com/ticket/7645) |
935 | this.attachListener( editor, 'key', function( evt ) { | 992 | this.attachListener( editor, 'key', function( evt ) { |
936 | if ( editor.readOnly ) | 993 | if ( editor.readOnly ) |
937 | return true; | 994 | return true; |
@@ -941,10 +998,15 @@ | |||
941 | var keyCode = evt.data.domEvent.getKey(), | 998 | var keyCode = evt.data.domEvent.getKey(), |
942 | isHandled; | 999 | isHandled; |
943 | 1000 | ||
1001 | // Prevent of reading path of empty range (http://dev.ckeditor.com/ticket/13096, #457). | ||
1002 | var sel = editor.getSelection(); | ||
1003 | if ( sel.getRanges().length === 0 ) { | ||
1004 | return; | ||
1005 | } | ||
1006 | |||
944 | // Backspace OR Delete. | 1007 | // Backspace OR Delete. |
945 | if ( keyCode in backspaceOrDelete ) { | 1008 | if ( keyCode in backspaceOrDelete ) { |
946 | var sel = editor.getSelection(), | 1009 | var selected, |
947 | selected, | ||
948 | range = sel.getRanges()[ 0 ], | 1010 | range = sel.getRanges()[ 0 ], |
949 | path = range.startPath(), | 1011 | path = range.startPath(), |
950 | block, | 1012 | block, |
@@ -952,16 +1014,17 @@ | |||
952 | next, | 1014 | next, |
953 | rtl = keyCode == 8; | 1015 | rtl = keyCode == 8; |
954 | 1016 | ||
1017 | |||
955 | if ( | 1018 | if ( |
956 | // [IE<11] Remove selected image/anchor/etc here to avoid going back in history. (#10055) | 1019 | // [IE<11] Remove selected image/anchor/etc here to avoid going back in history. (http://dev.ckeditor.com/ticket/10055) |
957 | ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 && ( selected = sel.getSelectedElement() ) ) || | 1020 | ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 && ( selected = sel.getSelectedElement() ) ) || |
958 | // Remove the entire list/table on fully selected content. (#7645) | 1021 | // Remove the entire list/table on fully selected content. (http://dev.ckeditor.com/ticket/7645) |
959 | ( selected = getSelectedTableList( sel ) ) ) { | 1022 | ( selected = getSelectedTableList( sel ) ) ) { |
960 | // Make undo snapshot. | 1023 | // Make undo snapshot. |
961 | editor.fire( 'saveSnapshot' ); | 1024 | editor.fire( 'saveSnapshot' ); |
962 | 1025 | ||
963 | // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will | 1026 | // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will |
964 | // break up the selection, safely manage it here. (#4795) | 1027 | // break up the selection, safely manage it here. (http://dev.ckeditor.com/ticket/4795) |
965 | range.moveToPosition( selected, CKEDITOR.POSITION_BEFORE_START ); | 1028 | range.moveToPosition( selected, CKEDITOR.POSITION_BEFORE_START ); |
966 | // Remove the control manually. | 1029 | // Remove the control manually. |
967 | selected.remove(); | 1030 | selected.remove(); |
@@ -971,7 +1034,7 @@ | |||
971 | 1034 | ||
972 | isHandled = 1; | 1035 | isHandled = 1; |
973 | } else if ( range.collapsed ) { | 1036 | } else if ( range.collapsed ) { |
974 | // Handle the following special cases: (#6217) | 1037 | // Handle the following special cases: (http://dev.ckeditor.com/ticket/6217) |
975 | // 1. Del/Backspace key before/after table; | 1038 | // 1. Del/Backspace key before/after table; |
976 | // 2. Backspace Key after start of table. | 1039 | // 2. Backspace Key after start of table. |
977 | if ( ( block = path.block ) && | 1040 | if ( ( block = path.block ) && |
@@ -1046,28 +1109,28 @@ | |||
1046 | editor.fire( 'doubleclick', data ); | 1109 | editor.fire( 'doubleclick', data ); |
1047 | } ); | 1110 | } ); |
1048 | 1111 | ||
1049 | // Prevent automatic submission in IE #6336 | 1112 | // Prevent automatic submission in IE http://dev.ckeditor.com/ticket/6336 |
1050 | CKEDITOR.env.ie && this.attachListener( this, 'click', blockInputClick ); | 1113 | CKEDITOR.env.ie && this.attachListener( this, 'click', blockInputClick ); |
1051 | 1114 | ||
1052 | // Gecko/Webkit need some help when selecting control type elements. (#3448) | 1115 | // Gecko/Webkit need some help when selecting control type elements. (http://dev.ckeditor.com/ticket/3448) |
1053 | // We apply same behavior for IE Edge. (#13386) | 1116 | // We apply same behavior for IE Edge. (http://dev.ckeditor.com/ticket/13386) |
1054 | if ( !CKEDITOR.env.ie || CKEDITOR.env.edge ) { | 1117 | if ( !CKEDITOR.env.ie || CKEDITOR.env.edge ) { |
1055 | this.attachListener( this, 'mousedown', function( ev ) { | 1118 | this.attachListener( this, 'mousedown', function( ev ) { |
1056 | var control = ev.data.getTarget(); | 1119 | var control = ev.data.getTarget(); |
1057 | // #11727. Note: htmlDP assures that input/textarea/select have contenteditable=false | 1120 | // http://dev.ckeditor.com/ticket/11727. Note: htmlDP assures that input/textarea/select have contenteditable=false |
1058 | // attributes. However, they also have data-cke-editable attribute, so isReadOnly() returns false, | 1121 | // attributes. However, they also have data-cke-editable attribute, so isReadOnly() returns false, |
1059 | // and therefore those elements are correctly selected by this code. | 1122 | // and therefore those elements are correctly selected by this code. |
1060 | if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) && !control.isReadOnly() ) { | 1123 | if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) && !control.isReadOnly() ) { |
1061 | editor.getSelection().selectElement( control ); | 1124 | editor.getSelection().selectElement( control ); |
1062 | 1125 | ||
1063 | // Prevent focus from stealing from the editable. (#9515) | 1126 | // Prevent focus from stealing from the editable. (http://dev.ckeditor.com/ticket/9515) |
1064 | if ( control.is( 'input', 'textarea', 'select' ) ) | 1127 | if ( control.is( 'input', 'textarea', 'select' ) ) |
1065 | ev.data.preventDefault(); | 1128 | ev.data.preventDefault(); |
1066 | } | 1129 | } |
1067 | } ); | 1130 | } ); |
1068 | } | 1131 | } |
1069 | 1132 | ||
1070 | // For some reason, after click event is done, IE Edge loses focus on the selected element. (#13386) | 1133 | // For some reason, after click event is done, IE Edge loses focus on the selected element. (http://dev.ckeditor.com/ticket/13386) |
1071 | if ( CKEDITOR.env.edge ) { | 1134 | if ( CKEDITOR.env.edge ) { |
1072 | this.attachListener( this, 'mouseup', function( ev ) { | 1135 | this.attachListener( this, 'mouseup', function( ev ) { |
1073 | var selectedElement = ev.data.getTarget(); | 1136 | var selectedElement = ev.data.getTarget(); |
@@ -1078,7 +1141,7 @@ | |||
1078 | } | 1141 | } |
1079 | 1142 | ||
1080 | // Prevent right click from selecting an empty block even | 1143 | // Prevent right click from selecting an empty block even |
1081 | // when selection is anchored inside it. (#5845) | 1144 | // when selection is anchored inside it. (http://dev.ckeditor.com/ticket/5845) |
1082 | if ( CKEDITOR.env.gecko ) { | 1145 | if ( CKEDITOR.env.gecko ) { |
1083 | this.attachListener( this, 'mouseup', function( ev ) { | 1146 | this.attachListener( this, 'mouseup', function( ev ) { |
1084 | if ( ev.data.$.button == 2 ) { | 1147 | if ( ev.data.$.button == 2 ) { |
@@ -1109,7 +1172,7 @@ | |||
1109 | } | 1172 | } |
1110 | 1173 | ||
1111 | // Prevent Webkit/Blink from going rogue when joining | 1174 | // Prevent Webkit/Blink from going rogue when joining |
1112 | // blocks on BACKSPACE/DEL (#11861,#9998). | 1175 | // blocks on BACKSPACE/DEL (http://dev.ckeditor.com/ticket/11861,http://dev.ckeditor.com/ticket/9998). |
1113 | if ( CKEDITOR.env.webkit ) { | 1176 | if ( CKEDITOR.env.webkit ) { |
1114 | this.attachListener( editor, 'key', function( evt ) { | 1177 | this.attachListener( editor, 'key', function( evt ) { |
1115 | if ( editor.readOnly ) { | 1178 | if ( editor.readOnly ) { |
@@ -1123,8 +1186,14 @@ | |||
1123 | if ( !( key in backspaceOrDelete ) ) | 1186 | if ( !( key in backspaceOrDelete ) ) |
1124 | return; | 1187 | return; |
1125 | 1188 | ||
1189 | // Prevent of reading path of empty range (http://dev.ckeditor.com/ticket/13096, #457). | ||
1190 | var sel = editor.getSelection(); | ||
1191 | if ( sel.getRanges().length === 0 ) { | ||
1192 | return; | ||
1193 | } | ||
1194 | |||
1126 | var backspace = key == 8, | 1195 | var backspace = key == 8, |
1127 | range = editor.getSelection().getRanges()[ 0 ], | 1196 | range = sel.getRanges()[ 0 ], |
1128 | startPath = range.startPath(); | 1197 | startPath = range.startPath(); |
1129 | 1198 | ||
1130 | if ( range.collapsed ) { | 1199 | if ( range.collapsed ) { |
@@ -1135,7 +1204,7 @@ | |||
1135 | return; | 1204 | return; |
1136 | } | 1205 | } |
1137 | 1206 | ||
1138 | // Scroll to the new position of the caret (#11960). | 1207 | // Scroll to the new position of the caret (http://dev.ckeditor.com/ticket/11960). |
1139 | editor.getSelection().scrollIntoView(); | 1208 | editor.getSelection().scrollIntoView(); |
1140 | editor.fire( 'saveSnapshot' ); | 1209 | editor.fire( 'saveSnapshot' ); |
1141 | 1210 | ||
@@ -1190,8 +1259,9 @@ | |||
1190 | * | 1259 | * |
1191 | * @method editable | 1260 | * @method editable |
1192 | * @member CKEDITOR.editor | 1261 | * @member CKEDITOR.editor |
1193 | * @param {CKEDITOR.dom.element/CKEDITOR.editable} elementOrEditable The | 1262 | * @param {CKEDITOR.dom.element/CKEDITOR.editable} [elementOrEditable] The |
1194 | * DOM element to become the editable or a {@link CKEDITOR.editable} object. | 1263 | * DOM element to become the editable or a {@link CKEDITOR.editable} object. |
1264 | * @returns {CKEDITOR.dom.element/null} The editor's editable element, or `null` if not available. | ||
1195 | */ | 1265 | */ |
1196 | CKEDITOR.editor.prototype.editable = function( element ) { | 1266 | CKEDITOR.editor.prototype.editable = function( element ) { |
1197 | var editable = this._.editable; | 1267 | var editable = this._.editable; |
@@ -1214,7 +1284,7 @@ | |||
1214 | CKEDITOR.on( 'instanceLoaded', function( evt ) { | 1284 | CKEDITOR.on( 'instanceLoaded', function( evt ) { |
1215 | var editor = evt.editor; | 1285 | var editor = evt.editor; |
1216 | 1286 | ||
1217 | // and flag that the element was locked by our code so it'll be editable by the editor functions (#6046). | 1287 | // and flag that the element was locked by our code so it'll be editable by the editor functions (http://dev.ckeditor.com/ticket/6046). |
1218 | editor.on( 'insertElement', function( evt ) { | 1288 | editor.on( 'insertElement', function( evt ) { |
1219 | var element = evt.data; | 1289 | var element = evt.data; |
1220 | if ( element.type == CKEDITOR.NODE_ELEMENT && ( element.is( 'input' ) || element.is( 'textarea' ) ) ) { | 1290 | if ( element.type == CKEDITOR.NODE_ELEMENT && ( element.is( 'input' ) || element.is( 'textarea' ) ) ) { |
@@ -1229,9 +1299,9 @@ | |||
1229 | if ( editor.readOnly ) | 1299 | if ( editor.readOnly ) |
1230 | return; | 1300 | return; |
1231 | 1301 | ||
1232 | // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189) | 1302 | // Auto fixing on some document structure weakness to enhance usabilities. (http://dev.ckeditor.com/ticket/3190 and http://dev.ckeditor.com/ticket/3189) |
1233 | var sel = editor.getSelection(); | 1303 | var sel = editor.getSelection(); |
1234 | // Do it only when selection is not locked. (#8222) | 1304 | // Do it only when selection is not locked. (http://dev.ckeditor.com/ticket/8222) |
1235 | if ( sel && !sel.isLocked ) { | 1305 | if ( sel && !sel.isLocked ) { |
1236 | var isDirty = editor.checkDirty(); | 1306 | var isDirty = editor.checkDirty(); |
1237 | 1307 | ||
@@ -1281,7 +1351,7 @@ | |||
1281 | } ); | 1351 | } ); |
1282 | } ); | 1352 | } ); |
1283 | 1353 | ||
1284 | // #9222: Show text cursor in Gecko. | 1354 | // http://dev.ckeditor.com/ticket/9222: Show text cursor in Gecko. |
1285 | // Show default cursor over control elements on all non-IEs. | 1355 | // Show default cursor over control elements on all non-IEs. |
1286 | CKEDITOR.addCss( '.cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}' ); | 1356 | CKEDITOR.addCss( '.cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}' ); |
1287 | 1357 | ||
@@ -1291,15 +1361,15 @@ | |||
1291 | // | 1361 | // |
1292 | // | 1362 | // |
1293 | 1363 | ||
1294 | var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ), | 1364 | isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ), |
1295 | isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ), | 1365 | isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ), |
1296 | isEmpty = CKEDITOR.dom.walker.empty(), | 1366 | isEmpty = CKEDITOR.dom.walker.empty(), |
1297 | isBogus = CKEDITOR.dom.walker.bogus(), | 1367 | isBogus = CKEDITOR.dom.walker.bogus(), |
1298 | // Matching an empty paragraph at the end of document. | 1368 | // Matching an empty paragraph at the end of document. |
1299 | emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi; | 1369 | emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi; |
1300 | 1370 | ||
1301 | // Auto-fixing block-less content by wrapping paragraph (#3190), prevent | 1371 | // Auto-fixing block-less content by wrapping paragraph (http://dev.ckeditor.com/ticket/3190), prevent |
1302 | // non-exitable-block by padding extra br.(#3189) | 1372 | // non-exitable-block by padding extra br.(http://dev.ckeditor.com/ticket/3189) |
1303 | // Returns truly value when dom was changed, falsy otherwise. | 1373 | // Returns truly value when dom was changed, falsy otherwise. |
1304 | function fixDom( evt ) { | 1374 | function fixDom( evt ) { |
1305 | var editor = evt.editor, | 1375 | var editor = evt.editor, |
@@ -1320,7 +1390,7 @@ | |||
1320 | } | 1390 | } |
1321 | 1391 | ||
1322 | // When we're in block enter mode, a new paragraph will be established | 1392 | // When we're in block enter mode, a new paragraph will be established |
1323 | // to encapsulate inline contents inside editable. (#3657) | 1393 | // to encapsulate inline contents inside editable. (http://dev.ckeditor.com/ticket/3657) |
1324 | // Don't autoparagraph if browser (namely - IE) incorrectly anchored selection | 1394 | // Don't autoparagraph if browser (namely - IE) incorrectly anchored selection |
1325 | // inside non-editable content. This happens e.g. if non-editable block is the only | 1395 | // inside non-editable content. This happens e.g. if non-editable block is the only |
1326 | // content of editable. | 1396 | // content of editable. |
@@ -1348,7 +1418,7 @@ | |||
1348 | 1418 | ||
1349 | selectionUpdateNeeded = 1; | 1419 | selectionUpdateNeeded = 1; |
1350 | 1420 | ||
1351 | // Cancel this selection change in favor of the next (correct). (#6811) | 1421 | // Cancel this selection change in favor of the next (correct). (http://dev.ckeditor.com/ticket/6811) |
1352 | evt.cancel(); | 1422 | evt.cancel(); |
1353 | } | 1423 | } |
1354 | } | 1424 | } |
@@ -1364,13 +1434,13 @@ | |||
1364 | if ( selection.isFake ) | 1434 | if ( selection.isFake ) |
1365 | return 0; | 1435 | return 0; |
1366 | 1436 | ||
1367 | // Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041) | 1437 | // Ensure bogus br could help to move cursor (out of styles) to the end of block. (http://dev.ckeditor.com/ticket/7041) |
1368 | var pathBlock = path.block || path.blockLimit, | 1438 | var pathBlock = path.block || path.blockLimit, |
1369 | lastNode = pathBlock && pathBlock.getLast( isNotEmpty ); | 1439 | lastNode = pathBlock && pathBlock.getLast( isNotEmpty ); |
1370 | 1440 | ||
1371 | // Check some specialities of the current path block: | 1441 | // Check some specialities of the current path block: |
1372 | // 1. It is really displayed as block; (#7221) | 1442 | // 1. It is really displayed as block; (http://dev.ckeditor.com/ticket/7221) |
1373 | // 2. It doesn't end with one inner block; (#7467) | 1443 | // 2. It doesn't end with one inner block; (http://dev.ckeditor.com/ticket/7467) |
1374 | // 3. It doesn't have bogus br yet. | 1444 | // 3. It doesn't have bogus br yet. |
1375 | if ( | 1445 | if ( |
1376 | pathBlock && pathBlock.isBlockBoundary() && | 1446 | pathBlock && pathBlock.isBlockBoundary() && |
@@ -1507,7 +1577,7 @@ | |||
1507 | // Whether in given context (pathBlock, pathBlockLimit and editor settings) | 1577 | // Whether in given context (pathBlock, pathBlockLimit and editor settings) |
1508 | // editor should automatically wrap inline contents with blocks. | 1578 | // editor should automatically wrap inline contents with blocks. |
1509 | function shouldAutoParagraph( editor, pathBlock, pathBlockLimit ) { | 1579 | function shouldAutoParagraph( editor, pathBlock, pathBlockLimit ) { |
1510 | // Check whether pathBlock equals pathBlockLimit to support nested editable (#12162). | 1580 | // Check whether pathBlock equals pathBlockLimit to support nested editable (http://dev.ckeditor.com/ticket/12162). |
1511 | return editor.config.autoParagraph !== false && | 1581 | return editor.config.autoParagraph !== false && |
1512 | editor.activeEnterMode != CKEDITOR.ENTER_BR && | 1582 | editor.activeEnterMode != CKEDITOR.ENTER_BR && |
1513 | ( | 1583 | ( |
@@ -1523,7 +1593,7 @@ | |||
1523 | // | 1593 | // |
1524 | // Functions related to insertXXX methods | 1594 | // Functions related to insertXXX methods |
1525 | // | 1595 | // |
1526 | var insert = ( function() { | 1596 | insert = ( function() { |
1527 | 'use strict'; | 1597 | 'use strict'; |
1528 | 1598 | ||
1529 | var DTD = CKEDITOR.dtd; | 1599 | var DTD = CKEDITOR.dtd; |
@@ -1574,7 +1644,7 @@ | |||
1574 | 1644 | ||
1575 | // Select range and stop execution. | 1645 | // Select range and stop execution. |
1576 | // If data has been totally emptied after the filtering, | 1646 | // If data has been totally emptied after the filtering, |
1577 | // any insertion is pointless (#10339). | 1647 | // any insertion is pointless (http://dev.ckeditor.com/ticket/10339). |
1578 | if ( data && processDataForInsertion( that, data ) ) { | 1648 | if ( data && processDataForInsertion( that, data ) ) { |
1579 | // DATA INSERTION | 1649 | // DATA INSERTION |
1580 | insertDataIntoRange( that ); | 1650 | insertDataIntoRange( that ); |
@@ -1959,7 +2029,7 @@ | |||
1959 | nodeName = node.getName(); | 2029 | nodeName = node.getName(); |
1960 | 2030 | ||
1961 | // Extract only the list items, when insertion happens | 2031 | // Extract only the list items, when insertion happens |
1962 | // inside of a list, reads as rearrange list items. (#7957) | 2032 | // inside of a list, reads as rearrange list items. (http://dev.ckeditor.com/ticket/7957) |
1963 | if ( insideOfList && nodeName in CKEDITOR.dtd.$list ) { | 2033 | if ( insideOfList && nodeName in CKEDITOR.dtd.$list ) { |
1964 | nodesData = nodesData.concat( extractNodesData( node, that ) ); | 2034 | nodesData = nodesData.concat( extractNodesData( node, that ) ); |
1965 | continue; | 2035 | continue; |
@@ -2207,7 +2277,7 @@ | |||
2207 | } | 2277 | } |
2208 | 2278 | ||
2209 | // Don't use String.replace because it fails in IE7 if special replacement | 2279 | // Don't use String.replace because it fails in IE7 if special replacement |
2210 | // characters ($$, $&, etc.) are in data (#10367). | 2280 | // characters ($$, $&, etc.) are in data (http://dev.ckeditor.com/ticket/10367). |
2211 | return wrapper.getOuterHtml().split( '{cke-peak}' ).join( data ); | 2281 | return wrapper.getOuterHtml().split( '{cke-peak}' ).join( data ); |
2212 | } | 2282 | } |
2213 | 2283 | ||
@@ -2232,7 +2302,7 @@ | |||
2232 | // 1. Fixes a range which is a result of deleteContents() and is placed in an intermediate element (see dtd.$intermediate), | 2302 | // 1. Fixes a range which is a result of deleteContents() and is placed in an intermediate element (see dtd.$intermediate), |
2233 | // inside a table. A goal is to find a closest <td> or <th> element and when this fails, recreate the structure of the table. | 2303 | // inside a table. A goal is to find a closest <td> or <th> element and when this fails, recreate the structure of the table. |
2234 | // 2. Fixes empty cells by appending bogus <br>s or deleting empty text nodes in IE<=8 case. | 2304 | // 2. Fixes empty cells by appending bogus <br>s or deleting empty text nodes in IE<=8 case. |
2235 | var fixTableAfterContentsDeletion = ( function() { | 2305 | fixTableAfterContentsDeletion = ( function() { |
2236 | // Creates an element walker which can only "go deeper". It won't | 2306 | // Creates an element walker which can only "go deeper". It won't |
2237 | // move out from any element. Therefore it can be used to find <td>x</td> in cases like: | 2307 | // move out from any element. Therefore it can be used to find <td>x</td> in cases like: |
2238 | // <table><tbody><tr><td>x</td></tr></tbody>^<tfoot>... | 2308 | // <table><tbody><tr><td>x</td></tr></tbody>^<tfoot>... |
@@ -2330,6 +2400,65 @@ | |||
2330 | }; | 2400 | }; |
2331 | } )(); | 2401 | } )(); |
2332 | 2402 | ||
2403 | fixListAfterContentsDelete = ( function() { | ||
2404 | // Creates an element walker which operates only within lists. | ||
2405 | function getFixListSelectionWalker( testRange ) { | ||
2406 | var walker = new CKEDITOR.dom.walker( testRange ); | ||
2407 | walker.guard = function( node, isMovingOut ) { | ||
2408 | if ( isMovingOut ) | ||
2409 | return false; | ||
2410 | if ( node.type == CKEDITOR.NODE_ELEMENT ) | ||
2411 | return node.is( CKEDITOR.dtd.$list ) || node.is( CKEDITOR.dtd.$listItem ); | ||
2412 | }; | ||
2413 | walker.evaluator = function( node ) { | ||
2414 | return node.type == CKEDITOR.NODE_ELEMENT && node.is( CKEDITOR.dtd.$listItem ); | ||
2415 | }; | ||
2416 | |||
2417 | return walker; | ||
2418 | } | ||
2419 | |||
2420 | return function( range ) { | ||
2421 | var container = range.startContainer, | ||
2422 | appendToStart = false, | ||
2423 | testRange, | ||
2424 | deeperSibling; | ||
2425 | |||
2426 | // Look left. | ||
2427 | testRange = range.clone(); | ||
2428 | testRange.setStart( container, 0 ); | ||
2429 | deeperSibling = getFixListSelectionWalker( testRange ).lastBackward(); | ||
2430 | |||
2431 | // If left is empty, look right. | ||
2432 | if ( !deeperSibling ) { | ||
2433 | testRange = range.clone(); | ||
2434 | testRange.setEndAt( container, CKEDITOR.POSITION_BEFORE_END ); | ||
2435 | deeperSibling = getFixListSelectionWalker( testRange ).lastForward(); | ||
2436 | appendToStart = true; | ||
2437 | } | ||
2438 | |||
2439 | // If there's no deeper nested element in both direction - container is empty - we'll use it then. | ||
2440 | if ( !deeperSibling ) | ||
2441 | deeperSibling = container; | ||
2442 | |||
2443 | // We found a list what means that it's empty - remove it completely. | ||
2444 | if ( deeperSibling.is( CKEDITOR.dtd.$list ) ) { | ||
2445 | range.setStartAt( deeperSibling, CKEDITOR.POSITION_BEFORE_START ); | ||
2446 | range.collapse( true ); | ||
2447 | deeperSibling.remove(); | ||
2448 | return; | ||
2449 | } | ||
2450 | |||
2451 | // To avoid setting selection after bogus, remove it from the target list item. | ||
2452 | // We can safely do that, because we'll insert element into that cell. | ||
2453 | var bogus = deeperSibling.getBogus(); | ||
2454 | if ( bogus ) | ||
2455 | bogus.remove(); | ||
2456 | |||
2457 | range.moveToPosition( deeperSibling, appendToStart ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_END ); | ||
2458 | range.select(); | ||
2459 | }; | ||
2460 | } )(); | ||
2461 | |||
2333 | function mergeBlocksCollapsedSelection( editor, range, backspace, startPath ) { | 2462 | function mergeBlocksCollapsedSelection( editor, range, backspace, startPath ) { |
2334 | var startBlock = startPath.block; | 2463 | var startBlock = startPath.block; |
2335 | 2464 | ||
@@ -2409,7 +2538,7 @@ | |||
2409 | if ( ( bogus = startBlock.getBogus() ) ) | 2538 | if ( ( bogus = startBlock.getBogus() ) ) |
2410 | bogus.remove(); | 2539 | bogus.remove(); |
2411 | 2540 | ||
2412 | // Changing end container to element from text node (#12503). | 2541 | // Changing end container to element from text node (http://dev.ckeditor.com/ticket/12503). |
2413 | range.enlarge( CKEDITOR.ENLARGE_INLINE ); | 2542 | range.enlarge( CKEDITOR.ENLARGE_INLINE ); |
2414 | 2543 | ||
2415 | // Delete range contents. Do NOT merge. Merging is weird. | 2544 | // Delete range contents. Do NOT merge. Merging is weird. |
@@ -2432,7 +2561,7 @@ | |||
2432 | range = editor.getSelection().getRanges()[ 0 ]; | 2561 | range = editor.getSelection().getRanges()[ 0 ]; |
2433 | range.collapse( 1 ); | 2562 | range.collapse( 1 ); |
2434 | 2563 | ||
2435 | // Optimizing range containers from text nodes to elements (#12503). | 2564 | // Optimizing range containers from text nodes to elements (http://dev.ckeditor.com/ticket/12503). |
2436 | range.optimize(); | 2565 | range.optimize(); |
2437 | if ( range.startContainer.getHtml() === '' ) { | 2566 | if ( range.startContainer.getHtml() === '' ) { |
2438 | range.startContainer.appendBogus(); | 2567 | range.startContainer.appendBogus(); |
@@ -2473,7 +2602,7 @@ | |||
2473 | // | 2602 | // |
2474 | // Helpers for editable.getHtmlFromRange. | 2603 | // Helpers for editable.getHtmlFromRange. |
2475 | // | 2604 | // |
2476 | var getHtmlFromRangeHelpers = { | 2605 | getHtmlFromRangeHelpers = { |
2477 | eol: { | 2606 | eol: { |
2478 | detect: function( that, editable ) { | 2607 | detect: function( that, editable ) { |
2479 | var range = that.range, | 2608 | var range = that.range, |
@@ -2638,7 +2767,7 @@ | |||
2638 | // | 2767 | // |
2639 | // Helpers for editable.extractHtmlFromRange. | 2768 | // Helpers for editable.extractHtmlFromRange. |
2640 | // | 2769 | // |
2641 | var extractHtmlFromRangeHelpers = ( function() { | 2770 | extractHtmlFromRangeHelpers = ( function() { |
2642 | function optimizeBookmarkNode( node, toStart ) { | 2771 | function optimizeBookmarkNode( node, toStart ) { |
2643 | var parent = node.getParent(); | 2772 | var parent = node.getParent(); |
2644 | 2773 | ||
@@ -2654,7 +2783,7 @@ | |||
2654 | while ( ( next = endBookmark.getNext() ) ) { | 2783 | while ( ( next = endBookmark.getNext() ) ) { |
2655 | next.insertAfter( startBookmark ); | 2784 | next.insertAfter( startBookmark ); |
2656 | 2785 | ||
2657 | // Update startBookmark after insertion to avoid the reversal of nodes (#13449). | 2786 | // Update startBookmark after insertion to avoid the reversal of nodes (http://dev.ckeditor.com/ticket/13449). |
2658 | startBookmark = next; | 2787 | startBookmark = next; |
2659 | } | 2788 | } |
2660 | 2789 | ||
@@ -2805,7 +2934,7 @@ | |||
2805 | 2934 | ||
2806 | walker.guard = function( node, leaving ) { | 2935 | walker.guard = function( node, leaving ) { |
2807 | // Guard may be executed on some node boundaries multiple times, | 2936 | // Guard may be executed on some node boundaries multiple times, |
2808 | // what results in creating more than one range for each selected cell. (#12964) | 2937 | // what results in creating more than one range for each selected cell. (http://dev.ckeditor.com/ticket/12964) |
2809 | if ( node.type == CKEDITOR.NODE_ELEMENT ) { | 2938 | if ( node.type == CKEDITOR.NODE_ELEMENT ) { |
2810 | var key = 'visited_' + ( leaving ? 'out' : 'in' ); | 2939 | var key = 'visited_' + ( leaving ? 'out' : 'in' ); |
2811 | if ( node.getCustomData( key ) ) { | 2940 | if ( node.getCustomData( key ) ) { |
diff --git a/sources/core/editor.js b/sources/core/editor.js index 31188d2..640cec0 100644 --- a/sources/core/editor.js +++ b/sources/core/editor.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -29,7 +29,7 @@ | |||
29 | // Call the CKEDITOR.event constructor to initialize this instance. | 29 | // Call the CKEDITOR.event constructor to initialize this instance. |
30 | CKEDITOR.event.call( this ); | 30 | CKEDITOR.event.call( this ); |
31 | 31 | ||
32 | // Make a clone of the config object, to avoid having it touched by our code. (#9636) | 32 | // Make a clone of the config object, to avoid having it touched by our code. (http://dev.ckeditor.com/ticket/9636) |
33 | instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig ); | 33 | instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig ); |
34 | 34 | ||
35 | // if editor is created off one page element. | 35 | // if editor is created off one page element. |
@@ -223,7 +223,7 @@ | |||
223 | function updateCommandsContext( editor, path, forceRefresh ) { | 223 | function updateCommandsContext( editor, path, forceRefresh ) { |
224 | // Commands cannot be refreshed without a path. In edge cases | 224 | // Commands cannot be refreshed without a path. In edge cases |
225 | // it may happen that there's no selection when this function is executed. | 225 | // it may happen that there's no selection when this function is executed. |
226 | // For example when active filter is changed in #10877. | 226 | // For example when active filter is changed in http://dev.ckeditor.com/ticket/10877. |
227 | if ( !path ) | 227 | if ( !path ) |
228 | return; | 228 | return; |
229 | 229 | ||
@@ -270,7 +270,7 @@ | |||
270 | } else { | 270 | } else { |
271 | // Load the custom configuration file. | 271 | // Load the custom configuration file. |
272 | // To resolve customConfig race conflicts, use scriptLoader#queue | 272 | // To resolve customConfig race conflicts, use scriptLoader#queue |
273 | // instead of scriptLoader#load (#6504). | 273 | // instead of scriptLoader#load (http://dev.ckeditor.com/ticket/6504). |
274 | CKEDITOR.scriptLoader.queue( customConfig, function() { | 274 | CKEDITOR.scriptLoader.queue( customConfig, function() { |
275 | // If the CKEDITOR.editorConfig function has been properly | 275 | // If the CKEDITOR.editorConfig function has been properly |
276 | // defined in the custom configuration file, cache it. | 276 | // defined in the custom configuration file, cache it. |
@@ -654,24 +654,49 @@ | |||
654 | return editor.blockless ? CKEDITOR.ENTER_BR : enterMode; | 654 | return editor.blockless ? CKEDITOR.ENTER_BR : enterMode; |
655 | } | 655 | } |
656 | 656 | ||
657 | // Create DocumentFragment from specified ranges. For now it handles only tables in Firefox | 657 | // Create DocumentFragment from specified ranges. For now it handles only tables |
658 | // and returns DocumentFragment from the 1. range for other cases. (#13884) | 658 | // and returns DocumentFragment from the 1. range for other cases. (http://dev.ckeditor.com/ticket/13884) |
659 | function createDocumentFragmentFromRanges( ranges, editable ) { | 659 | function createDocumentFragmentFromRanges( ranges, editable ) { |
660 | var docFragment = new CKEDITOR.dom.documentFragment(), | 660 | var docFragment = new CKEDITOR.dom.documentFragment(), |
661 | tableClone, | 661 | tableClone, |
662 | currentRow, | 662 | currentRow, |
663 | currentRowClone; | 663 | currentRowClone; |
664 | 664 | ||
665 | // We must handle two cases here: | ||
666 | // 1. <tr>[<td>Cell</td>]</tr> (IE9+, Edge, Chrome, Firefox) | ||
667 | // 2. <td>[Cell]</td> (IE8-, Safari) | ||
668 | function isSelectedCell( range ) { | ||
669 | var start = range.startContainer, | ||
670 | end = range.endContainer; | ||
671 | |||
672 | if ( start.is && ( start.is( 'tr' ) || | ||
673 | ( start.is( 'td' ) && start.equals( end ) && range.endOffset === start.getChildCount() ) ) ) { | ||
674 | return true; | ||
675 | } | ||
676 | |||
677 | return false; | ||
678 | } | ||
679 | |||
680 | function cloneCell( range ) { | ||
681 | var start = range.startContainer; | ||
682 | |||
683 | if ( start.is( 'tr' ) ) { | ||
684 | return range.cloneContents(); | ||
685 | } | ||
686 | |||
687 | return start.clone( true ); | ||
688 | } | ||
689 | |||
665 | for ( var i = 0; i < ranges.length; i++ ) { | 690 | for ( var i = 0; i < ranges.length; i++ ) { |
666 | var range = ranges[ i ], | 691 | var range = ranges[ i ], |
667 | container = range.startContainer; | 692 | container = range.startContainer.getAscendant( 'tr', true ); |
668 | 693 | ||
669 | if ( container.getName && container.getName() == 'tr' ) { | 694 | if ( isSelectedCell( range ) ) { |
670 | if ( !tableClone ) { | 695 | if ( !tableClone ) { |
671 | tableClone = container.getAscendant( 'table' ).clone(); | 696 | tableClone = container.getAscendant( 'table' ).clone(); |
672 | tableClone.append( container.getAscendant( 'tbody' ).clone() ); | 697 | tableClone.append( container.getAscendant( { thead: 1, tbody: 1, tfoot: 1 } ).clone() ); |
673 | docFragment.append( tableClone ); | 698 | docFragment.append( tableClone ); |
674 | tableClone = tableClone.findOne( 'tbody' ); | 699 | tableClone = tableClone.findOne( 'thead, tbody, tfoot' ); |
675 | } | 700 | } |
676 | 701 | ||
677 | if ( !( currentRow && currentRow.equals( container ) ) ) { | 702 | if ( !( currentRow && currentRow.equals( container ) ) ) { |
@@ -680,7 +705,7 @@ | |||
680 | tableClone.append( currentRowClone ); | 705 | tableClone.append( currentRowClone ); |
681 | } | 706 | } |
682 | 707 | ||
683 | currentRowClone.append( range.cloneContents() ); | 708 | currentRowClone.append( cloneCell( range ) ); |
684 | } else { | 709 | } else { |
685 | // If there was something else copied with table, | 710 | // If there was something else copied with table, |
686 | // append it to DocumentFragment. | 711 | // append it to DocumentFragment. |
@@ -717,7 +742,7 @@ | |||
717 | // This guarantees that commands added before first editor#mode | 742 | // This guarantees that commands added before first editor#mode |
718 | // aren't immediately updated, but waits for editor#mode and that | 743 | // aren't immediately updated, but waits for editor#mode and that |
719 | // commands added later are immediately refreshed, even when added | 744 | // commands added later are immediately refreshed, even when added |
720 | // before instanceReady. #10103, #10249 | 745 | // before instanceReady. http://dev.ckeditor.com/ticket/10103, http://dev.ckeditor.com/ticket/10249 |
721 | if ( this.mode ) | 746 | if ( this.mode ) |
722 | updateCommand( this, cmd ); | 747 | updateCommand( this, cmd ); |
723 | 748 | ||
@@ -761,7 +786,7 @@ | |||
761 | } ); | 786 | } ); |
762 | } | 787 | } |
763 | 788 | ||
764 | // Remove 'submit' events registered on form element before destroying.(#3988) | 789 | // Remove 'submit' events registered on form element before destroying.(http://dev.ckeditor.com/ticket/3988) |
765 | editor.on( 'destroy', function() { | 790 | editor.on( 'destroy', function() { |
766 | form.removeListener( 'submit', onSubmit ); | 791 | form.removeListener( 'submit', onSubmit ); |
767 | } ); | 792 | } ); |
@@ -771,7 +796,7 @@ | |||
771 | function onSubmit( evt ) { | 796 | function onSubmit( evt ) { |
772 | editor.updateElement(); | 797 | editor.updateElement(); |
773 | 798 | ||
774 | // #8031 If textarea had required attribute and editor is empty fire 'required' event and if | 799 | // http://dev.ckeditor.com/ticket/8031 If textarea had required attribute and editor is empty fire 'required' event and if |
775 | // it was cancelled, prevent submitting the form. | 800 | // it was cancelled, prevent submitting the form. |
776 | if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) { | 801 | if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) { |
777 | // When user press save button event (evt) is undefined (see save plugin). | 802 | // When user press save button event (evt) is undefined (see save plugin). |
@@ -860,10 +885,10 @@ | |||
860 | * | 885 | * |
861 | * editorInstance.execCommand( 'bold' ); | 886 | * editorInstance.execCommand( 'bold' ); |
862 | * | 887 | * |
863 | * @param {String} commandName The indentifier name of the command. | 888 | * @param {String} commandName The identifier name of the command. |
864 | * @param {Object} [data] The data to be passed to the command. | 889 | * @param {Object} [data] The data to be passed to the command. It defaults to |
865 | * @returns {Boolean} `true` if the command was executed | 890 | * an empty object starting from 4.7.0. |
866 | * successfully, otherwise `false`. | 891 | * @returns {Boolean} `true` if the command was executed successfully, `false` otherwise. |
867 | * @see CKEDITOR.editor#addCommand | 892 | * @see CKEDITOR.editor#addCommand |
868 | */ | 893 | */ |
869 | execCommand: function( commandName, data ) { | 894 | execCommand: function( commandName, data ) { |
@@ -871,7 +896,7 @@ | |||
871 | 896 | ||
872 | var eventData = { | 897 | var eventData = { |
873 | name: commandName, | 898 | name: commandName, |
874 | commandData: data, | 899 | commandData: data || {}, |
875 | command: command | 900 | command: command |
876 | }; | 901 | }; |
877 | 902 | ||
@@ -967,7 +992,7 @@ | |||
967 | } | 992 | } |
968 | else { | 993 | else { |
969 | // If we don't have a proper element, set data to an empty string, | 994 | // If we don't have a proper element, set data to an empty string, |
970 | // as this method is expected to return a string. (#13385) | 995 | // as this method is expected to return a string. (http://dev.ckeditor.com/ticket/13385) |
971 | data = ''; | 996 | data = ''; |
972 | } | 997 | } |
973 | } | 998 | } |
@@ -1069,7 +1094,7 @@ | |||
1069 | this.readOnly = isReadOnly; | 1094 | this.readOnly = isReadOnly; |
1070 | 1095 | ||
1071 | // Block or release BACKSPACE key according to current read-only | 1096 | // Block or release BACKSPACE key according to current read-only |
1072 | // state to prevent browser's history navigation (#9761). | 1097 | // state to prevent browser's history navigation (http://dev.ckeditor.com/ticket/9761). |
1073 | this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly; | 1098 | this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly; |
1074 | 1099 | ||
1075 | this.editable().setReadOnly( isReadOnly ); | 1100 | this.editable().setReadOnly( isReadOnly ); |
@@ -1198,17 +1223,20 @@ | |||
1198 | */ | 1223 | */ |
1199 | extractSelectedHtml: function( toString, removeEmptyBlock ) { | 1224 | extractSelectedHtml: function( toString, removeEmptyBlock ) { |
1200 | var editable = this.editable(), | 1225 | var editable = this.editable(), |
1201 | ranges = this.getSelection().getRanges(); | 1226 | ranges = this.getSelection().getRanges(), |
1227 | docFragment = new CKEDITOR.dom.documentFragment(), | ||
1228 | i; | ||
1202 | 1229 | ||
1203 | if ( !editable || ranges.length === 0 ) { | 1230 | if ( !editable || ranges.length === 0 ) { |
1204 | return null; | 1231 | return null; |
1205 | } | 1232 | } |
1206 | 1233 | ||
1207 | var range = ranges[ 0 ], | 1234 | for ( i = 0; i < ranges.length; i++ ) { |
1208 | docFragment = editable.extractHtmlFromRange( range, removeEmptyBlock ); | 1235 | docFragment.append( editable.extractHtmlFromRange( ranges[ i ], removeEmptyBlock ) ); |
1236 | } | ||
1209 | 1237 | ||
1210 | if ( !removeEmptyBlock ) { | 1238 | if ( !removeEmptyBlock ) { |
1211 | this.getSelection().selectRanges( [ range ] ); | 1239 | this.getSelection().selectRanges( [ ranges[ 0 ] ] ); |
1212 | } | 1240 | } |
1213 | 1241 | ||
1214 | return toString ? docFragment.getHtml() : docFragment; | 1242 | return toString ? docFragment.getHtml() : docFragment; |
@@ -1324,6 +1352,38 @@ | |||
1324 | }, | 1352 | }, |
1325 | 1353 | ||
1326 | /** | 1354 | /** |
1355 | * Returns the keystroke that is assigned to a specified {@link CKEDITOR.command}. If no keystroke is assigned, | ||
1356 | * it returns `null`. | ||
1357 | * | ||
1358 | * Since version 4.7.0 this function also accepts a `command` parameter as a string. | ||
1359 | * | ||
1360 | * @since 4.6.0 | ||
1361 | * @param {CKEDITOR.command/String} command The {@link CKEDITOR.command} instance or a string with the command name. | ||
1362 | * @returns {Number/null} The keystroke assigned to the provided command or `null` if there is no keystroke. | ||
1363 | */ | ||
1364 | getCommandKeystroke: function( command ) { | ||
1365 | var commandInstance = ( typeof command === 'string' ? this.getCommand( command ) : command ); | ||
1366 | |||
1367 | if ( commandInstance ) { | ||
1368 | var commandName = CKEDITOR.tools.object.findKey( this.commands, commandInstance ), | ||
1369 | keystrokes = this.keystrokeHandler.keystrokes, | ||
1370 | key; | ||
1371 | |||
1372 | // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively. | ||
1373 | if ( commandInstance.fakeKeystroke ) { | ||
1374 | return commandInstance.fakeKeystroke; | ||
1375 | } | ||
1376 | |||
1377 | for ( key in keystrokes ) { | ||
1378 | if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) { | ||
1379 | return key; | ||
1380 | } | ||
1381 | } | ||
1382 | } | ||
1383 | return null; | ||
1384 | }, | ||
1385 | |||
1386 | /** | ||
1327 | * Shorthand for {@link CKEDITOR.filter#addFeature}. | 1387 | * Shorthand for {@link CKEDITOR.filter#addFeature}. |
1328 | * | 1388 | * |
1329 | * @since 4.1 | 1389 | * @since 4.1 |
@@ -1512,7 +1572,7 @@ CKEDITOR.ELEMENT_MODE_INLINE = 3; | |||
1512 | * @member CKEDITOR.config | 1572 | * @member CKEDITOR.config |
1513 | */ | 1573 | */ |
1514 | 1574 | ||
1515 | /** | 1575 | /** |
1516 | * Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in | 1576 | * Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in |
1517 | * tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page), | 1577 | * tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page), |
1518 | * e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation. | 1578 | * e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation. |
@@ -1811,6 +1871,14 @@ CKEDITOR.ELEMENT_MODE_INLINE = 3; | |||
1811 | */ | 1871 | */ |
1812 | 1872 | ||
1813 | /** | 1873 | /** |
1874 | * Event fired when the {@link #method-destroy} method is called, | ||
1875 | * but before destroying the editor. | ||
1876 | * | ||
1877 | * @event beforeDestroy | ||
1878 | * @param {CKEDITOR.editor} editor This editor instance. | ||
1879 | */ | ||
1880 | |||
1881 | /** | ||
1814 | * Internal event to get the current data. | 1882 | * Internal event to get the current data. |
1815 | * | 1883 | * |
1816 | * @event beforeGetData | 1884 | * @event beforeGetData |
diff --git a/sources/core/editor_basic.js b/sources/core/editor_basic.js index 14f3446..b7ab577 100644 --- a/sources/core/editor_basic.js +++ b/sources/core/editor_basic.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/env.js b/sources/core/env.js index 4410ce9..cbf089c 100644 --- a/sources/core/env.js +++ b/sources/core/env.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -136,7 +136,7 @@ if ( !CKEDITOR.env ) { | |||
136 | var domain = document.domain, | 136 | var domain = document.domain, |
137 | hostname = window.location.hostname; | 137 | hostname = window.location.hostname; |
138 | 138 | ||
139 | return domain != hostname && domain != ( '[' + hostname + ']' ); // IPv6 IP support (#5434) | 139 | return domain != hostname && domain != ( '[' + hostname + ']' ); // IPv6 IP support (http://dev.ckeditor.com/ticket/5434) |
140 | }, | 140 | }, |
141 | 141 | ||
142 | /** | 142 | /** |
diff --git a/sources/core/event.js b/sources/core/event.js index 0dd1f41..89444b5 100644 --- a/sources/core/event.js +++ b/sources/core/event.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/eventInfo.js b/sources/core/eventInfo.js index e1cd65a..ea62ac9 100644 --- a/sources/core/eventInfo.js +++ b/sources/core/eventInfo.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/filter.js b/sources/core/filter.js index e9d5a37..3e64fc5 100644 --- a/sources/core/filter.js +++ b/sources/core/filter.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -158,7 +158,8 @@ | |||
158 | }, | 158 | }, |
159 | // Object: element name => array of transformations groups. | 159 | // Object: element name => array of transformations groups. |
160 | transformations: {}, | 160 | transformations: {}, |
161 | cachedTests: {} | 161 | cachedTests: {}, |
162 | cachedChecks: {} | ||
162 | }; | 163 | }; |
163 | 164 | ||
164 | // Register filter instance. | 165 | // Register filter instance. |
@@ -299,7 +300,7 @@ | |||
299 | if ( el.attributes[ 'data-cke-filter' ] == 'off' ) | 300 | if ( el.attributes[ 'data-cke-filter' ] == 'off' ) |
300 | return false; | 301 | return false; |
301 | 302 | ||
302 | // (#10260) Don't touch elements like spans with data-cke-* attribute since they're | 303 | // (http://dev.ckeditor.com/ticket/10260) Don't touch elements like spans with data-cke-* attribute since they're |
303 | // responsible e.g. for placing markers, bookmarks, odds and stuff. | 304 | // responsible e.g. for placing markers, bookmarks, odds and stuff. |
304 | // We love 'em and we don't wanna lose anything during the filtering. | 305 | // We love 'em and we don't wanna lose anything during the filtering. |
305 | // '|' is to avoid tricky joints like data-="foo" + cke-="bar". Yes, they're possible. | 306 | // '|' is to avoid tricky joints like data-="foo" + cke-="bar". Yes, they're possible. |
@@ -346,7 +347,7 @@ | |||
346 | if ( !element.parent ) | 347 | if ( !element.parent ) |
347 | continue; | 348 | continue; |
348 | 349 | ||
349 | // Handle custom elements as inline elements (#12683). | 350 | // Handle custom elements as inline elements (http://dev.ckeditor.com/ticket/12683). |
350 | parentDtd = DTD[ element.parent.name ] || DTD.span; | 351 | parentDtd = DTD[ element.parent.name ] || DTD.span; |
351 | 352 | ||
352 | switch ( check.check ) { | 353 | switch ( check.check ) { |
@@ -806,6 +807,32 @@ | |||
806 | } )(), | 807 | } )(), |
807 | 808 | ||
808 | /** | 809 | /** |
810 | * Returns a clone of this filter instance. | ||
811 | * | ||
812 | * @since 4.7.3 | ||
813 | * @returns {CKEDITOR.filter} | ||
814 | */ | ||
815 | clone: function() { | ||
816 | var ret = new CKEDITOR.filter(), | ||
817 | clone = CKEDITOR.tools.clone; | ||
818 | |||
819 | // Cloning allowed content related things. | ||
820 | ret.allowedContent = clone( this.allowedContent ); | ||
821 | ret._.allowedRules = clone( this._.allowedRules ); | ||
822 | |||
823 | // Disallowed content rules. | ||
824 | ret.disallowedContent = clone( this.disallowedContent ); | ||
825 | ret._.disallowedRules = clone( this._.disallowedRules ); | ||
826 | |||
827 | ret._.transformations = clone( this._.transformations ); | ||
828 | |||
829 | ret.disabled = this.disabled; | ||
830 | ret.editor = this.editor; | ||
831 | |||
832 | return ret; | ||
833 | }, | ||
834 | |||
835 | /** | ||
809 | * Destroys the filter instance and removes it from the global {@link CKEDITOR.filter#instances} object. | 836 | * Destroys the filter instance and removes it from the global {@link CKEDITOR.filter#instances} object. |
810 | * | 837 | * |
811 | * @since 4.4.5 | 838 | * @since 4.4.5 |
@@ -1248,22 +1275,20 @@ | |||
1248 | styles = styleDef.styles, | 1275 | styles = styleDef.styles, |
1249 | attrs = styleDef.attributes || {}; | 1276 | attrs = styleDef.attributes || {}; |
1250 | 1277 | ||
1251 | if ( styles ) { | 1278 | if ( styles && !CKEDITOR.tools.isEmpty( styles ) ) { |
1252 | styles = copy( styles ); | 1279 | styles = copy( styles ); |
1253 | attrs.style = CKEDITOR.tools.writeCssText( styles, true ); | 1280 | attrs.style = CKEDITOR.tools.writeCssText( styles, true ); |
1254 | } else { | 1281 | } else { |
1255 | styles = {}; | 1282 | styles = {}; |
1256 | } | 1283 | } |
1257 | 1284 | ||
1258 | var el = { | 1285 | return { |
1259 | name: styleDef.element, | 1286 | name: styleDef.element, |
1260 | attributes: attrs, | 1287 | attributes: attrs, |
1261 | classes: attrs[ 'class' ] ? attrs[ 'class' ].split( /\s+/ ) : [], | 1288 | classes: attrs[ 'class' ] ? attrs[ 'class' ].split( /\s+/ ) : [], |
1262 | styles: styles, | 1289 | styles: styles, |
1263 | children: [] | 1290 | children: [] |
1264 | }; | 1291 | }; |
1265 | |||
1266 | return el; | ||
1267 | } | 1292 | } |
1268 | 1293 | ||
1269 | // Mock hash based on string. | 1294 | // Mock hash based on string. |
@@ -1873,6 +1898,7 @@ | |||
1873 | // | 1898 | // |
1874 | // TRANSFORMATIONS -------------------------------------------------------- | 1899 | // TRANSFORMATIONS -------------------------------------------------------- |
1875 | // | 1900 | // |
1901 | var transformationsTools; | ||
1876 | 1902 | ||
1877 | // Apply given transformations group to the element. | 1903 | // Apply given transformations group to the element. |
1878 | function applyTransformationsGroup( filter, element, group ) { | 1904 | function applyTransformationsGroup( filter, element, group ) { |
@@ -2021,7 +2047,7 @@ | |||
2021 | * @class CKEDITOR.filter.transformationsTools | 2047 | * @class CKEDITOR.filter.transformationsTools |
2022 | * @singleton | 2048 | * @singleton |
2023 | */ | 2049 | */ |
2024 | var transformationsTools = CKEDITOR.filter.transformationsTools = { | 2050 | transformationsTools = CKEDITOR.filter.transformationsTools = { |
2025 | /** | 2051 | /** |
2026 | * Converts `width` and `height` attributes to styles. | 2052 | * Converts `width` and `height` attributes to styles. |
2027 | * | 2053 | * |
@@ -2070,8 +2096,8 @@ | |||
2070 | * Converts length in the `styleName` style to a valid length attribute (like `width` or `height`). | 2096 | * Converts length in the `styleName` style to a valid length attribute (like `width` or `height`). |
2071 | * | 2097 | * |
2072 | * @param {CKEDITOR.htmlParser.element} element | 2098 | * @param {CKEDITOR.htmlParser.element} element |
2073 | * @param {String} styleName Name of the style that will be converted. | 2099 | * @param {String} styleName The name of the style that will be converted. |
2074 | * @param {String} [attrName=styleName] Name of the attribute into which the style will be converted. | 2100 | * @param {String} [attrName=styleName] The name of the attribute into which the style will be converted. |
2075 | */ | 2101 | */ |
2076 | lengthToAttribute: function( element, styleName, attrName ) { | 2102 | lengthToAttribute: function( element, styleName, attrName ) { |
2077 | attrName = attrName || styleName; | 2103 | attrName = attrName || styleName; |
@@ -2091,7 +2117,7 @@ | |||
2091 | }, | 2117 | }, |
2092 | 2118 | ||
2093 | /** | 2119 | /** |
2094 | * Converts the `align` attribute to the `float` style if not set. Attribute | 2120 | * Converts the `align` attribute to the `float` style if not set. The attribute |
2095 | * is always removed. | 2121 | * is always removed. |
2096 | * | 2122 | * |
2097 | * @param {CKEDITOR.htmlParser.element} element | 2123 | * @param {CKEDITOR.htmlParser.element} element |
@@ -2109,7 +2135,7 @@ | |||
2109 | 2135 | ||
2110 | /** | 2136 | /** |
2111 | * Converts the `float` style to the `align` attribute if not set. | 2137 | * Converts the `float` style to the `align` attribute if not set. |
2112 | * Style is always removed. | 2138 | * The style is always removed. |
2113 | * | 2139 | * |
2114 | * @param {CKEDITOR.htmlParser.element} element | 2140 | * @param {CKEDITOR.htmlParser.element} element |
2115 | */ | 2141 | */ |
@@ -2125,6 +2151,107 @@ | |||
2125 | }, | 2151 | }, |
2126 | 2152 | ||
2127 | /** | 2153 | /** |
2154 | * Converts the shorthand form of the `border` style to seperate styles. | ||
2155 | * | ||
2156 | * @param {CKEDITOR.htmlParser.element} element | ||
2157 | */ | ||
2158 | splitBorderShorthand: function( element ) { | ||
2159 | if ( !element.styles.border ) { | ||
2160 | return; | ||
2161 | } | ||
2162 | |||
2163 | var widths = element.styles.border.match( /([\.\d]+\w+)/g ) || [ '0px' ]; | ||
2164 | switch ( widths.length ) { | ||
2165 | case 1: | ||
2166 | element.styles[ 'border-width' ] = widths[0]; | ||
2167 | break; | ||
2168 | case 2: | ||
2169 | mapStyles( [ 0, 1, 0, 1 ] ); | ||
2170 | break; | ||
2171 | case 3: | ||
2172 | mapStyles( [ 0, 1, 2, 1 ] ); | ||
2173 | break; | ||
2174 | case 4: | ||
2175 | mapStyles( [ 0, 1, 2, 3 ] ); | ||
2176 | break; | ||
2177 | } | ||
2178 | |||
2179 | element.styles[ 'border-style' ] = element.styles[ 'border-style' ] || | ||
2180 | ( element.styles.border.match( /(none|hidden|dotted|dashed|solid|double|groove|ridge|inset|outset|initial|inherit)/ ) || [] )[ 0 ]; | ||
2181 | if ( !element.styles[ 'border-style' ] ) | ||
2182 | delete element.styles[ 'border-style' ]; | ||
2183 | |||
2184 | delete element.styles.border; | ||
2185 | |||
2186 | function mapStyles( map ) { | ||
2187 | element.styles['border-top-width'] = widths[ map[0] ]; | ||
2188 | element.styles['border-right-width'] = widths[ map[1] ]; | ||
2189 | element.styles['border-bottom-width'] = widths[ map[2] ]; | ||
2190 | element.styles['border-left-width'] = widths[ map[3] ]; | ||
2191 | } | ||
2192 | }, | ||
2193 | |||
2194 | listTypeToStyle: function( element ) { | ||
2195 | if ( element.attributes.type ) { | ||
2196 | switch ( element.attributes.type ) { | ||
2197 | case 'a': | ||
2198 | element.styles[ 'list-style-type' ] = 'lower-alpha'; | ||
2199 | break; | ||
2200 | case 'A': | ||
2201 | element.styles[ 'list-style-type' ] = 'upper-alpha'; | ||
2202 | break; | ||
2203 | case 'i': | ||
2204 | element.styles[ 'list-style-type' ] = 'lower-roman'; | ||
2205 | break; | ||
2206 | case 'I': | ||
2207 | element.styles[ 'list-style-type' ] = 'upper-roman'; | ||
2208 | break; | ||
2209 | case '1': | ||
2210 | element.styles[ 'list-style-type' ] = 'decimal'; | ||
2211 | break; | ||
2212 | default: | ||
2213 | element.styles[ 'list-style-type' ] = element.attributes.type; | ||
2214 | } | ||
2215 | } | ||
2216 | }, | ||
2217 | |||
2218 | /** | ||
2219 | * Converts the shorthand form of the `margin` style to seperate styles. | ||
2220 | * | ||
2221 | * @param {CKEDITOR.htmlParser.element} element | ||
2222 | */ | ||
2223 | splitMarginShorthand: function( element ) { | ||
2224 | if ( !element.styles.margin ) { | ||
2225 | return; | ||
2226 | } | ||
2227 | |||
2228 | var widths = element.styles.margin.match( /(\-?[\.\d]+\w+)/g ) || [ '0px' ]; | ||
2229 | switch ( widths.length ) { | ||
2230 | case 1: | ||
2231 | mapStyles( [ 0, 0, 0, 0 ] ); | ||
2232 | break; | ||
2233 | case 2: | ||
2234 | mapStyles( [ 0, 1, 0, 1 ] ); | ||
2235 | break; | ||
2236 | case 3: | ||
2237 | mapStyles( [ 0, 1, 2, 1 ] ); | ||
2238 | break; | ||
2239 | case 4: | ||
2240 | mapStyles( [ 0, 1, 2, 3 ] ); | ||
2241 | break; | ||
2242 | } | ||
2243 | |||
2244 | delete element.styles.margin; | ||
2245 | |||
2246 | function mapStyles( map ) { | ||
2247 | element.styles['margin-top'] = widths[ map[0] ]; | ||
2248 | element.styles['margin-right'] = widths[ map[1] ]; | ||
2249 | element.styles['margin-bottom'] = widths[ map[2] ]; | ||
2250 | element.styles['margin-left'] = widths[ map[3] ]; | ||
2251 | } | ||
2252 | }, | ||
2253 | |||
2254 | /** | ||
2128 | * Checks whether an element matches a given {@link CKEDITOR.style}. | 2255 | * Checks whether an element matches a given {@link CKEDITOR.style}. |
2129 | * The element can be a "superset" of a style, e.g. it may have | 2256 | * The element can be a "superset" of a style, e.g. it may have |
2130 | * more classes, but needs to have at least those defined in the style. | 2257 | * more classes, but needs to have at least those defined in the style. |
@@ -2135,12 +2262,12 @@ | |||
2135 | matchesStyle: elementMatchesStyle, | 2262 | matchesStyle: elementMatchesStyle, |
2136 | 2263 | ||
2137 | /** | 2264 | /** |
2138 | * Transforms element to given form. | 2265 | * Transforms an element to a given form. |
2139 | * | 2266 | * |
2140 | * Form may be a: | 2267 | * Form may be a: |
2141 | * | 2268 | * |
2142 | * * {@link CKEDITOR.style}, | 2269 | * * {@link CKEDITOR.style}, |
2143 | * * string – the new name of an element. | 2270 | * * string – the new name of the element. |
2144 | * | 2271 | * |
2145 | * @param {CKEDITOR.htmlParser.element} el | 2272 | * @param {CKEDITOR.htmlParser.element} el |
2146 | * @param {CKEDITOR.style/String} form | 2273 | * @param {CKEDITOR.style/String} form |
@@ -2191,7 +2318,7 @@ | |||
2191 | * * {@link CKEDITOR.filter.allowedContentRules} – defined rules will be added | 2318 | * * {@link CKEDITOR.filter.allowedContentRules} – defined rules will be added |
2192 | * to the {@link CKEDITOR.editor#filter}. | 2319 | * to the {@link CKEDITOR.editor#filter}. |
2193 | * * `true` – will disable the filter (data will not be filtered, | 2320 | * * `true` – will disable the filter (data will not be filtered, |
2194 | * all features will be activated). | 2321 | * all features will be activated). Reading [security best practices](#!/guide/dev_best_practices) before setting `true` is recommended. |
2195 | * * default – the filter will be configured by loaded features | 2322 | * * default – the filter will be configured by loaded features |
2196 | * (toolbar items, commands, etc.). | 2323 | * (toolbar items, commands, etc.). |
2197 | * | 2324 | * |
diff --git a/sources/core/focusmanager.js b/sources/core/focusmanager.js index ee1bc39..45c3137 100644 --- a/sources/core/focusmanager.js +++ b/sources/core/focusmanager.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -147,8 +147,9 @@ | |||
147 | * @member CKEDITOR.focusManager | 147 | * @member CKEDITOR.focusManager |
148 | */ | 148 | */ |
149 | blur: function( noDelay ) { | 149 | blur: function( noDelay ) { |
150 | if ( this._.locked ) | 150 | if ( this._.locked ) { |
151 | return; | 151 | return; |
152 | } | ||
152 | 153 | ||
153 | function doBlur() { | 154 | function doBlur() { |
154 | if ( this.hasFocus ) { | 155 | if ( this.hasFocus ) { |
@@ -160,13 +161,14 @@ | |||
160 | } | 161 | } |
161 | } | 162 | } |
162 | 163 | ||
163 | if ( this._.timer ) | 164 | if ( this._.timer ) { |
164 | clearTimeout( this._.timer ); | 165 | clearTimeout( this._.timer ); |
166 | } | ||
165 | 167 | ||
166 | var delay = CKEDITOR.focusManager._.blurDelay; | 168 | var delay = CKEDITOR.focusManager._.blurDelay; |
167 | if ( noDelay || !delay ) | 169 | if ( noDelay || !delay ) { |
168 | doBlur.call( this ); | 170 | doBlur.call( this ); |
169 | else { | 171 | } else { |
170 | this._.timer = CKEDITOR.tools.setTimeout( function() { | 172 | this._.timer = CKEDITOR.tools.setTimeout( function() { |
171 | delete this._.timer; | 173 | delete this._.timer; |
172 | doBlur.call( this ); | 174 | doBlur.call( this ); |
diff --git a/sources/core/htmldataprocessor.js b/sources/core/htmldataprocessor.js index d079e4d..79e996b 100644 --- a/sources/core/htmldataprocessor.js +++ b/sources/core/htmldataprocessor.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -56,7 +56,7 @@ | |||
56 | // it up and apply the filter. | 56 | // it up and apply the filter. |
57 | data = protectSource( data, editor ); | 57 | data = protectSource( data, editor ); |
58 | 58 | ||
59 | // Protect content of textareas. (#9995) | 59 | // Protect content of textareas. (http://dev.ckeditor.com/ticket/9995) |
60 | // Do this before protecting attributes to avoid breaking: | 60 | // Do this before protecting attributes to avoid breaking: |
61 | // <textarea><img src="..." /></textarea> | 61 | // <textarea><img src="..." /></textarea> |
62 | data = protectElements( data, protectTextareaRegex ); | 62 | data = protectElements( data, protectTextareaRegex ); |
@@ -67,23 +67,23 @@ | |||
67 | data = protectAttributes( data ); | 67 | data = protectAttributes( data ); |
68 | 68 | ||
69 | // Protect elements than can't be set inside a DIV. E.g. IE removes | 69 | // Protect elements than can't be set inside a DIV. E.g. IE removes |
70 | // style tags from innerHTML. (#3710) | 70 | // style tags from innerHTML. (http://dev.ckeditor.com/ticket/3710) |
71 | data = protectElements( data, protectElementsRegex ); | 71 | data = protectElements( data, protectElementsRegex ); |
72 | 72 | ||
73 | // Certain elements has problem to go through DOM operation, protect | 73 | // Certain elements has problem to go through DOM operation, protect |
74 | // them by prefixing 'cke' namespace. (#3591) | 74 | // them by prefixing 'cke' namespace. (http://dev.ckeditor.com/ticket/3591) |
75 | data = protectElementsNames( data ); | 75 | data = protectElementsNames( data ); |
76 | 76 | ||
77 | // All none-IE browsers ignore self-closed custom elements, | 77 | // All none-IE browsers ignore self-closed custom elements, |
78 | // protecting them into open-close. (#3591) | 78 | // protecting them into open-close. (http://dev.ckeditor.com/ticket/3591) |
79 | data = protectSelfClosingElements( data ); | 79 | data = protectSelfClosingElements( data ); |
80 | 80 | ||
81 | // Compensate one leading line break after <pre> open as browsers | 81 | // Compensate one leading line break after <pre> open as browsers |
82 | // eat it up. (#5789) | 82 | // eat it up. (http://dev.ckeditor.com/ticket/5789) |
83 | data = protectPreFormatted( data ); | 83 | data = protectPreFormatted( data ); |
84 | 84 | ||
85 | // There are attributes which may execute JavaScript code inside fixBin. | 85 | // There are attributes which may execute JavaScript code inside fixBin. |
86 | // Encode them greedily. They will be unprotected right after getting HTML from fixBin. (#10) | 86 | // Encode them greedily. They will be unprotected right after getting HTML from fixBin. (http://dev.ckeditor.com/ticket/10) |
87 | data = protectInsecureAttributes( data ); | 87 | data = protectInsecureAttributes( data ); |
88 | 88 | ||
89 | var fixBin = evtData.context || editor.editable().getName(), | 89 | var fixBin = evtData.context || editor.editable().getName(), |
@@ -99,7 +99,7 @@ | |||
99 | // Call the browser to help us fixing a possibly invalid HTML | 99 | // Call the browser to help us fixing a possibly invalid HTML |
100 | // structure. | 100 | // structure. |
101 | var el = editor.document.createElement( fixBin ); | 101 | var el = editor.document.createElement( fixBin ); |
102 | // Add fake character to workaround IE comments bug. (#3801) | 102 | // Add fake character to workaround IE comments bug. (http://dev.ckeditor.com/ticket/3801) |
103 | el.setHtml( 'a' + data ); | 103 | el.setHtml( 'a' + data ); |
104 | data = el.getHtml().substr( 1 ); | 104 | data = el.getHtml().substr( 1 ); |
105 | 105 | ||
@@ -128,7 +128,7 @@ | |||
128 | data = CKEDITOR.htmlParser.fragment.fromHtml( data, evtData.context, fixBodyTag ); | 128 | data = CKEDITOR.htmlParser.fragment.fromHtml( data, evtData.context, fixBodyTag ); |
129 | 129 | ||
130 | // The empty root element needs to be fixed by adding 'p' or 'div' into it. | 130 | // The empty root element needs to be fixed by adding 'p' or 'div' into it. |
131 | // This avoids the need to create that element on the first focus (#12630). | 131 | // This avoids the need to create that element on the first focus (http://dev.ckeditor.com/ticket/12630). |
132 | if ( fixBodyTag ) { | 132 | if ( fixBodyTag ) { |
133 | fixEmptyRoot( data, fixBodyTag ); | 133 | fixEmptyRoot( data, fixBodyTag ); |
134 | } | 134 | } |
@@ -163,7 +163,7 @@ | |||
163 | editor.on( 'toDataFormat', function( evt ) { | 163 | editor.on( 'toDataFormat', function( evt ) { |
164 | var data = evt.data.dataValue; | 164 | var data = evt.data.dataValue; |
165 | 165 | ||
166 | // #10854 - we need to strip leading blockless <br> which FF adds | 166 | // http://dev.ckeditor.com/ticket/10854 - we need to strip leading blockless <br> which FF adds |
167 | // automatically when editable contains only non-editable content. | 167 | // automatically when editable contains only non-editable content. |
168 | // We do that for every browser (so it's a constant behavior) and | 168 | // We do that for every browser (so it's a constant behavior) and |
169 | // not in BR mode, in which chance of valid leading blockless <br> is higher. | 169 | // not in BR mode, in which chance of valid leading blockless <br> is higher. |
@@ -192,7 +192,7 @@ | |||
192 | data.writeChildrenHtml( writer ); | 192 | data.writeChildrenHtml( writer ); |
193 | data = writer.getHtml( true ); | 193 | data = writer.getHtml( true ); |
194 | 194 | ||
195 | // Restore those non-HTML protected source. (#4475,#4880) | 195 | // Restore those non-HTML protected source. (http://dev.ckeditor.com/ticket/4475,http://dev.ckeditor.com/ticket/4880) |
196 | data = unprotectRealComments( data ); | 196 | data = unprotectRealComments( data ); |
197 | data = unprotectSource( data, editor ); | 197 | data = unprotectSource( data, editor ); |
198 | 198 | ||
@@ -448,7 +448,7 @@ | |||
448 | return false; | 448 | return false; |
449 | 449 | ||
450 | // 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg; | 450 | // 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg; |
451 | // 2. For the rest, at least table cell and list item need no filler space. (#6248) | 451 | // 2. For the rest, at least table cell and list item need no filler space. (http://dev.ckeditor.com/ticket/6248) |
452 | if ( !isOutput && !CKEDITOR.env.needsBrFiller && | 452 | if ( !isOutput && !CKEDITOR.env.needsBrFiller && |
453 | ( document.documentMode > 7 || | 453 | ( document.documentMode > 7 || |
454 | block.name in CKEDITOR.dtd.tr || | 454 | block.name in CKEDITOR.dtd.tr || |
@@ -484,7 +484,7 @@ | |||
484 | } | 484 | } |
485 | 485 | ||
486 | // Regex to scan for at the end of blocks, which are actually placeholders. | 486 | // Regex to scan for at the end of blocks, which are actually placeholders. |
487 | // Safari transforms the to \xa0. (#4172) | 487 | // Safari transforms the to \xa0. (http://dev.ckeditor.com/ticket/4172) |
488 | var tailNbspRegex = /(?: |\xa0)$/; | 488 | var tailNbspRegex = /(?: |\xa0)$/; |
489 | 489 | ||
490 | var protectedSourceMarker = '{cke_protected}'; | 490 | var protectedSourceMarker = '{cke_protected}'; |
@@ -563,18 +563,35 @@ | |||
563 | // active in the editing area (IE|WebKit). | 563 | // active in the editing area (IE|WebKit). |
564 | [ ( /^on/ ), 'data-cke-pa-on' ], | 564 | [ ( /^on/ ), 'data-cke-pa-on' ], |
565 | 565 | ||
566 | // Prevent iframe's srcdoc attribute from being evaluated in the editable. | ||
567 | [ ( /^srcdoc/ ), 'data-cke-pa-srcdoc' ], | ||
568 | |||
566 | // Don't let some old expando enter editor. Concerns only IE8, | 569 | // Don't let some old expando enter editor. Concerns only IE8, |
567 | // but for consistency remove on all browsers. | 570 | // but for consistency remove on all browsers. |
568 | [ ( /^data-cke-expando$/ ), '' ] | 571 | [ ( /^data-cke-expando$/ ), '' ] |
569 | ] | 572 | ], |
573 | |||
574 | elements: { | ||
575 | // Prevent iframe's src attribute with javascript code or data protocol from being evaluated in the editable. | ||
576 | iframe: function( element ) { | ||
577 | if ( element.attributes && element.attributes.src ) { | ||
578 | |||
579 | var src = element.attributes.src.toLowerCase().replace( /[^a-z]/gi, '' ); | ||
580 | if ( src.indexOf( 'javascript' ) === 0 || src.indexOf( 'data' ) === 0 ) { | ||
581 | element.attributes[ 'data-cke-pa-src' ] = element.attributes.src; | ||
582 | delete element.attributes.src; | ||
583 | } | ||
584 | } | ||
585 | } | ||
586 | } | ||
570 | }; | 587 | }; |
571 | 588 | ||
572 | // Disable form elements editing mode provided by some browsers. (#5746) | 589 | // Disable form elements editing mode provided by some browsers. (http://dev.ckeditor.com/ticket/5746) |
573 | function protectReadOnly( element ) { | 590 | function protectReadOnly( element ) { |
574 | var attrs = element.attributes; | 591 | var attrs = element.attributes; |
575 | 592 | ||
576 | // We should flag that the element was locked by our code so | 593 | // We should flag that the element was locked by our code so |
577 | // it'll be editable by the editor functions (#6046). | 594 | // it'll be editable by the editor functions (http://dev.ckeditor.com/ticket/6046). |
578 | if ( attrs.contenteditable != 'false' ) | 595 | if ( attrs.contenteditable != 'false' ) |
579 | attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1; | 596 | attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1; |
580 | 597 | ||
@@ -602,7 +619,7 @@ | |||
602 | } | 619 | } |
603 | }, | 620 | }, |
604 | 621 | ||
605 | // Remove empty link but not empty anchor. (#3829, #13516) | 622 | // Remove empty link but not empty anchor. (http://dev.ckeditor.com/ticket/3829, http://dev.ckeditor.com/ticket/13516) |
606 | a: function( element ) { | 623 | a: function( element ) { |
607 | var attrs = element.attributes; | 624 | var attrs = element.attributes; |
608 | 625 | ||
@@ -641,7 +658,7 @@ | |||
641 | if ( attribs[ 'data-cke-temp' ] ) | 658 | if ( attribs[ 'data-cke-temp' ] ) |
642 | return false; | 659 | return false; |
643 | 660 | ||
644 | // Remove duplicated attributes - #3789. | 661 | // Remove duplicated attributes - http://dev.ckeditor.com/ticket/3789. |
645 | var attributeNames = [ 'name', 'href', 'src' ], | 662 | var attributeNames = [ 'name', 'href', 'src' ], |
646 | savedAttributeName; | 663 | savedAttributeName; |
647 | for ( var i = 0; i < attributeNames.length; i++ ) { | 664 | for ( var i = 0; i < attributeNames.length; i++ ) { |
@@ -653,7 +670,7 @@ | |||
653 | return element; | 670 | return element; |
654 | }, | 671 | }, |
655 | 672 | ||
656 | // The contents of table should be in correct order (#4809). | 673 | // The contents of table should be in correct order (http://dev.ckeditor.com/ticket/4809). |
657 | table: function( element ) { | 674 | table: function( element ) { |
658 | // Clone the array as it would become empty during the sort call. | 675 | // Clone the array as it would become empty during the sort call. |
659 | var children = element.children.slice( 0 ); | 676 | var children = element.children.slice( 0 ); |
@@ -712,7 +729,7 @@ | |||
712 | title: function( element ) { | 729 | title: function( element ) { |
713 | var titleText = element.children[ 0 ]; | 730 | var titleText = element.children[ 0 ]; |
714 | 731 | ||
715 | // Append text-node to title tag if not present (i.e. non-IEs) (#9882). | 732 | // Append text-node to title tag if not present (i.e. non-IEs) (http://dev.ckeditor.com/ticket/9882). |
716 | !titleText && append( element, titleText = new CKEDITOR.htmlParser.text() ); | 733 | !titleText && append( element, titleText = new CKEDITOR.htmlParser.text() ); |
717 | 734 | ||
718 | // Transfer data-saved title to title tag. | 735 | // Transfer data-saved title to title tag. |
@@ -733,7 +750,7 @@ | |||
733 | 750 | ||
734 | if ( CKEDITOR.env.ie ) { | 751 | if ( CKEDITOR.env.ie ) { |
735 | // IE outputs style attribute in capital letters. We should convert | 752 | // IE outputs style attribute in capital letters. We should convert |
736 | // them back to lower case, while not hurting the values (#5930) | 753 | // them back to lower case, while not hurting the values (http://dev.ckeditor.com/ticket/5930) |
737 | defaultHtmlFilterRulesForAll.attributes.style = function( value ) { | 754 | defaultHtmlFilterRulesForAll.attributes.style = function( value ) { |
738 | return value.replace( /(^|;)([^\:]+)/g, function( match ) { | 755 | return value.replace( /(^|;)([^\:]+)/g, function( match ) { |
739 | return match.toLowerCase(); | 756 | return match.toLowerCase(); |
@@ -741,7 +758,7 @@ | |||
741 | }; | 758 | }; |
742 | } | 759 | } |
743 | 760 | ||
744 | // Disable form elements editing mode provided by some browsers. (#5746) | 761 | // Disable form elements editing mode provided by some browsers. (http://dev.ckeditor.com/ticket/5746) |
745 | function unprotectReadyOnly( element ) { | 762 | function unprotectReadyOnly( element ) { |
746 | var attrs = element.attributes; | 763 | var attrs = element.attributes; |
747 | switch ( attrs[ 'data-cke-editable' ] ) { | 764 | switch ( attrs[ 'data-cke-editable' ] ) { |
@@ -773,7 +790,7 @@ | |||
773 | // | 790 | // |
774 | // 'data-x' => '<a href="X"' | 791 | // 'data-x' => '<a href="X"' |
775 | // | 792 | // |
776 | // which, can be easily filtered out (#11508). | 793 | // which, can be easily filtered out (http://dev.ckeditor.com/ticket/11508). |
777 | protectAttributeRegex = /([\w-:]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi, | 794 | protectAttributeRegex = /([\w-:]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi, |
778 | protectAttributeNameRegex = /^(href|src|name)$/i; | 795 | protectAttributeNameRegex = /^(href|src|name)$/i; |
779 | 796 | ||
@@ -790,8 +807,8 @@ | |||
790 | function protectAttributes( html ) { | 807 | function protectAttributes( html ) { |
791 | return html.replace( protectElementRegex, function( element, tag, attributes ) { | 808 | return html.replace( protectElementRegex, function( element, tag, attributes ) { |
792 | return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) { | 809 | return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) { |
793 | // Avoid corrupting the inline event attributes (#7243). | 810 | // Avoid corrupting the inline event attributes (http://dev.ckeditor.com/ticket/7243). |
794 | // We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218) | 811 | // We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (http://dev.ckeditor.com/ticket/5218) |
795 | if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 ) | 812 | if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 ) |
796 | return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr; | 813 | return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr; |
797 | 814 | ||
@@ -880,7 +897,7 @@ | |||
880 | // <noscript> tags (get lost in IE and messed up in FF). | 897 | // <noscript> tags (get lost in IE and messed up in FF). |
881 | /<noscript[\s\S]*?<\/noscript>/gi, | 898 | /<noscript[\s\S]*?<\/noscript>/gi, |
882 | 899 | ||
883 | // Avoid meta tags being stripped (#8117). | 900 | // Avoid meta tags being stripped (http://dev.ckeditor.com/ticket/8117). |
884 | /<meta[\s\S]*?\/?>/gi | 901 | /<meta[\s\S]*?\/?>/gi |
885 | ].concat( protectRegexes ); | 902 | ].concat( protectRegexes ); |
886 | 903 | ||
@@ -894,7 +911,7 @@ | |||
894 | 911 | ||
895 | for ( var i = 0; i < regexes.length; i++ ) { | 912 | for ( var i = 0; i < regexes.length; i++ ) { |
896 | data = data.replace( regexes[ i ], function( match ) { | 913 | data = data.replace( regexes[ i ], function( match ) { |
897 | match = match.replace( tempRegex, // There could be protected source inside another one. (#3869). | 914 | match = match.replace( tempRegex, // There could be protected source inside another one. (http://dev.ckeditor.com/ticket/3869). |
898 | function( $, isComment, id ) { | 915 | function( $, isComment, id ) { |
899 | return protectedHtml[ id ]; | 916 | return protectedHtml[ id ]; |
900 | } ); | 917 | } ); |
@@ -912,7 +929,7 @@ | |||
912 | 929 | ||
913 | // Different protection pattern is used for those that | 930 | // Different protection pattern is used for those that |
914 | // live in attributes to avoid from being HTML encoded. | 931 | // live in attributes to avoid from being HTML encoded. |
915 | // Why so serious? See #9205, #8216, #7805, #11754, #11846. | 932 | // Why so serious? See http://dev.ckeditor.com/ticket/9205, http://dev.ckeditor.com/ticket/8216, http://dev.ckeditor.com/ticket/7805, http://dev.ckeditor.com/ticket/11754, http://dev.ckeditor.com/ticket/11846. |
916 | data = data.replace( /<\w+(?:\s+(?:(?:[^\s=>]+\s*=\s*(?:[^'"\s>]+|'[^']*'|"[^"]*"))|[^\s=\/>]+))+\s*\/?>/g, function( match ) { | 933 | data = data.replace( /<\w+(?:\s+(?:(?:[^\s=>]+\s*=\s*(?:[^'"\s>]+|'[^']*'|"[^"]*"))|[^\s=\/>]+))+\s*\/?>/g, function( match ) { |
917 | return match.replace( /<!--\{cke_protected\}([^>]*)-->/g, function( match, data ) { | 934 | return match.replace( /<!--\{cke_protected\}([^>]*)-->/g, function( match, data ) { |
918 | store[ store.id ] = decodeURIComponent( data ); | 935 | store[ store.id ] = decodeURIComponent( data ); |
@@ -922,7 +939,7 @@ | |||
922 | 939 | ||
923 | // This RegExp searches for innerText in all the title/iframe/textarea elements. | 940 | // This RegExp searches for innerText in all the title/iframe/textarea elements. |
924 | // This is because browser doesn't allow HTML in these elements, that's why we can't | 941 | // This is because browser doesn't allow HTML in these elements, that's why we can't |
925 | // nest comments in there. (#11223) | 942 | // nest comments in there. (http://dev.ckeditor.com/ticket/11223) |
926 | data = data.replace( /<(title|iframe|textarea)([^>]*)>([\s\S]*?)<\/\1>/g, function( match, tagName, tagAttributes, innerText ) { | 943 | data = data.replace( /<(title|iframe|textarea)([^>]*)>([\s\S]*?)<\/\1>/g, function( match, tagName, tagAttributes, innerText ) { |
927 | return '<' + tagName + tagAttributes + '>' + unprotectSource( unprotectRealComments( innerText ), editor ) + '</' + tagName + '>'; | 944 | return '<' + tagName + tagAttributes + '>' + unprotectSource( unprotectRealComments( innerText ), editor ) + '</' + tagName + '>'; |
928 | } ); | 945 | } ); |
@@ -971,7 +988,7 @@ | |||
971 | * {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}. | 988 | * {@link CKEDITOR.htmlParser.fragment} {@link CKEDITOR.htmlParser.element}. |
972 | * * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#dataFilter} | 989 | * * 5-9: Data is available in the parsed format, but {@link CKEDITOR.htmlDataProcessor#dataFilter} |
973 | * is not applied yet. | 990 | * is not applied yet. |
974 | * * 6: Data is filtered with the {CKEDITOR.filter content filter}. | 991 | * * 6: Data is filtered with the {@link CKEDITOR.filter content filter}. |
975 | * * 10: Data is processed with {@link CKEDITOR.htmlDataProcessor#dataFilter}. | 992 | * * 10: Data is processed with {@link CKEDITOR.htmlDataProcessor#dataFilter}. |
976 | * * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#dataFilter} | 993 | * * 10-14: Data is available in the parsed format and {@link CKEDITOR.htmlDataProcessor#dataFilter} |
977 | * has already been applied. | 994 | * has already been applied. |
diff --git a/sources/core/htmlparser.js b/sources/core/htmlparser.js index dffde95..c95257c 100644 --- a/sources/core/htmlparser.js +++ b/sources/core/htmlparser.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -163,7 +163,7 @@ CKEDITOR.htmlParser = function() { | |||
163 | tagName = tagName.toLowerCase(); | 163 | tagName = tagName.toLowerCase(); |
164 | 164 | ||
165 | // There are some tag names that can break things, so let's | 165 | // There are some tag names that can break things, so let's |
166 | // simply ignore them when parsing. (#5224) | 166 | // simply ignore them when parsing. (http://dev.ckeditor.com/ticket/5224) |
167 | if ( /="/.test( tagName ) ) | 167 | if ( /="/.test( tagName ) ) |
168 | continue; | 168 | continue; |
169 | 169 | ||
diff --git a/sources/core/htmlparser/basicwriter.js b/sources/core/htmlparser/basicwriter.js index 62a97ef..61447f0 100644 --- a/sources/core/htmlparser/basicwriter.js +++ b/sources/core/htmlparser/basicwriter.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -67,7 +67,7 @@ CKEDITOR.htmlParser.basicWriter = CKEDITOR.tools.createClass( { | |||
67 | * @param {String} attValue The attribute value. | 67 | * @param {String} attValue The attribute value. |
68 | */ | 68 | */ |
69 | attribute: function( attName, attValue ) { | 69 | attribute: function( attName, attValue ) { |
70 | // Browsers don't always escape special character in attribute values. (#4683, #4719). | 70 | // Browsers don't always escape special character in attribute values. (http://dev.ckeditor.com/ticket/4683, http://dev.ckeditor.com/ticket/4719). |
71 | if ( typeof attValue == 'string' ) | 71 | if ( typeof attValue == 'string' ) |
72 | attValue = CKEDITOR.tools.htmlEncodeAttr( attValue ); | 72 | attValue = CKEDITOR.tools.htmlEncodeAttr( attValue ); |
73 | 73 | ||
diff --git a/sources/core/htmlparser/cdata.js b/sources/core/htmlparser/cdata.js index 4ece2b7..be8c5cf 100644 --- a/sources/core/htmlparser/cdata.js +++ b/sources/core/htmlparser/cdata.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/htmlparser/comment.js b/sources/core/htmlparser/comment.js index 171c62e..14a38f3 100644 --- a/sources/core/htmlparser/comment.js +++ b/sources/core/htmlparser/comment.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/htmlparser/element.js b/sources/core/htmlparser/element.js index 3654322..224d3e6 100644 --- a/sources/core/htmlparser/element.js +++ b/sources/core/htmlparser/element.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -35,7 +35,7 @@ CKEDITOR.htmlParser.element = function( name, attributes ) { | |||
35 | */ | 35 | */ |
36 | this.children = []; | 36 | this.children = []; |
37 | 37 | ||
38 | // Reveal the real semantic of our internal custom tag name (#6639), | 38 | // Reveal the real semantic of our internal custom tag name (http://dev.ckeditor.com/ticket/6639), |
39 | // when resolving whether it's block like. | 39 | // when resolving whether it's block like. |
40 | var realName = name || '', | 40 | var realName = name || '', |
41 | prefixed = realName.match( /^cke:(.*)/ ); | 41 | prefixed = realName.match( /^cke:(.*)/ ); |
@@ -56,7 +56,7 @@ CKEDITOR.htmlParser.element = function( name, attributes ) { | |||
56 | }; | 56 | }; |
57 | 57 | ||
58 | /** | 58 | /** |
59 | * Object presentation of CSS style declaration text. | 59 | * Object presentation of the CSS style declaration text. |
60 | * | 60 | * |
61 | * @class | 61 | * @class |
62 | * @constructor Creates a `cssStyle` class instance. | 62 | * @constructor Creates a `cssStyle` class instance. |
@@ -419,7 +419,7 @@ CKEDITOR.htmlParser.cssStyle = function() { | |||
419 | * | 419 | * |
420 | * @since 4.3 | 420 | * @since 4.3 |
421 | * @param {Number} index Index at which the element will be split — `0` means the beginning, | 421 | * @param {Number} index Index at which the element will be split — `0` means the beginning, |
422 | * `1` after first child node, etc. | 422 | * `1` after the first child node, etc. |
423 | * @returns {CKEDITOR.htmlParser.element} The new element following this one. | 423 | * @returns {CKEDITOR.htmlParser.element} The new element following this one. |
424 | */ | 424 | */ |
425 | split: function( index ) { | 425 | split: function( index ) { |
@@ -443,6 +443,38 @@ CKEDITOR.htmlParser.cssStyle = function() { | |||
443 | }, | 443 | }, |
444 | 444 | ||
445 | /** | 445 | /** |
446 | * Searches through the current node children to find nodes matching the `criteria`. | ||
447 | * | ||
448 | * @param {String/Function} criteria Tag name or evaluator function. | ||
449 | * @param {Boolean} [recursive=false] | ||
450 | * @returns {CKEDITOR.htmlParser.node[]} | ||
451 | */ | ||
452 | find: function( criteria, recursive ) { | ||
453 | if ( recursive === undefined ) { | ||
454 | recursive = false; | ||
455 | } | ||
456 | |||
457 | var ret = [], | ||
458 | i; | ||
459 | |||
460 | for ( i = 0; i < this.children.length; i++ ) { | ||
461 | var curChild = this.children[ i ]; | ||
462 | |||
463 | if ( typeof criteria == 'function' && criteria( curChild ) ) { | ||
464 | ret.push( curChild ); | ||
465 | } else if ( typeof criteria == 'string' && curChild.name === criteria ) { | ||
466 | ret.push( curChild ); | ||
467 | } | ||
468 | |||
469 | if ( recursive && curChild.find ) { | ||
470 | ret = ret.concat( curChild.find( criteria, recursive ) ); | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return ret; | ||
475 | }, | ||
476 | |||
477 | /** | ||
446 | * Adds a class name to the list of classes. | 478 | * Adds a class name to the list of classes. |
447 | * | 479 | * |
448 | * @since 4.4 | 480 | * @since 4.4 |
@@ -511,8 +543,8 @@ CKEDITOR.htmlParser.cssStyle = function() { | |||
511 | 543 | ||
512 | if ( !ctx.nonEditable && this.attributes.contenteditable == 'false' ) | 544 | if ( !ctx.nonEditable && this.attributes.contenteditable == 'false' ) |
513 | changes.push( 'nonEditable', true ); | 545 | changes.push( 'nonEditable', true ); |
514 | // A context to be given nestedEditable must be nonEditable first (by inheritance) (#11372, #11698). | 546 | // A context to be given nestedEditable must be nonEditable first (by inheritance) (http://dev.ckeditor.com/ticket/11372, http://dev.ckeditor.com/ticket/11698). |
515 | // Special case: #11504 - filter starts on <body contenteditable=true>, | 547 | // Special case: http://dev.ckeditor.com/ticket/11504 - filter starts on <body contenteditable=true>, |
516 | // so ctx.nonEditable has not been yet set to true. | 548 | // so ctx.nonEditable has not been yet set to true. |
517 | else if ( ctx.nonEditable && !ctx.nestedEditable && this.attributes.contenteditable == 'true' ) | 549 | else if ( ctx.nonEditable && !ctx.nestedEditable && this.attributes.contenteditable == 'true' ) |
518 | changes.push( 'nestedEditable', true ); | 550 | changes.push( 'nestedEditable', true ); |
diff --git a/sources/core/htmlparser/filter.js b/sources/core/htmlparser/filter.js index 72767b5..db6b91c 100644 --- a/sources/core/htmlparser/filter.js +++ b/sources/core/htmlparser/filter.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/htmlparser/fragment.js b/sources/core/htmlparser/fragment.js index c062986..7ef915c 100644 --- a/sources/core/htmlparser/fragment.js +++ b/sources/core/htmlparser/fragment.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -61,7 +61,7 @@ CKEDITOR.htmlParser.fragment = function() { | |||
61 | if ( node.attributes[ 'data-cke-survive' ] ) | 61 | if ( node.attributes[ 'data-cke-survive' ] ) |
62 | return false; | 62 | return false; |
63 | 63 | ||
64 | // Empty link is to be removed when empty but not anchor. (#7894) | 64 | // Empty link is to be removed when empty but not anchor. (http://dev.ckeditor.com/ticket/7894) |
65 | return node.name == 'a' && node.attributes.href || CKEDITOR.dtd.$removeEmpty[ node.name ]; | 65 | return node.name == 'a' && node.attributes.href || CKEDITOR.dtd.$removeEmpty[ node.name ]; |
66 | } | 66 | } |
67 | 67 | ||
@@ -130,7 +130,7 @@ CKEDITOR.htmlParser.fragment = function() { | |||
130 | i--; | 130 | i--; |
131 | } else { | 131 | } else { |
132 | // Some element of the same type cannot be nested, flat them, | 132 | // Some element of the same type cannot be nested, flat them, |
133 | // e.g. <a href="#">foo<a href="#">bar</a></a>. (#7894) | 133 | // e.g. <a href="#">foo<a href="#">bar</a></a>. (http://dev.ckeditor.com/ticket/7894) |
134 | if ( pendingName == currentNode.name ) | 134 | if ( pendingName == currentNode.name ) |
135 | addElement( currentNode, currentNode.parent, 1 ), i--; | 135 | addElement( currentNode, currentNode.parent, 1 ), i--; |
136 | } | 136 | } |
@@ -143,7 +143,7 @@ CKEDITOR.htmlParser.fragment = function() { | |||
143 | addElement( pendingBRs.shift(), currentNode ); | 143 | addElement( pendingBRs.shift(), currentNode ); |
144 | } | 144 | } |
145 | 145 | ||
146 | // Rtrim empty spaces on block end boundary. (#3585) | 146 | // Rtrim empty spaces on block end boundary. (http://dev.ckeditor.com/ticket/3585) |
147 | function removeTailWhitespace( element ) { | 147 | function removeTailWhitespace( element ) { |
148 | if ( element._.isBlockLike && element.name != 'pre' && element.name != 'textarea' ) { | 148 | if ( element._.isBlockLike && element.name != 'pre' && element.name != 'textarea' ) { |
149 | 149 | ||
@@ -275,10 +275,10 @@ CKEDITOR.htmlParser.fragment = function() { | |||
275 | // If the element cannot be child of the current element. | 275 | // If the element cannot be child of the current element. |
276 | if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) { | 276 | if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) { |
277 | // Current node doesn't have a close tag, time for a close | 277 | // Current node doesn't have a close tag, time for a close |
278 | // as this element isn't fit in. (#7497) | 278 | // as this element isn't fit in. (http://dev.ckeditor.com/ticket/7497) |
279 | if ( currentNode.isOptionalClose ) | 279 | if ( currentNode.isOptionalClose ) |
280 | parser.onTagClose( currentName ); | 280 | parser.onTagClose( currentName ); |
281 | // Fixing malformed nested lists by moving it into a previous list item. (#3828) | 281 | // Fixing malformed nested lists by moving it into a previous list item. (http://dev.ckeditor.com/ticket/3828) |
282 | else if ( tagName in listBlocks && currentName in listBlocks ) { | 282 | else if ( tagName in listBlocks && currentName in listBlocks ) { |
283 | var children = currentNode.children, | 283 | var children = currentNode.children, |
284 | lastChild = children[ children.length - 1 ]; | 284 | lastChild = children[ children.length - 1 ]; |
@@ -291,7 +291,7 @@ CKEDITOR.htmlParser.fragment = function() { | |||
291 | currentNode = lastChild; | 291 | currentNode = lastChild; |
292 | } | 292 | } |
293 | // Establish new list root for orphan list items, but NOT to create | 293 | // Establish new list root for orphan list items, but NOT to create |
294 | // new list for the following ones, fix them instead. (#6975) | 294 | // new list for the following ones, fix them instead. (http://dev.ckeditor.com/ticket/6975) |
295 | // <dl><dt>foo<dd>bar</dl> | 295 | // <dl><dt>foo<dd>bar</dl> |
296 | // <ul><li>foo<li>bar</ul> | 296 | // <ul><li>foo<li>bar</ul> |
297 | else if ( tagName in CKEDITOR.dtd.$listItem && | 297 | else if ( tagName in CKEDITOR.dtd.$listItem && |
@@ -409,7 +409,7 @@ CKEDITOR.htmlParser.fragment = function() { | |||
409 | var currentName = currentNode.name, | 409 | var currentName = currentNode.name, |
410 | currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd; | 410 | currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ] || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ) : rootDtd; |
411 | 411 | ||
412 | // Fix orphan text in list/table. (#8540) (#8870) | 412 | // Fix orphan text in list/table. (http://dev.ckeditor.com/ticket/8540) (http://dev.ckeditor.com/ticket/8870) |
413 | if ( !inTextarea && !currentDtd[ '#' ] && currentName in nonBreakingBlocks ) { | 413 | if ( !inTextarea && !currentDtd[ '#' ] && currentName in nonBreakingBlocks ) { |
414 | parser.onTagOpen( structureFixes[ currentName ] || '' ); | 414 | parser.onTagOpen( structureFixes[ currentName ] || '' ); |
415 | parser.onText( text ); | 415 | parser.onText( text ); |
diff --git a/sources/core/htmlparser/node.js b/sources/core/htmlparser/node.js index 0f1b307..b38c8a8 100644 --- a/sources/core/htmlparser/node.js +++ b/sources/core/htmlparser/node.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/htmlparser/text.js b/sources/core/htmlparser/text.js index 07cb865..190e85a 100644 --- a/sources/core/htmlparser/text.js +++ b/sources/core/htmlparser/text.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/keystrokehandler.js b/sources/core/keystrokehandler.js index e2a6bcd..c84089b 100644 --- a/sources/core/keystrokehandler.js +++ b/sources/core/keystrokehandler.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/lang.js b/sources/core/lang.js index 3519923..06f3a2a 100644 --- a/sources/core/lang.js +++ b/sources/core/lang.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -17,10 +17,10 @@ | |||
17 | * alert( CKEDITOR.lang.languages.en ); // 1 | 17 | * alert( CKEDITOR.lang.languages.en ); // 1 |
18 | */ | 18 | */ |
19 | languages: { | 19 | languages: { |
20 | af: 1, ar: 1, bg: 1, bn: 1, bs: 1, ca: 1, cs: 1, cy: 1, da: 1, de: 1, 'de-ch': 1, el: 1, | 20 | af: 1, ar: 1, az: 1, bg: 1, bn: 1, bs: 1, ca: 1, cs: 1, cy: 1, da: 1, de: 1, 'de-ch': 1, el: 1, |
21 | '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, | 21 | 'en-au': 1, 'en-ca': 1, 'en-gb': 1, en: 1, eo: 1, es: 1, 'es-mx':1, et: 1, eu: 1, fa: 1, fi: 1, fo: 1, |
22 | '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, | 22 | '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, |
23 | 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, | 23 | km: 1, ko: 1, ku: 1, lt: 1, lv: 1, mk: 1, mn: 1, ms: 1, nb: 1, nl: 1, no: 1, oc: 1, pl: 1, 'pt-br': 1, |
24 | 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, tt: 1, ug: 1, | 24 | 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, tt: 1, ug: 1, |
25 | uk: 1, vi: 1, 'zh-cn': 1, zh: 1 | 25 | uk: 1, vi: 1, 'zh-cn': 1, zh: 1 |
26 | }, | 26 | }, |
diff --git a/sources/core/loader.js b/sources/core/loader.js index 5a108df..dc02511 100644 --- a/sources/core/loader.js +++ b/sources/core/loader.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -142,7 +142,7 @@ if ( !CKEDITOR.loader ) { | |||
142 | } | 142 | } |
143 | 143 | ||
144 | // We must guarantee the execution order of the scripts, so we | 144 | // We must guarantee the execution order of the scripts, so we |
145 | // need to load them one by one. (#4145) | 145 | // need to load them one by one. (http://dev.ckeditor.com/ticket/4145) |
146 | // The following if/else block has been taken from the scriptloader core code. | 146 | // The following if/else block has been taken from the scriptloader core code. |
147 | if ( typeof script.onreadystatechange !== 'undefined' ) { | 147 | if ( typeof script.onreadystatechange !== 'undefined' ) { |
148 | /** @ignore */ | 148 | /** @ignore */ |
@@ -156,7 +156,7 @@ if ( !CKEDITOR.loader ) { | |||
156 | /** @ignore */ | 156 | /** @ignore */ |
157 | script.onload = function() { | 157 | script.onload = function() { |
158 | // Some browsers, such as Safari, may call the onLoad function | 158 | // Some browsers, such as Safari, may call the onLoad function |
159 | // immediately. Which will break the loading sequence. (#3661) | 159 | // immediately. Which will break the loading sequence. (http://dev.ckeditor.com/ticket/3661) |
160 | setTimeout( function() { | 160 | setTimeout( function() { |
161 | onScriptLoaded( scriptName ); | 161 | onScriptLoaded( scriptName ); |
162 | }, 0 ); | 162 | }, 0 ); |
diff --git a/sources/core/log.js b/sources/core/log.js index 6981612..228789e 100644 --- a/sources/core/log.js +++ b/sources/core/log.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/plugindefinition.js b/sources/core/plugindefinition.js index caff957..9ff7683 100644 --- a/sources/core/plugindefinition.js +++ b/sources/core/plugindefinition.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/plugins.js b/sources/core/plugins.js index 8e6c952..75cc41e 100644 --- a/sources/core/plugins.js +++ b/sources/core/plugins.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
diff --git a/sources/core/resourcemanager.js b/sources/core/resourcemanager.js index 7ba88a8..c33c0f8 100644 --- a/sources/core/resourcemanager.js +++ b/sources/core/resourcemanager.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -124,13 +124,13 @@ CKEDITOR.resourceManager.prototype = { | |||
124 | * Registers one or more resources to be loaded from an external path | 124 | * Registers one or more resources to be loaded from an external path |
125 | * instead of the core base path. | 125 | * instead of the core base path. |
126 | * | 126 | * |
127 | * // Loads a plugin from '/myplugin/samples/plugin.js'. | 127 | * // Loads a plugin from '/myplugins/sample/plugin.js'. |
128 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' ); | 128 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/' ); |
129 | * | 129 | * |
130 | * // Loads a plugin from '/myplugin/samples/my_plugin.js'. | 130 | * // Loads a plugin from '/myplugins/sample/my_plugin.js'. |
131 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/', 'my_plugin.js' ); | 131 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/', 'my_plugin.js' ); |
132 | * | 132 | * |
133 | * // Loads a plugin from '/myplugin/samples/my_plugin.js'. | 133 | * // Loads a plugin from '/myplugins/sample/my_plugin.js'. |
134 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/my_plugin.js', '' ); | 134 | * CKEDITOR.plugins.addExternal( 'sample', '/myplugins/sample/my_plugin.js', '' ); |
135 | * | 135 | * |
136 | * @param {String} names The resource names, separated by commas. | 136 | * @param {String} names The resource names, separated by commas. |
diff --git a/sources/core/scriptloader.js b/sources/core/scriptloader.js index 9ad536e..356996f 100644 --- a/sources/core/scriptloader.js +++ b/sources/core/scriptloader.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -119,8 +119,8 @@ CKEDITOR.scriptLoader = ( function() { | |||
119 | } ); | 119 | } ); |
120 | 120 | ||
121 | if ( callback ) { | 121 | if ( callback ) { |
122 | if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 ) { | 122 | // The onload or onerror event does not fire in IE8 and IE9 Quirks Mode (http://dev.ckeditor.com/ticket/14849). |
123 | // FIXME: For IE, we are not able to return false on error (like 404). | 123 | if ( CKEDITOR.env.ie && ( CKEDITOR.env.version <= 8 || CKEDITOR.env.ie9Compat ) ) { |
124 | script.$.onreadystatechange = function() { | 124 | script.$.onreadystatechange = function() { |
125 | if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' ) { | 125 | if ( script.$.readyState == 'loaded' || script.$.readyState == 'complete' ) { |
126 | script.$.onreadystatechange = null; | 126 | script.$.onreadystatechange = null; |
@@ -130,13 +130,12 @@ CKEDITOR.scriptLoader = ( function() { | |||
130 | } else { | 130 | } else { |
131 | script.$.onload = function() { | 131 | script.$.onload = function() { |
132 | // Some browsers, such as Safari, may call the onLoad function | 132 | // Some browsers, such as Safari, may call the onLoad function |
133 | // immediately. Which will break the loading sequence. (#3661) | 133 | // immediately. Which will break the loading sequence. (http://dev.ckeditor.com/ticket/3661) |
134 | setTimeout( function() { | 134 | setTimeout( function() { |
135 | onLoad( url, true ); | 135 | onLoad( url, true ); |
136 | }, 0 ); | 136 | }, 0 ); |
137 | }; | 137 | }; |
138 | 138 | ||
139 | // FIXME: Opera and Safari will not fire onerror. | ||
140 | script.$.onerror = function() { | 139 | script.$.onerror = function() { |
141 | onLoad( url, false ); | 140 | onLoad( url, false ); |
142 | }; | 141 | }; |
diff --git a/sources/core/selection.js b/sources/core/selection.js index 573b890..d44db3b 100644 --- a/sources/core/selection.js +++ b/sources/core/selection.js | |||
@@ -1,9 +1,217 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
6 | ( function() { | 6 | ( function() { |
7 | var isMSSelection = typeof window.getSelection != 'function', | ||
8 | nextRev = 1, | ||
9 | // http://dev.ckeditor.com/ticket/13816 | ||
10 | fillingCharSequence = CKEDITOR.tools.repeat( '\u200b', 7 ), | ||
11 | fillingCharSequenceRegExp = new RegExp( fillingCharSequence + '( )?', 'g' ), | ||
12 | isSelectingTable; | ||
13 | |||
14 | // #### table selection : START | ||
15 | // @param {CKEDITOR.dom.range[]} ranges | ||
16 | // @param {Boolean} allowPartially Whether a collapsed selection within table is recognized to be a valid selection. | ||
17 | // This happens for WebKits on MacOS, when you right click inside the table. | ||
18 | function isTableSelection( ranges, allowPartially ) { | ||
19 | if ( ranges.length === 0 ) { | ||
20 | return false; | ||
21 | } | ||
22 | |||
23 | var node, | ||
24 | i; | ||
25 | |||
26 | function isPartiallySelected( range ) { | ||
27 | var startCell = range.startContainer.getAscendant( { td: 1, th: 1 }, true ), | ||
28 | endCell = range.endContainer.getAscendant( { td: 1, th: 1 }, true ), | ||
29 | trim = CKEDITOR.tools.trim, | ||
30 | selected; | ||
31 | |||
32 | // Check if the selection is inside one cell and we don't have any nested table contents selected. | ||
33 | if ( !startCell || !startCell.equals( endCell ) || startCell.findOne( 'td, th, tr, tbody, table' ) ) { | ||
34 | return false; | ||
35 | } | ||
36 | |||
37 | selected = range.cloneContents(); | ||
38 | |||
39 | // Empty selection is still partially selected. | ||
40 | if ( !selected.getFirst() ) { | ||
41 | return true; | ||
42 | } | ||
43 | |||
44 | return trim( selected.getFirst().getText() ) !== trim( startCell.getText() ); | ||
45 | } | ||
46 | |||
47 | // Edge case: partially selected text node inside one table cell or cursor inside cell. | ||
48 | if ( !allowPartially && ranges.length === 1 && | ||
49 | ( ranges[ 0 ].collapsed || isPartiallySelected( ranges[ 0 ] ) ) ) { | ||
50 | return false; | ||
51 | } | ||
52 | |||
53 | for ( i = 0; i < ranges.length; i++ ) { | ||
54 | node = ranges[ i ]._getTableElement(); | ||
55 | |||
56 | if ( !node ) { | ||
57 | return false; | ||
58 | } | ||
59 | } | ||
60 | |||
61 | return true; | ||
62 | } | ||
63 | |||
64 | // After performing fake table selection, the real selection is limited | ||
65 | // to the first selected cell. Therefore to check if the real selection | ||
66 | // matches the fake selection, we check if the table cell from fake selection's | ||
67 | // first range and real selection's range are the same. | ||
68 | // Also if the selection is collapsed, we should check if it's placed inside the table | ||
69 | // in which the fake selection is or inside nested table. Such selection occurs after right mouse click. | ||
70 | function isRealTableSelection( selection, fakeSelection ) { | ||
71 | var ranges = selection.getRanges(), | ||
72 | fakeRanges = fakeSelection.getRanges(), | ||
73 | table = ranges.length && ranges[ 0 ]._getTableElement() && | ||
74 | ranges[ 0 ]._getTableElement().getAscendant( 'table', true ), | ||
75 | fakeTable = fakeRanges.length && fakeRanges[ 0 ]._getTableElement() && | ||
76 | fakeRanges[ 0 ]._getTableElement().getAscendant( 'table', true ), | ||
77 | isTableRange = ranges.length === 1 && ranges[ 0 ]._getTableElement() && | ||
78 | ranges[ 0 ]._getTableElement().is( 'table' ), | ||
79 | isFakeTableRange = fakeRanges.length === 1 && fakeRanges[ 0 ]._getTableElement() && | ||
80 | fakeRanges[ 0 ]._getTableElement().is( 'table' ); | ||
81 | |||
82 | function isValidTableSelection( table, fakeTable, ranges, fakeRanges ) { | ||
83 | var isMenuOpen = ranges.length === 1 && ranges[ 0 ].collapsed, | ||
84 | // In case of WebKit on MacOS, when checking real selection, we must allow selection to be partial. | ||
85 | // Otherwise the check will fail for table selection with opened context menu. | ||
86 | isInTable = isTableSelection( ranges, !!CKEDITOR.env.webkit ) && isTableSelection( fakeRanges ); | ||
87 | |||
88 | return isSameTable( table, fakeTable ) && ( isMenuOpen || isInTable ); | ||
89 | } | ||
90 | |||
91 | function isSameTable( table, fakeTable ) { | ||
92 | if ( !table || !fakeTable ) { | ||
93 | return false; | ||
94 | } | ||
95 | |||
96 | return table.equals( fakeTable ) || fakeTable.contains( table ); | ||
97 | } | ||
98 | |||
99 | if ( isValidTableSelection( table, fakeTable, ranges, fakeRanges ) ) { | ||
100 | // Edge case: when editor contains only table and that table is selected using selectAll command, | ||
101 | // then the selection is not properly refreshed and it must be done manually. | ||
102 | if ( isTableRange && !isFakeTableRange ) { | ||
103 | fakeSelection.selectRanges( ranges ); | ||
104 | } | ||
105 | return true; | ||
106 | } | ||
107 | |||
108 | return false; | ||
109 | } | ||
110 | |||
111 | function getSelectedCells( ranges ) { | ||
112 | var cells = [], | ||
113 | node, | ||
114 | i; | ||
115 | |||
116 | function getCellsFromElement( element ) { | ||
117 | var cells = element.find( 'td, th' ), | ||
118 | cellsArray = [], | ||
119 | i; | ||
120 | |||
121 | for ( i = 0; i < cells.count(); i++ ) { | ||
122 | cellsArray.push( cells.getItem( i ) ); | ||
123 | } | ||
124 | |||
125 | return cellsArray; | ||
126 | } | ||
127 | |||
128 | for ( i = 0; i < ranges.length; i++ ) { | ||
129 | node = ranges[ i ]._getTableElement(); | ||
130 | |||
131 | if ( node.is && node.is( { td: 1, th: 1 } ) ) { | ||
132 | cells.push( node ); | ||
133 | } else { | ||
134 | cells = cells.concat( getCellsFromElement( node ) ); | ||
135 | } | ||
136 | } | ||
137 | |||
138 | return cells; | ||
139 | } | ||
140 | |||
141 | // Cells in the same row are separated by tab and the rows are separated by new line, e.g. | ||
142 | // Cell 1.1 Cell 1.2 | ||
143 | // Cell 2.1 Cell 2.2 | ||
144 | function getTextFromSelectedCells( ranges ) { | ||
145 | var cells = getSelectedCells( ranges ), | ||
146 | txt = '', | ||
147 | currentRow = [], | ||
148 | lastRow, | ||
149 | i; | ||
150 | |||
151 | for ( i = 0; i < cells.length; i++ ) { | ||
152 | if ( lastRow && !lastRow.equals( cells[ i ].getAscendant( 'tr' ) ) ) { | ||
153 | txt += currentRow.join( '\t' ) + '\n'; | ||
154 | lastRow = cells[ i ].getAscendant( 'tr' ); | ||
155 | currentRow = []; | ||
156 | } else if ( i === 0 ) { | ||
157 | lastRow = cells[ i ].getAscendant( 'tr' ); | ||
158 | } | ||
159 | |||
160 | currentRow.push( cells[ i ].getText() ); | ||
161 | } | ||
162 | |||
163 | txt += currentRow.join( '\t' ); | ||
164 | |||
165 | return txt; | ||
166 | } | ||
167 | |||
168 | function performFakeTableSelection( ranges ) { | ||
169 | var editor = this.root.editor, | ||
170 | realSelection = editor.getSelection( 1 ), | ||
171 | cache; | ||
172 | |||
173 | // Cleanup after previous selection - e.g. remove hidden sel container. | ||
174 | this.reset(); | ||
175 | |||
176 | // Indicate that the table is being fake-selected to prevent infinite loop | ||
177 | // inside `selectRanges`. | ||
178 | isSelectingTable = true; | ||
179 | |||
180 | // Cancel selectionchange for the real selection. | ||
181 | realSelection.root.once( 'selectionchange', function( evt ) { | ||
182 | evt.cancel(); | ||
183 | }, null, null, 0 ); | ||
184 | |||
185 | // Move real selection to the first selected range. | ||
186 | realSelection.selectRanges( [ ranges[ 0 ] ] ); | ||
187 | |||
188 | cache = this._.cache; | ||
189 | |||
190 | // Caches given ranges. | ||
191 | cache.ranges = new CKEDITOR.dom.rangeList( ranges ); | ||
192 | cache.type = CKEDITOR.SELECTION_TEXT; | ||
193 | cache.selectedElement = ranges[ 0 ]._getTableElement(); | ||
194 | |||
195 | // `selectedText` should contain text from all selected data ("plain text table") | ||
196 | // to be compatible with Firefox's implementation. | ||
197 | cache.selectedText = getTextFromSelectedCells( ranges ); | ||
198 | |||
199 | // Properties that will not be available when isFake. | ||
200 | cache.nativeSel = null; | ||
201 | |||
202 | this.isFake = 1; | ||
203 | this.rev = nextRev++; | ||
204 | |||
205 | // Save this selection, so it can be returned by editor.getSelection(). | ||
206 | editor._.fakeSelection = this; | ||
207 | |||
208 | isSelectingTable = false; | ||
209 | |||
210 | // Fire selectionchange, just like a normal selection. | ||
211 | this.root.fire( 'selectionchange' ); | ||
212 | } | ||
213 | // #### table selection : END | ||
214 | |||
7 | // #### checkSelectionChange : START | 215 | // #### checkSelectionChange : START |
8 | 216 | ||
9 | // The selection change check basically saves the element parent tree of | 217 | // The selection change check basically saves the element parent tree of |
@@ -16,10 +224,9 @@ | |||
16 | 224 | ||
17 | if ( sel ) { | 225 | if ( sel ) { |
18 | realSel = this.getSelection( 1 ); | 226 | realSel = this.getSelection( 1 ); |
19 | 227 | // If real (not locked/stored) selection was moved from hidden container | |
20 | // If real (not locked/stored) selection was moved from hidden container, | 228 | // or is not a table one, then the fake-selection must be invalidated. |
21 | // then the fake-selection must be invalidated. | 229 | if ( !realSel || ( !realSel.isHidden() && !isRealTableSelection( realSel, sel ) ) ) { |
22 | if ( !realSel || !realSel.isHidden() ) { | ||
23 | // Remove the cache from fake-selection references in use elsewhere. | 230 | // Remove the cache from fake-selection references in use elsewhere. |
24 | sel.reset(); | 231 | sel.reset(); |
25 | 232 | ||
@@ -41,8 +248,10 @@ | |||
41 | 248 | ||
42 | var currentPath = this.elementPath(); | 249 | var currentPath = this.elementPath(); |
43 | if ( !currentPath.compare( this._.selectionPreviousPath ) ) { | 250 | if ( !currentPath.compare( this._.selectionPreviousPath ) ) { |
251 | // Handle case when dialog inserts new element but parent block and path (so also focus context) does not change. (http://dev.ckeditor.com/ticket/13362) | ||
252 | var sameBlockParent = this._.selectionPreviousPath && this._.selectionPreviousPath.blockLimit.equals( currentPath.blockLimit ); | ||
44 | // Cache the active element, which we'll eventually lose on Webkit. | 253 | // Cache the active element, which we'll eventually lose on Webkit. |
45 | if ( CKEDITOR.env.webkit ) | 254 | if ( CKEDITOR.env.webkit && !sameBlockParent ) |
46 | this._.previousActive = this.document.getActive(); | 255 | this._.previousActive = this.document.getActive(); |
47 | 256 | ||
48 | this._.selectionPreviousPath = currentPath; | 257 | this._.selectionPreviousPath = currentPath; |
@@ -89,7 +298,7 @@ | |||
89 | // * is a visible node, | 298 | // * is a visible node, |
90 | // * is a non-empty element (this rule will accept elements like <strong></strong> because they | 299 | // * is a non-empty element (this rule will accept elements like <strong></strong> because they |
91 | // they were not accepted by the isVisible() check, not not <br> which cannot absorb the caret). | 300 | // they were not accepted by the isVisible() check, not not <br> which cannot absorb the caret). |
92 | // See #12621. | 301 | // See http://dev.ckeditor.com/ticket/12621. |
93 | function mayAbsorbCaret( node ) { | 302 | function mayAbsorbCaret( node ) { |
94 | if ( isVisible( node ) ) | 303 | if ( isVisible( node ) ) |
95 | return true; | 304 | return true; |
@@ -130,8 +339,8 @@ | |||
130 | if ( ctxRequiresFix( previous ) || ctxRequiresFix( next, 1 ) ) | 339 | if ( ctxRequiresFix( previous ) || ctxRequiresFix( next, 1 ) ) |
131 | return true; | 340 | return true; |
132 | 341 | ||
133 | // Empty block/inline element is also affected. <span>^</span>, <p>^</p> (#7222) | 342 | // Empty block/inline element is also affected. <span>^</span>, <p>^</p> (http://dev.ckeditor.com/ticket/7222) |
134 | // If you found this line confusing check #12655. | 343 | // If you found this line confusing check http://dev.ckeditor.com/ticket/12655. |
135 | if ( !( previous || next ) && !( ct.type == CKEDITOR.NODE_ELEMENT && ct.isBlockBoundary() && ct.getBogus() ) ) | 344 | if ( !( previous || next ) && !( ct.type == CKEDITOR.NODE_ELEMENT && ct.isBlockBoundary() && ct.getBogus() ) ) |
136 | return true; | 345 | return true; |
137 | 346 | ||
@@ -147,7 +356,7 @@ | |||
147 | return fillingChar; | 356 | return fillingChar; |
148 | } | 357 | } |
149 | 358 | ||
150 | // Checks if a filling char has been used, eventualy removing it (#1272). | 359 | // Checks if a filling char has been used, eventually removing it (http://dev.ckeditor.com/ticket/1272). |
151 | function checkFillingCharSequenceNodeReady( editable ) { | 360 | function checkFillingCharSequenceNodeReady( editable ) { |
152 | var fillingChar = editable.getCustomData( 'cke-fillingChar' ); | 361 | var fillingChar = editable.getCustomData( 'cke-fillingChar' ); |
153 | 362 | ||
@@ -156,6 +365,7 @@ | |||
156 | // creating it. | 365 | // creating it. |
157 | if ( fillingChar.getCustomData( 'ready' ) ) { | 366 | if ( fillingChar.getCustomData( 'ready' ) ) { |
158 | removeFillingCharSequenceNode( editable ); | 367 | removeFillingCharSequenceNode( editable ); |
368 | editable.editor.fire( 'selectionCheck' ); | ||
159 | } else { | 369 | } else { |
160 | fillingChar.setCustomData( 'ready', 1 ); | 370 | fillingChar.setCustomData( 'ready', 1 ); |
161 | } | 371 | } |
@@ -167,7 +377,7 @@ | |||
167 | 377 | ||
168 | if ( fillingChar ) { | 378 | if ( fillingChar ) { |
169 | // Text selection position might get mangled by | 379 | // Text selection position might get mangled by |
170 | // subsequent dom modification, save it now for restoring. (#8617) | 380 | // subsequent dom modification, save it now for restoring. (http://dev.ckeditor.com/ticket/8617) |
171 | if ( keepSelection !== false ) { | 381 | if ( keepSelection !== false ) { |
172 | var sel = editable.getDocument().getSelection().getNative(), | 382 | var sel = editable.getDocument().getSelection().getNative(), |
173 | // Be error proof. | 383 | // Be error proof. |
@@ -203,11 +413,11 @@ | |||
203 | } | 413 | } |
204 | } | 414 | } |
205 | 415 | ||
206 | // #13816 | 416 | // http://dev.ckeditor.com/ticket/13816 |
207 | function removeFillingCharSequenceString( str, nbspAware ) { | 417 | function removeFillingCharSequenceString( str, nbspAware ) { |
208 | if ( nbspAware ) { | 418 | if ( nbspAware ) { |
209 | return str.replace( fillingCharSequenceRegExp, function( m, p ) { | 419 | return str.replace( fillingCharSequenceRegExp, function( m, p ) { |
210 | // #10291 if filling char is followed by a space replace it with NBSP. | 420 | // http://dev.ckeditor.com/ticket/10291 if filling char is followed by a space replace it with NBSP. |
211 | return p ? '\xa0' : ''; | 421 | return p ? '\xa0' : ''; |
212 | } ); | 422 | } ); |
213 | } else { | 423 | } else { |
@@ -234,10 +444,11 @@ | |||
234 | } | 444 | } |
235 | 445 | ||
236 | // Creates cke_hidden_sel container and puts real selection there. | 446 | // Creates cke_hidden_sel container and puts real selection there. |
237 | function hideSelection( editor ) { | 447 | function hideSelection( editor, ariaLabel ) { |
238 | var style = CKEDITOR.env.ie ? 'display:none' : 'position:fixed;top:0;left:-1000px', | 448 | var content = ariaLabel || ' ', |
449 | style = CKEDITOR.env.ie && CKEDITOR.env.version < 14 ? 'display:none' : 'position:fixed;top:0;left:-1000px', | ||
239 | hiddenEl = CKEDITOR.dom.element.createFromHtml( | 450 | hiddenEl = CKEDITOR.dom.element.createFromHtml( |
240 | '<div data-cke-hidden-sel="1" data-cke-temp="1" style="' + style + '"> </div>', | 451 | '<div data-cke-hidden-sel="1" data-cke-temp="1" style="' + style + '">' + content + '</div>', |
241 | editor.document ); | 452 | editor.document ); |
242 | 453 | ||
243 | editor.fire( 'lockSnapshot' ); | 454 | editor.fire( 'lockSnapshot' ); |
@@ -382,7 +593,7 @@ | |||
382 | ( enclosedNode = range.getEnclosedNode() ) && enclosedNode.type == CKEDITOR.NODE_ELEMENT ) { | 593 | ( enclosedNode = range.getEnclosedNode() ) && enclosedNode.type == CKEDITOR.NODE_ELEMENT ) { |
383 | // So far we can't say that enclosed element is non-editable. Before checking, | 594 | // So far we can't say that enclosed element is non-editable. Before checking, |
384 | // we'll shrink range (clone). Shrinking will stop on non-editable range, or | 595 | // we'll shrink range (clone). Shrinking will stop on non-editable range, or |
385 | // innermost element (#11114). | 596 | // innermost element (http://dev.ckeditor.com/ticket/11114). |
386 | clone = range.clone(); | 597 | clone = range.clone(); |
387 | clone.shrink( CKEDITOR.SHRINK_ELEMENT, true ); | 598 | clone.shrink( CKEDITOR.SHRINK_ELEMENT, true ); |
388 | 599 | ||
@@ -516,7 +727,7 @@ | |||
516 | 727 | ||
517 | // Give the editable an initial selection on first focus, | 728 | // Give the editable an initial selection on first focus, |
518 | // put selection at a consistent position at the start | 729 | // put selection at a consistent position at the start |
519 | // of the contents. (#9507) | 730 | // of the contents. (http://dev.ckeditor.com/ticket/9507) |
520 | if ( CKEDITOR.env.gecko ) { | 731 | if ( CKEDITOR.env.gecko ) { |
521 | editable.attachListener( editable, 'focus', function( evt ) { | 732 | editable.attachListener( editable, 'focus', function( evt ) { |
522 | evt.removeListener(); | 733 | evt.removeListener(); |
@@ -524,7 +735,7 @@ | |||
524 | if ( restoreSel !== 0 ) { | 735 | if ( restoreSel !== 0 ) { |
525 | var nativ = editor.getSelection().getNative(); | 736 | var nativ = editor.getSelection().getNative(); |
526 | // Do it only if the native selection is at an unwanted | 737 | // Do it only if the native selection is at an unwanted |
527 | // place (at the very start of the editable). #10119 | 738 | // place (at the very start of the editable). http://dev.ckeditor.com/ticket/10119 |
528 | if ( nativ && nativ.isCollapsed && nativ.anchorNode == editable.$ ) { | 739 | if ( nativ && nativ.isCollapsed && nativ.anchorNode == editable.$ ) { |
529 | var rng = editor.createRange(); | 740 | var rng = editor.createRange(); |
530 | rng.moveToElementEditStart( editable ); | 741 | rng.moveToElementEditStart( editable ); |
@@ -539,9 +750,17 @@ | |||
539 | // On Webkit we use DOMFocusIn which is fired more often than focus - e.g. when moving from main editable | 750 | // On Webkit we use DOMFocusIn which is fired more often than focus - e.g. when moving from main editable |
540 | // to nested editable (or the opposite). Unlock selection all, but restore only when it was locked | 751 | // to nested editable (or the opposite). Unlock selection all, but restore only when it was locked |
541 | // for the same active element, what will e.g. mean restoring after displaying dialog. | 752 | // for the same active element, what will e.g. mean restoring after displaying dialog. |
542 | if ( restoreSel && CKEDITOR.env.webkit ) | 753 | if ( restoreSel && CKEDITOR.env.webkit ) { |
543 | restoreSel = editor._.previousActive && editor._.previousActive.equals( doc.getActive() ); | 754 | restoreSel = editor._.previousActive && editor._.previousActive.equals( doc.getActive() ); |
544 | 755 | ||
756 | // On Webkit when editor uses divarea, native focus causes editable viewport to scroll | ||
757 | // to the top (when there is no active selection inside while focusing) so the scroll | ||
758 | // position should be restored after focusing back editable area. (http://dev.ckeditor.com/ticket/14659) | ||
759 | if ( restoreSel && editor._.previousScrollTop != null && editor._.previousScrollTop != editable.$.scrollTop ) { | ||
760 | editable.$.scrollTop = editor._.previousScrollTop; | ||
761 | } | ||
762 | } | ||
763 | |||
545 | editor.unlockSelection( restoreSel ); | 764 | editor.unlockSelection( restoreSel ); |
546 | restoreSel = 0; | 765 | restoreSel = 0; |
547 | }, null, null, -1 ); | 766 | }, null, null, -1 ); |
@@ -588,7 +807,7 @@ | |||
588 | editable.attachListener( editable, 'mousedown', function( evt ) { | 807 | editable.attachListener( editable, 'mousedown', function( evt ) { |
589 | // IE scrolls document to top on right mousedown | 808 | // IE scrolls document to top on right mousedown |
590 | // when editor has no focus, remember this scroll | 809 | // when editor has no focus, remember this scroll |
591 | // position and revert it before context menu opens. (#5778) | 810 | // position and revert it before context menu opens. (http://dev.ckeditor.com/ticket/5778) |
592 | if ( evt.data.$.button == 2 ) { | 811 | if ( evt.data.$.button == 2 ) { |
593 | var sel = editor.document.getSelection(); | 812 | var sel = editor.document.getSelection(); |
594 | if ( !sel || sel.getType() == CKEDITOR.SELECTION_NONE ) | 813 | if ( !sel || sel.getType() == CKEDITOR.SELECTION_NONE ) |
@@ -607,9 +826,12 @@ | |||
607 | 826 | ||
608 | // When content doc is in standards mode, IE doesn't focus the editor when | 827 | // When content doc is in standards mode, IE doesn't focus the editor when |
609 | // clicking at the region below body (on html element) content, we emulate | 828 | // clicking at the region below body (on html element) content, we emulate |
610 | // the normal behavior on old IEs. (#1659, #7932) | 829 | // the normal behavior on old IEs. (http://dev.ckeditor.com/ticket/1659, http://dev.ckeditor.com/ticket/7932) |
611 | if ( doc.$.compatMode != 'BackCompat' ) { | 830 | if ( doc.$.compatMode != 'BackCompat' ) { |
612 | if ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) { | 831 | if ( CKEDITOR.env.ie7Compat || CKEDITOR.env.ie6Compat ) { |
832 | var textRng, | ||
833 | startRng; | ||
834 | |||
613 | html.on( 'mousedown', function( evt ) { | 835 | html.on( 'mousedown', function( evt ) { |
614 | evt = evt.data; | 836 | evt = evt.data; |
615 | 837 | ||
@@ -641,7 +863,7 @@ | |||
641 | html.removeListener( 'mousemove', onHover ); | 863 | html.removeListener( 'mousemove', onHover ); |
642 | removeListeners(); | 864 | removeListeners(); |
643 | 865 | ||
644 | // Make it in effect on mouse up. (#9022) | 866 | // Make it in effect on mouse up. (http://dev.ckeditor.com/ticket/9022) |
645 | textRng.select(); | 867 | textRng.select(); |
646 | } | 868 | } |
647 | 869 | ||
@@ -652,11 +874,11 @@ | |||
652 | evt.$.y < html.$.clientHeight && | 874 | evt.$.y < html.$.clientHeight && |
653 | evt.$.x < html.$.clientWidth ) { | 875 | evt.$.x < html.$.clientWidth ) { |
654 | // Start to build the text range. | 876 | // Start to build the text range. |
655 | var textRng = body.$.createTextRange(); | 877 | textRng = body.$.createTextRange(); |
656 | moveRangeToPoint( textRng, evt.$.clientX, evt.$.clientY ); | 878 | moveRangeToPoint( textRng, evt.$.clientX, evt.$.clientY ); |
657 | 879 | ||
658 | // Records the dragging start of the above text range. | 880 | // Records the dragging start of the above text range. |
659 | var startRng = textRng.duplicate(); | 881 | startRng = textRng.duplicate(); |
660 | 882 | ||
661 | html.on( 'mousemove', onHover ); | 883 | html.on( 'mousemove', onHover ); |
662 | outerDoc.on( 'mouseup', onSelectEnd ); | 884 | outerDoc.on( 'mouseup', onSelectEnd ); |
@@ -670,7 +892,7 @@ | |||
670 | if ( CKEDITOR.env.version > 7 && CKEDITOR.env.version < 11 ) { | 892 | if ( CKEDITOR.env.version > 7 && CKEDITOR.env.version < 11 ) { |
671 | html.on( 'mousedown', function( evt ) { | 893 | html.on( 'mousedown', function( evt ) { |
672 | if ( evt.data.getTarget().is( 'html' ) ) { | 894 | if ( evt.data.getTarget().is( 'html' ) ) { |
673 | // Limit the text selection mouse move inside of editable. (#9715) | 895 | // Limit the text selection mouse move inside of editable. (http://dev.ckeditor.com/ticket/9715) |
674 | outerDoc.on( 'mouseup', onSelectEnd ); | 896 | outerDoc.on( 'mouseup', onSelectEnd ); |
675 | html.on( 'mouseup', onSelectEnd ); | 897 | html.on( 'mouseup', onSelectEnd ); |
676 | } | 898 | } |
@@ -684,6 +906,14 @@ | |||
684 | // 2. After the accomplish of keyboard and mouse events. | 906 | // 2. After the accomplish of keyboard and mouse events. |
685 | editable.attachListener( editable, 'selectionchange', checkSelectionChange, editor ); | 907 | editable.attachListener( editable, 'selectionchange', checkSelectionChange, editor ); |
686 | editable.attachListener( editable, 'keyup', checkSelectionChangeTimeout, editor ); | 908 | editable.attachListener( editable, 'keyup', checkSelectionChangeTimeout, editor ); |
909 | // http://dev.ckeditor.com/ticket/14407 - Don't even let anything happen if the selection is in a non-editable element. | ||
910 | editable.attachListener( editable, 'keydown', function( evt ) { | ||
911 | var sel = this.getSelection( 1 ); | ||
912 | if ( nonEditableAscendant( sel ) ) { | ||
913 | sel.selectElement( nonEditableAscendant( sel ) ); | ||
914 | evt.data.preventDefault(); | ||
915 | } | ||
916 | }, editor ); | ||
687 | // Always fire the selection change on focus gain. | 917 | // Always fire the selection change on focus gain. |
688 | // On Webkit do this on DOMFocusIn, because the selection is unlocked on it too and | 918 | // On Webkit do this on DOMFocusIn, because the selection is unlocked on it too and |
689 | // we need synchronization between those listeners to not lost cached editor._.previousActive property | 919 | // we need synchronization between those listeners to not lost cached editor._.previousActive property |
@@ -693,7 +923,7 @@ | |||
693 | editor.selectionChange( 1 ); | 923 | editor.selectionChange( 1 ); |
694 | } ); | 924 | } ); |
695 | 925 | ||
696 | // #9699: On Webkit&Gecko in inline editor we have to check selection when it was changed | 926 | // http://dev.ckeditor.com/ticket/9699: On Webkit&Gecko in inline editor we have to check selection when it was changed |
697 | // by dragging and releasing mouse button outside editable. Dragging (mousedown) | 927 | // by dragging and releasing mouse button outside editable. Dragging (mousedown) |
698 | // has to be initialized in editable, but for mouseup we listen on document element. | 928 | // has to be initialized in editable, but for mouseup we listen on document element. |
699 | if ( isInline && ( CKEDITOR.env.webkit || CKEDITOR.env.gecko ) ) { | 929 | if ( isInline && ( CKEDITOR.env.webkit || CKEDITOR.env.gecko ) ) { |
@@ -707,11 +937,11 @@ | |||
707 | mouseDown = 0; | 937 | mouseDown = 0; |
708 | } ); | 938 | } ); |
709 | } | 939 | } |
710 | // In all other cases listen on simple mouseup over editable, as we did before #9699. | 940 | // In all other cases listen on simple mouseup over editable, as we did before http://dev.ckeditor.com/ticket/9699. |
711 | // | 941 | // |
712 | // Use document instead of editable in non-IEs for observing mouseup | 942 | // Use document instead of editable in non-IEs for observing mouseup |
713 | // since editable won't fire the event if selection process started within iframe and ended out | 943 | // since editable won't fire the event if selection process started within iframe and ended out |
714 | // of the editor (#9851). | 944 | // of the editor (http://dev.ckeditor.com/ticket/9851). |
715 | else { | 945 | else { |
716 | editable.attachListener( CKEDITOR.env.ie ? editable : doc.getDocumentElement(), 'mouseup', checkSelectionChangeTimeout, editor ); | 946 | editable.attachListener( CKEDITOR.env.ie ? editable : doc.getDocumentElement(), 'mouseup', checkSelectionChangeTimeout, editor ); |
717 | } | 947 | } |
@@ -733,18 +963,18 @@ | |||
733 | case 8: // BACKSPACE | 963 | case 8: // BACKSPACE |
734 | case 45: // INS | 964 | case 45: // INS |
735 | case 46: // DEl | 965 | case 46: // DEl |
736 | removeFillingCharSequenceNode( editable ); | 966 | if ( editable.hasFocus ) { |
967 | removeFillingCharSequenceNode( editable ); | ||
968 | } | ||
737 | } | 969 | } |
738 | 970 | ||
739 | }, null, null, -1 ); | 971 | }, null, null, -1 ); |
740 | } | 972 | } |
741 | 973 | ||
742 | // Automatically select non-editable element when navigating into | ||
743 | // it by left/right or backspace/del keys. | ||
744 | editable.attachListener( editable, 'keydown', getOnKeyDownListener( editor ), null, null, -1 ); | 974 | editable.attachListener( editable, 'keydown', getOnKeyDownListener( editor ), null, null, -1 ); |
745 | 975 | ||
746 | function moveRangeToPoint( range, x, y ) { | 976 | function moveRangeToPoint( range, x, y ) { |
747 | // Error prune in IE7. (#9034, #9110) | 977 | // Error prune in IE7. (http://dev.ckeditor.com/ticket/9034, http://dev.ckeditor.com/ticket/9110) |
748 | try { | 978 | try { |
749 | range.moveToPoint( x, y ); | 979 | range.moveToPoint( x, y ); |
750 | } catch ( e ) {} | 980 | } catch ( e ) {} |
@@ -765,14 +995,27 @@ | |||
765 | range = sel.createRange(); | 995 | range = sel.createRange(); |
766 | 996 | ||
767 | // The selection range is reported on host, but actually it should applies to the content doc. | 997 | // The selection range is reported on host, but actually it should applies to the content doc. |
768 | if ( sel.type != 'None' && range.parentElement().ownerDocument == doc.$ ) | 998 | // The parentElement may be null for read only mode in IE10 and below (http://dev.ckeditor.com/ticket/9780). |
999 | if ( sel.type != 'None' && range.parentElement() && range.parentElement().ownerDocument == doc.$ ) | ||
769 | range.select(); | 1000 | range.select(); |
770 | } | 1001 | } |
1002 | |||
1003 | function nonEditableAscendant( sel ) { | ||
1004 | if ( CKEDITOR.env.ie ) { | ||
1005 | var range = sel.getRanges()[ 0 ], | ||
1006 | ascendant = range ? range.startContainer.getAscendant( function( parent ) { | ||
1007 | return parent.type == CKEDITOR.NODE_ELEMENT && | ||
1008 | ( parent.getAttribute( 'contenteditable' ) == 'false' || parent.getAttribute( 'contenteditable' ) == 'true' ); | ||
1009 | }, true ) : null ; | ||
1010 | |||
1011 | return range && ascendant.getAttribute( 'contenteditable' ) == 'false' && ascendant; | ||
1012 | } | ||
1013 | } | ||
771 | } ); | 1014 | } ); |
772 | 1015 | ||
773 | editor.on( 'setData', function() { | 1016 | editor.on( 'setData', function() { |
774 | // Invalidate locked selection when unloading DOM. | 1017 | // Invalidate locked selection when unloading DOM. |
775 | // (#9521, #5217#comment:32 and #11500#comment:11) | 1018 | // (http://dev.ckeditor.com/ticket/9521, http://dev.ckeditor.com/ticket/5217#comment:32 and http://dev.ckeditor.com/ticket/11500#comment:11) |
776 | editor.unlockSelection(); | 1019 | editor.unlockSelection(); |
777 | 1020 | ||
778 | // Webkit's selection will mess up after the data loading. | 1021 | // Webkit's selection will mess up after the data loading. |
@@ -786,7 +1029,7 @@ | |||
786 | editor.unlockSelection(); | 1029 | editor.unlockSelection(); |
787 | } ); | 1030 | } ); |
788 | 1031 | ||
789 | // IE9 might cease to work if there's an object selection inside the iframe (#7639). | 1032 | // IE9 might cease to work if there's an object selection inside the iframe (http://dev.ckeditor.com/ticket/7639). |
790 | if ( CKEDITOR.env.ie9Compat ) | 1033 | if ( CKEDITOR.env.ie9Compat ) |
791 | editor.on( 'beforeDestroy', clearSelection, null, null, 9 ); | 1034 | editor.on( 'beforeDestroy', clearSelection, null, null, 9 ); |
792 | 1035 | ||
@@ -802,7 +1045,7 @@ | |||
802 | // When loaded data are ready check whether hidden selection container was not loaded. | 1045 | // When loaded data are ready check whether hidden selection container was not loaded. |
803 | editor.on( 'loadSnapshot', function() { | 1046 | editor.on( 'loadSnapshot', function() { |
804 | var isElement = CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_ELEMENT ), | 1047 | var isElement = CKEDITOR.dom.walker.nodeType( CKEDITOR.NODE_ELEMENT ), |
805 | // TODO replace with el.find() which will be introduced in #9764, | 1048 | // TODO replace with el.find() which will be introduced in http://dev.ckeditor.com/ticket/9764, |
806 | // because it may happen that hidden sel container won't be the last element. | 1049 | // because it may happen that hidden sel container won't be the last element. |
807 | last = editor.editable().getLast( isElement ); | 1050 | last = editor.editable().getLast( isElement ); |
808 | 1051 | ||
@@ -849,7 +1092,7 @@ | |||
849 | } ); | 1092 | } ); |
850 | 1093 | ||
851 | // On WebKit only, we need a special "filling" char on some situations | 1094 | // On WebKit only, we need a special "filling" char on some situations |
852 | // (#1272). Here we set the events that should invalidate that char. | 1095 | // (http://dev.ckeditor.com/ticket/1272). Here we set the events that should invalidate that char. |
853 | if ( CKEDITOR.env.webkit ) { | 1096 | if ( CKEDITOR.env.webkit ) { |
854 | CKEDITOR.on( 'instanceReady', function( evt ) { | 1097 | CKEDITOR.on( 'instanceReady', function( evt ) { |
855 | var editor = evt.editor; | 1098 | var editor = evt.editor; |
@@ -864,7 +1107,7 @@ | |||
864 | 1107 | ||
865 | // Filter Undo snapshot's HTML to get rid of Filling Char Sequence. | 1108 | // Filter Undo snapshot's HTML to get rid of Filling Char Sequence. |
866 | // Note: CKEDITOR.dom.range.createBookmark2() normalizes snapshot's | 1109 | // Note: CKEDITOR.dom.range.createBookmark2() normalizes snapshot's |
867 | // bookmarks to anticipate the removal of FCSeq from the snapshot's HTML (#13816). | 1110 | // bookmarks to anticipate the removal of FCSeq from the snapshot's HTML (http://dev.ckeditor.com/ticket/13816). |
868 | editor.on( 'getSnapshot', function( evt ) { | 1111 | editor.on( 'getSnapshot', function( evt ) { |
869 | if ( evt.data ) { | 1112 | if ( evt.data ) { |
870 | evt.data = removeFillingCharSequenceString( evt.data ); | 1113 | evt.data = removeFillingCharSequenceString( evt.data ); |
@@ -873,7 +1116,7 @@ | |||
873 | 1116 | ||
874 | // Filter data to get rid of Filling Char Sequence. Filter on #toDataFormat | 1117 | // Filter data to get rid of Filling Char Sequence. Filter on #toDataFormat |
875 | // instead of #getData because once removed, FCSeq may leave an empty element, | 1118 | // instead of #getData because once removed, FCSeq may leave an empty element, |
876 | // which should be pruned by the dataProcessor (#13816). | 1119 | // which should be pruned by the dataProcessor (http://dev.ckeditor.com/ticket/13816). |
877 | // Note: Used low priority to filter when dataProcessor works on strings, | 1120 | // Note: Used low priority to filter when dataProcessor works on strings, |
878 | // not pseudo–DOM. | 1121 | // not pseudo–DOM. |
879 | editor.on( 'toDataFormat', function( evt ) { | 1122 | editor.on( 'toDataFormat', function( evt ) { |
@@ -1039,9 +1282,6 @@ | |||
1039 | */ | 1282 | */ |
1040 | CKEDITOR.SELECTION_ELEMENT = 3; | 1283 | CKEDITOR.SELECTION_ELEMENT = 3; |
1041 | 1284 | ||
1042 | var isMSSelection = typeof window.getSelection != 'function', | ||
1043 | nextRev = 1; | ||
1044 | |||
1045 | /** | 1285 | /** |
1046 | * Manipulates the selection within a DOM element. If the current browser selection | 1286 | * Manipulates the selection within a DOM element. If the current browser selection |
1047 | * spans outside of the element, an empty selection object is returned. | 1287 | * spans outside of the element, an empty selection object is returned. |
@@ -1121,7 +1361,7 @@ | |||
1121 | 1361 | ||
1122 | // Selection out of concerned range, empty the selection. | 1362 | // Selection out of concerned range, empty the selection. |
1123 | // TODO check whether this condition cannot be reverted to its old | 1363 | // TODO check whether this condition cannot be reverted to its old |
1124 | // form (commented out) after we closed #10438. | 1364 | // form (commented out) after we closed http://dev.ckeditor.com/ticket/10438. |
1125 | //if ( !( rangeParent && ( root.equals( rangeParent ) || root.contains( rangeParent ) ) ) ) { | 1365 | //if ( !( rangeParent && ( root.equals( rangeParent ) || root.contains( rangeParent ) ) ) ) { |
1126 | if ( !( | 1366 | if ( !( |
1127 | rangeParent && | 1367 | rangeParent && |
@@ -1142,10 +1382,6 @@ | |||
1142 | var styleObjectElements = { img: 1, hr: 1, li: 1, table: 1, tr: 1, td: 1, th: 1, embed: 1, object: 1, ol: 1, ul: 1, | 1382 | var styleObjectElements = { img: 1, hr: 1, li: 1, table: 1, tr: 1, td: 1, th: 1, embed: 1, object: 1, ol: 1, ul: 1, |
1143 | a: 1, input: 1, form: 1, select: 1, textarea: 1, button: 1, fieldset: 1, thead: 1, tfoot: 1 }; | 1383 | a: 1, input: 1, form: 1, select: 1, textarea: 1, button: 1, fieldset: 1, thead: 1, tfoot: 1 }; |
1144 | 1384 | ||
1145 | // #13816 | ||
1146 | var fillingCharSequence = CKEDITOR.tools.repeat( '\u200b', 7 ), | ||
1147 | fillingCharSequenceRegExp = new RegExp( fillingCharSequence + '( )?', 'g' ); | ||
1148 | |||
1149 | CKEDITOR.tools.extend( CKEDITOR.dom.selection, { | 1385 | CKEDITOR.tools.extend( CKEDITOR.dom.selection, { |
1150 | _removeFillingCharSequenceString: removeFillingCharSequenceString, | 1386 | _removeFillingCharSequenceString: removeFillingCharSequenceString, |
1151 | _createFillingCharSequenceNode: createFillingCharSequenceNode, | 1387 | _createFillingCharSequenceNode: createFillingCharSequenceNode, |
@@ -1167,7 +1403,7 @@ | |||
1167 | * | 1403 | * |
1168 | * var selection = editor.getSelection().getNative(); | 1404 | * var selection = editor.getSelection().getNative(); |
1169 | * | 1405 | * |
1170 | * @returns {Object} The native browser selection object. | 1406 | * @returns {Object} The native browser selection object or null if this is a fake selection. |
1171 | */ | 1407 | */ |
1172 | getNative: function() { | 1408 | getNative: function() { |
1173 | if ( this._.cache.nativeSel !== undefined ) | 1409 | if ( this._.cache.nativeSel !== undefined ) |
@@ -1259,7 +1495,7 @@ | |||
1259 | * alert( ranges.length ); | 1495 | * alert( ranges.length ); |
1260 | * | 1496 | * |
1261 | * @method | 1497 | * @method |
1262 | * @param {Boolean} [onlyEditables] If set to `true`, this function retrives editable ranges only. | 1498 | * @param {Boolean} [onlyEditables] If set to `true`, this function retrieves editable ranges only. |
1263 | * @returns {Array} Range instances that represent the current selection. | 1499 | * @returns {Array} Range instances that represent the current selection. |
1264 | */ | 1500 | */ |
1265 | getRanges: ( function() { | 1501 | getRanges: ( function() { |
@@ -1290,7 +1526,7 @@ | |||
1290 | index = -1, | 1526 | index = -1, |
1291 | position, distance, container; | 1527 | position, distance, container; |
1292 | 1528 | ||
1293 | // Binary search over all element childs to test the range to see whether | 1529 | // Binary search over all element children to test the range to see whether |
1294 | // range is right on the boundary of one element. | 1530 | // range is right on the boundary of one element. |
1295 | while ( startIndex <= endIndex ) { | 1531 | while ( startIndex <= endIndex ) { |
1296 | index = Math.floor( ( startIndex + endIndex ) / 2 ); | 1532 | index = Math.floor( ( startIndex + endIndex ) / 2 ); |
@@ -1306,8 +1542,8 @@ | |||
1306 | return { container: parent, offset: getNodeIndex( child ) }; | 1542 | return { container: parent, offset: getNodeIndex( child ) }; |
1307 | } | 1543 | } |
1308 | 1544 | ||
1309 | // All childs are text nodes, | 1545 | // All children are text nodes, |
1310 | // or to the right hand of test range are all text nodes. (#6992) | 1546 | // or to the right hand of test range are all text nodes. (http://dev.ckeditor.com/ticket/6992) |
1311 | if ( index == -1 || index == siblings.length - 1 && position < 0 ) { | 1547 | if ( index == -1 || index == siblings.length - 1 && position < 0 ) { |
1312 | // Adapt test range to embrace the entire parent contents. | 1548 | // Adapt test range to embrace the entire parent contents. |
1313 | testRange.moveToElementText( parent ); | 1549 | testRange.moveToElementText( parent ); |
@@ -1315,7 +1551,7 @@ | |||
1315 | 1551 | ||
1316 | // IE report line break as CRLF with range.text but | 1552 | // IE report line break as CRLF with range.text but |
1317 | // only LF with textnode.nodeValue, normalize them to avoid | 1553 | // only LF with textnode.nodeValue, normalize them to avoid |
1318 | // breaking character counting logic below. (#3949) | 1554 | // breaking character counting logic below. (http://dev.ckeditor.com/ticket/3949) |
1319 | distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; | 1555 | distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; |
1320 | 1556 | ||
1321 | siblings = parent.childNodes; | 1557 | siblings = parent.childNodes; |
@@ -1351,7 +1587,7 @@ | |||
1351 | 1587 | ||
1352 | // IE report line break as CRLF with range.text but | 1588 | // IE report line break as CRLF with range.text but |
1353 | // only LF with textnode.nodeValue, normalize them to avoid | 1589 | // only LF with textnode.nodeValue, normalize them to avoid |
1354 | // breaking character counting logic below. (#3949) | 1590 | // breaking character counting logic below. (http://dev.ckeditor.com/ticket/3949) |
1355 | distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; | 1591 | distance = testRange.text.replace( /(\r\n|\r)/g, '\n' ).length; |
1356 | 1592 | ||
1357 | // Actual range anchor right beside test range at the inner boundary of text node. | 1593 | // Actual range anchor right beside test range at the inner boundary of text node. |
@@ -1368,7 +1604,7 @@ | |||
1368 | } | 1604 | } |
1369 | child = sibling; | 1605 | child = sibling; |
1370 | } | 1606 | } |
1371 | // Measurement in IE could be somtimes wrong because of <select> element. (#4611) | 1607 | // Measurement in IE could be sometimes wrong because of <select> element. (http://dev.ckeditor.com/ticket/4611) |
1372 | catch ( e ) { | 1608 | catch ( e ) { |
1373 | return { container: parent, offset: getNodeIndex( child ) }; | 1609 | return { container: parent, offset: getNodeIndex( child ) }; |
1374 | } | 1610 | } |
@@ -1400,7 +1636,7 @@ | |||
1400 | boundaryInfo = getBoundaryInformation( nativeRange ); | 1636 | boundaryInfo = getBoundaryInformation( nativeRange ); |
1401 | range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); | 1637 | range.setEnd( new CKEDITOR.dom.node( boundaryInfo.container ), boundaryInfo.offset ); |
1402 | 1638 | ||
1403 | // Correct an invalid IE range case on empty list item. (#5850) | 1639 | // Correct an invalid IE range case on empty list item. (http://dev.ckeditor.com/ticket/5850) |
1404 | if ( range.endContainer.getPosition( range.startContainer ) & CKEDITOR.POSITION_PRECEDING && range.endOffset <= range.startContainer.getIndex() ) | 1640 | if ( range.endContainer.getPosition( range.startContainer ) & CKEDITOR.POSITION_PRECEDING && range.endOffset <= range.startContainer.getIndex() ) |
1405 | range.collapse(); | 1641 | range.collapse(); |
1406 | 1642 | ||
@@ -1432,7 +1668,7 @@ | |||
1432 | } )() : | 1668 | } )() : |
1433 | function() { | 1669 | function() { |
1434 | // On browsers implementing the W3C range, we simply | 1670 | // On browsers implementing the W3C range, we simply |
1435 | // tranform the native ranges in CKEDITOR.dom.range | 1671 | // transform the native ranges in CKEDITOR.dom.range |
1436 | // instances. | 1672 | // instances. |
1437 | 1673 | ||
1438 | var ranges = [], | 1674 | var ranges = [], |
@@ -1465,7 +1701,7 @@ | |||
1465 | return ranges; | 1701 | return ranges; |
1466 | 1702 | ||
1467 | // Split range into multiple by read-only nodes. | 1703 | // Split range into multiple by read-only nodes. |
1468 | // Clone ranges array to avoid changing cached ranges (#11493). | 1704 | // Clone ranges array to avoid changing cached ranges (http://dev.ckeditor.com/ticket/11493). |
1469 | return extractEditableRanges( new CKEDITOR.dom.rangeList( ranges.slice() ) ); | 1705 | return extractEditableRanges( new CKEDITOR.dom.rangeList( ranges.slice() ) ); |
1470 | }; | 1706 | }; |
1471 | } )(), | 1707 | } )(), |
@@ -1499,11 +1735,11 @@ | |||
1499 | 1735 | ||
1500 | // Decrease the range content to exclude particial | 1736 | // Decrease the range content to exclude particial |
1501 | // selected node on the start which doesn't have | 1737 | // selected node on the start which doesn't have |
1502 | // visual impact. ( #3231 ) | 1738 | // visual impact. ( http://dev.ckeditor.com/ticket/3231 ) |
1503 | while ( 1 ) { | 1739 | while ( 1 ) { |
1504 | var startContainer = range.startContainer, | 1740 | var startContainer = range.startContainer, |
1505 | startOffset = range.startOffset; | 1741 | startOffset = range.startOffset; |
1506 | // Limit the fix only to non-block elements.(#3950) | 1742 | // Limit the fix only to non-block elements.(http://dev.ckeditor.com/ticket/3950) |
1507 | if ( startOffset == ( startContainer.getChildCount ? startContainer.getChildCount() : startContainer.getLength() ) && !startContainer.isBlockBoundary() ) | 1743 | if ( startOffset == ( startContainer.getChildCount ? startContainer.getChildCount() : startContainer.getLength() ) && !startContainer.isBlockBoundary() ) |
1508 | range.setStartAfter( startContainer ); | 1744 | range.setStartAfter( startContainer ); |
1509 | else | 1745 | else |
@@ -1545,7 +1781,7 @@ | |||
1545 | * var element = editor.getSelection().getSelectedElement(); | 1781 | * var element = editor.getSelection().getSelectedElement(); |
1546 | * alert( element.getName() ); | 1782 | * alert( element.getName() ); |
1547 | * | 1783 | * |
1548 | * @returns {CKEDITOR.dom.element} The selected element. Null if no | 1784 | * @returns {CKEDITOR.dom.element/null} The selected element. `null` if no |
1549 | * selection is available or the selection type is not {@link CKEDITOR#SELECTION_ELEMENT}. | 1785 | * selection is available or the selection type is not {@link CKEDITOR#SELECTION_ELEMENT}. |
1550 | */ | 1786 | */ |
1551 | getSelectedElement: function() { | 1787 | getSelectedElement: function() { |
@@ -1628,7 +1864,7 @@ | |||
1628 | 1864 | ||
1629 | if ( restore ) { | 1865 | if ( restore ) { |
1630 | var selectedElement = this.getSelectedElement(), | 1866 | var selectedElement = this.getSelectedElement(), |
1631 | ranges = !selectedElement && this.getRanges(), | 1867 | ranges = this.getRanges(), |
1632 | faked = this.isFake; | 1868 | faked = this.isFake; |
1633 | } | 1869 | } |
1634 | 1870 | ||
@@ -1642,7 +1878,10 @@ | |||
1642 | if ( !( common && common.getAscendant( 'body', 1 ) ) ) | 1878 | if ( !( common && common.getAscendant( 'body', 1 ) ) ) |
1643 | return; | 1879 | return; |
1644 | 1880 | ||
1645 | if ( faked ) | 1881 | if ( isTableSelection( ranges ) ) { |
1882 | // Tables have it's own selection method. | ||
1883 | performFakeTableSelection.call( this, ranges ); | ||
1884 | } else if ( faked ) | ||
1646 | this.fake( selectedElement ); | 1885 | this.fake( selectedElement ); |
1647 | else if ( selectedElement ) | 1886 | else if ( selectedElement ) |
1648 | this.selectElement( selectedElement ); | 1887 | this.selectElement( selectedElement ); |
@@ -1714,7 +1953,7 @@ | |||
1714 | // Check if there's a hiddenSelectionContainer in editable at some index. | 1953 | // Check if there's a hiddenSelectionContainer in editable at some index. |
1715 | // Some ranges may be anchored after the hiddenSelectionContainer and, | 1954 | // Some ranges may be anchored after the hiddenSelectionContainer and, |
1716 | // once the container is removed while resetting the selection, they | 1955 | // once the container is removed while resetting the selection, they |
1717 | // may need new endOffset (one element less within the range) (#11021 #11393). | 1956 | // may need new endOffset (one element less within the range) (http://dev.ckeditor.com/ticket/11021 http://dev.ckeditor.com/ticket/11393). |
1718 | if ( hadHiddenSelectionContainer ) | 1957 | if ( hadHiddenSelectionContainer ) |
1719 | fixRangesAfterHiddenSelectionContainer( ranges, this.root ); | 1958 | fixRangesAfterHiddenSelectionContainer( ranges, this.root ); |
1720 | 1959 | ||
@@ -1742,6 +1981,15 @@ | |||
1742 | return; | 1981 | return; |
1743 | } | 1982 | } |
1744 | 1983 | ||
1984 | // Handle special case - fake selection of table cells. | ||
1985 | if ( editor && editor.plugins.tableselection && | ||
1986 | CKEDITOR.plugins.tableselection.isSupportedEnvironment && | ||
1987 | isTableSelection( ranges ) && !isSelectingTable | ||
1988 | ) { | ||
1989 | performFakeTableSelection.call( this, ranges ); | ||
1990 | return; | ||
1991 | } | ||
1992 | |||
1745 | if ( isMSSelection ) { | 1993 | if ( isMSSelection ) { |
1746 | var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ), | 1994 | var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true ), |
1747 | fillerTextRegex = /\ufeff|\u00a0/, | 1995 | fillerTextRegex = /\ufeff|\u00a0/, |
@@ -1775,7 +2023,7 @@ | |||
1775 | if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT && range.startContainer.getName() in nonCells || | 2023 | if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT && range.startContainer.getName() in nonCells || |
1776 | range.endContainer.type == CKEDITOR.NODE_ELEMENT && range.endContainer.getName() in nonCells ) { | 2024 | range.endContainer.type == CKEDITOR.NODE_ELEMENT && range.endContainer.getName() in nonCells ) { |
1777 | range.shrink( CKEDITOR.NODE_ELEMENT, true ); | 2025 | range.shrink( CKEDITOR.NODE_ELEMENT, true ); |
1778 | // The range might get collapsed (#7975). Update cached variable. | 2026 | // The range might get collapsed (http://dev.ckeditor.com/ticket/7975). Update cached variable. |
1779 | collapsed = range.collapsed; | 2027 | collapsed = range.collapsed; |
1780 | } | 2028 | } |
1781 | 2029 | ||
@@ -1817,18 +2065,18 @@ | |||
1817 | 2065 | ||
1818 | // Append a temporary <span></span> before the selection. | 2066 | // Append a temporary <span></span> before the selection. |
1819 | // This is needed to avoid IE destroying selections inside empty | 2067 | // This is needed to avoid IE destroying selections inside empty |
1820 | // inline elements, like <b></b> (#253). | 2068 | // inline elements, like <b></b> (http://dev.ckeditor.com/ticket/253). |
1821 | // It is also needed when placing the selection right after an inline | 2069 | // It is also needed when placing the selection right after an inline |
1822 | // element to avoid the selection moving inside of it. | 2070 | // element to avoid the selection moving inside of it. |
1823 | dummySpan = range.document.createElement( 'span' ); | 2071 | dummySpan = range.document.createElement( 'span' ); |
1824 | dummySpan.setHtml( '' ); // Zero Width No-Break Space (U+FEFF). See #1359. | 2072 | dummySpan.setHtml( '' ); // Zero Width No-Break Space (U+FEFF). See http://dev.ckeditor.com/ticket/1359. |
1825 | dummySpan.insertBefore( startNode ); | 2073 | dummySpan.insertBefore( startNode ); |
1826 | 2074 | ||
1827 | if ( isStartMarkerAlone ) { | 2075 | if ( isStartMarkerAlone ) { |
1828 | // To expand empty blocks or line spaces after <br>, we need | 2076 | // To expand empty blocks or line spaces after <br>, we need |
1829 | // instead to have any char, which will be later deleted using the | 2077 | // instead to have any char, which will be later deleted using the |
1830 | // selection. | 2078 | // selection. |
1831 | // \ufeff = Zero Width No-Break Space (U+FEFF). (#1359) | 2079 | // \ufeff = Zero Width No-Break Space (U+FEFF). (http://dev.ckeditor.com/ticket/1359) |
1832 | range.document.createText( '\ufeff' ).insertBefore( startNode ); | 2080 | range.document.createText( '\ufeff' ).insertBefore( startNode ); |
1833 | } | 2081 | } |
1834 | } | 2082 | } |
@@ -1860,7 +2108,7 @@ | |||
1860 | } else { | 2108 | } else { |
1861 | var sel = this.getNative(); | 2109 | var sel = this.getNative(); |
1862 | 2110 | ||
1863 | // getNative() returns null if iframe is "display:none" in FF. (#6577) | 2111 | // getNative() returns null if iframe is "display:none" in FF. (http://dev.ckeditor.com/ticket/6577) |
1864 | if ( !sel ) | 2112 | if ( !sel ) |
1865 | return; | 2113 | return; |
1866 | 2114 | ||
@@ -1876,7 +2124,7 @@ | |||
1876 | between.setStart( left.endContainer, left.endOffset ); | 2124 | between.setStart( left.endContainer, left.endOffset ); |
1877 | between.setEnd( right.startContainer, right.startOffset ); | 2125 | between.setEnd( right.startContainer, right.startOffset ); |
1878 | 2126 | ||
1879 | // Don't confused by Firefox adjancent multi-ranges | 2127 | // Don't confused by Firefox adjacent multi-ranges |
1880 | // introduced by table cells selection. | 2128 | // introduced by table cells selection. |
1881 | if ( !between.collapsed ) { | 2129 | if ( !between.collapsed ) { |
1882 | between.shrink( CKEDITOR.NODE_ELEMENT, true ); | 2130 | between.shrink( CKEDITOR.NODE_ELEMENT, true ); |
@@ -1885,7 +2133,7 @@ | |||
1885 | 2133 | ||
1886 | // The following cases has to be considered: | 2134 | // The following cases has to be considered: |
1887 | // 1. <span contenteditable="false">[placeholder]</span> | 2135 | // 1. <span contenteditable="false">[placeholder]</span> |
1888 | // 2. <input contenteditable="false" type="radio"/> (#6621) | 2136 | // 2. <input contenteditable="false" type="radio"/> (http://dev.ckeditor.com/ticket/6621) |
1889 | if ( ancestor.isReadOnly() || enclosed && enclosed.isReadOnly() ) { | 2137 | if ( ancestor.isReadOnly() || enclosed && enclosed.isReadOnly() ) { |
1890 | right.setStart( left.startContainer, left.startOffset ); | 2138 | right.setStart( left.startContainer, left.startOffset ); |
1891 | ranges.splice( i--, 1 ); | 2139 | ranges.splice( i--, 1 ); |
@@ -1900,7 +2148,7 @@ | |||
1900 | 2148 | ||
1901 | if ( range.collapsed && CKEDITOR.env.webkit && rangeRequiresFix( range ) ) { | 2149 | if ( range.collapsed && CKEDITOR.env.webkit && rangeRequiresFix( range ) ) { |
1902 | // Append a zero-width space so WebKit will not try to | 2150 | // Append a zero-width space so WebKit will not try to |
1903 | // move the selection by itself (#1272). | 2151 | // move the selection by itself (http://dev.ckeditor.com/ticket/1272). |
1904 | var fillingChar = createFillingCharSequenceNode( this.root ); | 2152 | var fillingChar = createFillingCharSequenceNode( this.root ); |
1905 | range.insertNode( fillingChar ); | 2153 | range.insertNode( fillingChar ); |
1906 | 2154 | ||
@@ -1954,14 +2202,20 @@ | |||
1954 | * displayed to the user. | 2202 | * displayed to the user. |
1955 | * | 2203 | * |
1956 | * @param {CKEDITOR.dom.element} element The element to be "selected". | 2204 | * @param {CKEDITOR.dom.element} element The element to be "selected". |
2205 | * @param {String} [ariaLabel] A string to be used by the screen reader to describe the selection. | ||
1957 | */ | 2206 | */ |
1958 | fake: function( element ) { | 2207 | fake: function( element, ariaLabel ) { |
1959 | var editor = this.root.editor; | 2208 | var editor = this.root.editor; |
1960 | 2209 | ||
2210 | // Attempt to retrieve aria-label if possible (http://dev.ckeditor.com/ticket/14539). | ||
2211 | if ( ariaLabel === undefined && element.hasAttribute( 'aria-label' ) ) { | ||
2212 | ariaLabel = element.getAttribute( 'aria-label' ); | ||
2213 | } | ||
2214 | |||
1961 | // Cleanup after previous selection - e.g. remove hidden sel container. | 2215 | // Cleanup after previous selection - e.g. remove hidden sel container. |
1962 | this.reset(); | 2216 | this.reset(); |
1963 | 2217 | ||
1964 | hideSelection( editor ); | 2218 | hideSelection( editor, ariaLabel ); |
1965 | 2219 | ||
1966 | // Set this value after executing hiseSelection, because it may | 2220 | // Set this value after executing hiseSelection, because it may |
1967 | // cause reset() which overwrites cache. | 2221 | // cause reset() which overwrites cache. |
@@ -2012,6 +2266,38 @@ | |||
2012 | }, | 2266 | }, |
2013 | 2267 | ||
2014 | /** | 2268 | /** |
2269 | * Checks if the selection contains an HTML element inside a table. | ||
2270 | * Returns `false` for text selection inside a table (e.g. it will return `false` | ||
2271 | * for text selected in one cell). | ||
2272 | * | ||
2273 | * editor.getSelection().isInTable(); | ||
2274 | * | ||
2275 | * @since 4.7.0 | ||
2276 | * @param {Boolean} [allowPartialSelection=false] Whether a partial cell selection should be included. | ||
2277 | * Added in 4.7.2. | ||
2278 | * @returns {Boolean} | ||
2279 | */ | ||
2280 | isInTable: function( allowPartialSelection ) { | ||
2281 | return isTableSelection( this.getRanges(), allowPartialSelection ); | ||
2282 | }, | ||
2283 | |||
2284 | /** | ||
2285 | * Checks if the selection contains only one range which is collapsed. | ||
2286 | * | ||
2287 | * if ( editor.getSelection().isCollapsed() ) { | ||
2288 | * // Do something when the selection is collapsed. | ||
2289 | * } | ||
2290 | * | ||
2291 | * @since 4.7.3 | ||
2292 | * @returns {Boolean} | ||
2293 | */ | ||
2294 | isCollapsed: function() { | ||
2295 | var ranges = this.getRanges(); | ||
2296 | |||
2297 | return ranges.length === 1 && ranges[ 0 ].collapsed; | ||
2298 | }, | ||
2299 | |||
2300 | /** | ||
2015 | * Creates a bookmark for each range of this selection (from {@link #getRanges}) | 2301 | * Creates a bookmark for each range of this selection (from {@link #getRanges}) |
2016 | * by calling the {@link CKEDITOR.dom.range#createBookmark} method, | 2302 | * by calling the {@link CKEDITOR.dom.range#createBookmark} method, |
2017 | * with extra care taken to avoid interference among those ranges. The arguments | 2303 | * with extra care taken to avoid interference among those ranges. The arguments |
@@ -2064,17 +2350,19 @@ | |||
2064 | 2350 | ||
2065 | // It may happen that the content change during loading, before selection is set so bookmark leads to text node. | 2351 | // It may happen that the content change during loading, before selection is set so bookmark leads to text node. |
2066 | if ( bookmarks.isFake ) { | 2352 | if ( bookmarks.isFake ) { |
2067 | node = ranges[ 0 ].getEnclosedNode(); | 2353 | node = isTableSelection( ranges ) ? ranges[ 0 ]._getTableElement() : ranges[ 0 ].getEnclosedNode(); |
2354 | |||
2068 | if ( !node || node.type != CKEDITOR.NODE_ELEMENT ) { | 2355 | if ( !node || node.type != CKEDITOR.NODE_ELEMENT ) { |
2069 | CKEDITOR.warn( 'selection-not-fake' ); | 2356 | CKEDITOR.warn( 'selection-not-fake' ); |
2070 | bookmarks.isFake = 0; | 2357 | bookmarks.isFake = 0; |
2071 | } | 2358 | } |
2072 | } | 2359 | } |
2073 | 2360 | ||
2074 | if ( bookmarks.isFake ) | 2361 | if ( bookmarks.isFake && !isTableSelection( ranges ) ) { |
2075 | this.fake( node ); | 2362 | this.fake( node ); |
2076 | else | 2363 | } else { |
2077 | this.selectRanges( ranges ); | 2364 | this.selectRanges( ranges ); |
2365 | } | ||
2078 | 2366 | ||
2079 | return this; | 2367 | return this; |
2080 | }, | 2368 | }, |
@@ -2111,7 +2399,7 @@ | |||
2111 | * Remove all the selection ranges from the document. | 2399 | * Remove all the selection ranges from the document. |
2112 | */ | 2400 | */ |
2113 | removeAllRanges: function() { | 2401 | removeAllRanges: function() { |
2114 | // Don't clear selection outside this selection's root (#11500). | 2402 | // Don't clear selection outside this selection's root (http://dev.ckeditor.com/ticket/11500). |
2115 | if ( this.getType() == CKEDITOR.SELECTION_NONE ) | 2403 | if ( this.getType() == CKEDITOR.SELECTION_NONE ) |
2116 | return; | 2404 | return; |
2117 | 2405 | ||
diff --git a/sources/core/skin.js b/sources/core/skin.js index 98b8536..290157d 100644 --- a/sources/core/skin.js +++ b/sources/core/skin.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -125,7 +125,7 @@ | |||
125 | offset = overrideOffset || ( icon && icon.offset ); | 125 | offset = overrideOffset || ( icon && icon.offset ); |
126 | bgsize = overrideBgsize || ( icon && icon.bgsize ) || '16px'; | 126 | bgsize = overrideBgsize || ( icon && icon.bgsize ) || '16px'; |
127 | 127 | ||
128 | // If we use apostrophes in background-image, we must escape apostrophes in path (just to be sure). (#13361) | 128 | // If we use apostrophes in background-image, we must escape apostrophes in path (just to be sure). (http://dev.ckeditor.com/ticket/13361) |
129 | if ( path ) | 129 | if ( path ) |
130 | path = path.replace( /'/g, '\\\'' ); | 130 | path = path.replace( /'/g, '\\\'' ); |
131 | 131 | ||
diff --git a/sources/core/style.js b/sources/core/style.js index 09b117b..b3cf0bc 100644 --- a/sources/core/style.js +++ b/sources/core/style.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -235,7 +235,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
235 | var initialEnterMode = this._.enterMode; | 235 | var initialEnterMode = this._.enterMode; |
236 | 236 | ||
237 | // Before CKEditor 4.4 style knew nothing about editor, so in order to provide enterMode | 237 | // Before CKEditor 4.4 style knew nothing about editor, so in order to provide enterMode |
238 | // which should be used developers were forced to hack the style object (see #10190). | 238 | // which should be used developers were forced to hack the style object (see http://dev.ckeditor.com/ticket/10190). |
239 | // Since CKEditor 4.4 style knows about editor (at least when it's being applied/removed), but we | 239 | // Since CKEditor 4.4 style knows about editor (at least when it's being applied/removed), but we |
240 | // use _.enterMode for backward compatibility with those hacks. | 240 | // use _.enterMode for backward compatibility with those hacks. |
241 | // Note: we should not change style's enter mode if it was already set. | 241 | // Note: we should not change style's enter mode if it was already set. |
@@ -569,7 +569,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
569 | var styleVal = stylesDef[ style ], | 569 | var styleVal = stylesDef[ style ], |
570 | text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' ); | 570 | text = ( style + ':' + styleVal ).replace( semicolonFixRegex, ';' ); |
571 | 571 | ||
572 | // Some browsers don't support 'inherit' property value, leave them intact. (#5242) | 572 | // Some browsers don't support 'inherit' property value, leave them intact. (http://dev.ckeditor.com/ticket/5242) |
573 | if ( styleVal == 'inherit' ) | 573 | if ( styleVal == 'inherit' ) |
574 | specialStylesText += text; | 574 | specialStylesText += text; |
575 | else | 575 | else |
@@ -1024,7 +1024,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1024 | if ( !CKEDITOR.env.ie ) | 1024 | if ( !CKEDITOR.env.ie ) |
1025 | styleNode.$.normalize(); | 1025 | styleNode.$.normalize(); |
1026 | } | 1026 | } |
1027 | // Style already inherit from parents, left just to clear up any internal overrides. (#5931) | 1027 | // Style already inherit from parents, left just to clear up any internal overrides. (http://dev.ckeditor.com/ticket/5931) |
1028 | else { | 1028 | else { |
1029 | styleNode = new CKEDITOR.dom.element( 'span' ); | 1029 | styleNode = new CKEDITOR.dom.element( 'span' ); |
1030 | styleRange.extractContents().appendTo( styleNode ); | 1030 | styleRange.extractContents().appendTo( styleNode ); |
@@ -1042,7 +1042,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1042 | // Remove the bookmark nodes. | 1042 | // Remove the bookmark nodes. |
1043 | range.moveToBookmark( boundaryNodes ); | 1043 | range.moveToBookmark( boundaryNodes ); |
1044 | 1044 | ||
1045 | // Minimize the result range to exclude empty text nodes. (#5374) | 1045 | // Minimize the result range to exclude empty text nodes. (http://dev.ckeditor.com/ticket/5374) |
1046 | range.shrink( CKEDITOR.SHRINK_TEXT ); | 1046 | range.shrink( CKEDITOR.SHRINK_TEXT ); |
1047 | 1047 | ||
1048 | // Get inside the remaining element if range.shrink( TEXT ) has failed because of non-editable elements inside. | 1048 | // Get inside the remaining element if range.shrink( TEXT ) has failed because of non-editable elements inside. |
@@ -1058,27 +1058,31 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1058 | range.enlarge( CKEDITOR.ENLARGE_INLINE, 1 ); | 1058 | range.enlarge( CKEDITOR.ENLARGE_INLINE, 1 ); |
1059 | 1059 | ||
1060 | var bookmark = range.createBookmark(), | 1060 | var bookmark = range.createBookmark(), |
1061 | startNode = bookmark.startNode; | 1061 | startNode = bookmark.startNode, |
1062 | alwaysRemoveElement = this._.definition.alwaysRemoveElement; | ||
1062 | 1063 | ||
1063 | if ( range.collapsed ) { | 1064 | if ( range.collapsed ) { |
1064 | var startPath = new CKEDITOR.dom.elementPath( startNode.getParent(), range.root ), | 1065 | var startPath = new CKEDITOR.dom.elementPath( startNode.getParent(), range.root ), |
1065 | // The topmost element in elementspatch which we should jump out of. | 1066 | // The topmost element in elements path which we should jump out of. |
1066 | boundaryElement; | 1067 | boundaryElement; |
1067 | 1068 | ||
1068 | |||
1069 | for ( var i = 0, element; i < startPath.elements.length && ( element = startPath.elements[ i ] ); i++ ) { | 1069 | for ( var i = 0, element; i < startPath.elements.length && ( element = startPath.elements[ i ] ); i++ ) { |
1070 | // 1. If it's collaped inside text nodes, try to remove the style from the whole element. | 1070 | // 1. If it's collaped inside text nodes, try to remove the style from the whole element. |
1071 | // | 1071 | // |
1072 | // 2. Otherwise if it's collapsed on element boundaries, moving the selection | 1072 | // 2. Otherwise if it's collapsed on element boundaries, moving the selection |
1073 | // outside the styles instead of removing the whole tag, | 1073 | // outside the styles instead of removing the whole tag, |
1074 | // also make sure other inner styles were well preserverd.(#3309) | 1074 | // also make sure other inner styles were well preserved.(http://dev.ckeditor.com/ticket/3309) |
1075 | if ( element == startPath.block || element == startPath.blockLimit ) | 1075 | // |
1076 | // 3. Force removing the element even if it's an boundary element when alwaysRemoveElement is true. | ||
1077 | // Without it, the links won't be unlinked if the cursor is placed right before/after it. (http://dev.ckeditor.com/ticket/13062) | ||
1078 | if ( element == startPath.block || element == startPath.blockLimit ) { | ||
1076 | break; | 1079 | break; |
1080 | } | ||
1077 | 1081 | ||
1078 | if ( this.checkElementRemovable( element ) ) { | 1082 | if ( this.checkElementRemovable( element ) ) { |
1079 | var isStart; | 1083 | var isStart; |
1080 | 1084 | ||
1081 | if ( range.collapsed && ( range.checkBoundaryOfElement( element, CKEDITOR.END ) || ( isStart = range.checkBoundaryOfElement( element, CKEDITOR.START ) ) ) ) { | 1085 | if ( !alwaysRemoveElement && range.collapsed && ( range.checkBoundaryOfElement( element, CKEDITOR.END ) || ( isStart = range.checkBoundaryOfElement( element, CKEDITOR.START ) ) ) ) { |
1082 | boundaryElement = element; | 1086 | boundaryElement = element; |
1083 | boundaryElement.match = isStart ? 'start' : 'end'; | 1087 | boundaryElement.match = isStart ? 'start' : 'end'; |
1084 | } else { | 1088 | } else { |
@@ -1087,10 +1091,11 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1087 | // no difference that they're separate entities in the DOM tree. So, merge | 1091 | // no difference that they're separate entities in the DOM tree. So, merge |
1088 | // them before removal. | 1092 | // them before removal. |
1089 | element.mergeSiblings(); | 1093 | element.mergeSiblings(); |
1090 | if ( element.is( this.element ) ) | 1094 | if ( element.is( this.element ) ) { |
1091 | removeFromElement.call( this, element ); | 1095 | removeFromElement.call( this, element ); |
1092 | else | 1096 | } else { |
1093 | removeOverrides( element, getOverrides( this )[ element.getName() ] ); | 1097 | removeOverrides( element, getOverrides( this )[ element.getName() ] ); |
1098 | } | ||
1094 | } | 1099 | } |
1095 | } | 1100 | } |
1096 | } | 1101 | } |
@@ -1235,7 +1240,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1235 | } | 1240 | } |
1236 | 1241 | ||
1237 | function applyObjectStyle( range ) { | 1242 | function applyObjectStyle( range ) { |
1238 | // Selected or parent element. (#9651) | 1243 | // Selected or parent element. (http://dev.ckeditor.com/ticket/9651) |
1239 | var start = range.getEnclosedNode() || range.getCommonAncestor( false, true ), | 1244 | var start = range.getEnclosedNode() || range.getCommonAncestor( false, true ), |
1240 | element = new CKEDITOR.dom.elementPath( start, range.root ).contains( this.element, 1 ); | 1245 | element = new CKEDITOR.dom.elementPath( start, range.root ).contains( this.element, 1 ); |
1241 | 1246 | ||
@@ -1276,7 +1281,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1276 | var iterator = range.createIterator(); | 1281 | var iterator = range.createIterator(); |
1277 | iterator.enforceRealBlocks = true; | 1282 | iterator.enforceRealBlocks = true; |
1278 | 1283 | ||
1279 | // make recognize <br /> tag as a separator in ENTER_BR mode (#5121) | 1284 | // make recognize <br /> tag as a separator in ENTER_BR mode (http://dev.ckeditor.com/ticket/5121) |
1280 | if ( this._.enterMode ) | 1285 | if ( this._.enterMode ) |
1281 | iterator.enlargeBr = ( this._.enterMode != CKEDITOR.ENTER_BR ); | 1286 | iterator.enlargeBr = ( this._.enterMode != CKEDITOR.ENTER_BR ); |
1282 | 1287 | ||
@@ -1326,7 +1331,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1326 | 1331 | ||
1327 | // Replace the original block with new one, with special treatment | 1332 | // Replace the original block with new one, with special treatment |
1328 | // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent | 1333 | // for <pre> blocks to make sure content format is well preserved, and merging/splitting adjacent |
1329 | // when necessary. (#3188) | 1334 | // when necessary. (http://dev.ckeditor.com/ticket/3188) |
1330 | function replaceBlock( block, newBlock ) { | 1335 | function replaceBlock( block, newBlock ) { |
1331 | // Block is to be removed, create a temp element to | 1336 | // Block is to be removed, create a temp element to |
1332 | // save contents. | 1337 | // save contents. |
@@ -1502,11 +1507,11 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1502 | 1507 | ||
1503 | // Remove definition attributes/style from the elemnt. | 1508 | // Remove definition attributes/style from the elemnt. |
1504 | for ( var attName in attributes ) { | 1509 | for ( var attName in attributes ) { |
1505 | // The 'class' element value must match (#1318). | 1510 | // The 'class' element value must match (http://dev.ckeditor.com/ticket/1318). |
1506 | if ( ( attName == 'class' || this._.definition.fullMatch ) && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) ) | 1511 | if ( ( attName == 'class' || this._.definition.fullMatch ) && element.getAttribute( attName ) != normalizeProperty( attName, attributes[ attName ] ) ) |
1507 | continue; | 1512 | continue; |
1508 | 1513 | ||
1509 | // Do not touch data-* attributes (#11011) (#11258). | 1514 | // Do not touch data-* attributes (http://dev.ckeditor.com/ticket/11011) (http://dev.ckeditor.com/ticket/11258). |
1510 | if ( keepDataAttrs && attName.slice( 0, 5 ) == 'data-' ) | 1515 | if ( keepDataAttrs && attName.slice( 0, 5 ) == 'data-' ) |
1511 | continue; | 1516 | continue; |
1512 | 1517 | ||
@@ -1515,7 +1520,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1515 | } | 1520 | } |
1516 | 1521 | ||
1517 | for ( var styleName in styles ) { | 1522 | for ( var styleName in styles ) { |
1518 | // Full match style insist on having fully equivalence. (#5018) | 1523 | // Full match style insist on having fully equivalence. (http://dev.ckeditor.com/ticket/5018) |
1519 | if ( this._.definition.fullMatch && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) ) | 1524 | if ( this._.definition.fullMatch && element.getStyle( styleName ) != normalizeProperty( styleName, styles[ styleName ], true ) ) |
1520 | continue; | 1525 | continue; |
1521 | 1526 | ||
@@ -1649,7 +1654,7 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1649 | // Create the element. | 1654 | // Create the element. |
1650 | el = new CKEDITOR.dom.element( elementName, targetDocument ); | 1655 | el = new CKEDITOR.dom.element( elementName, targetDocument ); |
1651 | 1656 | ||
1652 | // #6226: attributes should be copied before the new ones are applied | 1657 | // http://dev.ckeditor.com/ticket/6226: attributes should be copied before the new ones are applied |
1653 | if ( element ) | 1658 | if ( element ) |
1654 | element.copyAttributes( el ); | 1659 | element.copyAttributes( el ); |
1655 | 1660 | ||
@@ -1794,15 +1799,28 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1794 | // is treated as a wildcard which will match any value. | 1799 | // is treated as a wildcard which will match any value. |
1795 | // @param {Object/String} source | 1800 | // @param {Object/String} source |
1796 | // @param {Object/String} target | 1801 | // @param {Object/String} target |
1802 | // @returns {Boolean} | ||
1797 | function compareCssText( source, target ) { | 1803 | function compareCssText( source, target ) { |
1804 | function filter( string, propertyName ) { | ||
1805 | // In case of font-families we'll skip quotes. (http://dev.ckeditor.com/ticket/10750) | ||
1806 | return propertyName.toLowerCase() == 'font-family' ? string.replace( /["']/g, '' ) : string; | ||
1807 | } | ||
1808 | |||
1798 | if ( typeof source == 'string' ) | 1809 | if ( typeof source == 'string' ) |
1799 | source = CKEDITOR.tools.parseCssText( source ); | 1810 | source = CKEDITOR.tools.parseCssText( source ); |
1800 | if ( typeof target == 'string' ) | 1811 | if ( typeof target == 'string' ) |
1801 | target = CKEDITOR.tools.parseCssText( target, true ); | 1812 | target = CKEDITOR.tools.parseCssText( target, true ); |
1802 | 1813 | ||
1803 | for ( var name in source ) { | 1814 | for ( var name in source ) { |
1804 | if ( !( name in target && ( target[ name ] == source[ name ] || source[ name ] == 'inherit' || target[ name ] == 'inherit' ) ) ) | 1815 | if ( !( name in target ) ) { |
1805 | return false; | 1816 | return false; |
1817 | } | ||
1818 | |||
1819 | if ( !( filter( target[ name ], name ) == filter( source[ name ], name ) || | ||
1820 | source[ name ] == 'inherit' || | ||
1821 | target[ name ] == 'inherit' ) ) { | ||
1822 | return false; | ||
1823 | } | ||
1806 | } | 1824 | } |
1807 | return true; | 1825 | return true; |
1808 | } | 1826 | } |
@@ -1811,13 +1829,25 @@ CKEDITOR.STYLE_OBJECT = 3; | |||
1811 | var doc = selection.document, | 1829 | var doc = selection.document, |
1812 | ranges = selection.getRanges(), | 1830 | ranges = selection.getRanges(), |
1813 | func = remove ? this.removeFromRange : this.applyToRange, | 1831 | func = remove ? this.removeFromRange : this.applyToRange, |
1814 | range; | 1832 | originalRanges, |
1833 | range, | ||
1834 | i; | ||
1835 | |||
1836 | // In case of fake table selection, we would like to apply all styles and then select | ||
1837 | // the original ranges. Otherwise browsers would complain about discontiguous selection. | ||
1838 | if ( selection.isFake && selection.isInTable() ) { | ||
1839 | originalRanges = []; | ||
1840 | |||
1841 | for ( i = 0; i < ranges.length; i++ ) { | ||
1842 | originalRanges.push( ranges[ i ].clone() ); | ||
1843 | } | ||
1844 | } | ||
1815 | 1845 | ||
1816 | var iterator = ranges.createIterator(); | 1846 | var iterator = ranges.createIterator(); |
1817 | while ( ( range = iterator.getNextRange() ) ) | 1847 | while ( ( range = iterator.getNextRange() ) ) |
1818 | func.call( this, range, editor ); | 1848 | func.call( this, range, editor ); |
1819 | 1849 | ||
1820 | selection.selectRanges( ranges ); | 1850 | selection.selectRanges( originalRanges || ranges ); |
1821 | doc.removeCustomData( 'doc_processing_style' ); | 1851 | doc.removeCustomData( 'doc_processing_style' ); |
1822 | } | 1852 | } |
1823 | } )(); | 1853 | } )(); |
@@ -1888,7 +1918,7 @@ CKEDITOR.styleCommand.prototype.exec = function( editor ) { | |||
1888 | */ | 1918 | */ |
1889 | CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' ); | 1919 | CKEDITOR.stylesSet = new CKEDITOR.resourceManager( '', 'stylesSet' ); |
1890 | 1920 | ||
1891 | // Backward compatibility (#5025). | 1921 | // Backward compatibility (http://dev.ckeditor.com/ticket/5025). |
1892 | CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet ); | 1922 | CKEDITOR.addStylesSet = CKEDITOR.tools.bind( CKEDITOR.stylesSet.add, CKEDITOR.stylesSet ); |
1893 | CKEDITOR.loadStylesSet = function( name, url, callback ) { | 1923 | CKEDITOR.loadStylesSet = function( name, url, callback ) { |
1894 | CKEDITOR.stylesSet.addExternal( name, url, '' ); | 1924 | CKEDITOR.stylesSet.addExternal( name, url, '' ); |
@@ -1991,7 +2021,7 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { | |||
1991 | return; | 2021 | return; |
1992 | } | 2022 | } |
1993 | 2023 | ||
1994 | // #5352 Allow to define the styles directly in the config object | 2024 | // http://dev.ckeditor.com/ticket/5352 Allow to define the styles directly in the config object |
1995 | if ( configStyleSet instanceof Array ) { | 2025 | if ( configStyleSet instanceof Array ) { |
1996 | editor._.stylesDefinitions = configStyleSet; | 2026 | editor._.stylesDefinitions = configStyleSet; |
1997 | callback( configStyleSet ); | 2027 | callback( configStyleSet ); |
diff --git a/sources/core/template.js b/sources/core/template.js index a3fe55b..a627c34 100644 --- a/sources/core/template.js +++ b/sources/core/template.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -9,11 +9,7 @@ | |||
9 | */ | 9 | */ |
10 | 10 | ||
11 | ( function() { | 11 | ( function() { |
12 | var cache = {}, | 12 | var rePlaceholder = /{([^}]+)}/g; |
13 | rePlaceholder = /{([^}]+)}/g, | ||
14 | reEscapableChars = /([\\'])/g, | ||
15 | reNewLine = /\n/g, | ||
16 | reCarriageReturn = /\r/g; | ||
17 | 13 | ||
18 | /** | 14 | /** |
19 | * Lightweight template used to build the output string from variables. | 15 | * Lightweight template used to build the output string from variables. |
@@ -27,42 +23,35 @@ | |||
27 | * @param {String} source The template source. | 23 | * @param {String} source The template source. |
28 | */ | 24 | */ |
29 | CKEDITOR.template = function( source ) { | 25 | CKEDITOR.template = function( source ) { |
30 | // Builds an optimized function body for the output() method, focused on performance. | 26 | /** |
31 | // For example, if we have this "source": | 27 | * The current template source. |
32 | // '<div style="{style}">{editorName}</div>' | 28 | * |
33 | // ... the resulting function body will be (apart from the "buffer" handling): | 29 | * @readonly |
34 | // return [ '<div style="', data['style'] == undefined ? '{style}' : data['style'], '">', data['editorName'] == undefined ? '{editorName}' : data['editorName'], '</div>' ].join(''); | 30 | * @member CKEDITOR.template |
31 | * @property {String} | ||
32 | */ | ||
33 | this.source = String( source ); | ||
34 | }; | ||
35 | 35 | ||
36 | // Try to read from the cache. | 36 | /** |
37 | if ( cache[ source ] ) | 37 | * Processes the template, filling its variables with the provided data. |
38 | this.output = cache[ source ]; | 38 | * |
39 | else { | 39 | * @method |
40 | var fn = source | 40 | * @member CKEDITOR.template |
41 | // Escape chars like slash "\" or single quote "'". | 41 | * @param {Object} data An object containing properties whose values will be |
42 | .replace( reEscapableChars, '\\$1' ) | 42 | * used to fill the template variables. The property names must match the |
43 | .replace( reNewLine, '\\n' ) | 43 | * template variables names. Variables without matching properties will be |
44 | .replace( reCarriageReturn, '\\r' ) | 44 | * kept untouched. |
45 | // Inject the template keys replacement. | 45 | * @param {Array} [buffer] An array that the output data will be pushed into. |
46 | .replace( rePlaceholder, function( m, key ) { | 46 | * The number of entries appended to the array is unknown. |
47 | return "',data['" + key + "']==undefined?'{" + key + "}':data['" + key + "'],'"; | 47 | * @returns {String/Number} If `buffer` has not been provided, the processed |
48 | } ); | 48 | * template output data; otherwise the new length of `buffer`. |
49 | */ | ||
50 | CKEDITOR.template.prototype.output = function( data, buffer ) { | ||
51 | var output = this.source.replace( rePlaceholder, function( fullMatch, dataKey ) { | ||
52 | return data[ dataKey ] !== undefined ? data[ dataKey ] : fullMatch; | ||
53 | } ); | ||
49 | 54 | ||
50 | fn = "return buffer?buffer.push('" + fn + "'):['" + fn + "'].join('');"; | 55 | return buffer ? buffer.push( output ) : output; |
51 | this.output = cache[ source ] = Function( 'data', 'buffer', fn ); | ||
52 | } | ||
53 | }; | 56 | }; |
54 | } )(); | 57 | } )(); |
55 | |||
56 | /** | ||
57 | * Processes the template, filling its variables with the provided data. | ||
58 | * | ||
59 | * @method output | ||
60 | * @param {Object} data An object containing properties which values will be | ||
61 | * used to fill the template variables. The property names must match the | ||
62 | * template variables names. Variables without matching properties will be | ||
63 | * kept untouched. | ||
64 | * @param {Array} [buffer] An array into which the output data will be pushed into. | ||
65 | * The number of entries appended to the array is unknown. | ||
66 | * @returns {String/Number} If `buffer` has not been provided, the processed | ||
67 | * template output data, otherwise the new length of `buffer`. | ||
68 | */ | ||
diff --git a/sources/core/tools.js b/sources/core/tools.js index ae5b4d0..a4b736d 100644 --- a/sources/core/tools.js +++ b/sources/core/tools.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||
@@ -981,6 +981,23 @@ | |||
981 | }, | 981 | }, |
982 | 982 | ||
983 | /** | 983 | /** |
984 | * Normalizes hexadecimal notation so that the color string is always 6 characters long and lowercase. | ||
985 | * | ||
986 | * @param {String} styleText The style data (or just a string containing hex colors) to be converted. | ||
987 | * @returns {String} The style data with hex colors normalized. | ||
988 | */ | ||
989 | normalizeHex: function( styleText ) { | ||
990 | return styleText.replace( /#(([0-9a-f]{3}){1,2})($|;|\s+)/gi, function( match, hexColor, hexColorPart, separator ) { | ||
991 | var normalizedHexColor = hexColor.toLowerCase(); | ||
992 | if ( normalizedHexColor.length == 3 ) { | ||
993 | var parts = normalizedHexColor.split( '' ); | ||
994 | normalizedHexColor = [ parts[ 0 ], parts[ 0 ], parts[ 1 ], parts[ 1 ], parts[ 2 ], parts[ 2 ] ].join( '' ); | ||
995 | } | ||
996 | return '#' + normalizedHexColor + separator; | ||
997 | } ); | ||
998 | }, | ||
999 | |||
1000 | /** | ||
984 | * Turns inline style text properties into one hash. | 1001 | * Turns inline style text properties into one hash. |
985 | * | 1002 | * |
986 | * @param {String} styleText The style data to be parsed. | 1003 | * @param {String} styleText The style data to be parsed. |
@@ -996,21 +1013,24 @@ | |||
996 | // Injects the style in a temporary span object, so the browser parses it, | 1013 | // Injects the style in a temporary span object, so the browser parses it, |
997 | // retrieving its final format. | 1014 | // retrieving its final format. |
998 | var temp = new CKEDITOR.dom.element( 'span' ); | 1015 | var temp = new CKEDITOR.dom.element( 'span' ); |
999 | temp.setAttribute( 'style', styleText ); | 1016 | styleText = temp.setAttribute( 'style', styleText ).getAttribute( 'style' ) || ''; |
1000 | styleText = CKEDITOR.tools.convertRgbToHex( temp.getAttribute( 'style' ) || '' ); | 1017 | } |
1018 | |||
1019 | // Normalize colors. | ||
1020 | if ( styleText ) { | ||
1021 | styleText = CKEDITOR.tools.normalizeHex( CKEDITOR.tools.convertRgbToHex( styleText ) ); | ||
1001 | } | 1022 | } |
1002 | 1023 | ||
1003 | // IE will leave a single semicolon when failed to parse the style text. (#3891) | 1024 | // IE will leave a single semicolon when failed to parse the style text. (http://dev.ckeditor.com/ticket/3891) |
1004 | if ( !styleText || styleText == ';' ) | 1025 | if ( !styleText || styleText == ';' ) |
1005 | return retval; | 1026 | return retval; |
1006 | 1027 | ||
1007 | styleText.replace( /"/g, '"' ).replace( /\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) { | 1028 | styleText.replace( /"/g, '"' ).replace( /\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g, function( match, name, value ) { |
1008 | if ( normalize ) { | 1029 | if ( normalize ) { |
1009 | name = name.toLowerCase(); | 1030 | name = name.toLowerCase(); |
1010 | // Normalize font-family property, ignore quotes and being case insensitive. (#7322) | 1031 | // Drop extra whitespacing from font-family. |
1011 | // http://www.w3.org/TR/css3-fonts/#font-family-the-font-family-property | ||
1012 | if ( name == 'font-family' ) | 1032 | if ( name == 'font-family' ) |
1013 | value = value.toLowerCase().replace( /["']/g, '' ).replace( /\s*,\s*/g, ',' ); | 1033 | value = value.replace( /\s*,\s*/g, ',' ); |
1014 | value = CKEDITOR.tools.trim( value ); | 1034 | value = CKEDITOR.tools.trim( value ); |
1015 | } | 1035 | } |
1016 | 1036 | ||
@@ -1293,6 +1313,67 @@ | |||
1293 | }, | 1313 | }, |
1294 | 1314 | ||
1295 | /** | 1315 | /** |
1316 | * Converts a keystroke to its string representation. Returns an object with two fields: | ||
1317 | * | ||
1318 | * * `display` – A string that should be used for visible labels. | ||
1319 | * For Mac devices it uses `⌥` for `ALT`, `⇧` for `SHIFT` and `⌘` for `COMMAND`. | ||
1320 | * * `aria` – A string that should be used for ARIA descriptions. | ||
1321 | * It does not use special characters such as `⌥`, `⇧` or `⌘`. | ||
1322 | * | ||
1323 | * var lang = editor.lang.common.keyboard; | ||
1324 | * var shortcut = CKEDITOR.tools.keystrokeToString( lang, CKEDITOR.CTRL + 88 ); | ||
1325 | * console.log( shortcut.display ); // 'CTRL + X', on Mac '⌘ + X'. | ||
1326 | * console.log( shortcut.aria ); // 'CTRL + X', on Mac 'COMMAND + X'. | ||
1327 | * | ||
1328 | * @since 4.6.0 | ||
1329 | * @param {Object} lang A language object with the key name translation. | ||
1330 | * @param {Number} keystroke The keystroke to convert. | ||
1331 | * @returns {{display: String, aria: String}} | ||
1332 | */ | ||
1333 | keystrokeToString: function( lang, keystroke ) { | ||
1334 | var special = keystroke & 0xFF0000, | ||
1335 | key = keystroke & 0x00FFFF, | ||
1336 | isMac = CKEDITOR.env.mac, | ||
1337 | CTRL = 17, | ||
1338 | CMD = 224, | ||
1339 | ALT = 18, | ||
1340 | SHIFT = 16, | ||
1341 | display = [], | ||
1342 | aria = []; | ||
1343 | |||
1344 | |||
1345 | if ( special & CKEDITOR.CTRL ) { | ||
1346 | display.push( isMac ? '⌘' : lang[ CTRL ] ); | ||
1347 | aria.push( isMac ? lang[ CMD ] : lang[ CTRL ] ); | ||
1348 | } | ||
1349 | |||
1350 | if ( special & CKEDITOR.ALT ) { | ||
1351 | display.push( isMac ? '⌥' : lang[ ALT ] ); | ||
1352 | aria.push( lang[ ALT ] ); | ||
1353 | } | ||
1354 | |||
1355 | if ( special & CKEDITOR.SHIFT ) { | ||
1356 | display.push( isMac ? '⇧' : lang[ SHIFT ] ); | ||
1357 | aria.push( lang[ SHIFT ] ); | ||
1358 | } | ||
1359 | |||
1360 | if ( key ) { | ||
1361 | if ( lang[ key ] ) { | ||
1362 | display.push( lang[ key ] ); | ||
1363 | aria.push( lang[ key ] ); | ||
1364 | } else { | ||
1365 | display.push( String.fromCharCode( key ) ); | ||
1366 | aria.push( String.fromCharCode( key ) ); | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1370 | return { | ||
1371 | display: display.join( '+' ), | ||
1372 | aria: aria.join( '+' ) | ||
1373 | }; | ||
1374 | }, | ||
1375 | |||
1376 | /** | ||
1296 | * The data URI of a transparent image. May be used e.g. in HTML as an image source or in CSS in `url()`. | 1377 | * The data URI of a transparent image. May be used e.g. in HTML as an image source or in CSS in `url()`. |
1297 | * | 1378 | * |
1298 | * @since 4.4 | 1379 | * @since 4.4 |
@@ -1353,6 +1434,540 @@ | |||
1353 | } | 1434 | } |
1354 | 1435 | ||
1355 | return token; | 1436 | return token; |
1437 | }, | ||
1438 | |||
1439 | /** | ||
1440 | * Returns an escaped CSS selector. `CSS.escape()` is used if defined, leading digit is escaped otherwise. | ||
1441 | * | ||
1442 | * @since 4.5.10 | ||
1443 | * @param {String} selector A CSS selector to escape. | ||
1444 | * @returns {String} An escaped selector. | ||
1445 | */ | ||
1446 | escapeCss: function( selector ) { | ||
1447 | // Invalid input. | ||
1448 | if ( !selector ) { | ||
1449 | return ''; | ||
1450 | } | ||
1451 | |||
1452 | // CSS.escape() can be used. | ||
1453 | if ( window.CSS && CSS.escape ) { | ||
1454 | return CSS.escape( selector ); | ||
1455 | } | ||
1456 | |||
1457 | // Simple leading digit escape. | ||
1458 | if ( !isNaN( parseInt( selector.charAt( 0 ), 10 ) ) ) { | ||
1459 | return '\\3' + selector.charAt( 0 ) + ' ' + selector.substring( 1, selector.length ); | ||
1460 | } | ||
1461 | |||
1462 | return selector; | ||
1463 | }, | ||
1464 | |||
1465 | /** | ||
1466 | * Detects which mouse button generated a given DOM event. | ||
1467 | * | ||
1468 | * @since 4.7.3 | ||
1469 | * @param {CKEDITOR.dom.event} evt DOM event. | ||
1470 | * @returns {Number|Boolean} Returns a number indicating the mouse button or `false` | ||
1471 | * if the mouse button cannot be determined. | ||
1472 | */ | ||
1473 | getMouseButton: function( evt ) { | ||
1474 | var evtData = evt.data, | ||
1475 | domEvent = evtData && evtData.$; | ||
1476 | |||
1477 | if ( !( evtData && domEvent ) ) { | ||
1478 | // Added in case when there's no data available. That's the case in some unit test in built version which | ||
1479 | // mock event but doesn't put data object. | ||
1480 | return false; | ||
1481 | } | ||
1482 | |||
1483 | if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) { | ||
1484 | if ( domEvent.button === 4 ) { | ||
1485 | return CKEDITOR.MOUSE_BUTTON_MIDDLE; | ||
1486 | } else if ( domEvent.button === 1 ) { | ||
1487 | return CKEDITOR.MOUSE_BUTTON_LEFT; | ||
1488 | } else { | ||
1489 | return CKEDITOR.MOUSE_BUTTON_RIGHT; | ||
1490 | } | ||
1491 | } | ||
1492 | |||
1493 | return domEvent.button; | ||
1494 | }, | ||
1495 | |||
1496 | /** | ||
1497 | * A set of functions for operations on styles. | ||
1498 | * | ||
1499 | * @property {CKEDITOR.tools.style} | ||
1500 | */ | ||
1501 | style: { | ||
1502 | /** | ||
1503 | * Methods to parse miscellaneous CSS properties. | ||
1504 | * | ||
1505 | * @property {CKEDITOR.tools.style.parse} | ||
1506 | * @member CKEDITOR.tools.style | ||
1507 | */ | ||
1508 | parse: { | ||
1509 | // Color list based on https://www.w3.org/TR/css-color-4/#named-colors. | ||
1510 | _colors: { | ||
1511 | aliceblue: '#F0F8FF', | ||
1512 | antiquewhite: '#FAEBD7', | ||
1513 | aqua: '#00FFFF', | ||
1514 | aquamarine: '#7FFFD4', | ||
1515 | azure: '#F0FFFF', | ||
1516 | beige: '#F5F5DC', | ||
1517 | bisque: '#FFE4C4', | ||
1518 | black: '#000000', | ||
1519 | blanchedalmond: '#FFEBCD', | ||
1520 | blue: '#0000FF', | ||
1521 | blueviolet: '#8A2BE2', | ||
1522 | brown: '#A52A2A', | ||
1523 | burlywood: '#DEB887', | ||
1524 | cadetblue: '#5F9EA0', | ||
1525 | chartreuse: '#7FFF00', | ||
1526 | chocolate: '#D2691E', | ||
1527 | coral: '#FF7F50', | ||
1528 | cornflowerblue: '#6495ED', | ||
1529 | cornsilk: '#FFF8DC', | ||
1530 | crimson: '#DC143C', | ||
1531 | cyan: '#00FFFF', | ||
1532 | darkblue: '#00008B', | ||
1533 | darkcyan: '#008B8B', | ||
1534 | darkgoldenrod: '#B8860B', | ||
1535 | darkgray: '#A9A9A9', | ||
1536 | darkgreen: '#006400', | ||
1537 | darkgrey: '#A9A9A9', | ||
1538 | darkkhaki: '#BDB76B', | ||
1539 | darkmagenta: '#8B008B', | ||
1540 | darkolivegreen: '#556B2F', | ||
1541 | darkorange: '#FF8C00', | ||
1542 | darkorchid: '#9932CC', | ||
1543 | darkred: '#8B0000', | ||
1544 | darksalmon: '#E9967A', | ||
1545 | darkseagreen: '#8FBC8F', | ||
1546 | darkslateblue: '#483D8B', | ||
1547 | darkslategray: '#2F4F4F', | ||
1548 | darkslategrey: '#2F4F4F', | ||
1549 | darkturquoise: '#00CED1', | ||
1550 | darkviolet: '#9400D3', | ||
1551 | deeppink: '#FF1493', | ||
1552 | deepskyblue: '#00BFFF', | ||
1553 | dimgray: '#696969', | ||
1554 | dimgrey: '#696969', | ||
1555 | dodgerblue: '#1E90FF', | ||
1556 | firebrick: '#B22222', | ||
1557 | floralwhite: '#FFFAF0', | ||
1558 | forestgreen: '#228B22', | ||
1559 | fuchsia: '#FF00FF', | ||
1560 | gainsboro: '#DCDCDC', | ||
1561 | ghostwhite: '#F8F8FF', | ||
1562 | gold: '#FFD700', | ||
1563 | goldenrod: '#DAA520', | ||
1564 | gray: '#808080', | ||
1565 | green: '#008000', | ||
1566 | greenyellow: '#ADFF2F', | ||
1567 | grey: '#808080', | ||
1568 | honeydew: '#F0FFF0', | ||
1569 | hotpink: '#FF69B4', | ||
1570 | indianred: '#CD5C5C', | ||
1571 | indigo: '#4B0082', | ||
1572 | ivory: '#FFFFF0', | ||
1573 | khaki: '#F0E68C', | ||
1574 | lavender: '#E6E6FA', | ||
1575 | lavenderblush: '#FFF0F5', | ||
1576 | lawngreen: '#7CFC00', | ||
1577 | lemonchiffon: '#FFFACD', | ||
1578 | lightblue: '#ADD8E6', | ||
1579 | lightcoral: '#F08080', | ||
1580 | lightcyan: '#E0FFFF', | ||
1581 | lightgoldenrodyellow: '#FAFAD2', | ||
1582 | lightgray: '#D3D3D3', | ||
1583 | lightgreen: '#90EE90', | ||
1584 | lightgrey: '#D3D3D3', | ||
1585 | lightpink: '#FFB6C1', | ||
1586 | lightsalmon: '#FFA07A', | ||
1587 | lightseagreen: '#20B2AA', | ||
1588 | lightskyblue: '#87CEFA', | ||
1589 | lightslategray: '#778899', | ||
1590 | lightslategrey: '#778899', | ||
1591 | lightsteelblue: '#B0C4DE', | ||
1592 | lightyellow: '#FFFFE0', | ||
1593 | lime: '#00FF00', | ||
1594 | limegreen: '#32CD32', | ||
1595 | linen: '#FAF0E6', | ||
1596 | magenta: '#FF00FF', | ||
1597 | maroon: '#800000', | ||
1598 | mediumaquamarine: '#66CDAA', | ||
1599 | mediumblue: '#0000CD', | ||
1600 | mediumorchid: '#BA55D3', | ||
1601 | mediumpurple: '#9370DB', | ||
1602 | mediumseagreen: '#3CB371', | ||
1603 | mediumslateblue: '#7B68EE', | ||
1604 | mediumspringgreen: '#00FA9A', | ||
1605 | mediumturquoise: '#48D1CC', | ||
1606 | mediumvioletred: '#C71585', | ||
1607 | midnightblue: '#191970', | ||
1608 | mintcream: '#F5FFFA', | ||
1609 | mistyrose: '#FFE4E1', | ||
1610 | moccasin: '#FFE4B5', | ||
1611 | navajowhite: '#FFDEAD', | ||
1612 | navy: '#000080', | ||
1613 | oldlace: '#FDF5E6', | ||
1614 | olive: '#808000', | ||
1615 | olivedrab: '#6B8E23', | ||
1616 | orange: '#FFA500', | ||
1617 | orangered: '#FF4500', | ||
1618 | orchid: '#DA70D6', | ||
1619 | palegoldenrod: '#EEE8AA', | ||
1620 | palegreen: '#98FB98', | ||
1621 | paleturquoise: '#AFEEEE', | ||
1622 | palevioletred: '#DB7093', | ||
1623 | papayawhip: '#FFEFD5', | ||
1624 | peachpuff: '#FFDAB9', | ||
1625 | peru: '#CD853F', | ||
1626 | pink: '#FFC0CB', | ||
1627 | plum: '#DDA0DD', | ||
1628 | powderblue: '#B0E0E6', | ||
1629 | purple: '#800080', | ||
1630 | rebeccapurple: '#663399', | ||
1631 | red: '#FF0000', | ||
1632 | rosybrown: '#BC8F8F', | ||
1633 | royalblue: '#4169E1', | ||
1634 | saddlebrown: '#8B4513', | ||
1635 | salmon: '#FA8072', | ||
1636 | sandybrown: '#F4A460', | ||
1637 | seagreen: '#2E8B57', | ||
1638 | seashell: '#FFF5EE', | ||
1639 | sienna: '#A0522D', | ||
1640 | silver: '#C0C0C0', | ||
1641 | skyblue: '#87CEEB', | ||
1642 | slateblue: '#6A5ACD', | ||
1643 | slategray: '#708090', | ||
1644 | slategrey: '#708090', | ||
1645 | snow: '#FFFAFA', | ||
1646 | springgreen: '#00FF7F', | ||
1647 | steelblue: '#4682B4', | ||
1648 | tan: '#D2B48C', | ||
1649 | teal: '#008080', | ||
1650 | thistle: '#D8BFD8', | ||
1651 | tomato: '#FF6347', | ||
1652 | turquoise: '#40E0D0', | ||
1653 | violet: '#EE82EE', | ||
1654 | wheat: '#F5DEB3', | ||
1655 | white: '#FFFFFF', | ||
1656 | whitesmoke: '#F5F5F5', | ||
1657 | yellow: '#FFFF00', | ||
1658 | yellowgreen: '#9ACD32' | ||
1659 | }, | ||
1660 | |||
1661 | _borderStyle: [ | ||
1662 | 'none', | ||
1663 | 'hidden', | ||
1664 | 'dotted', | ||
1665 | 'dashed', | ||
1666 | 'solid', | ||
1667 | 'double', | ||
1668 | 'groove', | ||
1669 | 'ridge', | ||
1670 | 'inset', | ||
1671 | 'outset' | ||
1672 | ], | ||
1673 | |||
1674 | _widthRegExp: /^(thin|medium|thick|[\+-]?\d+(\.\d+)?[a-z%]+|[\+-]?0+(\.0+)?|\.\d+[a-z%]+)$/, | ||
1675 | |||
1676 | _rgbaRegExp: /rgba?\(\s*\d+%?\s*,\s*\d+%?\s*,\s*\d+%?\s*(?:,\s*[0-9.]+\s*)?\)/gi, | ||
1677 | |||
1678 | _hslaRegExp: /hsla?\(\s*[0-9.]+\s*,\s*\d+%\s*,\s*\d+%\s*(?:,\s*[0-9.]+\s*)?\)/gi, | ||
1679 | |||
1680 | /** | ||
1681 | * Parses the `value` used as a `background` property shorthand and returns information as an object. | ||
1682 | * | ||
1683 | * **Note:** Currently only the `color` property is extracted. Any other parts will go into the `unprocessed` property. | ||
1684 | * | ||
1685 | * var background = CKEDITOR.tools.style.parse.background( '#0C0 url(foo.png)' ); | ||
1686 | * console.log( background ); | ||
1687 | * // Logs: { color: '#0C0', unprocessed: 'url(foo.png)' } | ||
1688 | * | ||
1689 | * @param {String} value The value of the `background` property. | ||
1690 | * @returns {Object} An object with information extracted from the background. | ||
1691 | * @returns {String} return.color The **first** color value found. The color format remains the same as in input. | ||
1692 | * @returns {String} return.unprocessed The remaining part of the `value` that has not been processed. | ||
1693 | * @member CKEDITOR.tools.style.parse | ||
1694 | */ | ||
1695 | background: function( value ) { | ||
1696 | var ret = {}, | ||
1697 | colors = this._findColor( value ); | ||
1698 | |||
1699 | if ( colors.length ) { | ||
1700 | ret.color = colors[ 0 ]; | ||
1701 | |||
1702 | CKEDITOR.tools.array.forEach( colors, function( colorToken ) { | ||
1703 | value = value.replace( colorToken, '' ); | ||
1704 | } ); | ||
1705 | } | ||
1706 | |||
1707 | value = CKEDITOR.tools.trim( value ); | ||
1708 | |||
1709 | if ( value ) { | ||
1710 | // If anything was left unprocessed include it as unprocessed part. | ||
1711 | ret.unprocessed = value; | ||
1712 | } | ||
1713 | |||
1714 | return ret; | ||
1715 | }, | ||
1716 | |||
1717 | /** | ||
1718 | * Parses the `margin` CSS property shorthand format. | ||
1719 | * | ||
1720 | * console.log( CKEDITOR.tools.parse.margin( '3px 0 2' ) ); | ||
1721 | * // Logs: { top: "3px", right: "0", bottom: "2", left: "0" } | ||
1722 | * | ||
1723 | * @param {String} value The `margin` property value. | ||
1724 | * @returns {Object} | ||
1725 | * @returns {Number} return.top Top margin. | ||
1726 | * @returns {Number} return.right Right margin. | ||
1727 | * @returns {Number} return.bottom Bottom margin. | ||
1728 | * @returns {Number} return.left Left margin. | ||
1729 | * @member CKEDITOR.tools.style.parse | ||
1730 | */ | ||
1731 | margin: function( value ) { | ||
1732 | var ret = {}; | ||
1733 | |||
1734 | var widths = value.match( /(?:\-?[\.\d]+(?:%|\w*)|auto|inherit|initial|unset)/g ) || [ '0px' ]; | ||
1735 | |||
1736 | switch ( widths.length ) { | ||
1737 | case 1: | ||
1738 | mapStyles( [ 0, 0, 0, 0 ] ); | ||
1739 | break; | ||
1740 | case 2: | ||
1741 | mapStyles( [ 0, 1, 0, 1 ] ); | ||
1742 | break; | ||
1743 | case 3: | ||
1744 | mapStyles( [ 0, 1, 2, 1 ] ); | ||
1745 | break; | ||
1746 | case 4: | ||
1747 | mapStyles( [ 0, 1, 2, 3 ] ); | ||
1748 | break; | ||
1749 | } | ||
1750 | |||
1751 | function mapStyles( map ) { | ||
1752 | ret.top = widths[ map[ 0 ] ]; | ||
1753 | ret.right = widths[ map[ 1 ] ]; | ||
1754 | ret.bottom = widths[ map[ 2 ] ]; | ||
1755 | ret.left = widths[ map[ 3 ] ]; | ||
1756 | } | ||
1757 | |||
1758 | return ret; | ||
1759 | }, | ||
1760 | |||
1761 | /** | ||
1762 | * Parses the `border` CSS property shorthand format. | ||
1763 | * This CSS property does not support inheritance (https://www.w3.org/TR/css3-background/#the-border-shorthands). | ||
1764 | * | ||
1765 | * console.log( CKEDITOR.tools.style.parse.border( '3px solid #ffeedd' ) ); | ||
1766 | * // Logs: { width: "3px", style: "solid", color: "#ffeedd" } | ||
1767 | * | ||
1768 | * @param {String} value The `border` property value. | ||
1769 | * @returns {Object} | ||
1770 | * @returns {String} return.width The border-width attribute. | ||
1771 | * @returns {String} return.style The border-style attribute. | ||
1772 | * @returns {String} return.color The border-color attribute. | ||
1773 | * @member CKEDITOR.tools.style.parse | ||
1774 | */ | ||
1775 | border: function( value ) { | ||
1776 | var ret = {}, | ||
1777 | input = value.split( /\s+/ ); | ||
1778 | |||
1779 | CKEDITOR.tools.array.forEach( input, function( val ) { | ||
1780 | if ( !ret.color ) { | ||
1781 | var parseColor = CKEDITOR.tools.style.parse._findColor( val ); | ||
1782 | if ( parseColor.length ) { | ||
1783 | ret.color = parseColor[ 0 ]; | ||
1784 | return; | ||
1785 | } | ||
1786 | } | ||
1787 | |||
1788 | if ( !ret.style ) { | ||
1789 | if ( CKEDITOR.tools.indexOf( CKEDITOR.tools.style.parse._borderStyle, val ) !== -1 ) { | ||
1790 | ret.style = val; | ||
1791 | return; | ||
1792 | } | ||
1793 | } | ||
1794 | |||
1795 | if ( !ret.width ) { | ||
1796 | if ( CKEDITOR.tools.style.parse._widthRegExp.test( val ) ) { | ||
1797 | ret.width = val; | ||
1798 | return; | ||
1799 | } | ||
1800 | } | ||
1801 | |||
1802 | } ); | ||
1803 | return ret; | ||
1804 | }, | ||
1805 | |||
1806 | /** | ||
1807 | * Searches the `value` for any CSS color occurrences and returns it. | ||
1808 | * | ||
1809 | * @private | ||
1810 | * @param {String} value | ||
1811 | * @returns {String[]} An array of matched results. | ||
1812 | * @member CKEDITOR.tools.style.parse | ||
1813 | */ | ||
1814 | _findColor: function( value ) { | ||
1815 | var ret = [], | ||
1816 | arrayTools = CKEDITOR.tools.array; | ||
1817 | |||
1818 | |||
1819 | // Check for rgb(a). | ||
1820 | ret = ret.concat( value.match( this._rgbaRegExp ) || [] ); | ||
1821 | |||
1822 | // Check for hsl(a). | ||
1823 | ret = ret.concat( value.match( this._hslaRegExp ) || [] ); | ||
1824 | |||
1825 | ret = ret.concat( arrayTools.filter( value.split( /\s+/ ), function( colorEntry ) { | ||
1826 | // Check for hex format. | ||
1827 | if ( colorEntry.match( /^\#[a-f0-9]{3}(?:[a-f0-9]{3})?$/gi ) ) { | ||
1828 | return true; | ||
1829 | } | ||
1830 | |||
1831 | // Check for preset names. | ||
1832 | return colorEntry.toLowerCase() in CKEDITOR.tools.style.parse._colors; | ||
1833 | } ) ); | ||
1834 | |||
1835 | return ret; | ||
1836 | } | ||
1837 | } | ||
1838 | }, | ||
1839 | |||
1840 | /** | ||
1841 | * A set of array helpers. | ||
1842 | * | ||
1843 | * @property {CKEDITOR.tools.array} | ||
1844 | * @member CKEDITOR.tools | ||
1845 | */ | ||
1846 | array: { | ||
1847 | /** | ||
1848 | * Returns a copy of `array` filtered using the `fn` function. Any elements that the `fn` will return `false` for | ||
1849 | * will get removed from the returned array. | ||
1850 | * | ||
1851 | * var filtered = this.array.filter( [ 0, 1, 2, 3 ], function( value ) { | ||
1852 | * // Leave only values equal or greater than 2. | ||
1853 | * return value >= 2; | ||
1854 | * } ); | ||
1855 | * console.log( filtered ); | ||
1856 | * // Logs: [ 2, 3 ] | ||
1857 | * | ||
1858 | * @param {Array} array | ||
1859 | * @param {Function} fn A function that gets called with each `array` item. Any item that `fn` | ||
1860 | * returned a `false`-alike value for will be filtered out of the `array`. | ||
1861 | * @param {Mixed} fn.value The currently iterated array value. | ||
1862 | * @param {Number} fn.index The index of the currently iterated value in an array. | ||
1863 | * @param {Array} fn.array The original array passed as the `array` variable. | ||
1864 | * @param {Mixed} [thisArg=undefined] A context object for `fn`. | ||
1865 | * @returns {Array} The filtered array. | ||
1866 | * @member CKEDITOR.tools.array | ||
1867 | */ | ||
1868 | filter: function( array, fn, thisArg ) { | ||
1869 | var ret = []; | ||
1870 | |||
1871 | this.forEach( array, function( val, i ) { | ||
1872 | if ( fn.call( thisArg, val, i, array ) ) { | ||
1873 | ret.push( val ); | ||
1874 | } | ||
1875 | } ); | ||
1876 | |||
1877 | return ret; | ||
1878 | }, | ||
1879 | |||
1880 | /** | ||
1881 | * Iterates over every element in the `array`. | ||
1882 | * | ||
1883 | * @param {Array} array An array to be iterated over. | ||
1884 | * @param {Function} fn The function called for every `array` element. | ||
1885 | * @param {Mixed} fn.value The currently iterated array value. | ||
1886 | * @param {Number} fn.index The index of the currently iterated value in an array. | ||
1887 | * @param {Array} fn.array The original array passed as an `array` variable. | ||
1888 | * @param {Mixed} [thisArg=undefined] The context object for `fn`. | ||
1889 | * @member CKEDITOR.tools.array | ||
1890 | */ | ||
1891 | forEach: function( array, fn, thisArg ) { | ||
1892 | var len = array.length, | ||
1893 | i; | ||
1894 | |||
1895 | for ( i = 0; i < len; i++ ) { | ||
1896 | fn.call( thisArg, array[ i ], i, array ); | ||
1897 | } | ||
1898 | }, | ||
1899 | |||
1900 | /** | ||
1901 | * Applies a function to each element of an array and returns the array of results in the same order. | ||
1902 | * Note the order of the parameters. | ||
1903 | * | ||
1904 | * @param {Array} array An array of elements that `fn` is applied on. | ||
1905 | * @param {Function} fn A function with the signature `a -> b`. | ||
1906 | * @param {Mixed} [thisArg=undefined] The context object for `fn`. | ||
1907 | * @returns {Array} An array of mapped elements. | ||
1908 | * @member CKEDITOR.tools.array | ||
1909 | * @since 4.6.2 | ||
1910 | */ | ||
1911 | map: function( array, fn, thisArg ) { | ||
1912 | var result = []; | ||
1913 | for ( var i = 0; i < array.length; i++ ) { | ||
1914 | result.push( fn.call( thisArg, array[ i ], i, array ) ); | ||
1915 | } | ||
1916 | return result; | ||
1917 | }, | ||
1918 | |||
1919 | /** | ||
1920 | * Applies a function against each value in an array storing the result in an accumulator passed to the next iteration. | ||
1921 | * Note the order of the parameters. | ||
1922 | * | ||
1923 | * @param {Array} array An array of elements that `fn` is applied on. | ||
1924 | * @param {Function} fn A function with the signature `(accumulator, a, index, array) -> b`. | ||
1925 | * @param {Mixed} initial Initial value of the accumulator. | ||
1926 | * @param {Mixed} [thisArg=undefined] The context object for `fn`. | ||
1927 | * @returns {Mixed} The final value of the accumulator. | ||
1928 | * @member CKEDITOR.tools.array | ||
1929 | * @since 4.6.2 | ||
1930 | */ | ||
1931 | reduce: function( array, fn, initial, thisArg ) { | ||
1932 | var acc = initial; | ||
1933 | for ( var i = 0; i < array.length; i++ ) { | ||
1934 | acc = fn.call( thisArg, acc, array[ i ], i, array ); | ||
1935 | } | ||
1936 | return acc; | ||
1937 | } | ||
1938 | }, | ||
1939 | |||
1940 | /** | ||
1941 | * A set of object helpers. | ||
1942 | * | ||
1943 | * @property {CKEDITOR.tools.object} | ||
1944 | * @member CKEDITOR.tools | ||
1945 | */ | ||
1946 | object: { | ||
1947 | /** | ||
1948 | * Returns the first key from `obj` which has a given `value`. | ||
1949 | * | ||
1950 | * @param {Object} obj An object whose `key` is looked for. | ||
1951 | * @param {Mixed} value An object's `value` to be looked for. | ||
1952 | * @returns {String/null} Matched `key` or `null` if not found. | ||
1953 | * @member CKEDITOR.tools.object | ||
1954 | */ | ||
1955 | |||
1956 | findKey: function( obj, value ) { | ||
1957 | if ( typeof obj !== 'object' ) { | ||
1958 | return null; | ||
1959 | } | ||
1960 | |||
1961 | var key; | ||
1962 | |||
1963 | for ( key in obj ) { | ||
1964 | if ( obj[ key ] === value ) { | ||
1965 | return key; | ||
1966 | } | ||
1967 | } | ||
1968 | |||
1969 | return null; | ||
1970 | } | ||
1356 | } | 1971 | } |
1357 | }; | 1972 | }; |
1358 | 1973 | ||
@@ -1381,6 +1996,78 @@ | |||
1381 | 1996 | ||
1382 | return result; | 1997 | return result; |
1383 | } | 1998 | } |
1999 | |||
2000 | /** | ||
2001 | * @member CKEDITOR.tools.array | ||
2002 | * @method indexOf | ||
2003 | * @inheritdoc CKEDITOR.tools#indexOf | ||
2004 | */ | ||
2005 | CKEDITOR.tools.array.indexOf = CKEDITOR.tools.indexOf; | ||
2006 | |||
2007 | /** | ||
2008 | * @member CKEDITOR.tools.array | ||
2009 | * @method isArray | ||
2010 | * @inheritdoc CKEDITOR.tools#isArray | ||
2011 | */ | ||
2012 | CKEDITOR.tools.array.isArray = CKEDITOR.tools.isArray; | ||
2013 | |||
2014 | /** | ||
2015 | * Left mouse button. | ||
2016 | * | ||
2017 | * @since 4.7.3 | ||
2018 | * @readonly | ||
2019 | * @property {Number} [=0] | ||
2020 | * @member CKEDITOR | ||
2021 | */ | ||
2022 | CKEDITOR.MOUSE_BUTTON_LEFT = 0; | ||
2023 | |||
2024 | /** | ||
2025 | * Middle mouse button. | ||
2026 | * | ||
2027 | * @since 4.7.3 | ||
2028 | * @readonly | ||
2029 | * @property {Number} [=1] | ||
2030 | * @member CKEDITOR | ||
2031 | */ | ||
2032 | CKEDITOR.MOUSE_BUTTON_MIDDLE = 1; | ||
2033 | |||
2034 | /** | ||
2035 | * Right mouse button. | ||
2036 | * | ||
2037 | * @since 4.7.3 | ||
2038 | * @readonly | ||
2039 | * @property {Number} [=2] | ||
2040 | * @member CKEDITOR | ||
2041 | */ | ||
2042 | CKEDITOR.MOUSE_BUTTON_RIGHT = 2; | ||
2043 | |||
2044 | /** | ||
2045 | * The namespace containing functions to work on CSS properties. | ||
2046 | * | ||
2047 | * @since 4.6.1 | ||
2048 | * @class CKEDITOR.tools.style | ||
2049 | */ | ||
2050 | |||
2051 | /** | ||
2052 | * The namespace with helper functions to parse some common CSS properties. | ||
2053 | * | ||
2054 | * @since 4.6.1 | ||
2055 | * @class CKEDITOR.tools.style.parse | ||
2056 | */ | ||
2057 | |||
2058 | /** | ||
2059 | * The namespace with helper functions and polyfills for arrays. | ||
2060 | * | ||
2061 | * @since 4.6.1 | ||
2062 | * @class CKEDITOR.tools.array | ||
2063 | */ | ||
2064 | |||
2065 | /** | ||
2066 | * The namespace with helper functions and polyfills for objects. | ||
2067 | * | ||
2068 | * @since 4.7.1 | ||
2069 | * @class CKEDITOR.tools.object | ||
2070 | */ | ||
1384 | } )(); | 2071 | } )(); |
1385 | 2072 | ||
1386 | // PACKAGER_RENAME( CKEDITOR.tools ) | 2073 | // PACKAGER_RENAME( CKEDITOR.tools ) |
diff --git a/sources/core/ui.js b/sources/core/ui.js index 29ab0ad..ac5a285 100644 --- a/sources/core/ui.js +++ b/sources/core/ui.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /** | 1 | /** |
2 | * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | 2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. |
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | 3 | * For licensing, see LICENSE.md or http://ckeditor.com/license |
4 | */ | 4 | */ |
5 | 5 | ||