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
CommitLineData
adca7326
DW
1YUI.add('moodle-atto_image-button', function (Y, NAME) {
2
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/>.
17
62467795
AN
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 */
23
24/**
25 * @module moodle-atto_image_alignment-button
26 */
27
28/**
29 * Atto image selection tool.
30 *
31 * @namespace M.atto_image
32 * @class Button
33 * @extends M.editor_atto.EditorPlugin
34 */
35
36var CSS = {
d3931a7d 37 RESPONSIVE: 'img-responsive',
c9292b18
SH
38 INPUTALIGNMENT: 'atto_image_alignment',
39 INPUTALT: 'atto_image_altentry',
40 INPUTHEIGHT: 'atto_image_heightentry',
62467795 41 INPUTSUBMIT: 'atto_image_urlentrysubmit',
c9292b18 42 INPUTURL: 'atto_image_urlentry',
ffb8aff6 43 INPUTSIZE: 'atto_image_size',
c9292b18
SH
44 INPUTWIDTH: 'atto_image_widthentry',
45 IMAGEALTWARNING: 'atto_image_altwarning',
46 IMAGEBROWSER: 'openimagebrowser',
47 IMAGEPRESENTATION: 'atto_image_presentation',
ffb8aff6 48 INPUTCONSTRAIN: 'atto_image_constrain',
d28af3d4 49 INPUTCUSTOMSTYLE: 'atto_image_customstyle',
d3931a7d
DW
50 IMAGEPREVIEW: 'atto_image_preview',
51 IMAGEPREVIEWBOX: 'atto_image_preview_box'
c9292b18 52 },
e5ddec38
DW
53 SELECTORS = {
54 INPUTURL: '.' + CSS.INPUTURL
55 },
62467795
AN
56 ALIGNMENTS = [
57 // Vertical alignment.
58 {
62467795 59 name: 'text-top',
2e8cbbb3
DW
60 str: 'alignment_top',
61 value: 'vertical-align',
62 margin: '0 .5em'
62467795
AN
63 }, {
64 name: 'middle',
65 str: 'alignment_middle',
2e8cbbb3
DW
66 value: 'vertical-align',
67 margin: '0 .5em'
62467795
AN
68 }, {
69 name: 'text-bottom',
2e8cbbb3
DW
70 str: 'alignment_bottom',
71 value: 'vertical-align',
7bbc64b8
DW
72 margin: '0 .5em',
73 isDefault: true
62467795
AN
74 },
75
76 // Floats.
77 {
78 name: 'left',
79 str: 'alignment_left',
2e8cbbb3
DW
80 value: 'float',
81 margin: '0 .5em 0 0'
62467795
AN
82 }, {
83 name: 'right',
84 str: 'alignment_right',
2e8cbbb3 85 value: 'float',
6dccf359 86 margin: '0 0 0 .5em'
d28af3d4
DW
87 }, {
88 name: 'customstyle',
89 str: 'customstyle',
90 value: 'style'
62467795 91 }
d3931a7d 92 ],
c9292b18 93
d3931a7d
DW
94 REGEX = {
95 ISPERCENT: /\d+%/
96 },
97
98 COMPONENTNAME = 'atto_image',
c9292b18 99
62467795
AN
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/>' +
c9292b18 105
62467795
AN
106 // Add the repository browser button.
107 '{{#if showFilepicker}}' +
108 '<button class="{{CSS.IMAGEBROWSER}}" type="button">{{get_string "browserepositories" component}}</button>' +
109 '{{/if}}' +
110
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/>' +
118
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/>' +
123
ffb8aff6 124 // Add the size entry boxes.
8aa64bab 125 '<label class="sameline" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
ffb8aff6
DW
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 ' +
62467795
AN
129
130 // Add the height entry box.
ffb8aff6
DW
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"/>' +
133
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>' +
62467795
AN
138
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}}' +
da00661d 143 '<option value="{{value}}:{{name}};">{{get_string str ../component}}</option>' +
62467795
AN
144 '{{/each}}' +
145 '</select>' +
d28af3d4
DW
146 // Hidden input to store custom styles.
147 '<input type="hidden" class="{{CSS.INPUTCUSTOMSTYLE}}"/>' +
62467795
AN
148 '<br/>' +
149
150 // Add the image preview.
62467795 151 '<div class="mdl-align">' +
d3931a7d 152 '<div class="{{CSS.IMAGEPREVIEWBOX}}">' +
f3662213 153 '<img src="#" class="{{CSS.IMAGEPREVIEW}}" alt="" style="display: none;"/>' +
d3931a7d 154 '</div>' +
c9292b18 155
62467795 156 // Add the submit button and close the form.
d3931a7d 157 '<button class="{{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
62467795
AN
158 '</div>' +
159 '</form>',
160
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}}' +
d28af3d4 166 'style="{{alignment}}{{margin}}{{customstyle}}"' +
d3931a7d 167 '{{#if classlist}}class="{{classlist}}" {{/if}}' +
62467795
AN
168 '/>';
169
170Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
c9292b18 171 /**
62467795
AN
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
c9292b18 178 */
62467795 179 _currentSelection: null,
c9292b18
SH
180
181 /**
62467795
AN
182 * The most recently selected image.
183 *
184 * @param _selectedImage
185 * @type Node
186 * @private
c9292b18 187 */
62467795 188 _selectedImage: null,
c9292b18
SH
189
190 /**
62467795
AN
191 * A reference to the currently open form.
192 *
193 * @param _form
194 * @type Node
c9292b18 195 * @private
c9292b18 196 */
62467795
AN
197 _form: null,
198
ffb8aff6 199 /**
f3662213 200 * The dimensions of the raw image before we manipulate it.
ffb8aff6 201 *
f3662213
AN
202 * @param _rawImageDimensions
203 * @type Object
ffb8aff6
DW
204 * @private
205 */
f3662213 206 _rawImageDimensions: null,
ffb8aff6 207
62467795
AN
208 initializer: function() {
209 this.addButton({
210 icon: 'e/insert_edit_image',
211 callback: this._displayDialogue,
212 tags: 'img',
213 tagMatchRequiresAll: false
214 });
43a737d1
DW
215 this.editor.delegate('dblclick', this._handleDoubleClick, 'img', this);
216 },
217
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;
227
228 var selection = this.get('host').getSelectionFromNode(image);
229 this.get('host').setSelection(selection);
230 this._displayDialogue();
c9292b18
SH
231 },
232
233 /**
62467795
AN
234 * Display the image editing tool.
235 *
236 * @method _displayDialogue
237 * @private
c9292b18 238 */
62467795
AN
239 _displayDialogue: function() {
240 // Store the current selection.
241 this._currentSelection = this.get('host').getSelection();
242 if (this._currentSelection === false) {
243 return;
c9292b18 244 }
62467795 245
91bc9570
FM
246 // Reset the image dimensions.
247 this._rawImageDimensions = null;
248
62467795 249 var dialogue = this.getDialogue({
d3931a7d 250 headerContent: M.util.get_string('imageproperties', COMPONENTNAME),
da00661d 251 width: '480px',
e5ddec38 252 focusAfterHide: true,
c1660772 253 focusOnShowSelector: SELECTORS.INPUTURL
62467795
AN
254 });
255
256 // Set the dialogue content, and then show the dialogue.
257 dialogue.set('bodyContent', this._getDialogueContent())
258 .show();
c9292b18
SH
259 },
260
ffb8aff6
DW
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;
271
f3662213
AN
272 image.onerror = function() {
273 var preview = self._form.one('.' + CSS.IMAGEPREVIEW);
274 preview.setStyles({
275 'display': 'none'
276 });
277
278 // Centre the dialogue when clearing the image preview.
279 self.getDialogue().centerDialogue();
280 };
281
ffb8aff6
DW
282 image.onload = function() {
283 var input, currentwidth, currentheight, widthRatio, heightRatio;
284
f3662213
AN
285 self._rawImageDimensions = {
286 width: this.width,
287 height: this.height
288 };
ffb8aff6
DW
289
290 input = self._form.one('.' + CSS.INPUTWIDTH);
291 currentwidth = input.get('value');
292 if (currentwidth === '') {
293 input.set('value', this.width);
d3931a7d 294 currentwidth = "" + this.width;
ffb8aff6
DW
295 }
296 input = self._form.one('.' + CSS.INPUTHEIGHT);
297 currentheight = input.get('value');
298 if (currentheight === '') {
299 input.set('value', this.height);
d3931a7d 300 currentheight = "" + this.height;
ffb8aff6
DW
301 }
302 input = self._form.one('.' + CSS.IMAGEPREVIEW);
f3662213
AN
303 input.setAttribute('src', this.src);
304 input.setStyles({
305 'display': 'inline'
306 });
ffb8aff6 307
ffb8aff6 308 input = self._form.one('.' + CSS.INPUTCONSTRAIN);
d3931a7d
DW
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 }
ffb8aff6 323
f3662213
AN
324 // Apply the image sizing.
325 self._autoAdjustSize(self);
326
ffb8aff6
DW
327 // Centre the dialogue once the preview image has loaded.
328 self.getDialogue().centerDialogue();
329 };
330
331 image.src = url;
332 },
333
c9292b18 334 /**
62467795
AN
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
c9292b18 341 */
62467795
AN
342 _getDialogueContent: function() {
343 var template = Y.Handlebars.compile(TEMPLATE),
6cb48e04 344 canShowFilepicker = this.get('host').canShowFilepicker('image'),
62467795
AN
345 content = Y.Node.create(template({
346 elementid: this.get('host').get('elementid'),
347 CSS: CSS,
348 component: COMPONENTNAME,
6cb48e04 349 showFilepicker: canShowFilepicker,
62467795
AN
350 alignments: ALIGNMENTS
351 }));
352
353 this._form = content;
354
355 // Configure the view of the current image.
356 this._applyImageProperties(this._form);
357
ffb8aff6 358 this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
9754ab92
JF
359 this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
360 this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
f3662213
AN
361 this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
362 this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
d3931a7d
DW
363 this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
364 if (event.target.get('checked')) {
f3662213 365 this._autoAdjustSize(event);
d3931a7d
DW
366 }
367 }, this);
62467795
AN
368 this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
369 this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
6cb48e04
FM
370
371 if (canShowFilepicker) {
372 this._form.one('.' + CSS.IMAGEBROWSER).on('click', function() {
373 this.get('host').showFilepicker('image', this._filepickerCallback, this);
374 }, this);
375 }
adca7326 376
62467795 377 return content;
adca7326 378 },
adca7326 379
f3662213
AN
380 _autoAdjustSize: function(e, forceHeight) {
381 forceHeight = forceHeight || false;
382
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;
ffb8aff6 393
91bc9570
FM
394 // If we do not know the image size, do not do anything.
395 if (!this._rawImageDimensions) {
396 return;
397 }
398
d3931a7d 399 // Set the width back to default if it is empty.
f3662213
AN
400 if (keyFieldValue === '') {
401 keyFieldValue = this._rawImageDimensions[keyFieldType];
402 keyField.set('value', keyFieldValue);
403 keyFieldValue = keyField.get('value');
d3931a7d
DW
404 }
405
f3662213
AN
406 // Clear the existing preview sizes.
407 imagePreview.setStyles({
408 width: null,
409 height: null
410 });
ffb8aff6 411
f3662213
AN
412 // Now update with the new values.
413 if (!constrainField.get('checked')) {
414 // We are not keeping the image proportion - update the preview accordingly.
d3931a7d 415
f3662213
AN
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');
d3931a7d 423 }
ffb8aff6 424
f3662213
AN
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;
441
442 _temporaryValue = keyFieldType;
443 keyFieldType = subFieldType;
444 subFieldType = _temporaryValue;
445
446 _temporaryValue = keyFieldValue;
447 keyFieldValue = subFieldValue;
448 subFieldValue = _temporaryValue;
449 }
ffb8aff6 450
f3662213
AN
451 if (keyFieldValue.match(REGEX.ISPERCENT)) {
452 // This is a percentage based change. Copy it verbatim.
453 subFieldValue = keyFieldValue;
d3931a7d 454
f3662213
AN
455 // Set the width to the calculated pixel width.
456 rawPercentage = parseInt(keyFieldValue, 10);
457 rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
ffb8aff6 458
f3662213
AN
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]);
467
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 }
d3931a7d 479 }
f3662213
AN
480
481 // Update the subField's value within the form to reflect the changes.
482 subField.set('value', subFieldValue);
d3931a7d 483 }
ffb8aff6
DW
484 },
485
62467795
AN
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) {
adca7326 495 if (params.url !== '') {
ffb8aff6 496 var input = this._form.one('.' + CSS.INPUTURL);
adca7326
DW
497 input.set('value', params.url);
498
499 // Auto set the width and height.
8d1c0179
DW
500 this._form.one('.' + CSS.INPUTWIDTH).set('value', '');
501 this._form.one('.' + CSS.INPUTHEIGHT).set('value', '');
ffb8aff6
DW
502
503 // Load the preview image.
504 this._loadPreviewImage(params.url);
adca7326
DW
505 }
506 },
adca7326 507
62467795
AN
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(),
d3931a7d
DW
517 img = form.one('.' + CSS.IMAGEPREVIEW),
518 i;
adca7326 519
62467795
AN
520 if (properties === false) {
521 img.setStyle('display', 'none');
7bbc64b8
DW
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 }
d28af3d4
DW
529 // Remove the custom style option if this is a new image.
530 form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
adca7326 531 return;
adca7326
DW
532 }
533
62467795 534 if (properties.align) {
da00661d 535 form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align);
d28af3d4
DW
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);
62467795 543 }
62467795
AN
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);
ffb8aff6 555 this._loadPreviewImage(properties.src);
62467795
AN
556 }
557 if (properties.presentation) {
558 form.one('.' + CSS.IMAGEPRESENTATION).set('checked', 'checked');
adca7326 559 }
f3662213
AN
560
561 // Update the image preview based on the form properties.
562 this._autoAdjustSize();
adca7326 563 },
62467795 564
c9292b18
SH
565 /**
566 * Gets the properties of the currently selected image.
567 *
568 * The first image only if multiple images are selected.
569 *
62467795
AN
570 * @method _getSelectedImageProperties
571 * @return {object}
c9292b18 572 * @private
c9292b18 573 */
62467795 574 _getSelectedImageProperties: function() {
c9292b18
SH
575 var properties = {
576 src: null,
577 alt :null,
578 width: null,
579 height: null,
d28af3d4 580 align: '',
c9292b18
SH
581 presentation: false
582 },
62467795
AN
583
584 // Get the current selection.
585 images = this.get('host').getSelectedNodes(),
da00661d 586 i, width, height, style, css;
c9292b18 587
d321f68b
DW
588 if (images) {
589 images = images.filter('img');
590 }
591
592 if (images && images.size()) {
c9292b18 593 image = images.item(0);
62467795 594 this._selectedImage = image;
d321f68b 595
c9292b18 596 style = image.getAttribute('style');
d28af3d4
DW
597 properties.customstyle = style;
598 style = style.replace(/ /g, '');
c9292b18 599
d3931a7d
DW
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 }
608
609 if (width !== 0) {
c9292b18
SH
610 properties.width = width;
611 }
d3931a7d 612 if (height !== 0) {
c9292b18
SH
613 properties.height = height;
614 }
615 for (i in ALIGNMENTS) {
da00661d 616 css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
d28af3d4
DW
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 }
c9292b18
SH
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 }
62467795
AN
632
633 // No image selected - clean up.
634 this._selectedImage = null;
c9292b18
SH
635 return false;
636 },
c9292b18 637
62467795
AN
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() {
ffb8aff6 646 var input = this._form.one('.' + CSS.INPUTURL);
b269f635 647
62467795 648 if (input.get('value') !== '') {
ffb8aff6
DW
649 // Load the preview image.
650 this._loadPreviewImage(input.get('value'));
b269f635 651 }
c9292b18 652 },
62467795 653
c9292b18 654 /**
62467795 655 * Update the image in the contenteditable.
c9292b18 656 *
62467795
AN
657 * @method _setImage
658 * @param {EventFacade} e
c9292b18 659 * @private
c9292b18 660 */
62467795
AN
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'),
2e8cbbb3 668 margin = '',
62467795 669 presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'),
d3931a7d 670 constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'),
62467795 671 imagehtml,
d28af3d4 672 customstyle = '',
2e8cbbb3 673 i,
d3931a7d 674 classlist = [],
62467795 675 host = this.get('host');
c9292b18 676
62467795
AN
677 e.preventDefault();
678
9754ab92
JF
679 // Check if there are any accessibility issues.
680 if (this._updateWarning()) {
c9292b18
SH
681 return;
682 }
683
62467795
AN
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 }
d28af3d4
DW
692
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 }
2e8cbbb3
DW
702 }
703 }
704
d3931a7d
DW
705 if (constrain) {
706 classlist.push(CSS.RESPONSIVE);
707 }
708
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 }
717
62467795
AN
718 template = Y.Handlebars.compile(IMAGETEMPLATE);
719 imagehtml = template({
720 url: url,
721 alt: alt,
722 width: width,
723 height: height,
724 presentation: presentation,
2e8cbbb3 725 alignment: alignment,
d28af3d4 726 margin: margin,
d3931a7d
DW
727 customstyle: customstyle,
728 classlist: classlist.join(' ')
62467795
AN
729 });
730
731 this.get('host').insertContentAtFocusPoint(imagehtml);
732
733 this.markUpdated();
c9292b18 734 }
d3931a7d
DW
735
736 this.getDialogue({
737 focusAfterHide: null
738 }).hide();
739
9754ab92
JF
740 },
741
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);
b983a5d8 763 state = false;
9754ab92
JF
764 }
765 this.getDialogue().centerDialogue();
766 return state;
adca7326 767 }
62467795 768});
adca7326
DW
769
770
62467795 771}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin"]});