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 | */ | ||
