diff options
Diffstat (limited to 'sources/plugins/wysiwygarea/plugin.js')
-rw-r--r-- | sources/plugins/wysiwygarea/plugin.js | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/sources/plugins/wysiwygarea/plugin.js b/sources/plugins/wysiwygarea/plugin.js new file mode 100644 index 0000000..962f31e --- /dev/null +++ b/sources/plugins/wysiwygarea/plugin.js | |||
@@ -0,0 +1,713 @@ | |||
1 | /** | ||
2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | ||
4 | */ | ||
5 | |||
6 | /** | ||
7 | * @fileOverview The WYSIWYG Area plugin. It registers the "wysiwyg" editing | ||
8 | * mode, which handles the main editing area space. | ||
9 | */ | ||
10 | |||
11 | ( function() { | ||
12 | var framedWysiwyg; | ||
13 | |||
14 | CKEDITOR.plugins.add( 'wysiwygarea', { | ||
15 | init: function( editor ) { | ||
16 | if ( editor.config.fullPage ) { | ||
17 | editor.addFeature( { | ||
18 | allowedContent: 'html head title; style [media,type]; body (*)[id]; meta link [*]', | ||
19 | requiredContent: 'body' | ||
20 | } ); | ||
21 | } | ||
22 | |||
23 | editor.addMode( 'wysiwyg', function( callback ) { | ||
24 | var src = 'document.open();' + | ||
25 | // In IE, the document domain must be set any time we call document.open(). | ||
26 | ( CKEDITOR.env.ie ? '(' + CKEDITOR.tools.fixDomain + ')();' : '' ) + | ||
27 | 'document.close();'; | ||
28 | |||
29 | // With IE, the custom domain has to be taken care at first, | ||
30 | // for other browers, the 'src' attribute should be left empty to | ||
31 | // trigger iframe's 'load' event. | ||
32 | // Microsoft Edge throws "Permission Denied" if treated like an IE (#13441). | ||
33 | if ( CKEDITOR.env.air ) { | ||
34 | src = 'javascript:void(0)'; // jshint ignore:line | ||
35 | } else if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) { | ||
36 | src = 'javascript:void(function(){' + encodeURIComponent( src ) + '}())'; // jshint ignore:line | ||
37 | } else { | ||
38 | src = ''; | ||
39 | } | ||
40 | |||
41 | var iframe = CKEDITOR.dom.element.createFromHtml( '<iframe src="' + src + '" frameBorder="0"></iframe>' ); | ||
42 | iframe.setStyles( { width: '100%', height: '100%' } ); | ||
43 | iframe.addClass( 'cke_wysiwyg_frame' ).addClass( 'cke_reset' ); | ||
44 | |||
45 | var contentSpace = editor.ui.space( 'contents' ); | ||
46 | contentSpace.append( iframe ); | ||
47 | |||
48 | |||
49 | // Asynchronous iframe loading is only required in IE>8 and Gecko (other reasons probably). | ||
50 | // Do not use it on WebKit as it'll break the browser-back navigation. | ||
51 | var useOnloadEvent = ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) || CKEDITOR.env.gecko; | ||
52 | if ( useOnloadEvent ) | ||
53 | iframe.on( 'load', onLoad ); | ||
54 | |||
55 | var frameLabel = editor.title, | ||
56 | helpLabel = editor.fire( 'ariaEditorHelpLabel', {} ).label; | ||
57 | |||
58 | if ( frameLabel ) { | ||
59 | if ( CKEDITOR.env.ie && helpLabel ) | ||
60 | frameLabel += ', ' + helpLabel; | ||
61 | |||
62 | iframe.setAttribute( 'title', frameLabel ); | ||
63 | } | ||
64 | |||
65 | if ( helpLabel ) { | ||
66 | var labelId = CKEDITOR.tools.getNextId(), | ||
67 | desc = CKEDITOR.dom.element.createFromHtml( '<span id="' + labelId + '" class="cke_voice_label">' + helpLabel + '</span>' ); | ||
68 | |||
69 | contentSpace.append( desc, 1 ); | ||
70 | iframe.setAttribute( 'aria-describedby', labelId ); | ||
71 | } | ||
72 | |||
73 | // Remove the ARIA description. | ||
74 | editor.on( 'beforeModeUnload', function( evt ) { | ||
75 | evt.removeListener(); | ||
76 | if ( desc ) | ||
77 | desc.remove(); | ||
78 | } ); | ||
79 | |||
80 | iframe.setAttributes( { | ||
81 | tabIndex: editor.tabIndex, | ||
82 | allowTransparency: 'true' | ||
83 | } ); | ||
84 | |||
85 | // Execute onLoad manually for all non IE||Gecko browsers. | ||
86 | !useOnloadEvent && onLoad(); | ||
87 | |||
88 | editor.fire( 'ariaWidget', iframe ); | ||
89 | |||
90 | function onLoad( evt ) { | ||
91 | evt && evt.removeListener(); | ||
92 | editor.editable( new framedWysiwyg( editor, iframe.$.contentWindow.document.body ) ); | ||
93 | editor.setData( editor.getData( 1 ), callback ); | ||
94 | } | ||
95 | } ); | ||
96 | } | ||
97 | } ); | ||
98 | |||
99 | /** | ||
100 | * Adds the path to a stylesheet file to the exisiting {@link CKEDITOR.config#contentsCss} value. | ||
101 | * | ||
102 | * **Note:** This method is available only with the `wysiwygarea` plugin and only affects | ||
103 | * classic editors based on it (so it does not affect inline editors). | ||
104 | * | ||
105 | * editor.addContentsCss( 'assets/contents.css' ); | ||
106 | * | ||
107 | * @since 4.4 | ||
108 | * @param {String} cssPath The path to the stylesheet file which should be added. | ||
109 | * @member CKEDITOR.editor | ||
110 | */ | ||
111 | CKEDITOR.editor.prototype.addContentsCss = function( cssPath ) { | ||
112 | var cfg = this.config, | ||
113 | curContentsCss = cfg.contentsCss; | ||
114 | |||
115 | // Convert current value into array. | ||
116 | if ( !CKEDITOR.tools.isArray( curContentsCss ) ) | ||
117 | cfg.contentsCss = curContentsCss ? [ curContentsCss ] : []; | ||
118 | |||
119 | cfg.contentsCss.push( cssPath ); | ||
120 | }; | ||
121 | |||
122 | function onDomReady( win ) { | ||
123 | var editor = this.editor, | ||
124 | doc = win.document, | ||
125 | body = doc.body; | ||
126 | |||
127 | // Remove helper scripts from the DOM. | ||
128 | var script = doc.getElementById( 'cke_actscrpt' ); | ||
129 | script && script.parentNode.removeChild( script ); | ||
130 | script = doc.getElementById( 'cke_shimscrpt' ); | ||
131 | script && script.parentNode.removeChild( script ); | ||
132 | script = doc.getElementById( 'cke_basetagscrpt' ); | ||
133 | script && script.parentNode.removeChild( script ); | ||
134 | |||
135 | body.contentEditable = true; | ||
136 | |||
137 | if ( CKEDITOR.env.ie ) { | ||
138 | // Don't display the focus border. | ||
139 | body.hideFocus = true; | ||
140 | |||
141 | // Disable and re-enable the body to avoid IE from | ||
142 | // taking the editing focus at startup. (#141 / #523) | ||
143 | body.disabled = true; | ||
144 | body.removeAttribute( 'disabled' ); | ||
145 | } | ||
146 | |||
147 | delete this._.isLoadingData; | ||
148 | |||
149 | // Play the magic to alter element reference to the reloaded one. | ||
150 | this.$ = body; | ||
151 | |||
152 | doc = new CKEDITOR.dom.document( doc ); | ||
153 | |||
154 | this.setup(); | ||
155 | this.fixInitialSelection(); | ||
156 | |||
157 | var editable = this; | ||
158 | |||
159 | // Without it IE8 has problem with removing selection in nested editable. (#13785) | ||
160 | if ( CKEDITOR.env.ie && !CKEDITOR.env.edge ) { | ||
161 | doc.getDocumentElement().addClass( doc.$.compatMode ); | ||
162 | } | ||
163 | |||
164 | // Prevent IE/Edge from leaving a new paragraph/div after deleting all contents in body. (#6966, #13142) | ||
165 | if ( CKEDITOR.env.ie && !CKEDITOR.env.edge && editor.enterMode != CKEDITOR.ENTER_P ) { | ||
166 | removeSuperfluousElement( 'p' ); | ||
167 | } else if ( CKEDITOR.env.edge && editor.enterMode != CKEDITOR.ENTER_DIV ) { | ||
168 | removeSuperfluousElement( 'div' ); | ||
169 | } | ||
170 | |||
171 | // Fix problem with cursor not appearing in Webkit and IE11+ when clicking below the body (#10945, #10906). | ||
172 | // Fix for older IEs (8-10 and QM) is placed inside selection.js. | ||
173 | if ( CKEDITOR.env.webkit || ( CKEDITOR.env.ie && CKEDITOR.env.version > 10 ) ) { | ||
174 | doc.getDocumentElement().on( 'mousedown', function( evt ) { | ||
175 | if ( evt.data.getTarget().is( 'html' ) ) { | ||
176 | // IE needs this timeout. Webkit does not, but it does not cause problems too. | ||
177 | setTimeout( function() { | ||
178 | editor.editable().focus(); | ||
179 | } ); | ||
180 | } | ||
181 | } ); | ||
182 | } | ||
183 | |||
184 | // Config props: disableObjectResizing and disableNativeTableHandles handler. | ||
185 | objectResizeDisabler( editor ); | ||
186 | |||
187 | // Enable dragging of position:absolute elements in IE. | ||
188 | try { | ||
189 | editor.document.$.execCommand( '2D-position', false, true ); | ||
190 | } catch ( e ) {} | ||
191 | |||
192 | if ( CKEDITOR.env.gecko || CKEDITOR.env.ie && editor.document.$.compatMode == 'CSS1Compat' ) { | ||
193 | this.attachListener( this, 'keydown', function( evt ) { | ||
194 | var keyCode = evt.data.getKeystroke(); | ||
195 | |||
196 | // PageUp OR PageDown | ||
197 | if ( keyCode == 33 || keyCode == 34 ) { | ||
198 | // PageUp/PageDown scrolling is broken in document | ||
199 | // with standard doctype, manually fix it. (#4736) | ||
200 | if ( CKEDITOR.env.ie ) { | ||
201 | setTimeout( function() { | ||
202 | editor.getSelection().scrollIntoView(); | ||
203 | }, 0 ); | ||
204 | } | ||
205 | // Page up/down cause editor selection to leak | ||
206 | // outside of editable thus we try to intercept | ||
207 | // the behavior, while it affects only happen | ||
208 | // when editor contents are not overflowed. (#7955) | ||
209 | else if ( editor.window.$.innerHeight > this.$.offsetHeight ) { | ||
210 | var range = editor.createRange(); | ||
211 | range[ keyCode == 33 ? 'moveToElementEditStart' : 'moveToElementEditEnd' ]( this ); | ||
212 | range.select(); | ||
213 | evt.data.preventDefault(); | ||
214 | } | ||
215 | } | ||
216 | } ); | ||
217 | } | ||
218 | |||
219 | if ( CKEDITOR.env.ie ) { | ||
220 | // [IE] Iframe will still keep the selection when blurred, if | ||
221 | // focus is moved onto a non-editing host, e.g. link or button, but | ||
222 | // it becomes a problem for the object type selection, since the resizer | ||
223 | // handler attached on it will mark other part of the UI, especially | ||
224 | // for the dialog. (#8157) | ||
225 | // [IE<8 & Opera] Even worse For old IEs, the cursor will not vanish even if | ||
226 | // the selection has been moved to another text input in some cases. (#4716) | ||
227 | // | ||
228 | // Now the range restore is disabled, so we simply force IE to clean | ||
229 | // up the selection before blur. | ||
230 | this.attachListener( doc, 'blur', function() { | ||
231 | // Error proof when the editor is not visible. (#6375) | ||
232 | try { | ||
233 | doc.$.selection.empty(); | ||
234 | } catch ( er ) {} | ||
235 | } ); | ||
236 | } | ||
237 | |||
238 | if ( CKEDITOR.env.iOS ) { | ||
239 | // [iOS] If touch is bound to any parent of the iframe blur happens on any touch | ||
240 | // event and body becomes the focused element (#10714). | ||
241 | this.attachListener( doc, 'touchend', function() { | ||
242 | win.focus(); | ||
243 | } ); | ||
244 | } | ||
245 | |||
246 | var title = editor.document.getElementsByTag( 'title' ).getItem( 0 ); | ||
247 | // document.title is malfunctioning on Chrome, so get value from the element (#12402). | ||
248 | title.data( 'cke-title', title.getText() ); | ||
249 | |||
250 | // [IE] JAWS will not recognize the aria label we used on the iframe | ||
251 | // unless the frame window title string is used as the voice label, | ||
252 | // backup the original one and restore it on output. | ||
253 | if ( CKEDITOR.env.ie ) | ||
254 | editor.document.$.title = this._.docTitle; | ||
255 | |||
256 | CKEDITOR.tools.setTimeout( function() { | ||
257 | // Editable is ready after first setData. | ||
258 | if ( this.status == 'unloaded' ) | ||
259 | this.status = 'ready'; | ||
260 | |||
261 | editor.fire( 'contentDom' ); | ||
262 | |||
263 | if ( this._.isPendingFocus ) { | ||
264 | editor.focus(); | ||
265 | this._.isPendingFocus = false; | ||
266 | } | ||
267 | |||
268 | setTimeout( function() { | ||
269 | editor.fire( 'dataReady' ); | ||
270 | }, 0 ); | ||
271 | }, 0, this ); | ||
272 | |||
273 | function removeSuperfluousElement( tagName ) { | ||
274 | var lockRetain = false; | ||
275 | |||
276 | // Superfluous elements appear after keydown | ||
277 | // and before keyup, so the procedure is as follows: | ||
278 | // 1. On first keydown mark all elements with | ||
279 | // a specified tag name as non-superfluous. | ||
280 | editable.attachListener( editable, 'keydown', function() { | ||
281 | var body = doc.getBody(), | ||
282 | retained = body.getElementsByTag( tagName ); | ||
283 | |||
284 | if ( !lockRetain ) { | ||
285 | for ( var i = 0; i < retained.count(); i++ ) { | ||
286 | retained.getItem( i ).setCustomData( 'retain', true ); | ||
287 | } | ||
288 | lockRetain = true; | ||
289 | } | ||
290 | }, null, null, 1 ); | ||
291 | |||
292 | // 2. On keyup remove all elements that were not marked | ||
293 | // as non-superfluous (which means they must have had appeared in the meantime). | ||
294 | // Also we should preserve all temporary elements inserted by editor – otherwise we'd likely | ||
295 | // leak fake selection's content into editable due to removing hidden selection container (#14831). | ||
296 | editable.attachListener( editable, 'keyup', function() { | ||
297 | var elements = doc.getElementsByTag( tagName ); | ||
298 | if ( lockRetain ) { | ||
299 | if ( elements.count() == 1 && !elements.getItem( 0 ).getCustomData( 'retain' ) && | ||
300 | !elements.getItem( 0 ).hasAttribute( 'data-cke-temp' ) ) { | ||
301 | elements.getItem( 0 ).remove( 1 ); | ||
302 | } | ||
303 | lockRetain = false; | ||
304 | } | ||
305 | } ); | ||
306 | } | ||
307 | } | ||
308 | |||
309 | framedWysiwyg = CKEDITOR.tools.createClass( { | ||
310 | $: function() { | ||
311 | this.base.apply( this, arguments ); | ||
312 | |||
313 | this._.frameLoadedHandler = CKEDITOR.tools.addFunction( function( win ) { | ||
314 | // Avoid opening design mode in a frame window thread, | ||
315 | // which will cause host page scrolling.(#4397) | ||
316 | CKEDITOR.tools.setTimeout( onDomReady, 0, this, win ); | ||
317 | }, this ); | ||
318 | |||
319 | this._.docTitle = this.getWindow().getFrame().getAttribute( 'title' ); | ||
320 | }, | ||
321 | |||
322 | base: CKEDITOR.editable, | ||
323 | |||
324 | proto: { | ||
325 | setData: function( data, isSnapshot ) { | ||
326 | var editor = this.editor; | ||
327 | |||
328 | if ( isSnapshot ) { | ||
329 | this.setHtml( data ); | ||
330 | this.fixInitialSelection(); | ||
331 | |||
332 | // Fire dataReady for the consistency with inline editors | ||
333 | // and because it makes sense. (#10370) | ||
334 | editor.fire( 'dataReady' ); | ||
335 | } | ||
336 | else { | ||
337 | this._.isLoadingData = true; | ||
338 | editor._.dataStore = { id: 1 }; | ||
339 | |||
340 | var config = editor.config, | ||
341 | fullPage = config.fullPage, | ||
342 | docType = config.docType; | ||
343 | |||
344 | // Build the additional stuff to be included into <head>. | ||
345 | var headExtra = CKEDITOR.tools.buildStyleHtml( iframeCssFixes() ).replace( /<style>/, '<style data-cke-temp="1">' ); | ||
346 | |||
347 | if ( !fullPage ) | ||
348 | headExtra += CKEDITOR.tools.buildStyleHtml( editor.config.contentsCss ); | ||
349 | |||
350 | var baseTag = config.baseHref ? '<base href="' + config.baseHref + '" data-cke-temp="1" />' : ''; | ||
351 | |||
352 | if ( fullPage ) { | ||
353 | // Search and sweep out the doctype declaration. | ||
354 | data = data.replace( /<!DOCTYPE[^>]*>/i, function( match ) { | ||
355 | editor.docType = docType = match; | ||
356 | return ''; | ||
357 | } ).replace( /<\?xml\s[^\?]*\?>/i, function( match ) { | ||
358 | editor.xmlDeclaration = match; | ||
359 | return ''; | ||
360 | } ); | ||
361 | } | ||
362 | |||
363 | // Get the HTML version of the data. | ||
364 | data = editor.dataProcessor.toHtml( data ); | ||
365 | |||
366 | if ( fullPage ) { | ||
367 | // Check if the <body> tag is available. | ||
368 | if ( !( /<body[\s|>]/ ).test( data ) ) | ||
369 | data = '<body>' + data; | ||
370 | |||
371 | // Check if the <html> tag is available. | ||
372 | if ( !( /<html[\s|>]/ ).test( data ) ) | ||
373 | data = '<html>' + data + '</html>'; | ||
374 | |||
375 | // Check if the <head> tag is available. | ||
376 | if ( !( /<head[\s|>]/ ).test( data ) ) | ||
377 | data = data.replace( /<html[^>]*>/, '$&<head><title></title></head>' ); | ||
378 | else if ( !( /<title[\s|>]/ ).test( data ) ) | ||
379 | data = data.replace( /<head[^>]*>/, '$&<title></title>' ); | ||
380 | |||
381 | // The base must be the first tag in the HEAD, e.g. to get relative | ||
382 | // links on styles. | ||
383 | baseTag && ( data = data.replace( /<head[^>]*?>/, '$&' + baseTag ) ); | ||
384 | |||
385 | // Inject the extra stuff into <head>. | ||
386 | // Attention: do not change it before testing it well. (V2) | ||
387 | // This is tricky... if the head ends with <meta ... content type>, | ||
388 | // Firefox will break. But, it works if we place our extra stuff as | ||
389 | // the last elements in the HEAD. | ||
390 | data = data.replace( /<\/head\s*>/, headExtra + '$&' ); | ||
391 | |||
392 | // Add the DOCTYPE back to it. | ||
393 | data = docType + data; | ||
394 | } else { | ||
395 | data = config.docType + | ||
396 | '<html dir="' + config.contentsLangDirection + '"' + | ||
397 | ' lang="' + ( config.contentsLanguage || editor.langCode ) + '">' + | ||
398 | '<head>' + | ||
399 | '<title>' + this._.docTitle + '</title>' + | ||
400 | baseTag + | ||
401 | headExtra + | ||
402 | '</head>' + | ||
403 | '<body' + ( config.bodyId ? ' id="' + config.bodyId + '"' : '' ) + | ||
404 | ( config.bodyClass ? ' class="' + config.bodyClass + '"' : '' ) + | ||
405 | '>' + | ||
406 | data + | ||
407 | '</body>' + | ||
408 | '</html>'; | ||
409 | } | ||
410 | |||
411 | if ( CKEDITOR.env.gecko ) { | ||
412 | // Hack to make Fx put cursor at the start of doc on fresh focus. | ||
413 | data = data.replace( /<body/, '<body contenteditable="true" ' ); | ||
414 | |||
415 | // Another hack which is used by onDomReady to remove a leading | ||
416 | // <br> which is inserted by Firefox 3.6 when document.write is called. | ||
417 | // This additional <br> is present because of contenteditable="true" | ||
418 | if ( CKEDITOR.env.version < 20000 ) | ||
419 | data = data.replace( /<body[^>]*>/, '$&<!-- cke-content-start -->' ); | ||
420 | } | ||
421 | |||
422 | // The script that launches the bootstrap logic on 'domReady', so the document | ||
423 | // is fully editable even before the editing iframe is fully loaded (#4455). | ||
424 | var bootstrapCode = | ||
425 | '<script id="cke_actscrpt" type="text/javascript"' + ( CKEDITOR.env.ie ? ' defer="defer" ' : '' ) + '>' + | ||
426 | 'var wasLoaded=0;' + // It must be always set to 0 as it remains as a window property. | ||
427 | 'function onload(){' + | ||
428 | 'if(!wasLoaded)' + // FF3.6 calls onload twice when editor.setData. Stop that. | ||
429 | 'window.parent.CKEDITOR.tools.callFunction(' + this._.frameLoadedHandler + ',window);' + | ||
430 | 'wasLoaded=1;' + | ||
431 | '}' + | ||
432 | ( CKEDITOR.env.ie ? 'onload();' : 'document.addEventListener("DOMContentLoaded", onload, false );' ) + | ||
433 | '</script>'; | ||
434 | |||
435 | // For IE<9 add support for HTML5's elements. | ||
436 | // Note: this code must not be deferred. | ||
437 | if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 ) { | ||
438 | bootstrapCode += | ||
439 | '<script id="cke_shimscrpt">' + | ||
440 | 'window.parent.CKEDITOR.tools.enableHtml5Elements(document)' + | ||
441 | '</script>'; | ||
442 | } | ||
443 | |||
444 | // IE<10 needs this hack to properly enable <base href="...">. | ||
445 | // See: http://stackoverflow.com/a/13373180/1485219 (#11910). | ||
446 | if ( baseTag && CKEDITOR.env.ie && CKEDITOR.env.version < 10 ) { | ||
447 | bootstrapCode += | ||
448 | '<script id="cke_basetagscrpt">' + | ||
449 | 'var baseTag = document.querySelector( "base" );' + | ||
450 | 'baseTag.href = baseTag.href;' + | ||
451 | '</script>'; | ||
452 | } | ||
453 | |||
454 | data = data.replace( /(?=\s*<\/(:?head)>)/, bootstrapCode ); | ||
455 | |||
456 | // Current DOM will be deconstructed by document.write, cleanup required. | ||
457 | this.clearCustomData(); | ||
458 | this.clearListeners(); | ||
459 | |||
460 | editor.fire( 'contentDomUnload' ); | ||
461 | |||
462 | var doc = this.getDocument(); | ||
463 | |||
464 | // Work around Firefox bug - error prune when called from XUL (#320), | ||
465 | // defer it thanks to the async nature of this method. | ||
466 | try { | ||
467 | doc.write( data ); | ||
468 | } catch ( e ) { | ||
469 | setTimeout( function() { | ||
470 | doc.write( data ); | ||
471 | }, 0 ); | ||
472 | } | ||
473 | } | ||
474 | }, | ||
475 | |||
476 | getData: function( isSnapshot ) { | ||
477 | if ( isSnapshot ) | ||
478 | return this.getHtml(); | ||
479 | else { | ||
480 | var editor = this.editor, | ||
481 | config = editor.config, | ||
482 | fullPage = config.fullPage, | ||
483 | docType = fullPage && editor.docType, | ||
484 | xmlDeclaration = fullPage && editor.xmlDeclaration, | ||
485 | doc = this.getDocument(); | ||
486 | |||
487 | var data = fullPage ? doc.getDocumentElement().getOuterHtml() : doc.getBody().getHtml(); | ||
488 | |||
489 | // BR at the end of document is bogus node for Mozilla. (#5293). | ||
490 | // Prevent BRs from disappearing from the end of the content | ||
491 | // while enterMode is ENTER_BR (#10146). | ||
492 | if ( CKEDITOR.env.gecko && config.enterMode != CKEDITOR.ENTER_BR ) | ||
493 | data = data.replace( /<br>(?=\s*(:?$|<\/body>))/, '' ); | ||
494 | |||
495 | data = editor.dataProcessor.toDataFormat( data ); | ||
496 | |||
497 | if ( xmlDeclaration ) | ||
498 | data = xmlDeclaration + '\n' + data; | ||
499 | if ( docType ) | ||
500 | data = docType + '\n' + data; | ||
501 | |||
502 | return data; | ||
503 | } | ||
504 | }, | ||
505 | |||
506 | focus: function() { | ||
507 | if ( this._.isLoadingData ) | ||
508 | this._.isPendingFocus = true; | ||
509 | else | ||
510 | framedWysiwyg.baseProto.focus.call( this ); | ||
511 | }, | ||
512 | |||
513 | detach: function() { | ||
514 | var editor = this.editor, | ||
515 | doc = editor.document, | ||
516 | iframe, | ||
517 | onResize; | ||
518 | |||
519 | // Trying to access window's frameElement property on Edge throws an exception | ||
520 | // when frame was already removed from DOM. (#13850, #13790) | ||
521 | try { | ||
522 | iframe = editor.window.getFrame(); | ||
523 | } catch ( e ) {} | ||
524 | |||
525 | framedWysiwyg.baseProto.detach.call( this ); | ||
526 | |||
527 | // Memory leak proof. | ||
528 | this.clearCustomData(); | ||
529 | doc.getDocumentElement().clearCustomData(); | ||
530 | CKEDITOR.tools.removeFunction( this._.frameLoadedHandler ); | ||
531 | |||
532 | // On IE, iframe is returned even after remove() method is called on it. | ||
533 | // Checking if parent is present fixes this issue. (#13850) | ||
534 | if ( iframe && iframe.getParent() ) { | ||
535 | iframe.clearCustomData(); | ||
536 | onResize = iframe.removeCustomData( 'onResize' ); | ||
537 | onResize && onResize.removeListener(); | ||
538 | |||
539 | // IE BUG: When destroying editor DOM with the selection remains inside | ||
540 | // editing area would break IE7/8's selection system, we have to put the editing | ||
541 | // iframe offline first. (#3812 and #5441) | ||
542 | iframe.remove(); | ||
543 | } else { | ||
544 | CKEDITOR.warn( 'editor-destroy-iframe' ); | ||
545 | } | ||
546 | } | ||
547 | } | ||
548 | } ); | ||
549 | |||
550 | function objectResizeDisabler( editor ) { | ||
551 | if ( CKEDITOR.env.gecko ) { | ||
552 | // FF allows to change resizing preferences by calling execCommand. | ||
553 | try { | ||
554 | var doc = editor.document.$; | ||
555 | doc.execCommand( 'enableObjectResizing', false, !editor.config.disableObjectResizing ); | ||
556 | doc.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ); | ||
557 | } catch ( e ) {} | ||
558 | } else if ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 && editor.config.disableObjectResizing ) { | ||
559 | // It's possible to prevent resizing up to IE10. | ||
560 | blockResizeStart( editor ); | ||
561 | } | ||
562 | |||
563 | // Disables resizing by preventing default action on resizestart event. | ||
564 | function blockResizeStart() { | ||
565 | var lastListeningElement; | ||
566 | |||
567 | // We'll attach only one listener at a time, instead of adding it to every img, input, hr etc. | ||
568 | // Listener will be attached upon selectionChange, we'll also check if there was any element that | ||
569 | // got listener before (lastListeningElement) - if so we need to remove previous listener. | ||
570 | editor.editable().attachListener( editor, 'selectionChange', function() { | ||
571 | var selectedElement = editor.getSelection().getSelectedElement(); | ||
572 | |||
573 | if ( selectedElement ) { | ||
574 | if ( lastListeningElement ) { | ||
575 | lastListeningElement.detachEvent( 'onresizestart', resizeStartListener ); | ||
576 | lastListeningElement = null; | ||
577 | } | ||
578 | |||
579 | // IE requires using attachEvent, because it does not work using W3C compilant addEventListener, | ||
580 | // tested with IE10. | ||
581 | selectedElement.$.attachEvent( 'onresizestart', resizeStartListener ); | ||
582 | lastListeningElement = selectedElement.$; | ||
583 | } | ||
584 | } ); | ||
585 | } | ||
586 | |||
587 | function resizeStartListener( evt ) { | ||
588 | evt.returnValue = false; | ||
589 | } | ||
590 | } | ||
591 | |||
592 | function iframeCssFixes() { | ||
593 | var css = []; | ||
594 | |||
595 | // IE>=8 stricts mode doesn't have 'contentEditable' in effect | ||
596 | // on element unless it has layout. (#5562) | ||
597 | if ( CKEDITOR.document.$.documentMode >= 8 ) { | ||
598 | css.push( 'html.CSS1Compat [contenteditable=false]{min-height:0 !important}' ); | ||
599 | |||
600 | var selectors = []; | ||
601 | |||
602 | for ( var tag in CKEDITOR.dtd.$removeEmpty ) | ||
603 | selectors.push( 'html.CSS1Compat ' + tag + '[contenteditable=false]' ); | ||
604 | |||
605 | css.push( selectors.join( ',' ) + '{display:inline-block}' ); | ||
606 | } | ||
607 | // Set the HTML style to 100% to have the text cursor in affect (#6341) | ||
608 | else if ( CKEDITOR.env.gecko ) { | ||
609 | css.push( 'html{height:100% !important}' ); | ||
610 | css.push( 'img:-moz-broken{-moz-force-broken-image-icon:1;min-width:24px;min-height:24px}' ); | ||
611 | } | ||
612 | |||
613 | // #6341: The text cursor must be set on the editor area. | ||
614 | // #6632: Avoid having "text" shape of cursor in IE7 scrollbars. | ||
615 | css.push( 'html{cursor:text;*cursor:auto}' ); | ||
616 | |||
617 | // Use correct cursor for these elements | ||
618 | css.push( 'img,input,textarea{cursor:default}' ); | ||
619 | |||
620 | return css.join( '\n' ); | ||
621 | } | ||
622 | } )(); | ||
623 | |||
624 | /** | ||
625 | * Disables the ability to resize objects (images and tables) in the editing area. | ||
626 | * | ||
627 | * config.disableObjectResizing = true; | ||
628 | * | ||
629 | * **Note:** Because of incomplete implementation of editing features in browsers | ||
630 | * this option does not work for inline editors (see ticket [#10197](http://dev.ckeditor.com/ticket/10197)), | ||
631 | * does not work in Internet Explorer 11+ (see [#9317](http://dev.ckeditor.com/ticket/9317#comment:16) and | ||
632 | * [IE11+ issue](https://connect.microsoft.com/IE/feedback/details/742593/please-respect-execcommand-enableobjectresizing-in-contenteditable-elements)). | ||
633 | * In Internet Explorer 8-10 this option only blocks resizing, but it is unable to hide the resize handles. | ||
634 | * | ||
635 | * @cfg | ||
636 | * @member CKEDITOR.config | ||
637 | */ | ||
638 | CKEDITOR.config.disableObjectResizing = false; | ||
639 | |||
640 | /** | ||
641 | * Disables the "table tools" offered natively by the browser (currently | ||
642 | * Firefox only) to perform quick table editing operations, like adding or | ||
643 | * deleting rows and columns. | ||
644 | * | ||
645 | * config.disableNativeTableHandles = false; | ||
646 | * | ||
647 | * @cfg | ||
648 | * @member CKEDITOR.config | ||
649 | */ | ||
650 | CKEDITOR.config.disableNativeTableHandles = true; | ||
651 | |||
652 | /** | ||
653 | * Disables the built-in spell checker if the browser provides one. | ||
654 | * | ||
655 | * **Note:** Although word suggestions provided natively by the browsers will | ||
656 | * not appear in CKEditor's default context menu, | ||
657 | * users can always reach the native context menu by holding the | ||
658 | * *Ctrl* key when right-clicking if {@link #browserContextMenuOnCtrl} | ||
659 | * is enabled or you are simply not using the | ||
660 | * [context menu](http://ckeditor.com/addon/contextmenu) plugin. | ||
661 | * | ||
662 | * config.disableNativeSpellChecker = false; | ||
663 | * | ||
664 | * @cfg | ||
665 | * @member CKEDITOR.config | ||
666 | */ | ||
667 | CKEDITOR.config.disableNativeSpellChecker = true; | ||
668 | |||
669 | /** | ||
670 | * Language code of the writing language which is used to author the editor | ||
671 | * content. This option accepts one single entry value in the format defined in the | ||
672 | * [Tags for Identifying Languages (BCP47)](http://www.ietf.org/rfc/bcp/bcp47.txt) | ||
673 | * IETF document and is used in the `lang` attribute. | ||
674 | * | ||
675 | * config.contentsLanguage = 'fr'; | ||
676 | * | ||
677 | * @cfg {String} [contentsLanguage=same value with editor's UI language] | ||
678 | * @member CKEDITOR.config | ||
679 | */ | ||
680 | |||
681 | /** | ||
682 | * The base href URL used to resolve relative and absolute URLs in the | ||
683 | * editor content. | ||
684 | * | ||
685 | * config.baseHref = 'http://www.example.com/path/'; | ||
686 | * | ||
687 | * @cfg {String} [baseHref=''] | ||
688 | * @member CKEDITOR.config | ||
689 | */ | ||
690 | |||
691 | /** | ||
692 | * Whether to automatically create wrapping blocks around inline content inside the document body. | ||
693 | * This helps to ensure the integrity of the block *Enter* mode. | ||
694 | * | ||
695 | * **Note:** This option is deprecated. Changing the default value might introduce unpredictable usability issues and is | ||
696 | * highly unrecommended. | ||
697 | * | ||
698 | * config.autoParagraph = false; | ||
699 | * | ||
700 | * @deprecated | ||
701 | * @since 3.6 | ||
702 | * @cfg {Boolean} [autoParagraph=true] | ||
703 | * @member CKEDITOR.config | ||
704 | */ | ||
705 | |||
706 | /** | ||
707 | * Fired when some elements are added to the document. | ||
708 | * | ||
709 | * @event ariaWidget | ||
710 | * @member CKEDITOR.editor | ||
711 | * @param {CKEDITOR.editor} editor This editor instance. | ||
712 | * @param {CKEDITOR.dom.element} data The element being added. | ||
713 | */ | ||