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