From c3e1c98f04101a371f1e4e96e8b256b21758a650 Mon Sep 17 00:00:00 2001 From: Neill Magill Date: Thu, 11 Aug 2016 13:58:09 +0100 Subject: [PATCH] MDL-52317 Atto: Large resized images may display scroll bars When an image was added via Atto and it was set to resize automatically if the alignment was set to Top, Middle or Bottom when it was made smaller then a vertical scroll bar would be displayed. This was caused by the interaction of the margin settings on the image and the 100% width set via the img-responsive css class. The width of an element only includes the content area and not any borders, padding or margins. This change stops atto hardcoding the styles for image alignment into the images style tag, it instead adds a class that defines the alignment the image should have. This change has the benefit that themers will now be able to adjust how atto image alignments work if they wish. Images added before this patch will continue to have the issue until they are edited by the plugin, at which time their legacy style will be removed and the new alignment method will be added. Thanks to Andrew Nicols who provided a refactor for the Alignment and style detection code. --- lib/editor/atto/plugins/image/styles.css | 33 ++++ .../moodle-atto_image-button-debug.js | 186 +++++++++++------- .../moodle-atto_image-button-min.js | 6 +- .../moodle-atto_image-button.js | 184 ++++++++++------- .../plugins/image/yui/src/button/js/button.js | 186 +++++++++++------- 5 files changed, 373 insertions(+), 222 deletions(-) diff --git a/lib/editor/atto/plugins/image/styles.css b/lib/editor/atto/plugins/image/styles.css index 95a6b9769fd..4d455831966 100644 --- a/lib/editor/atto/plugins/image/styles.css +++ b/lib/editor/atto/plugins/image/styles.css @@ -27,3 +27,36 @@ .atto_image_size label { display: inline-block; } + +.atto_image_button_text-top { + vertical-align: text-top; + margin: 0 0.5em; +} + +.atto_image_button_middle { + vertical-align: middle; + margin: 0 0.5em; +} + +.atto_image_button_text-bottom { + vertical-align: text-bottom; + margin: 0 0.5em; +} + +.atto_image_button_text-top.img-responsive, +.atto_image_button_middle.img-responsive, +.atto_image_button_text-bottom.img-responsive { + max-width: calc(100% - 1em); +} + +.atto_image_button_left { + float: left; + margin: 0 0.5em 0 0; + max-width: calc(100% - 1em); +} + +.atto_image_button_right { + float: right; + margin: 0 0 0 0.5em; + max-width: calc(100% - 1em); +} diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js index 64b888e277d..69ab2fdce7e 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-debug.js @@ -48,7 +48,8 @@ var CSS = { INPUTCONSTRAIN: 'atto_image_constrain', INPUTCUSTOMSTYLE: 'atto_image_customstyle', IMAGEPREVIEW: 'atto_image_preview', - IMAGEPREVIEWBOX: 'atto_image_preview_box' + IMAGEPREVIEWBOX: 'atto_image_preview_box', + ALIGNSETTINGS: 'atto_image_button' }, SELECTORS = { INPUTURL: '.' + CSS.INPUTURL @@ -56,38 +57,34 @@ var CSS = { ALIGNMENTS = [ // Vertical alignment. { - name: 'text-top', + name: 'verticalAlign', str: 'alignment_top', - value: 'vertical-align', - margin: '0 .5em' + value: 'text-top', + margin: '0 0.5em' }, { - name: 'middle', + name: 'verticalAlign', str: 'alignment_middle', - value: 'vertical-align', - margin: '0 .5em' + value: 'middle', + margin: '0 0.5em' }, { - name: 'text-bottom', + name: 'verticalAlign', str: 'alignment_bottom', - value: 'vertical-align', - margin: '0 .5em', + value: 'text-bottom', + margin: '0 0.5em', isDefault: true }, // Floats. { - name: 'left', + name: 'float', str: 'alignment_left', - value: 'float', - margin: '0 .5em 0 0' + value: 'left', + margin: '0 0.5em 0 0' }, { - name: 'right', + name: 'float', str: 'alignment_right', - value: 'float', - margin: '0 0 0 .5em' - }, { - name: 'customstyle', - str: 'customstyle', - value: 'style' + value: 'right', + margin: '0 0 0 0.5em' } ], @@ -142,7 +139,7 @@ var CSS = { '' + '' + // Hidden input to store custom styles. @@ -165,7 +162,7 @@ var CSS = { '{{#if width}}width="{{width}}" {{/if}}' + '{{#if height}}height="{{height}}" {{/if}}' + '{{#if presentation}}role="presentation" {{/if}}' + - 'style="{{alignment}}{{margin}}{{customstyle}}"' + + '{{#if customstyle}}style="{{customstyle}}" {{/if}}' + '{{#if classlist}}class="{{classlist}}" {{/if}}' + '{{#if id}}id="{{id}}" {{/if}}' + '/>'; @@ -234,6 +231,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e + * @return mixed * @private */ _handleDragDrop: function(e) { @@ -647,30 +645,25 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi */ _applyImageProperties: function(form) { var properties = this._getSelectedImageProperties(), - img = form.one('.' + CSS.IMAGEPREVIEW), - i, - css; + img = form.one('.' + CSS.IMAGEPREVIEW); if (properties === false) { img.setStyle('display', 'none'); // Set the default alignment. - for (i in ALIGNMENTS) { - if (ALIGNMENTS[i].isDefault === true) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - form.one('.' + CSS.INPUTALIGNMENT).set('value', css); + ALIGNMENTS.some(function(alignment) { + if (alignment.isDefault) { + form.one('.' + CSS.INPUTALIGNMENT).set('value', alignment.value); + return true; } - } - // Remove the custom style option if this is a new image. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); + + return false; + }, this); + return; } if (properties.align) { form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align); - // Remove the custom style option if we have a standard alignment. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); - } else { - form.one('.' + CSS.INPUTALIGNMENT).set('value', 'style:customstyle;'); } if (properties.customstyle) { form.one('.' + CSS.INPUTCUSTOMSTYLE).set('value', properties.customstyle); @@ -717,25 +710,21 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi // Get the current selection. images = this.get('host').getSelectedNodes(), - i, width, height, style, - css, - image, - margin; + image; if (images) { images = images.filter('img'); } if (images && images.size()) { - image = images.item(0); + image = this._removeLegacyAlignment(images.item(0)); this._selectedImage = image; style = image.getAttribute('style'); properties.customstyle = style; - style = style.replace(/ /g, ''); width = image.getAttribute('width'); if (!width.match(REGEX.ISPERCENT)) { @@ -752,18 +741,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi if (height !== 0) { properties.height = height; } - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (style.indexOf(css) !== -1) { - margin = 'margin:' + ALIGNMENTS[i].margin + ';'; - margin = margin.replace(/ /g, ''); - // Must match alignment and margins - otherwise custom style is selected. - if (style.indexOf(margin) !== -1) { - properties.align = css; - break; - } - } - } + this._getAlignmentPropeties(image, properties); properties.src = image.getAttribute('src'); properties.alt = image.getAttribute('alt') || ''; properties.presentation = (image.get('role') === 'presentation'); @@ -775,6 +753,40 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi return false; }, + /** + * Sets the alignment of a properties object. + * + * @method _getAlignmentPropeties + * @param {Node} image The image that the alignment properties should be found for + * @param {Object} properties The properties object that is created in _getSelectedImageProperties() + * @private + */ + _getAlignmentPropeties: function(image, properties) { + var complete = false, + defaultAlignment; + + // Check for an alignment value. + complete = ALIGNMENTS.some(function(alignment) { + var classname = this._getAlignmentClass(alignment.value); + if (image.hasClass(classname)) { + properties.align = alignment.value; + Y.log('Found alignment ' + alignment.value, 'debug', 'atto_image-button'); + + return true; + } + + if (alignment.isDefault) { + defaultAlignment = alignment.value; + } + + return false; + }, this); + + if (!complete && defaultAlignment) { + properties.align = defaultAlignment; + } + }, + /** * Update the form when the URL was changed. This includes updating the * height, width, and image preview. @@ -804,14 +816,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi alt = form.one('.' + CSS.INPUTALT).get('value'), width = form.one('.' + CSS.INPUTWIDTH).get('value'), height = form.one('.' + CSS.INPUTHEIGHT).get('value'), - alignment = form.one('.' + CSS.INPUTALIGNMENT).get('value'), - margin = '', + alignment = this._getAlignmentClass(form.one('.' + CSS.INPUTALIGNMENT).get('value')), presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'), constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'), imagehtml, - customstyle = '', - i, - css, + customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'), classlist = [], host = this.get('host'); @@ -831,22 +840,13 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi host.setSelection(this._currentSelection); } - if (alignment === 'style:customstyle;') { - alignment = ''; - customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'); - } else { - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (alignment === css) { - margin = ' margin: ' + ALIGNMENTS[i].margin + ';'; - } - } - } - if (constrain) { classlist.push(CSS.RESPONSIVE); } + // Add the alignment class for the image. + classlist.push(alignment); + if (!width.match(REGEX.ISPERCENT) && isNaN(parseInt(width, 10))) { form.one('.' + CSS.INPUTWIDTH).focus(); return; @@ -863,8 +863,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi width: width, height: height, presentation: presentation, - alignment: alignment, - margin: margin, customstyle: customstyle, classlist: classlist.join(' ') }); @@ -880,6 +878,48 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }, + /** + * Removes any legacy styles added by previous versions of the atto image button. + * + * @method _removeLegacyAlignment + * @param {Y.Node} imageNode + * @return {Y.Node} + * @private + */ + _removeLegacyAlignment: function(imageNode) { + if (!imageNode.getStyle('margin')) { + // There is no margin therefore this cannot match any known alignments. + return imageNode; + } + + ALIGNMENTS.some(function(alignment) { + if (imageNode.getStyle(alignment.name) !== alignment.value) { + // The name/value do not match. Skip. + return false; + } + + var normalisedNode = Y.Node.create('
'); + normalisedNode.setStyle('margin', alignment.margin); + if (imageNode.getStyle('margin') !== normalisedNode.getStyle('margin')) { + // The margin does not match. + return false; + } + + Y.log('Legacy alignment found and removed.', 'info', 'atto_image-button'); + imageNode.addClass(this._getAlignmentClass(alignment.value)); + imageNode.setStyle(alignment.name, null); + imageNode.setStyle('margin', null); + + return true; + }, this); + + return imageNode; + }, + + _getAlignmentClass: function(alignment) { + return CSS.ALIGNSETTINGS + '_' + alignment; + }, + /** * Update the alt text warning live. * diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js index a973de75067..78edf53af19 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button-min.js @@ -1,3 +1,3 @@ -YUI.add("moodle-atto_image-button",function(e,t){var n={RESPONSIVE:"img-responsive",INPUTALIGNMENT:"atto_image_alignment",INPUTALT:"atto_image_altentry",INPUTHEIGHT:"atto_image_heightentry",INPUTSUBMIT:"atto_image_urlentrysubmit",INPUTURL:"atto_image_urlentry",INPUTSIZE:"atto_image_size",INPUTWIDTH:"atto_image_widthentry",IMAGEALTWARNING:"atto_image_altwarning",IMAGEBROWSER:"openimagebrowser",IMAGEPRESENTATION:"atto_image_presentation",INPUTCONSTRAIN:"atto_image_constrain",INPUTCUSTOMSTYLE:"atto_image_customstyle",IMAGEPREVIEW:"atto_image_preview",IMAGEPREVIEWBOX:"atto_image_preview_box"},r={INPUTURL:"."+n.INPUTURL},i=[{name:"text-top",str:"alignment_top",value:"vertical-align",margin:"0 .5em"},{name:"middle",str:"alignment_middle",value:"vertical-align",margin:"0 .5em"},{name:"text-bottom",str:"alignment_bottom",value:"vertical-align",margin:"0 .5em",isDefault:!0},{name:"left",str:"alignment_left",value:"float",margin:"0 .5em 0 0"},{name:"right",str:"alignment_right",value:"float",margin:"0 0 0 .5em"},{name:"customstyle",str:"customstyle",value:"style"}],s={ISPERCENT:/\d+%/},o="atto_image",u='

{{#if showFilepicker}}{{/if}}

x

',a='{{alt}}';e.namespace("M.atto_image").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_selectedImage:null,_form:null,_rawImageDimensions:null,initializer:function(){this.addButton({icon:"e/insert_edit_image",callback:this._displayDialogue,tags:"img",tagMatchRequiresAll:!1}),this.editor.delegate("dblclick",this._displayDialogue,"img",this),this.editor.delegate("click",this._handleClick,"img",this),this.editor.on("drop",this._handleDragDrop,this),this.editor.on("dragover",function(e){e.preventDefault()},this),this.editor.on("dragenter",function(e){e.preventDefault()},this)},_handleDragDrop:function(t){var n=this,r=this.get("host"),i=e.Handlebars.compile(a);r.saveSelection(),t=t._event;var s=t.dataTransfer&&t.dataTransfer.files&&t.dataTransfer.files.length;if(s&&/^image\//.test(t.dataTransfer.files[0].type)){var u=r.get("filepickeroptions").image,f=u.savepath===undefined?"/":u.savepath,l=new FormData,c=0,h="",p=new XMLHttpRequest,d="",v=Object.keys(u.repositories);t.preventDefault(),t.stopPropagation(),l.append("repo_upload_file",t.dataTransfer.files[0]),l.append("itemid",u.itemid);for(var m=0;m
{{#if showFilepicker}}{{/if}}

x

',a='{{alt}}';e.namespace("M.atto_image").Button=e.Base.create("button",e.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_selectedImage:null,_form:null,_rawImageDimensions:null,initializer:function(){this.addButton({icon:"e/insert_edit_image",callback:this._displayDialogue,tags:"img",tagMatchRequiresAll:!1}),this.editor.delegate("dblclick",this._displayDialogue,"img",this),this.editor.delegate("click",this._handleClick,"img",this),this.editor.on("drop",this._handleDragDrop,this),this.editor.on("dragover",function(e){e.preventDefault()},this),this.editor.on("dragenter",function(e){e.preventDefault()},this)},_handleDragDrop:function(t){var n=this,r=this.get("host"),i=e.Handlebars.compile(a);r.saveSelection(),t=t._event;var s=t.dataTransfer&&t.dataTransfer.files&&t.dataTransfer.files.length;if(s&&/^image\//.test(t.dataTransfer.files[0].type)){var u=r.get("filepickeroptions").image,f=u.savepath===undefined?"/":u.savepath,l=new FormData,c=0,h="",p=new XMLHttpRequest,d="",v=Object.keys(u.repositories);t.preventDefault(),t.stopPropagation(),l.append("repo_upload_file",t.dataTransfer.files[0]),l.append("itemid",u.itemid);for(var m=0;m");return r.setStyle("margin",n.margin),t.getStyle("margin")!==r.getStyle("margin")?!1:(t.addClass(this._getAlignmentClass(n.value)),t.setStyle(n.name,null),t.setStyle("margin",null),!0)},this),t):t},_getAlignmentClass:function(e){return n.ALIGNSETTINGS+"_"+e},_updateWarning:function(){var e=this._form,t=!0,r=e.one("."+n.INPUTALT).get("value"),i=e.one("."+n.IMAGEPRESENTATION).get("checked");return r===""&&!i?(e.one("."+n.IMAGEALTWARNING).setStyle("display","block"),e.one("."+n.INPUTALT).setAttribute("aria-invalid",!0),e.one("."+n.IMAGEPRESENTATION).setAttribute("aria-invalid",!0),t=!0):(e.one("."+n.IMAGEALTWARNING).setStyle("display","none"),e.one("."+n.INPUTALT).setAttribute("aria-invalid",!1),e.one("."+n.IMAGEPRESENTATION).setAttribute("aria-invalid",!1),t=!1),this.getDialogue().centerDialogue(),t}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]}); diff --git a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js index 64b888e277d..18416c3ae3a 100644 --- a/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js +++ b/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/moodle-atto_image-button.js @@ -48,7 +48,8 @@ var CSS = { INPUTCONSTRAIN: 'atto_image_constrain', INPUTCUSTOMSTYLE: 'atto_image_customstyle', IMAGEPREVIEW: 'atto_image_preview', - IMAGEPREVIEWBOX: 'atto_image_preview_box' + IMAGEPREVIEWBOX: 'atto_image_preview_box', + ALIGNSETTINGS: 'atto_image_button' }, SELECTORS = { INPUTURL: '.' + CSS.INPUTURL @@ -56,38 +57,34 @@ var CSS = { ALIGNMENTS = [ // Vertical alignment. { - name: 'text-top', + name: 'verticalAlign', str: 'alignment_top', - value: 'vertical-align', - margin: '0 .5em' + value: 'text-top', + margin: '0 0.5em' }, { - name: 'middle', + name: 'verticalAlign', str: 'alignment_middle', - value: 'vertical-align', - margin: '0 .5em' + value: 'middle', + margin: '0 0.5em' }, { - name: 'text-bottom', + name: 'verticalAlign', str: 'alignment_bottom', - value: 'vertical-align', - margin: '0 .5em', + value: 'text-bottom', + margin: '0 0.5em', isDefault: true }, // Floats. { - name: 'left', + name: 'float', str: 'alignment_left', - value: 'float', - margin: '0 .5em 0 0' + value: 'left', + margin: '0 0.5em 0 0' }, { - name: 'right', + name: 'float', str: 'alignment_right', - value: 'float', - margin: '0 0 0 .5em' - }, { - name: 'customstyle', - str: 'customstyle', - value: 'style' + value: 'right', + margin: '0 0 0 0.5em' } ], @@ -142,7 +139,7 @@ var CSS = { '' + '' + // Hidden input to store custom styles. @@ -165,7 +162,7 @@ var CSS = { '{{#if width}}width="{{width}}" {{/if}}' + '{{#if height}}height="{{height}}" {{/if}}' + '{{#if presentation}}role="presentation" {{/if}}' + - 'style="{{alignment}}{{margin}}{{customstyle}}"' + + '{{#if customstyle}}style="{{customstyle}}" {{/if}}' + '{{#if classlist}}class="{{classlist}}" {{/if}}' + '{{#if id}}id="{{id}}" {{/if}}' + '/>'; @@ -234,6 +231,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e + * @return mixed * @private */ _handleDragDrop: function(e) { @@ -647,30 +645,25 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi */ _applyImageProperties: function(form) { var properties = this._getSelectedImageProperties(), - img = form.one('.' + CSS.IMAGEPREVIEW), - i, - css; + img = form.one('.' + CSS.IMAGEPREVIEW); if (properties === false) { img.setStyle('display', 'none'); // Set the default alignment. - for (i in ALIGNMENTS) { - if (ALIGNMENTS[i].isDefault === true) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - form.one('.' + CSS.INPUTALIGNMENT).set('value', css); + ALIGNMENTS.some(function(alignment) { + if (alignment.isDefault) { + form.one('.' + CSS.INPUTALIGNMENT).set('value', alignment.value); + return true; } - } - // Remove the custom style option if this is a new image. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); + + return false; + }, this); + return; } if (properties.align) { form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align); - // Remove the custom style option if we have a standard alignment. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); - } else { - form.one('.' + CSS.INPUTALIGNMENT).set('value', 'style:customstyle;'); } if (properties.customstyle) { form.one('.' + CSS.INPUTCUSTOMSTYLE).set('value', properties.customstyle); @@ -717,25 +710,21 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi // Get the current selection. images = this.get('host').getSelectedNodes(), - i, width, height, style, - css, - image, - margin; + image; if (images) { images = images.filter('img'); } if (images && images.size()) { - image = images.item(0); + image = this._removeLegacyAlignment(images.item(0)); this._selectedImage = image; style = image.getAttribute('style'); properties.customstyle = style; - style = style.replace(/ /g, ''); width = image.getAttribute('width'); if (!width.match(REGEX.ISPERCENT)) { @@ -752,18 +741,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi if (height !== 0) { properties.height = height; } - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (style.indexOf(css) !== -1) { - margin = 'margin:' + ALIGNMENTS[i].margin + ';'; - margin = margin.replace(/ /g, ''); - // Must match alignment and margins - otherwise custom style is selected. - if (style.indexOf(margin) !== -1) { - properties.align = css; - break; - } - } - } + this._getAlignmentPropeties(image, properties); properties.src = image.getAttribute('src'); properties.alt = image.getAttribute('alt') || ''; properties.presentation = (image.get('role') === 'presentation'); @@ -775,6 +753,39 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi return false; }, + /** + * Sets the alignment of a properties object. + * + * @method _getAlignmentPropeties + * @param {Node} image The image that the alignment properties should be found for + * @param {Object} properties The properties object that is created in _getSelectedImageProperties() + * @private + */ + _getAlignmentPropeties: function(image, properties) { + var complete = false, + defaultAlignment; + + // Check for an alignment value. + complete = ALIGNMENTS.some(function(alignment) { + var classname = this._getAlignmentClass(alignment.value); + if (image.hasClass(classname)) { + properties.align = alignment.value; + + return true; + } + + if (alignment.isDefault) { + defaultAlignment = alignment.value; + } + + return false; + }, this); + + if (!complete && defaultAlignment) { + properties.align = defaultAlignment; + } + }, + /** * Update the form when the URL was changed. This includes updating the * height, width, and image preview. @@ -804,14 +815,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi alt = form.one('.' + CSS.INPUTALT).get('value'), width = form.one('.' + CSS.INPUTWIDTH).get('value'), height = form.one('.' + CSS.INPUTHEIGHT).get('value'), - alignment = form.one('.' + CSS.INPUTALIGNMENT).get('value'), - margin = '', + alignment = this._getAlignmentClass(form.one('.' + CSS.INPUTALIGNMENT).get('value')), presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'), constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'), imagehtml, - customstyle = '', - i, - css, + customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'), classlist = [], host = this.get('host'); @@ -831,22 +839,13 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi host.setSelection(this._currentSelection); } - if (alignment === 'style:customstyle;') { - alignment = ''; - customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'); - } else { - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (alignment === css) { - margin = ' margin: ' + ALIGNMENTS[i].margin + ';'; - } - } - } - if (constrain) { classlist.push(CSS.RESPONSIVE); } + // Add the alignment class for the image. + classlist.push(alignment); + if (!width.match(REGEX.ISPERCENT) && isNaN(parseInt(width, 10))) { form.one('.' + CSS.INPUTWIDTH).focus(); return; @@ -863,8 +862,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi width: width, height: height, presentation: presentation, - alignment: alignment, - margin: margin, customstyle: customstyle, classlist: classlist.join(' ') }); @@ -880,6 +877,47 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }, + /** + * Removes any legacy styles added by previous versions of the atto image button. + * + * @method _removeLegacyAlignment + * @param {Y.Node} imageNode + * @return {Y.Node} + * @private + */ + _removeLegacyAlignment: function(imageNode) { + if (!imageNode.getStyle('margin')) { + // There is no margin therefore this cannot match any known alignments. + return imageNode; + } + + ALIGNMENTS.some(function(alignment) { + if (imageNode.getStyle(alignment.name) !== alignment.value) { + // The name/value do not match. Skip. + return false; + } + + var normalisedNode = Y.Node.create('
'); + normalisedNode.setStyle('margin', alignment.margin); + if (imageNode.getStyle('margin') !== normalisedNode.getStyle('margin')) { + // The margin does not match. + return false; + } + + imageNode.addClass(this._getAlignmentClass(alignment.value)); + imageNode.setStyle(alignment.name, null); + imageNode.setStyle('margin', null); + + return true; + }, this); + + return imageNode; + }, + + _getAlignmentClass: function(alignment) { + return CSS.ALIGNSETTINGS + '_' + alignment; + }, + /** * Update the alt text warning live. * diff --git a/lib/editor/atto/plugins/image/yui/src/button/js/button.js b/lib/editor/atto/plugins/image/yui/src/button/js/button.js index 411f105bfcf..f9c0d607e72 100644 --- a/lib/editor/atto/plugins/image/yui/src/button/js/button.js +++ b/lib/editor/atto/plugins/image/yui/src/button/js/button.js @@ -46,7 +46,8 @@ var CSS = { INPUTCONSTRAIN: 'atto_image_constrain', INPUTCUSTOMSTYLE: 'atto_image_customstyle', IMAGEPREVIEW: 'atto_image_preview', - IMAGEPREVIEWBOX: 'atto_image_preview_box' + IMAGEPREVIEWBOX: 'atto_image_preview_box', + ALIGNSETTINGS: 'atto_image_button' }, SELECTORS = { INPUTURL: '.' + CSS.INPUTURL @@ -54,38 +55,34 @@ var CSS = { ALIGNMENTS = [ // Vertical alignment. { - name: 'text-top', + name: 'verticalAlign', str: 'alignment_top', - value: 'vertical-align', - margin: '0 .5em' + value: 'text-top', + margin: '0 0.5em' }, { - name: 'middle', + name: 'verticalAlign', str: 'alignment_middle', - value: 'vertical-align', - margin: '0 .5em' + value: 'middle', + margin: '0 0.5em' }, { - name: 'text-bottom', + name: 'verticalAlign', str: 'alignment_bottom', - value: 'vertical-align', - margin: '0 .5em', + value: 'text-bottom', + margin: '0 0.5em', isDefault: true }, // Floats. { - name: 'left', + name: 'float', str: 'alignment_left', - value: 'float', - margin: '0 .5em 0 0' + value: 'left', + margin: '0 0.5em 0 0' }, { - name: 'right', + name: 'float', str: 'alignment_right', - value: 'float', - margin: '0 0 0 .5em' - }, { - name: 'customstyle', - str: 'customstyle', - value: 'style' + value: 'right', + margin: '0 0 0 0.5em' } ], @@ -140,7 +137,7 @@ var CSS = { '' + '' + // Hidden input to store custom styles. @@ -163,7 +160,7 @@ var CSS = { '{{#if width}}width="{{width}}" {{/if}}' + '{{#if height}}height="{{height}}" {{/if}}' + '{{#if presentation}}role="presentation" {{/if}}' + - 'style="{{alignment}}{{margin}}{{customstyle}}"' + + '{{#if customstyle}}style="{{customstyle}}" {{/if}}' + '{{#if classlist}}class="{{classlist}}" {{/if}}' + '{{#if id}}id="{{id}}" {{/if}}' + '/>'; @@ -232,6 +229,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi * * @method _handleDragDrop * @param {EventFacade} e + * @return mixed * @private */ _handleDragDrop: function(e) { @@ -645,30 +643,25 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi */ _applyImageProperties: function(form) { var properties = this._getSelectedImageProperties(), - img = form.one('.' + CSS.IMAGEPREVIEW), - i, - css; + img = form.one('.' + CSS.IMAGEPREVIEW); if (properties === false) { img.setStyle('display', 'none'); // Set the default alignment. - for (i in ALIGNMENTS) { - if (ALIGNMENTS[i].isDefault === true) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - form.one('.' + CSS.INPUTALIGNMENT).set('value', css); + ALIGNMENTS.some(function(alignment) { + if (alignment.isDefault) { + form.one('.' + CSS.INPUTALIGNMENT).set('value', alignment.value); + return true; } - } - // Remove the custom style option if this is a new image. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); + + return false; + }, this); + return; } if (properties.align) { form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align); - // Remove the custom style option if we have a standard alignment. - form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1); - } else { - form.one('.' + CSS.INPUTALIGNMENT).set('value', 'style:customstyle;'); } if (properties.customstyle) { form.one('.' + CSS.INPUTCUSTOMSTYLE).set('value', properties.customstyle); @@ -715,25 +708,21 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi // Get the current selection. images = this.get('host').getSelectedNodes(), - i, width, height, style, - css, - image, - margin; + image; if (images) { images = images.filter('img'); } if (images && images.size()) { - image = images.item(0); + image = this._removeLegacyAlignment(images.item(0)); this._selectedImage = image; style = image.getAttribute('style'); properties.customstyle = style; - style = style.replace(/ /g, ''); width = image.getAttribute('width'); if (!width.match(REGEX.ISPERCENT)) { @@ -750,18 +739,7 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi if (height !== 0) { properties.height = height; } - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (style.indexOf(css) !== -1) { - margin = 'margin:' + ALIGNMENTS[i].margin + ';'; - margin = margin.replace(/ /g, ''); - // Must match alignment and margins - otherwise custom style is selected. - if (style.indexOf(margin) !== -1) { - properties.align = css; - break; - } - } - } + this._getAlignmentPropeties(image, properties); properties.src = image.getAttribute('src'); properties.alt = image.getAttribute('alt') || ''; properties.presentation = (image.get('role') === 'presentation'); @@ -773,6 +751,40 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi return false; }, + /** + * Sets the alignment of a properties object. + * + * @method _getAlignmentPropeties + * @param {Node} image The image that the alignment properties should be found for + * @param {Object} properties The properties object that is created in _getSelectedImageProperties() + * @private + */ + _getAlignmentPropeties: function(image, properties) { + var complete = false, + defaultAlignment; + + // Check for an alignment value. + complete = ALIGNMENTS.some(function(alignment) { + var classname = this._getAlignmentClass(alignment.value); + if (image.hasClass(classname)) { + properties.align = alignment.value; + Y.log('Found alignment ' + alignment.value, 'debug', 'atto_image-button'); + + return true; + } + + if (alignment.isDefault) { + defaultAlignment = alignment.value; + } + + return false; + }, this); + + if (!complete && defaultAlignment) { + properties.align = defaultAlignment; + } + }, + /** * Update the form when the URL was changed. This includes updating the * height, width, and image preview. @@ -802,14 +814,11 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi alt = form.one('.' + CSS.INPUTALT).get('value'), width = form.one('.' + CSS.INPUTWIDTH).get('value'), height = form.one('.' + CSS.INPUTHEIGHT).get('value'), - alignment = form.one('.' + CSS.INPUTALIGNMENT).get('value'), - margin = '', + alignment = this._getAlignmentClass(form.one('.' + CSS.INPUTALIGNMENT).get('value')), presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'), constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'), imagehtml, - customstyle = '', - i, - css, + customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'), classlist = [], host = this.get('host'); @@ -829,22 +838,13 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi host.setSelection(this._currentSelection); } - if (alignment === 'style:customstyle;') { - alignment = ''; - customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value'); - } else { - for (i in ALIGNMENTS) { - css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';'; - if (alignment === css) { - margin = ' margin: ' + ALIGNMENTS[i].margin + ';'; - } - } - } - if (constrain) { classlist.push(CSS.RESPONSIVE); } + // Add the alignment class for the image. + classlist.push(alignment); + if (!width.match(REGEX.ISPERCENT) && isNaN(parseInt(width, 10))) { form.one('.' + CSS.INPUTWIDTH).focus(); return; @@ -861,8 +861,6 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi width: width, height: height, presentation: presentation, - alignment: alignment, - margin: margin, customstyle: customstyle, classlist: classlist.join(' ') }); @@ -878,6 +876,48 @@ Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.Edi }, + /** + * Removes any legacy styles added by previous versions of the atto image button. + * + * @method _removeLegacyAlignment + * @param {Y.Node} imageNode + * @return {Y.Node} + * @private + */ + _removeLegacyAlignment: function(imageNode) { + if (!imageNode.getStyle('margin')) { + // There is no margin therefore this cannot match any known alignments. + return imageNode; + } + + ALIGNMENTS.some(function(alignment) { + if (imageNode.getStyle(alignment.name) !== alignment.value) { + // The name/value do not match. Skip. + return false; + } + + var normalisedNode = Y.Node.create('
'); + normalisedNode.setStyle('margin', alignment.margin); + if (imageNode.getStyle('margin') !== normalisedNode.getStyle('margin')) { + // The margin does not match. + return false; + } + + Y.log('Legacy alignment found and removed.', 'info', 'atto_image-button'); + imageNode.addClass(this._getAlignmentClass(alignment.value)); + imageNode.setStyle(alignment.name, null); + imageNode.setStyle('margin', null); + + return true; + }, this); + + return imageNode; + }, + + _getAlignmentClass: function(alignment) { + return CSS.ALIGNSETTINGS + '_' + alignment; + }, + /** * Update the alt text warning live. *