MDL-46302 correct atto img float right
[moodle.git] / lib / editor / atto / plugins / image / yui / src / button / js / button.js
CommitLineData
adca7326
DW
1// This file is part of Moodle - http://moodle.org/
2//
3// Moodle is free software: you can redistribute it and/or modify
4// it under the terms of the GNU General Public License as published by
5// the Free Software Foundation, either version 3 of the License, or
6// (at your option) any later version.
7//
8// Moodle is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11// GNU General Public License for more details.
12//
13// You should have received a copy of the GNU General Public License
14// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
15
62467795
AN
16/*
17 * @package atto_image
18 * @copyright 2013 Damyon Wiese <damyon@moodle.com>
19 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
20 */
21
22/**
23 * @module moodle-atto_image_alignment-button
24 */
25
26/**
27 * Atto image selection tool.
28 *
29 * @namespace M.atto_image
30 * @class Button
31 * @extends M.editor_atto.EditorPlugin
32 */
33
34var CSS = {
d3931a7d 35 RESPONSIVE: 'img-responsive',
c9292b18
SH
36 INPUTALIGNMENT: 'atto_image_alignment',
37 INPUTALT: 'atto_image_altentry',
38 INPUTHEIGHT: 'atto_image_heightentry',
62467795 39 INPUTSUBMIT: 'atto_image_urlentrysubmit',
c9292b18 40 INPUTURL: 'atto_image_urlentry',
ffb8aff6 41 INPUTSIZE: 'atto_image_size',
c9292b18
SH
42 INPUTWIDTH: 'atto_image_widthentry',
43 IMAGEALTWARNING: 'atto_image_altwarning',
44 IMAGEBROWSER: 'openimagebrowser',
45 IMAGEPRESENTATION: 'atto_image_presentation',
ffb8aff6 46 INPUTCONSTRAIN: 'atto_image_constrain',
d28af3d4 47 INPUTCUSTOMSTYLE: 'atto_image_customstyle',
d3931a7d
DW
48 IMAGEPREVIEW: 'atto_image_preview',
49 IMAGEPREVIEWBOX: 'atto_image_preview_box'
c9292b18 50 },
e5ddec38
DW
51 SELECTORS = {
52 INPUTURL: '.' + CSS.INPUTURL
53 },
62467795
AN
54 ALIGNMENTS = [
55 // Vertical alignment.
56 {
62467795 57 name: 'text-top',
2e8cbbb3
DW
58 str: 'alignment_top',
59 value: 'vertical-align',
60 margin: '0 .5em'
62467795
AN
61 }, {
62 name: 'middle',
63 str: 'alignment_middle',
2e8cbbb3
DW
64 value: 'vertical-align',
65 margin: '0 .5em'
62467795
AN
66 }, {
67 name: 'text-bottom',
2e8cbbb3
DW
68 str: 'alignment_bottom',
69 value: 'vertical-align',
7bbc64b8
DW
70 margin: '0 .5em',
71 isDefault: true
62467795
AN
72 },
73
74 // Floats.
75 {
76 name: 'left',
77 str: 'alignment_left',
2e8cbbb3
DW
78 value: 'float',
79 margin: '0 .5em 0 0'
62467795
AN
80 }, {
81 name: 'right',
82 str: 'alignment_right',
2e8cbbb3 83 value: 'float',
6dccf359 84 margin: '0 0 0 .5em'
d28af3d4
DW
85 }, {
86 name: 'customstyle',
87 str: 'customstyle',
88 value: 'style'
62467795 89 }
d3931a7d 90 ],
c9292b18 91
d3931a7d
DW
92 REGEX = {
93 ISPERCENT: /\d+%/
94 },
95
96 COMPONENTNAME = 'atto_image',
c9292b18 97
62467795
AN
98 TEMPLATE = '' +
99 '<form class="atto_form">' +
100 '<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
101 '<input class="fullwidth {{CSS.INPUTURL}}" type="url" id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
102 '<br/>' +
c9292b18 103
62467795
AN
104 // Add the repository browser button.
105 '{{#if showFilepicker}}' +
106 '<button class="{{CSS.IMAGEBROWSER}}" type="button">{{get_string "browserepositories" component}}</button>' +
107 '{{/if}}' +
108
109 // Add the Alt box.
110 '<div style="display:none" role="alert" class="warning {{CSS.IMAGEALTWARNING}}">' +
111 '{{get_string "presentationoraltrequired" component}}' +
112 '</div>' +
113 '<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
114 '<input class="fullwidth {{CSS.INPUTALT}}" type="text" value="" id="{{elementid}}_{{CSS.INPUTALT}}" size="32"/>' +
115 '<br/>' +
116
117 // Add the presentation select box.
118 '<input type="checkbox" class="{{CSS.IMAGEPRESENTATION}}" id="{{elementid}}_{{CSS.IMAGEPRESENTATION}}"/>' +
119 '<label class="sameline" for="{{elementid}}_{{CSS.IMAGEPRESENTATION}}">{{get_string "presentation" component}}</label>' +
120 '<br/>' +
121
ffb8aff6 122 // Add the size entry boxes.
8aa64bab 123 '<label class="sameline" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
ffb8aff6
DW
124 '<div id="{{elementid}}_{{CSS.INPUTSIZE}}" class="{{CSS.INPUTSIZE}}">' +
125 '<label class="accesshide" for="{{elementid}}_{{CSS.INPUTWIDTH}}">{{get_string "width" component}}</label>' +
126 '<input type="text" class="{{CSS.INPUTWIDTH}} input-mini" id="{{elementid}}_{{CSS.INPUTWIDTH}}" size="4"/> x ' +
62467795
AN
127
128 // Add the height entry box.
ffb8aff6
DW
129 '<label class="accesshide" for="{{elementid}}_{{CSS.INPUTHEIGHT}}">{{get_string "height" component}}</label>' +
130 '<input type="text" class="{{CSS.INPUTHEIGHT}} input-mini" id="{{elementid}}_{{CSS.INPUTHEIGHT}}" size="4"/>' +
131
132 // Add the constrain checkbox.
133 '<input type="checkbox" class="{{CSS.INPUTCONSTRAIN}} sameline" id="{{elementid}}_{{CSS.INPUTCONSTRAIN}}"/>' +
134 '<label for="{{elementid}}_{{CSS.INPUTCONSTRAIN}}">{{get_string "constrain" component}}</label>' +
135 '</div>' +
62467795
AN
136
137 // Add the alignment selector.
138 '<label class="sameline" for="{{elementid}}_{{CSS.INPUTALIGNMENT}}">{{get_string "alignment" component}}</label>' +
139 '<select class="{{CSS.INPUTALIGNMENT}}" id="{{elementid}}_{{CSS.INPUTALIGNMENT}}">' +
140 '{{#each alignments}}' +
da00661d 141 '<option value="{{value}}:{{name}};">{{get_string str ../component}}</option>' +
62467795
AN
142 '{{/each}}' +
143 '</select>' +
d28af3d4
DW
144 // Hidden input to store custom styles.
145 '<input type="hidden" class="{{CSS.INPUTCUSTOMSTYLE}}"/>' +
62467795
AN
146 '<br/>' +
147
148 // Add the image preview.
62467795 149 '<div class="mdl-align">' +
d3931a7d 150 '<div class="{{CSS.IMAGEPREVIEWBOX}}">' +
f3662213 151 '<img src="#" class="{{CSS.IMAGEPREVIEW}}" alt="" style="display: none;"/>' +
d3931a7d 152 '</div>' +
c9292b18 153
62467795 154 // Add the submit button and close the form.
d3931a7d 155 '<button class="{{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
62467795
AN
156 '</div>' +
157 '</form>',
158
159 IMAGETEMPLATE = '' +
160 '<img src="{{url}}" alt="{{alt}}" ' +
161 '{{#if width}}width="{{width}}" {{/if}}' +
162 '{{#if height}}height="{{height}}" {{/if}}' +
163 '{{#if presentation}}role="presentation" {{/if}}' +
d28af3d4 164 'style="{{alignment}}{{margin}}{{customstyle}}"' +
d3931a7d 165 '{{#if classlist}}class="{{classlist}}" {{/if}}' +
62467795
AN
166 '/>';
167
168Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
c9292b18 169 /**
62467795
AN
170 * A reference to the current selection at the time that the dialogue
171 * was opened.
172 *
173 * @property _currentSelection
174 * @type Range
175 * @private
c9292b18 176 */
62467795 177 _currentSelection: null,
c9292b18
SH
178
179 /**
62467795
AN
180 * The most recently selected image.
181 *
182 * @param _selectedImage
183 * @type Node
184 * @private
c9292b18 185 */
62467795 186 _selectedImage: null,
c9292b18
SH
187
188 /**
62467795
AN
189 * A reference to the currently open form.
190 *
191 * @param _form
192 * @type Node
c9292b18 193 * @private
c9292b18 194 */
62467795
AN
195 _form: null,
196
ffb8aff6 197 /**
f3662213 198 * The dimensions of the raw image before we manipulate it.
ffb8aff6 199 *
f3662213
AN
200 * @param _rawImageDimensions
201 * @type Object
ffb8aff6
DW
202 * @private
203 */
f3662213 204 _rawImageDimensions: null,
ffb8aff6 205
62467795
AN
206 initializer: function() {
207 this.addButton({
208 icon: 'e/insert_edit_image',
209 callback: this._displayDialogue,
210 tags: 'img',
211 tagMatchRequiresAll: false
212 });
43a737d1
DW
213 this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this);
214 },
215
216 /**
217 * Handle a double click on an image.
218 *
219 * @method _handleDoubleClick
220 * @param {EventFacade} e
221 * @private
222 */
223 _handleDoubleClick: function(e) {
224 var image = e.target;
225
226 var selection = this.get('host').getSelectionFromNode(image);
227 this.get('host').setSelection(selection);
228 this._displayDialogue();
c9292b18
SH
229 },
230
231 /**
62467795
AN
232 * Display the image editing tool.
233 *
234 * @method _displayDialogue
235 * @private
c9292b18 236 */
62467795
AN
237 _displayDialogue: function() {
238 // Store the current selection.
239 this._currentSelection = this.get('host').getSelection();
240 if (this._currentSelection === false) {
241 return;
c9292b18 242 }
62467795 243
91bc9570
FM
244 // Reset the image dimensions.
245 this._rawImageDimensions = null;
246
62467795 247 var dialogue = this.getDialogue({
d3931a7d 248 headerContent: M.util.get_string('imageproperties', COMPONENTNAME),
da00661d 249 width: '480px',
e5ddec38 250 focusAfterHide: true,
c1660772 251 focusOnShowSelector: SELECTORS.INPUTURL
62467795
AN
252 });
253
254 // Set the dialogue content, and then show the dialogue.
255 dialogue.set('bodyContent', this._getDialogueContent())
256 .show();
c9292b18
SH
257 },
258
ffb8aff6
DW
259 /**
260 * Set the inputs for width and height if they are not set, and calculate
261 * if the constrain checkbox should be checked or not.
262 *
263 * @method _loadPreviewImage
264 * @param {String} url
265 * @private
266 */
267 _loadPreviewImage: function(url) {
268 var image = new Image(), self = this;
269
f3662213
AN
270 image.onerror = function() {
271 var preview = self._form.one('.' + CSS.IMAGEPREVIEW);
272 preview.setStyles({
273 'display': 'none'
274 });
275
276 // Centre the dialogue when clearing the image preview.
277 self.getDialogue().centerDialogue();
278 };
279
ffb8aff6
DW
280 image.onload = function() {
281 var input, currentwidth, currentheight, widthRatio, heightRatio;
282
f3662213
AN
283 self._rawImageDimensions = {
284 width: this.width,
285 height: this.height
286 };
ffb8aff6
DW
287
288 input = self._form.one('.' + CSS.INPUTWIDTH);
289 currentwidth = input.get('value');
290 if (currentwidth === '') {
291 input.set('value', this.width);
d3931a7d 292 currentwidth = "" + this.width;
ffb8aff6
DW
293 }
294 input = self._form.one('.' + CSS.INPUTHEIGHT);
295 currentheight = input.get('value');
296 if (currentheight === '') {
297 input.set('value', this.height);
d3931a7d 298 currentheight = "" + this.height;
ffb8aff6
DW
299 }
300 input = self._form.one('.' + CSS.IMAGEPREVIEW);
f3662213
AN
301 input.setAttribute('src', this.src);
302 input.setStyles({
303 'display': 'inline'
304 });
ffb8aff6 305
ffb8aff6 306 input = self._form.one('.' + CSS.INPUTCONSTRAIN);
d3931a7d
DW
307 if (currentwidth.match(REGEX.ISPERCENT) && currentheight.match(REGEX.ISPERCENT)) {
308 input.set('checked', currentwidth === currentheight);
309 } else {
310 if (this.width === 0) {
311 this.width = 1;
312 }
313 if (this.height === 0) {
314 this.height = 1;
315 }
316 // This is the same as comparing to 3 decimal places.
317 widthRatio = Math.round(1000*parseInt(currentwidth, 10) / this.width);
318 heightRatio = Math.round(1000*parseInt(currentheight, 10) / this.height);
319 input.set('checked', widthRatio === heightRatio);
320 }
ffb8aff6 321
f3662213
AN
322 // Apply the image sizing.
323 self._autoAdjustSize(self);
324
ffb8aff6
DW
325 // Centre the dialogue once the preview image has loaded.
326 self.getDialogue().centerDialogue();
327 };
328
329 image.src = url;
330 },
331
c9292b18 332 /**
62467795
AN
333 * Return the dialogue content for the tool, attaching any required
334 * events.
335 *
336 * @method _getDialogueContent
337 * @return {Node} The content to place in the dialogue.
338 * @private
c9292b18 339 */
62467795
AN
340 _getDialogueContent: function() {
341 var template = Y.Handlebars.compile(TEMPLATE),
6cb48e04 342 canShowFilepicker = this.get('host').canShowFilepicker('image'),
62467795
AN
343 content = Y.Node.create(template({
344 elementid: this.get('host').get('elementid'),
345 CSS: CSS,
346 component: COMPONENTNAME,
6cb48e04 347 showFilepicker: canShowFilepicker,
62467795
AN
348 alignments: ALIGNMENTS
349 }));
350
351 this._form = content;
352
353 // Configure the view of the current image.
354 this._applyImageProperties(this._form);
355
ffb8aff6 356 this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
9754ab92
JF
357 this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
358 this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
f3662213
AN
359 this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
360 this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
d3931a7d
DW
361 this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
362 if (event.target.get('checked')) {
f3662213 363 this._autoAdjustSize(event);
d3931a7d
DW
364 }
365 }, this);
62467795
AN
366 this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
367 this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
6cb48e04
FM
368
369 if (canShowFilepicker) {
370 this._form.one('.' + CSS.IMAGEBROWSER).on('click', function() {
371 this.get('host').showFilepicker('image', this._filepickerCallback, this);
372 }, this);
373 }
adca7326 374
62467795 375 return content;
adca7326 376 },
adca7326 377
f3662213
AN
378 _autoAdjustSize: function(e, forceHeight) {
379 forceHeight = forceHeight || false;
380
381 var keyField = this._form.one('.' + CSS.INPUTWIDTH),
382 keyFieldType = 'width',
383 subField = this._form.one('.' + CSS.INPUTHEIGHT),
384 subFieldType = 'height',
385 constrainField = this._form.one('.' + CSS.INPUTCONSTRAIN),
386 keyFieldValue = keyField.get('value'),
387 subFieldValue = subField.get('value'),
388 imagePreview = this._form.one('.' + CSS.IMAGEPREVIEW),
389 rawPercentage,
390 rawSize;
ffb8aff6 391
91bc9570
FM
392 // If we do not know the image size, do not do anything.
393 if (!this._rawImageDimensions) {
394 return;
395 }
396
d3931a7d 397 // Set the width back to default if it is empty.
f3662213
AN
398 if (keyFieldValue === '') {
399 keyFieldValue = this._rawImageDimensions[keyFieldType];
400 keyField.set('value', keyFieldValue);
401 keyFieldValue = keyField.get('value');
d3931a7d
DW
402 }
403
f3662213
AN
404 // Clear the existing preview sizes.
405 imagePreview.setStyles({
406 width: null,
407 height: null
408 });
ffb8aff6 409
f3662213
AN
410 // Now update with the new values.
411 if (!constrainField.get('checked')) {
412 // We are not keeping the image proportion - update the preview accordingly.
d3931a7d 413
f3662213
AN
414 // Width.
415 if (keyFieldValue.match(REGEX.ISPERCENT)) {
416 rawPercentage = parseInt(keyFieldValue, 10);
417 rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
418 imagePreview.setStyle('width', rawSize + 'px');
419 } else {
420 imagePreview.setStyle('width', keyFieldValue + 'px');
d3931a7d 421 }
ffb8aff6 422
f3662213
AN
423 // Height.
424 if (subFieldValue.match(REGEX.ISPERCENT)) {
425 rawPercentage = parseInt(subFieldValue, 10);
426 rawSize = this._rawImageDimensions.height / 100 * rawPercentage;
427 imagePreview.setStyle('height', rawSize + 'px');
428 } else {
429 imagePreview.setStyle('height', subFieldValue + 'px');
430 }
431 } else {
432 // We are keeping the image in proportion.
433 if (forceHeight) {
434 // By default we update based on width. Swap the key and sub fields around to achieve a height-based scale.
435 var _temporaryValue;
436 _temporaryValue = keyField;
437 keyField = subField;
438 subField = _temporaryValue;
439
440 _temporaryValue = keyFieldType;
441 keyFieldType = subFieldType;
442 subFieldType = _temporaryValue;
443
444 _temporaryValue = keyFieldValue;
445 keyFieldValue = subFieldValue;
446 subFieldValue = _temporaryValue;
447 }
ffb8aff6 448
f3662213
AN
449 if (keyFieldValue.match(REGEX.ISPERCENT)) {
450 // This is a percentage based change. Copy it verbatim.
451 subFieldValue = keyFieldValue;
d3931a7d 452
f3662213
AN
453 // Set the width to the calculated pixel width.
454 rawPercentage = parseInt(keyFieldValue, 10);
455 rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
ffb8aff6 456
f3662213
AN
457 // And apply the width/height to the container.
458 imagePreview.setStyle('width', rawSize);
459 rawSize = this._rawImageDimensions.height / 100 * rawPercentage;
460 imagePreview.setStyle('height', rawSize);
461 } else {
462 // Calculate the scaled subFieldValue from the keyFieldValue.
463 subFieldValue = Math.round((keyFieldValue / this._rawImageDimensions[keyFieldType]) *
464 this._rawImageDimensions[subFieldType]);
465
466 if (forceHeight) {
467 imagePreview.setStyles({
468 'width': subFieldValue,
469 'height': keyFieldValue
470 });
471 } else {
472 imagePreview.setStyles({
473 'width': keyFieldValue,
474 'height': subFieldValue
475 });
476 }
d3931a7d 477 }
f3662213
AN
478
479 // Update the subField's value within the form to reflect the changes.
480 subField.set('value', subFieldValue);
d3931a7d 481 }
ffb8aff6
DW
482 },
483
62467795
AN
484 /**
485 * Update the dialogue after an image was selected in the File Picker.
486 *
487 * @method _filepickerCallback
488 * @param {object} params The parameters provided by the filepicker
489 * containing information about the image.
490 * @private
491 */
492 _filepickerCallback: function(params) {
adca7326 493 if (params.url !== '') {
ffb8aff6 494 var input = this._form.one('.' + CSS.INPUTURL);
adca7326
DW
495 input.set('value', params.url);
496
497 // Auto set the width and height.
8d1c0179
DW
498 this._form.one('.' + CSS.INPUTWIDTH).set('value', '');
499 this._form.one('.' + CSS.INPUTHEIGHT).set('value', '');
ffb8aff6
DW
500
501 // Load the preview image.
502 this._loadPreviewImage(params.url);
adca7326
DW
503 }
504 },
adca7326 505
62467795
AN
506 /**
507 * Applies properties of an existing image to the image dialogue for editing.
508 *
509 * @method _applyImageProperties
510 * @param {Node} form
511 * @private
512 */
513 _applyImageProperties: function(form) {
514 var properties = this._getSelectedImageProperties(),
7bbc64b8
DW
515 img = form.one('.' + CSS.IMAGEPREVIEW),
516 i;
adca7326 517
62467795
AN
518 if (properties === false) {
519 img.setStyle('display', 'none');
7bbc64b8
DW
520 // Set the default alignment.
521 for (i in ALIGNMENTS) {
522 if (ALIGNMENTS[i].isDefault === true) {
523 css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
524 form.one('.' + CSS.INPUTALIGNMENT).set('value', css);
525 }
526 }
d28af3d4
DW
527 // Remove the custom style option if this is a new image.
528 form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
adca7326 529 return;
adca7326
DW
530 }
531
62467795 532 if (properties.align) {
da00661d 533 form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align);
d28af3d4
DW
534 // Remove the custom style option if we have a standard alignment.
535 form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
536 } else {
537 form.one('.' + CSS.INPUTALIGNMENT).set('value', 'style:customstyle;');
538 }
539 if (properties.customstyle) {
540 form.one('.' + CSS.INPUTCUSTOMSTYLE).set('value', properties.customstyle);
62467795 541 }
62467795
AN
542 if (properties.width) {
543 form.one('.' + CSS.INPUTWIDTH).set('value', properties.width);
544 }
545 if (properties.height) {
546 form.one('.' + CSS.INPUTHEIGHT).set('value', properties.height);
547 }
548 if (properties.alt) {
549 form.one('.' + CSS.INPUTALT).set('value', properties.alt);
550 }
551 if (properties.src) {
552 form.one('.' + CSS.INPUTURL).set('value', properties.src);
ffb8aff6 553 this._loadPreviewImage(properties.src);
62467795
AN
554 }
555 if (properties.presentation) {
556 form.one('.' + CSS.IMAGEPRESENTATION).set('checked', 'checked');
adca7326 557 }
f3662213
AN
558
559 // Update the image preview based on the form properties.
560 this._autoAdjustSize();
adca7326 561 },
62467795 562
c9292b18
SH
563 /**
564 * Gets the properties of the currently selected image.
565 *
566 * The first image only if multiple images are selected.
567 *
62467795
AN
568 * @method _getSelectedImageProperties
569 * @return {object}
c9292b18 570 * @private
c9292b18 571 */
62467795 572 _getSelectedImageProperties: function() {
c9292b18
SH
573 var properties = {
574 src: null,
575 alt :null,
576 width: null,
577 height: null,
d28af3d4 578 align: '',
c9292b18
SH
579 presentation: false
580 },
62467795
AN
581
582 // Get the current selection.
583 images = this.get('host').getSelectedNodes(),
da00661d 584 i, width, height, style, css;
c9292b18 585
d321f68b
DW
586 if (images) {
587 images = images.filter('img');
588 }
589
590 if (images && images.size()) {
c9292b18 591 image = images.item(0);
62467795 592 this._selectedImage = image;
d321f68b 593
c9292b18 594 style = image.getAttribute('style');
d28af3d4
DW
595 properties.customstyle = style;
596 style = style.replace(/ /g, '');
c9292b18 597
d3931a7d
DW
598 width = image.getAttribute('width');
599 if (!width.match(REGEX.ISPERCENT)) {
600 width = parseInt(width, 10);
601 }
602 height = image.getAttribute('height');
603 if (!height.match(REGEX.ISPERCENT)) {
604 height = parseInt(height, 10);
605 }
606
607 if (width !== 0) {
c9292b18
SH
608 properties.width = width;
609 }
d3931a7d 610 if (height !== 0) {
c9292b18
SH
611 properties.height = height;
612 }
613 for (i in ALIGNMENTS) {
da00661d 614 css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
d28af3d4
DW
615 if (style.indexOf(css) !== -1) {
616 margin = 'margin:' + ALIGNMENTS[i].margin + ';';
617 margin = margin.replace(/ /g, '');
618 // Must match alignment and margins - otherwise custom style is selected.
619 if (style.indexOf(margin) !== -1) {
620 properties.align = css;
621 break;
622 }
c9292b18
SH
623 }
624 }
625 properties.src = image.getAttribute('src');
626 properties.alt = image.getAttribute('alt') || '';
627 properties.presentation = (image.get('role') === 'presentation');
628 return properties;
629 }
62467795
AN
630
631 // No image selected - clean up.
632 this._selectedImage = null;
c9292b18
SH
633 return false;
634 },
c9292b18 635
62467795
AN
636 /**
637 * Update the form when the URL was changed. This includes updating the
638 * height, width, and image preview.
639 *
640 * @method _urlChanged
641 * @private
642 */
643 _urlChanged: function() {
ffb8aff6 644 var input = this._form.one('.' + CSS.INPUTURL);
b269f635 645
62467795 646 if (input.get('value') !== '') {
ffb8aff6
DW
647 // Load the preview image.
648 this._loadPreviewImage(input.get('value'));
b269f635 649 }
c9292b18 650 },
62467795 651
c9292b18 652 /**
62467795 653 * Update the image in the contenteditable.
c9292b18 654 *
62467795
AN
655 * @method _setImage
656 * @param {EventFacade} e
c9292b18 657 * @private
c9292b18 658 */
62467795
AN
659 _setImage: function(e) {
660 var form = this._form,
661 url = form.one('.' + CSS.INPUTURL).get('value'),
662 alt = form.one('.' + CSS.INPUTALT).get('value'),
663 width = form.one('.' + CSS.INPUTWIDTH).get('value'),
664 height = form.one('.' + CSS.INPUTHEIGHT).get('value'),
665 alignment = form.one('.' + CSS.INPUTALIGNMENT).get('value'),
2e8cbbb3 666 margin = '',
62467795 667 presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'),
d3931a7d 668 constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'),
62467795 669 imagehtml,
d28af3d4 670 customstyle = '',
2e8cbbb3 671 i,
d3931a7d 672 classlist = [],
62467795 673 host = this.get('host');
c9292b18 674
62467795
AN
675 e.preventDefault();
676
9754ab92
JF
677 // Check if there are any accessibility issues.
678 if (this._updateWarning()) {
c9292b18
SH
679 return;
680 }
681
62467795
AN
682 // Focus on the editor in preparation for inserting the image.
683 host.focus();
684 if (url !== '') {
685 if (this._selectedImage) {
686 host.setSelection(host.getSelectionFromNode(this._selectedImage));
687 } else {
688 host.setSelection(this._currentSelection);
689 }
d28af3d4
DW
690
691 if (alignment === 'style:customstyle;') {
692 alignment = '';
693 customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value');
694 } else {
695 for (i in ALIGNMENTS) {
696 css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
697 if (alignment === css) {
698 margin = ' margin: ' + ALIGNMENTS[i].margin + ';';
699 }
2e8cbbb3
DW
700 }
701 }
702
d3931a7d
DW
703 if (constrain) {
704 classlist.push(CSS.RESPONSIVE);
705 }
706
707 if (!width.match(REGEX.ISPERCENT) && isNaN(parseInt(width, 10))) {
708 form.one('.' + CSS.INPUTWIDTH).focus();
709 return;
710 }
711 if (!height.match(REGEX.ISPERCENT) && isNaN(parseInt(height, 10))) {
712 form.one('.' + CSS.INPUTHEIGHT).focus();
713 return;
714 }
715
62467795
AN
716 template = Y.Handlebars.compile(IMAGETEMPLATE);
717 imagehtml = template({
718 url: url,
719 alt: alt,
720 width: width,
721 height: height,
722 presentation: presentation,
2e8cbbb3 723 alignment: alignment,
d28af3d4 724 margin: margin,
d3931a7d
DW
725 customstyle: customstyle,
726 classlist: classlist.join(' ')
62467795
AN
727 });
728
729 this.get('host').insertContentAtFocusPoint(imagehtml);
730
731 this.markUpdated();
c9292b18 732 }
d3931a7d
DW
733
734 this.getDialogue({
735 focusAfterHide: null
736 }).hide();
737
9754ab92
JF
738 },
739
740 /**
741 * Update the alt text warning live.
742 *
743 * @method _updateWarning
744 * @return {boolean} whether a warning should be displayed.
745 * @private
746 */
747 _updateWarning: function() {
748 var form = this._form,
749 state = true,
750 alt = form.one('.' + CSS.INPUTALT).get('value'),
751 presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
752 if (alt === '' && !presentation) {
753 form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
754 form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
755 form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
756 state = true;
757 } else {
758 form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
759 form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
760 form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
761 state = false;
762 }
763 this.getDialogue().centerDialogue();
764 return state;
adca7326 765 }
62467795 766});