MDL-46302 correct atto img float right
[moodle.git] / lib / editor / atto / plugins / image / yui / src / button / js / button.js
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/>.
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  */
22 /**
23  * @module moodle-atto_image_alignment-button
24  */
26 /**
27  * Atto image selection tool.
28  *
29  * @namespace M.atto_image
30  * @class Button
31  * @extends M.editor_atto.EditorPlugin
32  */
34 var CSS = {
35         RESPONSIVE: 'img-responsive',
36         INPUTALIGNMENT: 'atto_image_alignment',
37         INPUTALT: 'atto_image_altentry',
38         INPUTHEIGHT: 'atto_image_heightentry',
39         INPUTSUBMIT: 'atto_image_urlentrysubmit',
40         INPUTURL: 'atto_image_urlentry',
41         INPUTSIZE: 'atto_image_size',
42         INPUTWIDTH: 'atto_image_widthentry',
43         IMAGEALTWARNING: 'atto_image_altwarning',
44         IMAGEBROWSER: 'openimagebrowser',
45         IMAGEPRESENTATION: 'atto_image_presentation',
46         INPUTCONSTRAIN: 'atto_image_constrain',
47         INPUTCUSTOMSTYLE: 'atto_image_customstyle',
48         IMAGEPREVIEW: 'atto_image_preview',
49         IMAGEPREVIEWBOX: 'atto_image_preview_box'
50     },
51     SELECTORS = {
52         INPUTURL: '.' + CSS.INPUTURL
53     },
54     ALIGNMENTS = [
55         // Vertical alignment.
56         {
57             name: 'text-top',
58             str: 'alignment_top',
59             value: 'vertical-align',
60             margin: '0 .5em'
61         }, {
62             name: 'middle',
63             str: 'alignment_middle',
64             value: 'vertical-align',
65             margin: '0 .5em'
66         }, {
67             name: 'text-bottom',
68             str: 'alignment_bottom',
69             value: 'vertical-align',
70             margin: '0 .5em',
71             isDefault: true
72         },
74         // Floats.
75         {
76             name: 'left',
77             str: 'alignment_left',
78             value: 'float',
79             margin: '0 .5em 0 0'
80         }, {
81             name: 'right',
82             str: 'alignment_right',
83             value: 'float',
84             margin: '0 0 0 .5em'
85         }, {
86             name: 'customstyle',
87             str: 'customstyle',
88             value: 'style'
89         }
90     ],
92     REGEX = {
93         ISPERCENT: /\d+%/
94     },
96     COMPONENTNAME = 'atto_image',
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/>' +
104                 // Add the repository browser button.
105                 '{{#if showFilepicker}}' +
106                     '<button class="{{CSS.IMAGEBROWSER}}" type="button">{{get_string "browserepositories" component}}</button>' +
107                 '{{/if}}' +
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/>' +
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/>' +
122                 // Add the size entry boxes.
123                 '<label class="sameline" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
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 ' +
128                 // Add the height entry box.
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"/>' +
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>' +
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}}' +
141                         '<option value="{{value}}:{{name}};">{{get_string str ../component}}</option>' +
142                     '{{/each}}' +
143                 '</select>' +
144                 // Hidden input to store custom styles.
145                 '<input type="hidden" class="{{CSS.INPUTCUSTOMSTYLE}}"/>' +
146                 '<br/>' +
148                 // Add the image preview.
149                 '<div class="mdl-align">' +
150                 '<div class="{{CSS.IMAGEPREVIEWBOX}}">' +
151                     '<img src="#" class="{{CSS.IMAGEPREVIEW}}" alt="" style="display: none;"/>' +
152                 '</div>' +
154                 // Add the submit button and close the form.
155                 '<button class="{{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
156                 '</div>' +
157             '</form>',
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}}' +
164                 'style="{{alignment}}{{margin}}{{customstyle}}"' +
165                 '{{#if classlist}}class="{{classlist}}" {{/if}}' +
166                 '/>';
168 Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
169     /**
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
176      */
177     _currentSelection: null,
179     /**
180      * The most recently selected image.
181      *
182      * @param _selectedImage
183      * @type Node
184      * @private
185      */
186     _selectedImage: null,
188     /**
189      * A reference to the currently open form.
190      *
191      * @param _form
192      * @type Node
193      * @private
194      */
195     _form: null,
197     /**
198      * The dimensions of the raw image before we manipulate it.
199      *
200      * @param _rawImageDimensions
201      * @type Object
202      * @private
203      */
204     _rawImageDimensions: null,
206     initializer: function() {
207         this.addButton({
208             icon: 'e/insert_edit_image',
209             callback: this._displayDialogue,
210             tags: 'img',
211             tagMatchRequiresAll: false
212         });
213         this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this);
214     },
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;
226         var selection = this.get('host').getSelectionFromNode(image);
227         this.get('host').setSelection(selection);
228         this._displayDialogue();
229     },
231     /**
232      * Display the image editing tool.
233      *
234      * @method _displayDialogue
235      * @private
236      */
237     _displayDialogue: function() {
238         // Store the current selection.
239         this._currentSelection = this.get('host').getSelection();
240         if (this._currentSelection === false) {
241             return;
242         }
244         // Reset the image dimensions.
245         this._rawImageDimensions = null;
247         var dialogue = this.getDialogue({
248             headerContent: M.util.get_string('imageproperties', COMPONENTNAME),
249             width: '480px',
250             focusAfterHide: true,
251             focusOnShowSelector: SELECTORS.INPUTURL
252         });
254         // Set the dialogue content, and then show the dialogue.
255         dialogue.set('bodyContent', this._getDialogueContent())
256                 .show();
257     },
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;
270         image.onerror = function() {
271             var preview = self._form.one('.' + CSS.IMAGEPREVIEW);
272             preview.setStyles({
273                 'display': 'none'
274             });
276             // Centre the dialogue when clearing the image preview.
277             self.getDialogue().centerDialogue();
278         };
280         image.onload = function() {
281             var input, currentwidth, currentheight, widthRatio, heightRatio;
283             self._rawImageDimensions = {
284                 width: this.width,
285                 height: this.height
286             };
288             input = self._form.one('.' + CSS.INPUTWIDTH);
289             currentwidth = input.get('value');
290             if (currentwidth === '') {
291                 input.set('value', this.width);
292                 currentwidth = "" + this.width;
293             }
294             input = self._form.one('.' + CSS.INPUTHEIGHT);
295             currentheight = input.get('value');
296             if (currentheight === '') {
297                 input.set('value', this.height);
298                 currentheight = "" + this.height;
299             }
300             input = self._form.one('.' + CSS.IMAGEPREVIEW);
301             input.setAttribute('src', this.src);
302             input.setStyles({
303                 'display': 'inline'
304             });
306             input = self._form.one('.' + CSS.INPUTCONSTRAIN);
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             }
322             // Apply the image sizing.
323             self._autoAdjustSize(self);
325             // Centre the dialogue once the preview image has loaded.
326             self.getDialogue().centerDialogue();
327         };
329         image.src = url;
330     },
332     /**
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
339      */
340     _getDialogueContent: function() {
341         var template = Y.Handlebars.compile(TEMPLATE),
342             canShowFilepicker = this.get('host').canShowFilepicker('image'),
343             content = Y.Node.create(template({
344                 elementid: this.get('host').get('elementid'),
345                 CSS: CSS,
346                 component: COMPONENTNAME,
347                 showFilepicker: canShowFilepicker,
348                 alignments: ALIGNMENTS
349             }));
351         this._form = content;
353         // Configure the view of the current image.
354         this._applyImageProperties(this._form);
356         this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
357         this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
358         this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
359         this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
360         this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
361         this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
362             if (event.target.get('checked')) {
363                 this._autoAdjustSize(event);
364             }
365         }, this);
366         this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
367         this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
369         if (canShowFilepicker) {
370             this._form.one('.' + CSS.IMAGEBROWSER).on('click', function() {
371                     this.get('host').showFilepicker('image', this._filepickerCallback, this);
372             }, this);
373         }
375         return content;
376     },
378     _autoAdjustSize: function(e, forceHeight) {
379         forceHeight = forceHeight || false;
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;
392         // If we do not know the image size, do not do anything.
393         if (!this._rawImageDimensions) {
394             return;
395         }
397         // Set the width back to default if it is empty.
398         if (keyFieldValue === '') {
399             keyFieldValue = this._rawImageDimensions[keyFieldType];
400             keyField.set('value', keyFieldValue);
401             keyFieldValue = keyField.get('value');
402         }
404         // Clear the existing preview sizes.
405         imagePreview.setStyles({
406             width: null,
407             height: null
408         });
410         // Now update with the new values.
411         if (!constrainField.get('checked')) {
412             // We are not keeping the image proportion - update the preview accordingly.
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');
421             }
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;
440                 _temporaryValue = keyFieldType;
441                 keyFieldType = subFieldType;
442                 subFieldType = _temporaryValue;
444                 _temporaryValue = keyFieldValue;
445                 keyFieldValue = subFieldValue;
446                 subFieldValue = _temporaryValue;
447             }
449             if (keyFieldValue.match(REGEX.ISPERCENT)) {
450                 // This is a percentage based change. Copy it verbatim.
451                 subFieldValue = keyFieldValue;
453                 // Set the width to the calculated pixel width.
454                 rawPercentage = parseInt(keyFieldValue, 10);
455                 rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
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]);
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                 }
477             }
479             // Update the subField's value within the form to reflect the changes.
480             subField.set('value', subFieldValue);
481         }
482     },
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) {
493         if (params.url !== '') {
494             var input = this._form.one('.' + CSS.INPUTURL);
495             input.set('value', params.url);
497             // Auto set the width and height.
498             this._form.one('.' + CSS.INPUTWIDTH).set('value', '');
499             this._form.one('.' + CSS.INPUTHEIGHT).set('value', '');
501             // Load the preview image.
502             this._loadPreviewImage(params.url);
503         }
504     },
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(),
515             img = form.one('.' + CSS.IMAGEPREVIEW),
516             i;
518         if (properties === false) {
519             img.setStyle('display', 'none');
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             }
527             // Remove the custom style option if this is a new image.
528             form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
529             return;
530         }
532         if (properties.align) {
533             form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align);
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);
541         }
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);
553             this._loadPreviewImage(properties.src);
554         }
555         if (properties.presentation) {
556             form.one('.' + CSS.IMAGEPRESENTATION).set('checked', 'checked');
557         }
559         // Update the image preview based on the form properties.
560         this._autoAdjustSize();
561     },
563     /**
564      * Gets the properties of the currently selected image.
565      *
566      * The first image only if multiple images are selected.
567      *
568      * @method _getSelectedImageProperties
569      * @return {object}
570      * @private
571      */
572     _getSelectedImageProperties: function() {
573         var properties = {
574                 src: null,
575                 alt :null,
576                 width: null,
577                 height: null,
578                 align: '',
579                 presentation: false
580             },
582             // Get the current selection.
583             images = this.get('host').getSelectedNodes(),
584             i, width, height, style, css;
586         if (images) {
587             images = images.filter('img');
588         }
590         if (images && images.size()) {
591             image = images.item(0);
592             this._selectedImage = image;
594             style = image.getAttribute('style');
595             properties.customstyle = style;
596             style = style.replace(/ /g, '');
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             }
607             if (width !== 0) {
608                 properties.width = width;
609             }
610             if (height !== 0) {
611                 properties.height = height;
612             }
613             for (i in ALIGNMENTS) {
614                 css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
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                     }
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         }
631         // No image selected - clean up.
632         this._selectedImage = null;
633         return false;
634     },
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() {
644         var input = this._form.one('.' + CSS.INPUTURL);
646         if (input.get('value') !== '') {
647             // Load the preview image.
648             this._loadPreviewImage(input.get('value'));
649         }
650     },
652     /**
653      * Update the image in the contenteditable.
654      *
655      * @method _setImage
656      * @param {EventFacade} e
657      * @private
658      */
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'),
666             margin = '',
667             presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'),
668             constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'),
669             imagehtml,
670             customstyle = '',
671             i,
672             classlist = [],
673             host = this.get('host');
675         e.preventDefault();
677         // Check if there are any accessibility issues.
678         if (this._updateWarning()) {
679             return;
680         }
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             }
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                     }
700                 }
701             }
703             if (constrain) {
704                 classlist.push(CSS.RESPONSIVE);
705             }
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             }
716             template = Y.Handlebars.compile(IMAGETEMPLATE);
717             imagehtml = template({
718                 url: url,
719                 alt: alt,
720                 width: width,
721                 height: height,
722                 presentation: presentation,
723                 alignment: alignment,
724                 margin: margin,
725                 customstyle: customstyle,
726                 classlist: classlist.join(' ')
727             });
729             this.get('host').insertContentAtFocusPoint(imagehtml);
731             this.markUpdated();
732         }
734         this.getDialogue({
735             focusAfterHide: null
736         }).hide();
738     },
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;
765     }
766 });