diff options
Diffstat (limited to 'app/assets/javascripts/ckeditor/plugins/image2/plugin.js')
-rw-r--r-- | app/assets/javascripts/ckeditor/plugins/image2/plugin.js | 1712 |
1 files changed, 0 insertions, 1712 deletions
diff --git a/app/assets/javascripts/ckeditor/plugins/image2/plugin.js b/app/assets/javascripts/ckeditor/plugins/image2/plugin.js deleted file mode 100644 index 3a55255..0000000 --- a/app/assets/javascripts/ckeditor/plugins/image2/plugin.js +++ /dev/null | |||
@@ -1,1712 +0,0 @@ | |||
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 | 'use strict'; | ||
7 | |||
8 | ( function() { | ||
9 | |||
10 | var template = '<img alt="" src="" />', | ||
11 | templateBlock = new CKEDITOR.template( | ||
12 | '<figure class="{captionedClass}">' + | ||
13 | template + | ||
14 | '<figcaption>{captionPlaceholder}</figcaption>' + | ||
15 | '</figure>' ), | ||
16 | alignmentsObj = { left: 0, center: 1, right: 2 }, | ||
17 | regexPercent = /^\s*(\d+\%)\s*$/i; | ||
18 | |||
19 | CKEDITOR.plugins.add( 'image2', { | ||
20 | // jscs:disable maximumLineLength | ||
21 | lang: 'af,ar,az,bg,bn,bs,ca,cs,cy,da,de,de-ch,el,en,en-au,en-ca,en-gb,eo,es,es-mx,et,eu,fa,fi,fo,fr,fr-ca,gl,gu,he,hi,hr,hu,id,is,it,ja,ka,km,ko,ku,lt,lv,mk,mn,ms,nb,nl,no,oc,pl,pt,pt-br,ro,ru,si,sk,sl,sq,sr,sr-latn,sv,th,tr,tt,ug,uk,vi,zh,zh-cn', // %REMOVE_LINE_CORE% | ||
22 | // jscs:enable maximumLineLength | ||
23 | requires: 'widget,dialog', | ||
24 | icons: 'image', | ||
25 | hidpi: true, | ||
26 | |||
27 | onLoad: function() { | ||
28 | CKEDITOR.addCss( | ||
29 | '.cke_image_nocaption{' + | ||
30 | // This is to remove unwanted space so resize | ||
31 | // wrapper is displayed property. | ||
32 | 'line-height:0' + | ||
33 | '}' + | ||
34 | '.cke_editable.cke_image_sw, .cke_editable.cke_image_sw *{cursor:sw-resize !important}' + | ||
35 | '.cke_editable.cke_image_se, .cke_editable.cke_image_se *{cursor:se-resize !important}' + | ||
36 | '.cke_image_resizer{' + | ||
37 | 'display:none;' + | ||
38 | 'position:absolute;' + | ||
39 | 'width:10px;' + | ||
40 | 'height:10px;' + | ||
41 | 'bottom:-5px;' + | ||
42 | 'right:-5px;' + | ||
43 | 'background:#000;' + | ||
44 | 'outline:1px solid #fff;' + | ||
45 | // Prevent drag handler from being misplaced (http://dev.ckeditor.com/ticket/11207). | ||
46 | 'line-height:0;' + | ||
47 | 'cursor:se-resize;' + | ||
48 | '}' + | ||
49 | '.cke_image_resizer_wrapper{' + | ||
50 | 'position:relative;' + | ||
51 | 'display:inline-block;' + | ||
52 | 'line-height:0;' + | ||
53 | '}' + | ||
54 | // Bottom-left corner style of the resizer. | ||
55 | '.cke_image_resizer.cke_image_resizer_left{' + | ||
56 | 'right:auto;' + | ||
57 | 'left:-5px;' + | ||
58 | 'cursor:sw-resize;' + | ||
59 | '}' + | ||
60 | '.cke_widget_wrapper:hover .cke_image_resizer,' + | ||
61 | '.cke_image_resizer.cke_image_resizing{' + | ||
62 | 'display:block' + | ||
63 | '}' + | ||
64 | // Expand widget wrapper when linked inline image. | ||
65 | '.cke_widget_wrapper>a{' + | ||
66 | 'display:inline-block' + | ||
67 | '}' ); | ||
68 | }, | ||
69 | |||
70 | init: function( editor ) { | ||
71 | // Adapts configuration from original image plugin. Should be removed | ||
72 | // when we'll rename image2 to image. | ||
73 | var config = editor.config, | ||
74 | lang = editor.lang.image2, | ||
75 | image = widgetDef( editor ); | ||
76 | |||
77 | // Since filebrowser plugin discovers config properties by dialog (plugin?) | ||
78 | // names (sic!), this hack will be necessary as long as Image2 is not named | ||
79 | // Image. And since Image2 will never be Image, for sure some filebrowser logic | ||
80 | // got to be refined. | ||
81 | config.filebrowserImage2BrowseUrl = config.filebrowserImageBrowseUrl; | ||
82 | config.filebrowserImage2UploadUrl = config.filebrowserImageUploadUrl; | ||
83 | |||
84 | // Add custom elementspath names to widget definition. | ||
85 | image.pathName = lang.pathName; | ||
86 | image.editables.caption.pathName = lang.pathNameCaption; | ||
87 | |||
88 | // Register the widget. | ||
89 | editor.widgets.add( 'image', image ); | ||
90 | |||
91 | // Add toolbar button for this plugin. | ||
92 | editor.ui.addButton && editor.ui.addButton( 'Image', { | ||
93 | label: editor.lang.common.image, | ||
94 | command: 'image', | ||
95 | toolbar: 'insert,10' | ||
96 | } ); | ||
97 | |||
98 | // Register context menu option for editing widget. | ||
99 | if ( editor.contextMenu ) { | ||
100 | editor.addMenuGroup( 'image', 10 ); | ||
101 | |||
102 | editor.addMenuItem( 'image', { | ||
103 | label: lang.menu, | ||
104 | command: 'image', | ||
105 | group: 'image' | ||
106 | } ); | ||
107 | } | ||
108 | |||
109 | CKEDITOR.dialog.add( 'image2', this.path + 'dialogs/image2.js' ); | ||
110 | }, | ||
111 | |||
112 | afterInit: function( editor ) { | ||
113 | // Integrate with align commands (justify plugin). | ||
114 | var align = { left: 1, right: 1, center: 1, block: 1 }, | ||
115 | integrate = alignCommandIntegrator( editor ); | ||
116 | |||
117 | for ( var value in align ) | ||
118 | integrate( value ); | ||
119 | |||
120 | // Integrate with link commands (link plugin). | ||
121 | linkCommandIntegrator( editor ); | ||
122 | } | ||
123 | } ); | ||
124 | |||
125 | // Wiget states (forms) depending on alignment and configuration. | ||
126 | // | ||
127 | // Non-captioned widget (inline styles) | ||
128 | // ┌──────┬───────────────────────────────┬─────────────────────────────┐ | ||
129 | // │Align │Internal form │Data │ | ||
130 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
131 | // │none │<wrapper> │<img /> │ | ||
132 | // │ │ <img /> │ │ | ||
133 | // │ │</wrapper> │ │ | ||
134 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
135 | // │left │<wrapper style=”float:left”> │<img style=”float:left” /> │ | ||
136 | // │ │ <img /> │ │ | ||
137 | // │ │</wrapper> │ │ | ||
138 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
139 | // │center│<wrapper> │<p style=”text-align:center”>│ | ||
140 | // │ │ <p style=”text-align:center”> │ <img /> │ | ||
141 | // │ │ <img /> │</p> │ | ||
142 | // │ │ </p> │ │ | ||
143 | // │ │</wrapper> │ │ | ||
144 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
145 | // │right │<wrapper style=”float:right”> │<img style=”float:right” /> │ | ||
146 | // │ │ <img /> │ │ | ||
147 | // │ │</wrapper> │ │ | ||
148 | // └──────┴───────────────────────────────┴─────────────────────────────┘ | ||
149 | // | ||
150 | // Non-captioned widget (config.image2_alignClasses defined) | ||
151 | // ┌──────┬───────────────────────────────┬─────────────────────────────┐ | ||
152 | // │Align │Internal form │Data │ | ||
153 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
154 | // │none │<wrapper> │<img /> │ | ||
155 | // │ │ <img /> │ │ | ||
156 | // │ │</wrapper> │ │ | ||
157 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
158 | // │left │<wrapper class=”left”> │<img class=”left” /> │ | ||
159 | // │ │ <img /> │ │ | ||
160 | // │ │</wrapper> │ │ | ||
161 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
162 | // │center│<wrapper> │<p class=”center”> │ | ||
163 | // │ │ <p class=”center”> │ <img /> │ | ||
164 | // │ │ <img /> │</p> │ | ||
165 | // │ │ </p> │ │ | ||
166 | // │ │</wrapper> │ │ | ||
167 | // ├──────┼───────────────────────────────┼─────────────────────────────┤ | ||
168 | // │right │<wrapper class=”right”> │<img class=”right” /> │ | ||
169 | // │ │ <img /> │ │ | ||
170 | // │ │</wrapper> │ │ | ||
171 | // └──────┴───────────────────────────────┴─────────────────────────────┘ | ||
172 | // | ||
173 | // Captioned widget (inline styles) | ||
174 | // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐ | ||
175 | // │Align │Internal form │Data │ | ||
176 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
177 | // │none │<wrapper> │<figure /> │ | ||
178 | // │ │ <figure /> │ │ | ||
179 | // │ │</wrapper> │ │ | ||
180 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
181 | // │left │<wrapper style=”float:left”> │<figure style=”float:left” /> │ | ||
182 | // │ │ <figure /> │ │ | ||
183 | // │ │</wrapper> │ │ | ||
184 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
185 | // │center│<wrapper style=”text-align:center”> │<div style=”text-align:center”> │ | ||
186 | // │ │ <figure style=”display:inline-block” />│ <figure style=”display:inline-block” />│ | ||
187 | // │ │</wrapper> │</p> │ | ||
188 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
189 | // │right │<wrapper style=”float:right”> │<figure style=”float:right” /> │ | ||
190 | // │ │ <figure /> │ │ | ||
191 | // │ │</wrapper> │ │ | ||
192 | // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘ | ||
193 | // | ||
194 | // Captioned widget (config.image2_alignClasses defined) | ||
195 | // ┌──────┬────────────────────────────────────────┬────────────────────────────────────────┐ | ||
196 | // │Align │Internal form │Data │ | ||
197 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
198 | // │none │<wrapper> │<figure /> │ | ||
199 | // │ │ <figure /> │ │ | ||
200 | // │ │</wrapper> │ │ | ||
201 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
202 | // │left │<wrapper class=”left”> │<figure class=”left” /> │ | ||
203 | // │ │ <figure /> │ │ | ||
204 | // │ │</wrapper> │ │ | ||
205 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
206 | // │center│<wrapper class=”center”> │<div class=”center”> │ | ||
207 | // │ │ <figure /> │ <figure /> │ | ||
208 | // │ │</wrapper> │</p> │ | ||
209 | // ├──────┼────────────────────────────────────────┼────────────────────────────────────────┤ | ||
210 | // │right │<wrapper class=”right”> │<figure class=”right” /> │ | ||
211 | // │ │ <figure /> │ │ | ||
212 | // │ │</wrapper> │ │ | ||
213 | // └──────┴────────────────────────────────────────┴────────────────────────────────────────┘ | ||
214 | // | ||
215 | // @param {CKEDITOR.editor} | ||
216 | // @returns {Object} | ||
217 | function widgetDef( editor ) { | ||
218 | var alignClasses = editor.config.image2_alignClasses, | ||
219 | captionedClass = editor.config.image2_captionedClass; | ||
220 | |||
221 | function deflate() { | ||
222 | if ( this.deflated ) | ||
223 | return; | ||
224 | |||
225 | // Remember whether widget was focused before destroyed. | ||
226 | if ( editor.widgets.focused == this.widget ) | ||
227 | this.focused = true; | ||
228 | |||
229 | editor.widgets.destroy( this.widget ); | ||
230 | |||
231 | // Mark widget was destroyed. | ||
232 | this.deflated = true; | ||
233 | } | ||
234 | |||
235 | function inflate() { | ||
236 | var editable = editor.editable(), | ||
237 | doc = editor.document; | ||
238 | |||
239 | // Create a new widget. This widget will be either captioned | ||
240 | // non-captioned, block or inline according to what is the | ||
241 | // new state of the widget. | ||
242 | if ( this.deflated ) { | ||
243 | this.widget = editor.widgets.initOn( this.element, 'image', this.widget.data ); | ||
244 | |||
245 | // Once widget was re-created, it may become an inline element without | ||
246 | // block wrapper (i.e. when unaligned, end not captioned). Let's do some | ||
247 | // sort of autoparagraphing here (http://dev.ckeditor.com/ticket/10853). | ||
248 | if ( this.widget.inline && !( new CKEDITOR.dom.elementPath( this.widget.wrapper, editable ).block ) ) { | ||
249 | var block = doc.createElement( editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ); | ||
250 | block.replace( this.widget.wrapper ); | ||
251 | this.widget.wrapper.move( block ); | ||
252 | } | ||
253 | |||
254 | // The focus must be transferred from the old one (destroyed) | ||
255 | // to the new one (just created). | ||
256 | if ( this.focused ) { | ||
257 | this.widget.focus(); | ||
258 | delete this.focused; | ||
259 | } | ||
260 | |||
261 | delete this.deflated; | ||
262 | } | ||
263 | |||
264 | // If now widget was destroyed just update wrapper's alignment. | ||
265 | // According to the new state. | ||
266 | else { | ||
267 | setWrapperAlign( this.widget, alignClasses ); | ||
268 | } | ||
269 | } | ||
270 | |||
271 | return { | ||
272 | allowedContent: getWidgetAllowedContent( editor ), | ||
273 | |||
274 | requiredContent: 'img[src,alt]', | ||
275 | |||
276 | features: getWidgetFeatures( editor ), | ||
277 | |||
278 | styleableElements: 'img figure', | ||
279 | |||
280 | // This widget converts style-driven dimensions to attributes. | ||
281 | contentTransformations: [ | ||
282 | [ 'img[width]: sizeToAttribute' ] | ||
283 | ], | ||
284 | |||
285 | // This widget has an editable caption. | ||
286 | editables: { | ||
287 | caption: { | ||
288 | selector: 'figcaption', | ||
289 | allowedContent: 'br em strong sub sup u s; a[!href,target]' | ||
290 | } | ||
291 | }, | ||
292 | |||
293 | parts: { | ||
294 | image: 'img', | ||
295 | caption: 'figcaption' | ||
296 | // parts#link defined in widget#init | ||
297 | }, | ||
298 | |||
299 | // The name of this widget's dialog. | ||
300 | dialog: 'image2', | ||
301 | |||
302 | // Template of the widget: plain image. | ||
303 | template: template, | ||
304 | |||
305 | data: function() { | ||
306 | var features = this.features; | ||
307 | |||
308 | // Image can't be captioned when figcaption is disallowed (http://dev.ckeditor.com/ticket/11004). | ||
309 | if ( this.data.hasCaption && !editor.filter.checkFeature( features.caption ) ) | ||
310 | this.data.hasCaption = false; | ||
311 | |||
312 | // Image can't be aligned when floating is disallowed (http://dev.ckeditor.com/ticket/11004). | ||
313 | if ( this.data.align != 'none' && !editor.filter.checkFeature( features.align ) ) | ||
314 | this.data.align = 'none'; | ||
315 | |||
316 | // Convert the internal form of the widget from the old state to the new one. | ||
317 | this.shiftState( { | ||
318 | widget: this, | ||
319 | element: this.element, | ||
320 | oldData: this.oldData, | ||
321 | newData: this.data, | ||
322 | deflate: deflate, | ||
323 | inflate: inflate | ||
324 | } ); | ||
325 | |||
326 | // Update widget.parts.link since it will not auto-update unless widget | ||
327 | // is destroyed and re-inited. | ||
328 | if ( !this.data.link ) { | ||
329 | if ( this.parts.link ) | ||
330 | delete this.parts.link; | ||
331 | } else { | ||
332 | if ( !this.parts.link ) | ||
333 | this.parts.link = this.parts.image.getParent(); | ||
334 | } | ||
335 | |||
336 | this.parts.image.setAttributes( { | ||
337 | src: this.data.src, | ||
338 | |||
339 | // This internal is required by the editor. | ||
340 | 'data-cke-saved-src': this.data.src, | ||
341 | |||
342 | alt: this.data.alt | ||
343 | } ); | ||
344 | |||
345 | // If shifting non-captioned -> captioned, remove classes | ||
346 | // related to styles from <img/>. | ||
347 | if ( this.oldData && !this.oldData.hasCaption && this.data.hasCaption ) { | ||
348 | for ( var c in this.data.classes ) | ||
349 | this.parts.image.removeClass( c ); | ||
350 | } | ||
351 | |||
352 | // Set dimensions of the image according to gathered data. | ||
353 | // Do it only when the attributes are allowed (http://dev.ckeditor.com/ticket/11004). | ||
354 | if ( editor.filter.checkFeature( features.dimension ) ) | ||
355 | setDimensions( this ); | ||
356 | |||
357 | // Cache current data. | ||
358 | this.oldData = CKEDITOR.tools.extend( {}, this.data ); | ||
359 | }, | ||
360 | |||
361 | init: function() { | ||
362 | var helpers = CKEDITOR.plugins.image2, | ||
363 | image = this.parts.image, | ||
364 | data = { | ||
365 | hasCaption: !!this.parts.caption, | ||
366 | src: image.getAttribute( 'src' ), | ||
367 | alt: image.getAttribute( 'alt' ) || '', | ||
368 | width: image.getAttribute( 'width' ) || '', | ||
369 | height: image.getAttribute( 'height' ) || '', | ||
370 | |||
371 | // Lock ratio is on by default (http://dev.ckeditor.com/ticket/10833). | ||
372 | lock: this.ready ? helpers.checkHasNaturalRatio( image ) : true | ||
373 | }; | ||
374 | |||
375 | // If we used 'a' in widget#parts definition, it could happen that | ||
376 | // selected element is a child of widget.parts#caption. Since there's no clever | ||
377 | // way to solve it with CSS selectors, it's done like that. (http://dev.ckeditor.com/ticket/11783). | ||
378 | var link = image.getAscendant( 'a' ); | ||
379 | |||
380 | if ( link && this.wrapper.contains( link ) ) | ||
381 | this.parts.link = link; | ||
382 | |||
383 | // Depending on configuration, read style/class from element and | ||
384 | // then remove it. Removed style/class will be set on wrapper in #data listener. | ||
385 | // Note: Center alignment is detected during upcast, so only left/right cases | ||
386 | // are checked below. | ||
387 | if ( !data.align ) { | ||
388 | var alignElement = data.hasCaption ? this.element : image; | ||
389 | |||
390 | // Read the initial left/right alignment from the class set on element. | ||
391 | if ( alignClasses ) { | ||
392 | if ( alignElement.hasClass( alignClasses[ 0 ] ) ) { | ||
393 | data.align = 'left'; | ||
394 | } else if ( alignElement.hasClass( alignClasses[ 2 ] ) ) { | ||
395 | data.align = 'right'; | ||
396 | } | ||
397 | |||
398 | if ( data.align ) { | ||
399 | alignElement.removeClass( alignClasses[ alignmentsObj[ data.align ] ] ); | ||
400 | } else { | ||
401 | data.align = 'none'; | ||
402 | } | ||
403 | } | ||
404 | // Read initial float style from figure/image and then remove it. | ||
405 | else { | ||
406 | data.align = alignElement.getStyle( 'float' ) || 'none'; | ||
407 | alignElement.removeStyle( 'float' ); | ||
408 | } | ||
409 | } | ||
410 | |||
411 | // Update data.link object with attributes if the link has been discovered. | ||
412 | if ( editor.plugins.link && this.parts.link ) { | ||
413 | data.link = helpers.getLinkAttributesParser()( editor, this.parts.link ); | ||
414 | |||
415 | // Get rid of cke_widget_* classes in data. Otherwise | ||
416 | // they might appear in link dialog. | ||
417 | var advanced = data.link.advanced; | ||
418 | if ( advanced && advanced.advCSSClasses ) { | ||
419 | advanced.advCSSClasses = CKEDITOR.tools.trim( advanced.advCSSClasses.replace( /cke_\S+/, '' ) ); | ||
420 | } | ||
421 | } | ||
422 | |||
423 | // Get rid of extra vertical space when there's no caption. | ||
424 | // It will improve the look of the resizer. | ||
425 | this.wrapper[ ( data.hasCaption ? 'remove' : 'add' ) + 'Class' ]( 'cke_image_nocaption' ); | ||
426 | |||
427 | this.setData( data ); | ||
428 | |||
429 | // Setup dynamic image resizing with mouse. | ||
430 | // Don't initialize resizer when dimensions are disallowed (http://dev.ckeditor.com/ticket/11004). | ||
431 | if ( editor.filter.checkFeature( this.features.dimension ) && editor.config.image2_disableResizer !== true ) | ||
432 | setupResizer( this ); | ||
433 | |||
434 | this.shiftState = helpers.stateShifter( this.editor ); | ||
435 | |||
436 | // Add widget editing option to its context menu. | ||
437 | this.on( 'contextMenu', function( evt ) { | ||
438 | evt.data.image = CKEDITOR.TRISTATE_OFF; | ||
439 | |||
440 | // Integrate context menu items for link. | ||
441 | // Note that widget may be wrapped in a link, which | ||
442 | // does not belong to that widget (http://dev.ckeditor.com/ticket/11814). | ||
443 | if ( this.parts.link || this.wrapper.getAscendant( 'a' ) ) | ||
444 | evt.data.link = evt.data.unlink = CKEDITOR.TRISTATE_OFF; | ||
445 | } ); | ||
446 | |||
447 | // Pass the reference to this widget to the dialog. | ||
448 | this.on( 'dialog', function( evt ) { | ||
449 | evt.data.widget = this; | ||
450 | }, this ); | ||
451 | }, | ||
452 | |||
453 | // Overrides default method to handle internal mutability of Image2. | ||
454 | // @see CKEDITOR.plugins.widget#addClass | ||
455 | addClass: function( className ) { | ||
456 | getStyleableElement( this ).addClass( className ); | ||
457 | }, | ||
458 | |||
459 | // Overrides default method to handle internal mutability of Image2. | ||
460 | // @see CKEDITOR.plugins.widget#hasClass | ||
461 | hasClass: function( className ) { | ||
462 | return getStyleableElement( this ).hasClass( className ); | ||
463 | }, | ||
464 | |||
465 | // Overrides default method to handle internal mutability of Image2. | ||
466 | // @see CKEDITOR.plugins.widget#removeClass | ||
467 | removeClass: function( className ) { | ||
468 | getStyleableElement( this ).removeClass( className ); | ||
469 | }, | ||
470 | |||
471 | // Overrides default method to handle internal mutability of Image2. | ||
472 | // @see CKEDITOR.plugins.widget#getClasses | ||
473 | getClasses: ( function() { | ||
474 | var classRegex = new RegExp( '^(' + [].concat( captionedClass, alignClasses ).join( '|' ) + ')$' ); | ||
475 | |||
476 | return function() { | ||
477 | var classes = this.repository.parseElementClasses( getStyleableElement( this ).getAttribute( 'class' ) ); | ||
478 | |||
479 | // Neither config.image2_captionedClass nor config.image2_alignClasses | ||
480 | // do not belong to style classes. | ||
481 | for ( var c in classes ) { | ||
482 | if ( classRegex.test( c ) ) | ||
483 | delete classes[ c ]; | ||
484 | } | ||
485 | |||
486 | return classes; | ||
487 | }; | ||
488 | } )(), | ||
489 | |||
490 | upcast: upcastWidgetElement( editor ), | ||
491 | downcast: downcastWidgetElement( editor ), | ||
492 | |||
493 | getLabel: function() { | ||
494 | var label = ( this.data.alt || '' ) + ' ' + this.pathName; | ||
495 | |||
496 | return this.editor.lang.widget.label.replace( /%1/, label ); | ||
497 | } | ||
498 | }; | ||
499 | } | ||
500 | |||
501 | /** | ||
502 | * A set of Enhanced Image (image2) plugin helpers. | ||
503 | * | ||
504 | * @class | ||
505 | * @singleton | ||
506 | */ | ||
507 | CKEDITOR.plugins.image2 = { | ||
508 | stateShifter: function( editor ) { | ||
509 | // Tag name used for centering non-captioned widgets. | ||
510 | var doc = editor.document, | ||
511 | alignClasses = editor.config.image2_alignClasses, | ||
512 | captionedClass = editor.config.image2_captionedClass, | ||
513 | editable = editor.editable(), | ||
514 | |||
515 | // The order that stateActions get executed. It matters! | ||
516 | shiftables = [ 'hasCaption', 'align', 'link' ]; | ||
517 | |||
518 | // Atomic procedures, one per state variable. | ||
519 | var stateActions = { | ||
520 | align: function( shift, oldValue, newValue ) { | ||
521 | var el = shift.element; | ||
522 | |||
523 | // Alignment changed. | ||
524 | if ( shift.changed.align ) { | ||
525 | // No caption in the new state. | ||
526 | if ( !shift.newData.hasCaption ) { | ||
527 | // Changed to "center" (non-captioned). | ||
528 | if ( newValue == 'center' ) { | ||
529 | shift.deflate(); | ||
530 | shift.element = wrapInCentering( editor, el ); | ||
531 | } | ||
532 | |||
533 | // Changed to "non-center" from "center" while caption removed. | ||
534 | if ( !shift.changed.hasCaption && oldValue == 'center' && newValue != 'center' ) { | ||
535 | shift.deflate(); | ||
536 | shift.element = unwrapFromCentering( el ); | ||
537 | } | ||
538 | } | ||
539 | } | ||
540 | |||
541 | // Alignment remains and "center" removed caption. | ||
542 | else if ( newValue == 'center' && shift.changed.hasCaption && !shift.newData.hasCaption ) { | ||
543 | shift.deflate(); | ||
544 | shift.element = wrapInCentering( editor, el ); | ||
545 | } | ||
546 | |||
547 | // Finally set display for figure. | ||
548 | if ( !alignClasses && el.is( 'figure' ) ) { | ||
549 | if ( newValue == 'center' ) | ||
550 | el.setStyle( 'display', 'inline-block' ); | ||
551 | else | ||
552 | el.removeStyle( 'display' ); | ||
553 | } | ||
554 | }, | ||
555 | |||
556 | hasCaption: function( shift, oldValue, newValue ) { | ||
557 | // This action is for real state change only. | ||
558 | if ( !shift.changed.hasCaption ) | ||
559 | return; | ||
560 | |||
561 | // Get <img/> or <a><img/></a> from widget. Note that widget element might itself | ||
562 | // be what we're looking for. Also element can be <p style="text-align:center"><a>...</a></p>. | ||
563 | var imageOrLink; | ||
564 | if ( shift.element.is( { img: 1, a: 1 } ) ) | ||
565 | imageOrLink = shift.element; | ||
566 | else | ||
567 | imageOrLink = shift.element.findOne( 'a,img' ); | ||
568 | |||
569 | // Switching hasCaption always destroys the widget. | ||
570 | shift.deflate(); | ||
571 | |||
572 | // There was no caption, but the caption is to be added. | ||
573 | if ( newValue ) { | ||
574 | // Create new <figure> from widget template. | ||
575 | var figure = CKEDITOR.dom.element.createFromHtml( templateBlock.output( { | ||
576 | captionedClass: captionedClass, | ||
577 | captionPlaceholder: editor.lang.image2.captionPlaceholder | ||
578 | } ), doc ); | ||
579 | |||
580 | // Replace element with <figure>. | ||
581 | replaceSafely( figure, shift.element ); | ||
582 | |||
583 | // Use old <img/> or <a><img/></a> instead of the one from the template, | ||
584 | // so we won't lose additional attributes. | ||
585 | imageOrLink.replace( figure.findOne( 'img' ) ); | ||
586 | |||
587 | // Update widget's element. | ||
588 | shift.element = figure; | ||
589 | } | ||
590 | |||
591 | // The caption was present, but now it's to be removed. | ||
592 | else { | ||
593 | // Unwrap <img/> or <a><img/></a> from figure. | ||
594 | imageOrLink.replace( shift.element ); | ||
595 | |||
596 | // Update widget's element. | ||
597 | shift.element = imageOrLink; | ||
598 | } | ||
599 | }, | ||
600 | |||
601 | link: function( shift, oldValue, newValue ) { | ||
602 | if ( shift.changed.link ) { | ||
603 | var img = shift.element.is( 'img' ) ? | ||
604 | shift.element : shift.element.findOne( 'img' ), | ||
605 | link = shift.element.is( 'a' ) ? | ||
606 | shift.element : shift.element.findOne( 'a' ), | ||
607 | // Why deflate: | ||
608 | // If element is <img/>, it will be wrapped into <a>, | ||
609 | // which becomes a new widget.element. | ||
610 | // If element is <a><img/></a>, it will be unlinked | ||
611 | // so <img/> becomes a new widget.element. | ||
612 | needsDeflate = ( shift.element.is( 'a' ) && !newValue ) || ( shift.element.is( 'img' ) && newValue ), | ||
613 | newEl; | ||
614 | |||
615 | if ( needsDeflate ) | ||
616 | shift.deflate(); | ||
617 | |||
618 | // If unlinked the image, returned element is <img>. | ||
619 | if ( !newValue ) | ||
620 | newEl = unwrapFromLink( link ); | ||
621 | else { | ||
622 | // If linked the image, returned element is <a>. | ||
623 | if ( !oldValue ) | ||
624 | newEl = wrapInLink( img, shift.newData.link ); | ||
625 | |||
626 | // Set and remove all attributes associated with this state. | ||
627 | var attributes = CKEDITOR.plugins.image2.getLinkAttributesGetter()( editor, newValue ); | ||
628 | |||
629 | if ( !CKEDITOR.tools.isEmpty( attributes.set ) ) | ||
630 | ( newEl || link ).setAttributes( attributes.set ); | ||
631 | |||
632 | if ( attributes.removed.length ) | ||
633 | ( newEl || link ).removeAttributes( attributes.removed ); | ||
634 | } | ||
635 | |||
636 | if ( needsDeflate ) | ||
637 | shift.element = newEl; | ||
638 | } | ||
639 | } | ||
640 | }; | ||
641 | |||
642 | function wrapInCentering( editor, element ) { | ||
643 | var attribsAndStyles = {}; | ||
644 | |||
645 | if ( alignClasses ) | ||
646 | attribsAndStyles.attributes = { 'class': alignClasses[ 1 ] }; | ||
647 | else | ||
648 | attribsAndStyles.styles = { 'text-align': 'center' }; | ||
649 | |||
650 | // There's no gentle way to center inline element with CSS, so create p/div | ||
651 | // that wraps widget contents and does the trick either with style or class. | ||
652 | var center = doc.createElement( | ||
653 | editor.activeEnterMode == CKEDITOR.ENTER_P ? 'p' : 'div', attribsAndStyles ); | ||
654 | |||
655 | // Replace element with centering wrapper. | ||
656 | replaceSafely( center, element ); | ||
657 | element.move( center ); | ||
658 | |||
659 | return center; | ||
660 | } | ||
661 | |||
662 | function unwrapFromCentering( element ) { | ||
663 | var imageOrLink = element.findOne( 'a,img' ); | ||
664 | |||
665 | imageOrLink.replace( element ); | ||
666 | |||
667 | return imageOrLink; | ||
668 | } | ||
669 | |||
670 | // Wraps <img/> -> <a><img/></a>. | ||
671 | // Returns reference to <a>. | ||
672 | // | ||
673 | // @param {CKEDITOR.dom.element} img | ||
674 | // @param {Object} linkData | ||
675 | // @returns {CKEDITOR.dom.element} | ||
676 | function wrapInLink( img, linkData ) { | ||
677 | var link = doc.createElement( 'a', { | ||
678 | attributes: { | ||
679 | href: linkData.url | ||
680 | } | ||
681 | } ); | ||
682 | |||
683 | link.replace( img ); | ||
684 | img.move( link ); | ||
685 | |||
686 | return link; | ||
687 | } | ||
688 | |||
689 | // De-wraps <a><img/></a> -> <img/>. | ||
690 | // Returns the reference to <img/> | ||
691 | // | ||
692 | // @param {CKEDITOR.dom.element} link | ||
693 | // @returns {CKEDITOR.dom.element} | ||
694 | function unwrapFromLink( link ) { | ||
695 | var img = link.findOne( 'img' ); | ||
696 | |||
697 | img.replace( link ); | ||
698 | |||
699 | return img; | ||
700 | } | ||
701 | |||
702 | function replaceSafely( replacing, replaced ) { | ||
703 | if ( replaced.getParent() ) { | ||
704 | var range = editor.createRange(); | ||
705 | |||
706 | range.moveToPosition( replaced, CKEDITOR.POSITION_BEFORE_START ); | ||
707 | |||
708 | // Remove old element. Do it before insertion to avoid a case when | ||
709 | // element is moved from 'replaced' element before it, what creates | ||
710 | // a tricky case which insertElementIntorRange does not handle. | ||
711 | replaced.remove(); | ||
712 | |||
713 | editable.insertElementIntoRange( replacing, range ); | ||
714 | } | ||
715 | else { | ||
716 | replacing.replace( replaced ); | ||
717 | } | ||
718 | } | ||
719 | |||
720 | return function( shift ) { | ||
721 | var name, i; | ||
722 | |||
723 | shift.changed = {}; | ||
724 | |||
725 | for ( i = 0; i < shiftables.length; i++ ) { | ||
726 | name = shiftables[ i ]; | ||
727 | |||
728 | shift.changed[ name ] = shift.oldData ? | ||
729 | shift.oldData[ name ] !== shift.newData[ name ] : false; | ||
730 | } | ||
731 | |||
732 | // Iterate over possible state variables. | ||
733 | for ( i = 0; i < shiftables.length; i++ ) { | ||
734 | name = shiftables[ i ]; | ||
735 | |||
736 | stateActions[ name ]( shift, | ||
737 | shift.oldData ? shift.oldData[ name ] : null, | ||
738 | shift.newData[ name ] ); | ||
739 | } | ||
740 | |||
741 | shift.inflate(); | ||
742 | }; | ||
743 | }, | ||
744 | |||
745 | /** | ||
746 | * Checks whether the current image ratio matches the natural one | ||
747 | * by comparing dimensions. | ||
748 | * | ||
749 | * @param {CKEDITOR.dom.element} image | ||
750 | * @returns {Boolean} | ||
751 | */ | ||
752 | checkHasNaturalRatio: function( image ) { | ||
753 | var $ = image.$, | ||
754 | natural = this.getNatural( image ); | ||
755 | |||
756 | // The reason for two alternative comparisons is that the rounding can come from | ||
757 | // both dimensions, e.g. there are two cases: | ||
758 | // 1. height is computed as a rounded relation of the real height and the value of width, | ||
759 | // 2. width is computed as a rounded relation of the real width and the value of heigh. | ||
760 | return Math.round( $.clientWidth / natural.width * natural.height ) == $.clientHeight || | ||
761 | Math.round( $.clientHeight / natural.height * natural.width ) == $.clientWidth; | ||
762 | }, | ||
763 | |||
764 | /** | ||
765 | * Returns natural dimensions of the image. For modern browsers | ||
766 | * it uses natural(Width|Height). For old ones (IE8) it creates | ||
767 | * a new image and reads the dimensions. | ||
768 | * | ||
769 | * @param {CKEDITOR.dom.element} image | ||
770 | * @returns {Object} | ||
771 | */ | ||
772 | getNatural: function( image ) { | ||
773 | var dimensions; | ||
774 | |||
775 | if ( image.$.naturalWidth ) { | ||
776 | dimensions = { | ||
777 | width: image.$.naturalWidth, | ||
778 | height: image.$.naturalHeight | ||
779 | }; | ||
780 | } else { | ||
781 | var img = new Image(); | ||
782 | img.src = image.getAttribute( 'src' ); | ||
783 | |||
784 | dimensions = { | ||
785 | width: img.width, | ||
786 | height: img.height | ||
787 | }; | ||
788 | } | ||
789 | |||
790 | return dimensions; | ||
791 | }, | ||
792 | |||
793 | /** | ||
794 | * Returns an attribute getter function. Default getter comes from the Link plugin | ||
795 | * and is documented by {@link CKEDITOR.plugins.link#getLinkAttributes}. | ||
796 | * | ||
797 | * **Note:** It is possible to override this method and use a custom getter e.g. | ||
798 | * in the absence of the Link plugin. | ||
799 | * | ||
800 | * **Note:** If a custom getter is used, a data model format it produces | ||
801 | * must be compatible with {@link CKEDITOR.plugins.link#getLinkAttributes}. | ||
802 | * | ||
803 | * **Note:** A custom getter must understand the data model format produced by | ||
804 | * {@link #getLinkAttributesParser} to work correctly. | ||
805 | * | ||
806 | * @returns {Function} A function that gets (composes) link attributes. | ||
807 | * @since 4.5.5 | ||
808 | */ | ||
809 | getLinkAttributesGetter: function() { | ||
810 | // http://dev.ckeditor.com/ticket/13885 | ||
811 | return CKEDITOR.plugins.link.getLinkAttributes; | ||
812 | }, | ||
813 | |||
814 | /** | ||
815 | * Returns an attribute parser function. Default parser comes from the Link plugin | ||
816 | * and is documented by {@link CKEDITOR.plugins.link#parseLinkAttributes}. | ||
817 | * | ||
818 | * **Note:** It is possible to override this method and use a custom parser e.g. | ||
819 | * in the absence of the Link plugin. | ||
820 | * | ||
821 | * **Note:** If a custom parser is used, a data model format produced by the parser | ||
822 | * must be compatible with {@link #getLinkAttributesGetter}. | ||
823 | * | ||
824 | * **Note:** If a custom parser is used, it should be compatible with the | ||
825 | * {@link CKEDITOR.plugins.link#parseLinkAttributes} data model format. Otherwise the | ||
826 | * Link plugin dialog may not be populated correctly with parsed data. However | ||
827 | * as long as Enhanced Image is **not** used with the Link plugin dialog, any custom data model | ||
828 | * will work, being stored as an internal property of Enhanced Image widget's data only. | ||
829 | * | ||
830 | * @returns {Function} A function that parses attributes. | ||
831 | * @since 4.5.5 | ||
832 | */ | ||
833 | getLinkAttributesParser: function() { | ||
834 | // http://dev.ckeditor.com/ticket/13885 | ||
835 | return CKEDITOR.plugins.link.parseLinkAttributes; | ||
836 | } | ||
837 | }; | ||
838 | |||
839 | function setWrapperAlign( widget, alignClasses ) { | ||
840 | var wrapper = widget.wrapper, | ||
841 | align = widget.data.align, | ||
842 | hasCaption = widget.data.hasCaption; | ||
843 | |||
844 | if ( alignClasses ) { | ||
845 | // Remove all align classes first. | ||
846 | for ( var i = 3; i--; ) | ||
847 | wrapper.removeClass( alignClasses[ i ] ); | ||
848 | |||
849 | if ( align == 'center' ) { | ||
850 | // Avoid touching non-captioned, centered widgets because | ||
851 | // they have the class set on the element instead of wrapper: | ||
852 | // | ||
853 | // <div class="cke_widget_wrapper"> | ||
854 | // <p class="center-class"> | ||
855 | // <img /> | ||
856 | // </p> | ||
857 | // </div> | ||
858 | if ( hasCaption ) { | ||
859 | wrapper.addClass( alignClasses[ 1 ] ); | ||
860 | } | ||
861 | } else if ( align != 'none' ) { | ||
862 | wrapper.addClass( alignClasses[ alignmentsObj[ align ] ] ); | ||
863 | } | ||
864 | } else { | ||
865 | if ( align == 'center' ) { | ||
866 | if ( hasCaption ) | ||
867 | wrapper.setStyle( 'text-align', 'center' ); | ||
868 | else | ||
869 | wrapper.removeStyle( 'text-align' ); | ||
870 | |||
871 | wrapper.removeStyle( 'float' ); | ||
872 | } | ||
873 | else { | ||
874 | if ( align == 'none' ) | ||
875 | wrapper.removeStyle( 'float' ); | ||
876 | else | ||
877 | wrapper.setStyle( 'float', align ); | ||
878 | |||
879 | wrapper.removeStyle( 'text-align' ); | ||
880 | } | ||
881 | } | ||
882 | } | ||
883 | |||
884 | // Returns a function that creates widgets from all <img> and | ||
885 | // <figure class="{config.image2_captionedClass}"> elements. | ||
886 | // | ||
887 | // @param {CKEDITOR.editor} editor | ||
888 | // @returns {Function} | ||
889 | function upcastWidgetElement( editor ) { | ||
890 | var isCenterWrapper = centerWrapperChecker( editor ), | ||
891 | captionedClass = editor.config.image2_captionedClass; | ||
892 | |||
893 | // @param {CKEDITOR.htmlParser.element} el | ||
894 | // @param {Object} data | ||
895 | return function( el, data ) { | ||
896 | var dimensions = { width: 1, height: 1 }, | ||
897 | name = el.name, | ||
898 | image; | ||
899 | |||
900 | // http://dev.ckeditor.com/ticket/11110 Don't initialize on pasted fake objects. | ||
901 | if ( el.attributes[ 'data-cke-realelement' ] ) | ||
902 | return; | ||
903 | |||
904 | // If a center wrapper is found, there are 3 possible cases: | ||
905 | // | ||
906 | // 1. <div style="text-align:center"><figure>...</figure></div>. | ||
907 | // In this case centering is done with a class set on widget.wrapper. | ||
908 | // Simply replace centering wrapper with figure (it's no longer necessary). | ||
909 | // | ||
910 | // 2. <p style="text-align:center"><img/></p>. | ||
911 | // Nothing to do here: <p> remains for styling purposes. | ||
912 | // | ||
913 | // 3. <div style="text-align:center"><img/></div>. | ||
914 | // Nothing to do here (2.) but that case is only possible in enterMode different | ||
915 | // than ENTER_P. | ||
916 | if ( isCenterWrapper( el ) ) { | ||
917 | if ( name == 'div' ) { | ||
918 | var figure = el.getFirst( 'figure' ); | ||
919 | |||
920 | // Case #1. | ||
921 | if ( figure ) { | ||
922 | el.replaceWith( figure ); | ||
923 | el = figure; | ||
924 | } | ||
925 | } | ||
926 | // Cases #2 and #3 (handled transparently) | ||
927 | |||
928 | // If there's a centering wrapper, save it in data. | ||
929 | data.align = 'center'; | ||
930 | |||
931 | // Image can be wrapped in link <a><img/></a>. | ||
932 | image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' ); | ||
933 | } | ||
934 | |||
935 | // No center wrapper has been found. | ||
936 | else if ( name == 'figure' && el.hasClass( captionedClass ) ) { | ||
937 | image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' ); | ||
938 | |||
939 | // Upcast linked image like <a><img/></a>. | ||
940 | } else if ( isLinkedOrStandaloneImage( el ) ) { | ||
941 | image = el.name == 'a' ? el.children[ 0 ] : el; | ||
942 | } | ||
943 | |||
944 | if ( !image ) | ||
945 | return; | ||
946 | |||
947 | // If there's an image, then cool, we got a widget. | ||
948 | // Now just remove dimension attributes expressed with %. | ||
949 | for ( var d in dimensions ) { | ||
950 | var dimension = image.attributes[ d ]; | ||
951 | |||
952 | if ( dimension && dimension.match( regexPercent ) ) | ||
953 | delete image.attributes[ d ]; | ||
954 | } | ||
955 | |||
956 | return el; | ||
957 | }; | ||
958 | } | ||
959 | |||
960 | // Returns a function which transforms the widget to the external format | ||
961 | // according to the current configuration. | ||
962 | // | ||
963 | // @param {CKEDITOR.editor} | ||
964 | function downcastWidgetElement( editor ) { | ||
965 | var alignClasses = editor.config.image2_alignClasses; | ||
966 | |||
967 | // @param {CKEDITOR.htmlParser.element} el | ||
968 | return function( el ) { | ||
969 | // In case of <a><img/></a>, <img/> is the element to hold | ||
970 | // inline styles or classes (image2_alignClasses). | ||
971 | var attrsHolder = el.name == 'a' ? el.getFirst() : el, | ||
972 | attrs = attrsHolder.attributes, | ||
973 | align = this.data.align; | ||
974 | |||
975 | // De-wrap the image from resize handle wrapper. | ||
976 | // Only block widgets have one. | ||
977 | if ( !this.inline ) { | ||
978 | var resizeWrapper = el.getFirst( 'span' ); | ||
979 | |||
980 | if ( resizeWrapper ) | ||
981 | resizeWrapper.replaceWith( resizeWrapper.getFirst( { img: 1, a: 1 } ) ); | ||
982 | } | ||
983 | |||
984 | if ( align && align != 'none' ) { | ||
985 | var styles = CKEDITOR.tools.parseCssText( attrs.style || '' ); | ||
986 | |||
987 | // When the widget is captioned (<figure>) and internally centering is done | ||
988 | // with widget's wrapper style/class, in the external data representation, | ||
989 | // <figure> must be wrapped with an element holding an style/class: | ||
990 | // | ||
991 | // <div style="text-align:center"> | ||
992 | // <figure class="image" style="display:inline-block">...</figure> | ||
993 | // </div> | ||
994 | // or | ||
995 | // <div class="some-center-class"> | ||
996 | // <figure class="image">...</figure> | ||
997 | // </div> | ||
998 | // | ||
999 | if ( align == 'center' && el.name == 'figure' ) { | ||
1000 | el = el.wrapWith( new CKEDITOR.htmlParser.element( 'div', | ||
1001 | alignClasses ? { 'class': alignClasses[ 1 ] } : { style: 'text-align:center' } ) ); | ||
1002 | } | ||
1003 | |||
1004 | // If left/right, add float style to the downcasted element. | ||
1005 | else if ( align in { left: 1, right: 1 } ) { | ||
1006 | if ( alignClasses ) | ||
1007 | attrsHolder.addClass( alignClasses[ alignmentsObj[ align ] ] ); | ||
1008 | else | ||
1009 | styles[ 'float' ] = align; | ||
1010 | } | ||
1011 | |||
1012 | // Update element styles. | ||
1013 | if ( !alignClasses && !CKEDITOR.tools.isEmpty( styles ) ) | ||
1014 | attrs.style = CKEDITOR.tools.writeCssText( styles ); | ||
1015 | } | ||
1016 | |||
1017 | return el; | ||
1018 | }; | ||
1019 | } | ||
1020 | |||
1021 | // Returns a function that checks if an element is a centering wrapper. | ||
1022 | // | ||
1023 | // @param {CKEDITOR.editor} editor | ||
1024 | // @returns {Function} | ||
1025 | function centerWrapperChecker( editor ) { | ||
1026 | var captionedClass = editor.config.image2_captionedClass, | ||
1027 | alignClasses = editor.config.image2_alignClasses, | ||
1028 | validChildren = { figure: 1, a: 1, img: 1 }; | ||
1029 | |||
1030 | return function( el ) { | ||
1031 | // Wrapper must be either <div> or <p>. | ||
1032 | if ( !( el.name in { div: 1, p: 1 } ) ) | ||
1033 | return false; | ||
1034 | |||
1035 | var children = el.children; | ||
1036 | |||
1037 | // Centering wrapper can have only one child. | ||
1038 | if ( children.length !== 1 ) | ||
1039 | return false; | ||
1040 | |||
1041 | var child = children[ 0 ]; | ||
1042 | |||
1043 | // Only <figure> or <img /> can be first (only) child of centering wrapper, | ||
1044 | // regardless of its type. | ||
1045 | if ( !( child.name in validChildren ) ) | ||
1046 | return false; | ||
1047 | |||
1048 | // If centering wrapper is <p>, only <img /> can be the child. | ||
1049 | // <p style="text-align:center"><img /></p> | ||
1050 | if ( el.name == 'p' ) { | ||
1051 | if ( !isLinkedOrStandaloneImage( child ) ) | ||
1052 | return false; | ||
1053 | } | ||
1054 | // Centering <div> can hold <img/> or <figure>, depending on enterMode. | ||
1055 | else { | ||
1056 | // If a <figure> is the first (only) child, it must have a class. | ||
1057 | // <div style="text-align:center"><figure>...</figure><div> | ||
1058 | if ( child.name == 'figure' ) { | ||
1059 | if ( !child.hasClass( captionedClass ) ) | ||
1060 | return false; | ||
1061 | } else { | ||
1062 | // Centering <div> can hold <img/> or <a><img/></a> only when enterMode | ||
1063 | // is ENTER_(BR|DIV). | ||
1064 | // <div style="text-align:center"><img /></div> | ||
1065 | // <div style="text-align:center"><a><img /></a></div> | ||
1066 | if ( editor.enterMode == CKEDITOR.ENTER_P ) | ||
1067 | return false; | ||
1068 | |||
1069 | // Regardless of enterMode, a child which is not <figure> must be | ||
1070 | // either <img/> or <a><img/></a>. | ||
1071 | if ( !isLinkedOrStandaloneImage( child ) ) | ||
1072 | return false; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | // Centering wrapper got to be... centering. If image2_alignClasses are defined, | ||
1077 | // check for centering class. Otherwise, check the style. | ||
1078 | if ( alignClasses ? el.hasClass( alignClasses[ 1 ] ) : | ||
1079 | CKEDITOR.tools.parseCssText( el.attributes.style || '', true )[ 'text-align' ] == 'center' ) | ||
1080 | return true; | ||
1081 | |||
1082 | return false; | ||
1083 | }; | ||
1084 | } | ||
1085 | |||
1086 | // Checks whether element is <img/> or <a><img/></a>. | ||
1087 | // | ||
1088 | // @param {CKEDITOR.htmlParser.element} | ||
1089 | function isLinkedOrStandaloneImage( el ) { | ||
1090 | if ( el.name == 'img' ) | ||
1091 | return true; | ||
1092 | else if ( el.name == 'a' ) | ||
1093 | return el.children.length == 1 && el.getFirst( 'img' ); | ||
1094 | |||
1095 | return false; | ||
1096 | } | ||
1097 | |||
1098 | // Sets width and height of the widget image according to current widget data. | ||
1099 | // | ||
1100 | // @param {CKEDITOR.plugins.widget} widget | ||
1101 | function setDimensions( widget ) { | ||
1102 | var data = widget.data, | ||
1103 | dimensions = { width: data.width, height: data.height }, | ||
1104 | image = widget.parts.image; | ||
1105 | |||
1106 | for ( var d in dimensions ) { | ||
1107 | if ( dimensions[ d ] ) | ||
1108 | image.setAttribute( d, dimensions[ d ] ); | ||
1109 | else | ||
1110 | image.removeAttribute( d ); | ||
1111 | } | ||
1112 | } | ||
1113 | |||
1114 | // Defines all features related to drag-driven image resizing. | ||
1115 | // | ||
1116 | // @param {CKEDITOR.plugins.widget} widget | ||
1117 | function setupResizer( widget ) { | ||
1118 | var editor = widget.editor, | ||
1119 | editable = editor.editable(), | ||
1120 | doc = editor.document, | ||
1121 | |||
1122 | // Store the resizer in a widget for testing (http://dev.ckeditor.com/ticket/11004). | ||
1123 | resizer = widget.resizer = doc.createElement( 'span' ); | ||
1124 | |||
1125 | resizer.addClass( 'cke_image_resizer' ); | ||
1126 | resizer.setAttribute( 'title', editor.lang.image2.resizer ); | ||
1127 | resizer.append( new CKEDITOR.dom.text( '\u200b', doc ) ); | ||
1128 | |||
1129 | // Inline widgets don't need a resizer wrapper as an image spans the entire widget. | ||
1130 | if ( !widget.inline ) { | ||
1131 | var imageOrLink = widget.parts.link || widget.parts.image, | ||
1132 | oldResizeWrapper = imageOrLink.getParent(), | ||
1133 | resizeWrapper = doc.createElement( 'span' ); | ||
1134 | |||
1135 | resizeWrapper.addClass( 'cke_image_resizer_wrapper' ); | ||
1136 | resizeWrapper.append( imageOrLink ); | ||
1137 | resizeWrapper.append( resizer ); | ||
1138 | widget.element.append( resizeWrapper, true ); | ||
1139 | |||
1140 | // Remove the old wrapper which could came from e.g. pasted HTML | ||
1141 | // and which could be corrupted (e.g. resizer span has been lost). | ||
1142 | if ( oldResizeWrapper.is( 'span' ) ) | ||
1143 | oldResizeWrapper.remove(); | ||
1144 | } else { | ||
1145 | widget.wrapper.append( resizer ); | ||
1146 | } | ||
1147 | |||
1148 | // Calculate values of size variables and mouse offsets. | ||
1149 | resizer.on( 'mousedown', function( evt ) { | ||
1150 | var image = widget.parts.image, | ||
1151 | |||
1152 | // "factor" can be either 1 or -1. I.e.: For right-aligned images, we need to | ||
1153 | // subtract the difference to get proper width, etc. Without "factor", | ||
1154 | // resizer starts working the opposite way. | ||
1155 | factor = widget.data.align == 'right' ? -1 : 1, | ||
1156 | |||
1157 | // The x-coordinate of the mouse relative to the screen | ||
1158 | // when button gets pressed. | ||
1159 | startX = evt.data.$.screenX, | ||
1160 | startY = evt.data.$.screenY, | ||
1161 | |||
1162 | // The initial dimensions and aspect ratio of the image. | ||
1163 | startWidth = image.$.clientWidth, | ||
1164 | startHeight = image.$.clientHeight, | ||
1165 | ratio = startWidth / startHeight, | ||
1166 | |||
1167 | listeners = [], | ||
1168 | |||
1169 | // A class applied to editable during resizing. | ||
1170 | cursorClass = 'cke_image_s' + ( !~factor ? 'w' : 'e' ), | ||
1171 | |||
1172 | nativeEvt, newWidth, newHeight, updateData, | ||
1173 | moveDiffX, moveDiffY, moveRatio; | ||
1174 | |||
1175 | // Save the undo snapshot first: before resizing. | ||
1176 | editor.fire( 'saveSnapshot' ); | ||
1177 | |||
1178 | // Mousemove listeners are removed on mouseup. | ||
1179 | attachToDocuments( 'mousemove', onMouseMove, listeners ); | ||
1180 | |||
1181 | // Clean up the mousemove listener. Update widget data if valid. | ||
1182 | attachToDocuments( 'mouseup', onMouseUp, listeners ); | ||
1183 | |||
1184 | // The entire editable will have the special cursor while resizing goes on. | ||
1185 | editable.addClass( cursorClass ); | ||
1186 | |||
1187 | // This is to always keep the resizer element visible while resizing. | ||
1188 | resizer.addClass( 'cke_image_resizing' ); | ||
1189 | |||
1190 | // Attaches an event to a global document if inline editor. | ||
1191 | // Additionally, if classic (`iframe`-based) editor, also attaches the same event to `iframe`'s document. | ||
1192 | function attachToDocuments( name, callback, collection ) { | ||
1193 | var globalDoc = CKEDITOR.document, | ||
1194 | listeners = []; | ||
1195 | |||
1196 | if ( !doc.equals( globalDoc ) ) | ||
1197 | listeners.push( globalDoc.on( name, callback ) ); | ||
1198 | |||
1199 | listeners.push( doc.on( name, callback ) ); | ||
1200 | |||
1201 | if ( collection ) { | ||
1202 | for ( var i = listeners.length; i--; ) | ||
1203 | collection.push( listeners.pop() ); | ||
1204 | } | ||
1205 | } | ||
1206 | |||
1207 | // Calculate with first, and then adjust height, preserving ratio. | ||
1208 | function adjustToX() { | ||
1209 | newWidth = startWidth + factor * moveDiffX; | ||
1210 | newHeight = Math.round( newWidth / ratio ); | ||
1211 | } | ||
1212 | |||
1213 | // Calculate height first, and then adjust width, preserving ratio. | ||
1214 | function adjustToY() { | ||
1215 | newHeight = startHeight - moveDiffY; | ||
1216 | newWidth = Math.round( newHeight * ratio ); | ||
1217 | } | ||
1218 | |||
1219 | // This is how variables refer to the geometry. | ||
1220 | // Note: x corresponds to moveOffset, this is the position of mouse | ||
1221 | // Note: o corresponds to [startX, startY]. | ||
1222 | // | ||
1223 | // +--------------+--------------+ | ||
1224 | // | | | | ||
1225 | // | I | II | | ||
1226 | // | | | | ||
1227 | // +------------- o -------------+ _ _ _ | ||
1228 | // | | | ^ | ||
1229 | // | VI | III | | moveDiffY | ||
1230 | // | | x _ _ _ _ _ v | ||
1231 | // +--------------+---------|----+ | ||
1232 | // | | | ||
1233 | // <-------> | ||
1234 | // moveDiffX | ||
1235 | function onMouseMove( evt ) { | ||
1236 | nativeEvt = evt.data.$; | ||
1237 | |||
1238 | // This is how far the mouse is from the point the button was pressed. | ||
1239 | moveDiffX = nativeEvt.screenX - startX; | ||
1240 | moveDiffY = startY - nativeEvt.screenY; | ||
1241 | |||
1242 | // This is the aspect ratio of the move difference. | ||
1243 | moveRatio = Math.abs( moveDiffX / moveDiffY ); | ||
1244 | |||
1245 | // Left, center or none-aligned widget. | ||
1246 | if ( factor == 1 ) { | ||
1247 | if ( moveDiffX <= 0 ) { | ||
1248 | // Case: IV. | ||
1249 | if ( moveDiffY <= 0 ) | ||
1250 | adjustToX(); | ||
1251 | |||
1252 | // Case: I. | ||
1253 | else { | ||
1254 | if ( moveRatio >= ratio ) | ||
1255 | adjustToX(); | ||
1256 | else | ||
1257 | adjustToY(); | ||
1258 | } | ||
1259 | } else { | ||
1260 | // Case: III. | ||
1261 | if ( moveDiffY <= 0 ) { | ||
1262 | if ( moveRatio >= ratio ) | ||
1263 | adjustToY(); | ||
1264 | else | ||
1265 | adjustToX(); | ||
1266 | } | ||
1267 | |||
1268 | // Case: II. | ||
1269 | else { | ||
1270 | adjustToY(); | ||
1271 | } | ||
1272 | } | ||
1273 | } | ||
1274 | |||
1275 | // Right-aligned widget. It mirrors behaviours, so I becomes II, | ||
1276 | // IV becomes III and vice-versa. | ||
1277 | else { | ||
1278 | if ( moveDiffX <= 0 ) { | ||
1279 | // Case: IV. | ||
1280 | if ( moveDiffY <= 0 ) { | ||
1281 | if ( moveRatio >= ratio ) | ||
1282 | adjustToY(); | ||
1283 | else | ||
1284 | adjustToX(); | ||
1285 | } | ||
1286 | |||
1287 | // Case: I. | ||
1288 | else { | ||
1289 | adjustToY(); | ||
1290 | } | ||
1291 | } else { | ||
1292 | // Case: III. | ||
1293 | if ( moveDiffY <= 0 ) | ||
1294 | adjustToX(); | ||
1295 | |||
1296 | // Case: II. | ||
1297 | else { | ||
1298 | if ( moveRatio >= ratio ) { | ||
1299 | adjustToX(); | ||
1300 | } else { | ||
1301 | adjustToY(); | ||
1302 | } | ||
1303 | } | ||
1304 | } | ||
1305 | } | ||
1306 | |||
1307 | // Don't update attributes if less than 10. | ||
1308 | // This is to prevent images to visually disappear. | ||
1309 | if ( newWidth >= 15 && newHeight >= 15 ) { | ||
1310 | image.setAttributes( { width: newWidth, height: newHeight } ); | ||
1311 | updateData = true; | ||
1312 | } else { | ||
1313 | updateData = false; | ||
1314 | } | ||
1315 | } | ||
1316 | |||
1317 | function onMouseUp() { | ||
1318 | var l; | ||
1319 | |||
1320 | while ( ( l = listeners.pop() ) ) | ||
1321 | l.removeListener(); | ||
1322 | |||
1323 | // Restore default cursor by removing special class. | ||
1324 | editable.removeClass( cursorClass ); | ||
1325 | |||
1326 | // This is to bring back the regular behaviour of the resizer. | ||
1327 | resizer.removeClass( 'cke_image_resizing' ); | ||
1328 | |||
1329 | if ( updateData ) { | ||
1330 | widget.setData( { width: newWidth, height: newHeight } ); | ||
1331 | |||
1332 | // Save another undo snapshot: after resizing. | ||
1333 | editor.fire( 'saveSnapshot' ); | ||
1334 | } | ||
1335 | |||
1336 | // Don't update data twice or more. | ||
1337 | updateData = false; | ||
1338 | } | ||
1339 | } ); | ||
1340 | |||
1341 | // Change the position of the widget resizer when data changes. | ||
1342 | widget.on( 'data', function() { | ||
1343 | resizer[ widget.data.align == 'right' ? 'addClass' : 'removeClass' ]( 'cke_image_resizer_left' ); | ||
1344 | } ); | ||
1345 | } | ||
1346 | |||
1347 | // Integrates widget alignment setting with justify | ||
1348 | // plugin's commands (execution and refreshment). | ||
1349 | // @param {CKEDITOR.editor} editor | ||
1350 | // @param {String} value 'left', 'right', 'center' or 'block' | ||
1351 | function alignCommandIntegrator( editor ) { | ||
1352 | var execCallbacks = [], | ||
1353 | enabled; | ||
1354 | |||
1355 | return function( value ) { | ||
1356 | var command = editor.getCommand( 'justify' + value ); | ||
1357 | |||
1358 | // Most likely, the justify plugin isn't loaded. | ||
1359 | if ( !command ) | ||
1360 | return; | ||
1361 | |||
1362 | // This command will be manually refreshed along with | ||
1363 | // other commands after exec. | ||
1364 | execCallbacks.push( function() { | ||
1365 | command.refresh( editor, editor.elementPath() ); | ||
1366 | } ); | ||
1367 | |||
1368 | if ( value in { right: 1, left: 1, center: 1 } ) { | ||
1369 | command.on( 'exec', function( evt ) { | ||
1370 | var widget = getFocusedWidget( editor ); | ||
1371 | |||
1372 | if ( widget ) { | ||
1373 | widget.setData( 'align', value ); | ||
1374 | |||
1375 | // Once the widget changed its align, all the align commands | ||
1376 | // must be refreshed: the event is to be cancelled. | ||
1377 | for ( var i = execCallbacks.length; i--; ) | ||
1378 | execCallbacks[ i ](); | ||
1379 | |||
1380 | evt.cancel(); | ||
1381 | } | ||
1382 | } ); | ||
1383 | } | ||
1384 | |||
1385 | command.on( 'refresh', function( evt ) { | ||
1386 | var widget = getFocusedWidget( editor ), | ||
1387 | allowed = { right: 1, left: 1, center: 1 }; | ||
1388 | |||
1389 | if ( !widget ) | ||
1390 | return; | ||
1391 | |||
1392 | // Cache "enabled" on first use. This is because filter#checkFeature may | ||
1393 | // not be available during plugin's afterInit in the future — a moment when | ||
1394 | // alignCommandIntegrator is called. | ||
1395 | if ( enabled === undefined ) | ||
1396 | enabled = editor.filter.checkFeature( editor.widgets.registered.image.features.align ); | ||
1397 | |||
1398 | // Don't allow justify commands when widget alignment is disabled (http://dev.ckeditor.com/ticket/11004). | ||
1399 | if ( !enabled ) | ||
1400 | this.setState( CKEDITOR.TRISTATE_DISABLED ); | ||
1401 | else { | ||
1402 | this.setState( | ||
1403 | ( widget.data.align == value ) ? ( | ||
1404 | CKEDITOR.TRISTATE_ON | ||
1405 | ) : ( | ||
1406 | ( value in allowed ) ? CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED | ||
1407 | ) | ||
1408 | ); | ||
1409 | } | ||
1410 | |||
1411 | evt.cancel(); | ||
1412 | } ); | ||
1413 | }; | ||
1414 | } | ||
1415 | |||
1416 | function linkCommandIntegrator( editor ) { | ||
1417 | // Nothing to integrate with if link is not loaded. | ||
1418 | if ( !editor.plugins.link ) | ||
1419 | return; | ||
1420 | |||
1421 | CKEDITOR.on( 'dialogDefinition', function( evt ) { | ||
1422 | var dialog = evt.data; | ||
1423 | |||
1424 | if ( dialog.name == 'link' ) { | ||
1425 | var def = dialog.definition; | ||
1426 | |||
1427 | var onShow = def.onShow, | ||
1428 | onOk = def.onOk; | ||
1429 | |||
1430 | def.onShow = function() { | ||
1431 | var widget = getFocusedWidget( editor ), | ||
1432 | displayTextField = this.getContentElement( 'info', 'linkDisplayText' ).getElement().getParent().getParent(); | ||
1433 | |||
1434 | // Widget cannot be enclosed in a link, i.e. | ||
1435 | // <a>foo<inline widget/>bar</a> | ||
1436 | if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) { | ||
1437 | this.setupContent( widget.data.link || {} ); | ||
1438 | |||
1439 | // Hide the display text in case of linking image2 widget. | ||
1440 | displayTextField.hide(); | ||
1441 | } else { | ||
1442 | // Make sure that display text is visible, as it might be hidden by image2 integration | ||
1443 | // before. | ||
1444 | displayTextField.show(); | ||
1445 | onShow.apply( this, arguments ); | ||
1446 | } | ||
1447 | }; | ||
1448 | |||
1449 | // Set widget data if linking the widget using | ||
1450 | // link dialog (instead of default action). | ||
1451 | // State shifter handles data change and takes | ||
1452 | // care of internal DOM structure of linked widget. | ||
1453 | def.onOk = function() { | ||
1454 | var widget = getFocusedWidget( editor ); | ||
1455 | |||
1456 | // Widget cannot be enclosed in a link, i.e. | ||
1457 | // <a>foo<inline widget/>bar</a> | ||
1458 | if ( widget && ( widget.inline ? !widget.wrapper.getAscendant( 'a' ) : 1 ) ) { | ||
1459 | var data = {}; | ||
1460 | |||
1461 | // Collect data from fields. | ||
1462 | this.commitContent( data ); | ||
1463 | |||
1464 | // Set collected data to widget. | ||
1465 | widget.setData( 'link', data ); | ||
1466 | } else { | ||
1467 | onOk.apply( this, arguments ); | ||
1468 | } | ||
1469 | }; | ||
1470 | } | ||
1471 | } ); | ||
1472 | |||
1473 | // Overwrite default behaviour of unlink command. | ||
1474 | editor.getCommand( 'unlink' ).on( 'exec', function( evt ) { | ||
1475 | var widget = getFocusedWidget( editor ); | ||
1476 | |||
1477 | // Override unlink only when link truly belongs to the widget. | ||
1478 | // If wrapped inline widget in a link, let default unlink work (http://dev.ckeditor.com/ticket/11814). | ||
1479 | if ( !widget || !widget.parts.link ) | ||
1480 | return; | ||
1481 | |||
1482 | widget.setData( 'link', null ); | ||
1483 | |||
1484 | // Selection (which is fake) may not change if unlinked image in focused widget, | ||
1485 | // i.e. if captioned image. Let's refresh command state manually here. | ||
1486 | this.refresh( editor, editor.elementPath() ); | ||
1487 | |||
1488 | evt.cancel(); | ||
1489 | } ); | ||
1490 | |||
1491 | // Overwrite default refresh of unlink command. | ||
1492 | editor.getCommand( 'unlink' ).on( 'refresh', function( evt ) { | ||
1493 | var widget = getFocusedWidget( editor ); | ||
1494 | |||
1495 | if ( !widget ) | ||
1496 | return; | ||
1497 | |||
1498 | // Note that widget may be wrapped in a link, which | ||
1499 | // does not belong to that widget (http://dev.ckeditor.com/ticket/11814). | ||
1500 | this.setState( widget.data.link || widget.wrapper.getAscendant( 'a' ) ? | ||
1501 | CKEDITOR.TRISTATE_OFF : CKEDITOR.TRISTATE_DISABLED ); | ||
1502 | |||
1503 | evt.cancel(); | ||
1504 | } ); | ||
1505 | } | ||
1506 | |||
1507 | // Returns the focused widget, if of the type specific for this plugin. | ||
1508 | // If no widget is focused, `null` is returned. | ||
1509 | // | ||
1510 | // @param {CKEDITOR.editor} | ||
1511 | // @returns {CKEDITOR.plugins.widget} | ||
1512 | function getFocusedWidget( editor ) { | ||
1513 | var widget = editor.widgets.focused; | ||
1514 | |||
1515 | if ( widget && widget.name == 'image' ) | ||
1516 | return widget; | ||
1517 | |||
1518 | return null; | ||
1519 | } | ||
1520 | |||
1521 | // Returns a set of widget allowedContent rules, depending | ||
1522 | // on configurations like config#image2_alignClasses or | ||
1523 | // config#image2_captionedClass. | ||
1524 | // | ||
1525 | // @param {CKEDITOR.editor} | ||
1526 | // @returns {Object} | ||
1527 | function getWidgetAllowedContent( editor ) { | ||
1528 | var alignClasses = editor.config.image2_alignClasses, | ||
1529 | rules = { | ||
1530 | // Widget may need <div> or <p> centering wrapper. | ||
1531 | div: { | ||
1532 | match: centerWrapperChecker( editor ) | ||
1533 | }, | ||
1534 | p: { | ||
1535 | match: centerWrapperChecker( editor ) | ||
1536 | }, | ||
1537 | img: { | ||
1538 | attributes: '!src,alt,width,height' | ||
1539 | }, | ||
1540 | figure: { | ||
1541 | classes: '!' + editor.config.image2_captionedClass | ||
1542 | }, | ||
1543 | figcaption: true | ||
1544 | }; | ||
1545 | |||
1546 | if ( alignClasses ) { | ||
1547 | // Centering class from the config. | ||
1548 | rules.div.classes = alignClasses[ 1 ]; | ||
1549 | rules.p.classes = rules.div.classes; | ||
1550 | |||
1551 | // Left/right classes from the config. | ||
1552 | rules.img.classes = alignClasses[ 0 ] + ',' + alignClasses[ 2 ]; | ||
1553 | rules.figure.classes += ',' + rules.img.classes; | ||
1554 | } else { | ||
1555 | // Centering with text-align. | ||
1556 | rules.div.styles = 'text-align'; | ||
1557 | rules.p.styles = 'text-align'; | ||
1558 | |||
1559 | rules.img.styles = 'float'; | ||
1560 | rules.figure.styles = 'float,display'; | ||
1561 | } | ||
1562 | |||
1563 | return rules; | ||
1564 | } | ||
1565 | |||
1566 | // Returns a set of widget feature rules, depending | ||
1567 | // on editor configuration. Note that the following may not cover | ||
1568 | // all the possible cases since requiredContent supports a single | ||
1569 | // tag only. | ||
1570 | // | ||
1571 | // @param {CKEDITOR.editor} | ||
1572 | // @returns {Object} | ||
1573 | function getWidgetFeatures( editor ) { | ||
1574 | var alignClasses = editor.config.image2_alignClasses, | ||
1575 | features = { | ||
1576 | dimension: { | ||
1577 | requiredContent: 'img[width,height]' | ||
1578 | }, | ||
1579 | align: { | ||
1580 | requiredContent: 'img' + | ||
1581 | ( alignClasses ? '(' + alignClasses[ 0 ] + ')' : '{float}' ) | ||
1582 | }, | ||
1583 | caption: { | ||
1584 | requiredContent: 'figcaption' | ||
1585 | } | ||
1586 | }; | ||
1587 | |||
1588 | return features; | ||
1589 | } | ||
1590 | |||
1591 | // Returns element which is styled, considering current | ||
1592 | // state of the widget. | ||
1593 | // | ||
1594 | // @see CKEDITOR.plugins.widget#applyStyle | ||
1595 | // @param {CKEDITOR.plugins.widget} widget | ||
1596 | // @returns {CKEDITOR.dom.element} | ||
1597 | function getStyleableElement( widget ) { | ||
1598 | return widget.data.hasCaption ? widget.element : widget.parts.image; | ||
1599 | } | ||
1600 | } )(); | ||
1601 | |||
1602 | /** | ||
1603 | * A CSS class applied to the `<figure>` element of a captioned image. | ||
1604 | * | ||
1605 | * Read more in the [documentation](#!/guide/dev_captionedimage) and see the | ||
1606 | * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html). | ||
1607 | * | ||
1608 | * // Changes the class to "captionedImage". | ||
1609 | * config.image2_captionedClass = 'captionedImage'; | ||
1610 | * | ||
1611 | * @cfg {String} [image2_captionedClass='image'] | ||
1612 | * @member CKEDITOR.config | ||
1613 | */ | ||
1614 | CKEDITOR.config.image2_captionedClass = 'image'; | ||
1615 | |||
1616 | /** | ||
1617 | * Determines whether dimension inputs should be automatically filled when the image URL changes in the Enhanced Image | ||
1618 | * plugin dialog window. | ||
1619 | * | ||
1620 | * Read more in the [documentation](#!/guide/dev_captionedimage) and see the | ||
1621 | * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html). | ||
1622 | * | ||
1623 | * config.image2_prefillDimensions = false; | ||
1624 | * | ||
1625 | * @since 4.5 | ||
1626 | * @cfg {Boolean} [image2_prefillDimensions=true] | ||
1627 | * @member CKEDITOR.config | ||
1628 | */ | ||
1629 | |||
1630 | /** | ||
1631 | * Disables the image resizer. By default the resizer is enabled. | ||
1632 | * | ||
1633 | * Read more in the [documentation](#!/guide/dev_captionedimage) and see the | ||
1634 | * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html). | ||
1635 | * | ||
1636 | * config.image2_disableResizer = true; | ||
1637 | * | ||
1638 | * @since 4.5 | ||
1639 | * @cfg {Boolean} [image2_disableResizer=false] | ||
1640 | * @member CKEDITOR.config | ||
1641 | */ | ||
1642 | |||
1643 | /** | ||
1644 | * CSS classes applied to aligned images. Useful to take control over the way | ||
1645 | * the images are aligned, i.e. to customize output HTML and integrate external stylesheets. | ||
1646 | * | ||
1647 | * Classes should be defined in an array of three elements, containing left, center, and right | ||
1648 | * alignment classes, respectively. For example: | ||
1649 | * | ||
1650 | * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ]; | ||
1651 | * | ||
1652 | * **Note**: Once this configuration option is set, the plugin will no longer produce inline | ||
1653 | * styles for alignment. It means that e.g. the following HTML will be produced: | ||
1654 | * | ||
1655 | * <img alt="My image" class="custom-center-class" src="foo.png" /> | ||
1656 | * | ||
1657 | * instead of: | ||
1658 | * | ||
1659 | * <img alt="My image" style="float:left" src="foo.png" /> | ||
1660 | * | ||
1661 | * **Note**: Once this configuration option is set, corresponding style definitions | ||
1662 | * must be supplied to the editor: | ||
1663 | * | ||
1664 | * * For [classic editor](#!/guide/dev_framed) it can be done by defining additional | ||
1665 | * styles in the {@link CKEDITOR.config#contentsCss stylesheets loaded by the editor}. The same | ||
1666 | * styles must be provided on the target page where the content will be loaded. | ||
1667 | * * For [inline editor](#!/guide/dev_inline) the styles can be defined directly | ||
1668 | * with `<style> ... <style>` or `<link href="..." rel="stylesheet">`, i.e. within the `<head>` | ||
1669 | * of the page. | ||
1670 | * | ||
1671 | * For example, considering the following configuration: | ||
1672 | * | ||
1673 | * config.image2_alignClasses = [ 'align-left', 'align-center', 'align-right' ]; | ||
1674 | * | ||
1675 | * CSS rules can be defined as follows: | ||
1676 | * | ||
1677 | * .align-left { | ||
1678 | * float: left; | ||
1679 | * } | ||
1680 | * | ||
1681 | * .align-right { | ||
1682 | * float: right; | ||
1683 | * } | ||
1684 | * | ||
1685 | * .align-center { | ||
1686 | * text-align: center; | ||
1687 | * } | ||
1688 | * | ||
1689 | * .align-center > figure { | ||
1690 | * display: inline-block; | ||
1691 | * } | ||
1692 | * | ||
1693 | * Read more in the [documentation](#!/guide/dev_captionedimage) and see the | ||
1694 | * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html). | ||
1695 | * | ||
1696 | * @since 4.4 | ||
1697 | * @cfg {String[]} [image2_alignClasses=null] | ||
1698 | * @member CKEDITOR.config | ||
1699 | */ | ||
1700 | |||
1701 | /** | ||
1702 | * Determines whether alternative text is required for the captioned image. | ||
1703 | * | ||
1704 | * config.image2_altRequired = true; | ||
1705 | * | ||
1706 | * Read more in the [documentation](#!/guide/dev_captionedimage) and see the | ||
1707 | * [SDK sample](http://sdk.ckeditor.com/samples/captionedimage.html). | ||
1708 | * | ||
1709 | * @since 4.6.0 | ||
1710 | * @cfg {Boolean} [image2_altRequired=false] | ||
1711 | * @member CKEDITOR.config | ||
1712 | */ | ||