Refactor media manager from image block to a behavior [MAILPOET-1403]

This will make the media manager reusable in other blocks like e.g. container.
This commit is contained in:
Rostislav Wolny
2018-07-16 17:02:24 +02:00
parent 8ee72bd8ed
commit 2d30a0884b
4 changed files with 316 additions and 281 deletions

View File

@ -0,0 +1,304 @@
/* eslint-disable func-names */
/**
* Media manager behaviour
*
* Adds a media manager integration with the view
*/
define([
'backbone.marionette',
'underscore',
'newsletter_editor/behaviors/BehaviorsLookup',
'jquery'
], function (Marionette, _, BehaviorsLookup, jQuery) {
var BL = BehaviorsLookup;
BL.MediaManagerBehavior = Marionette.Behavior.extend({
ui: {
'select-image': '.mailpoet_field_image_select_image',
'address-input': '.mailpoet_field_image_address'
},
events: {
'click @ui.select-image': 'showMediaManager',
'input @ui.address-input': 'changeAddress'
},
initialize: function () {
if (this.view.options.showImageManager) {
this.showMediaManager();
}
},
changeAddress: function (event) {
var src = jQuery(event.target).val();
var image = new Image();
image.onload = function () {
if (this.options.onSelect) {
this.view[this.options.onSelect]({
src: src,
width: image.naturalWidth + 'px',
height: image.naturalHeight + 'px'
});
}
}.bind(this);
image.src = src;
},
showMediaManager: function () {
var that = this;
var MediaManager;
var theFrame;
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
initialize: function () {
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
_.defaults(this.options, {
multiple: true,
editing: false,
state: 'insert'
});
this.createSelection();
this.createStates();
this.bindHandlers();
this.createIframeStates();
// Hide title
this.$el.addClass('hide-title');
},
resetSelections: function () {
this.state().get('selection').reset();
},
createQuery: function (options) {
var query = window.wp.media.query(options);
return query;
},
createStates: function () {
var options = this.options;
// Add the default states.
this.states.add([
// Main states.
new window.wp.media.controller.Library({
id: 'insert',
title: 'Add images',
priority: 20,
toolbar: 'main-insert',
filterable: 'image',
library: this.createQuery(options.library),
multiple: options.multiple ? 'reset' : false,
editable: false,
// If the user isn't allowed to edit fields,
// can they still edit it locally?
allowLocalEdits: false,
// Show the attachment display settings.
displaySettings: false,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: false
})
]);
if (window.wp.media.view.settings.post.featuredImageId) {
this.states.add(new window.wp.media.controller.FeaturedImage());
}
},
bindHandlers: function () {
var handlers;
// from Select
this.on('router:create:browse', this.createRouter, this);
this.on('router:render:browse', this.browseRouter, this);
this.on('content:create:browse', this.browseContent, this);
this.on('content:render:upload', this.uploadContent, this);
this.on('toolbar:create:select', this.createSelectToolbar, this);
this.on('menu:create:gallery', this.createMenu, this);
this.on('toolbar:create:main-insert', this.createToolbar, this);
this.on('toolbar:create:main-gallery', this.createToolbar, this);
this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
this.on('updateExcluded', this.browseContent, this);
handlers = {
content: {
embed: 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each(handlers, function (regionHandlers, region) {
_.each(regionHandlers, function (callback, handler) {
this.on(region + ':render:' + handler, this[callback], this);
}, this);
}, this);
},
uploadContent: function () {
window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
this.$el.addClass('hide-toolbar');
},
// Content
embedContent: function () {
var view = new window.wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
this.content.set(view);
view.url.focus();
},
editSelectionContent: function () {
var state = this.state();
var selection = state.get('selection');
var view;
view = new window.wp.media.view.AttachmentsBrowser({
controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: window.wp.media.view.Attachment.EditSelection
}).render();
view.toolbar.set('backToLibrary', {
text: 'Return to library',
priority: -100,
click: function () {
this.controller.content.mode('browse');
}
});
// Browse our library of attachments.
this.content.set(view);
},
// Toolbars
selectionStatusToolbar: function (view) {
var editable = this.state().get('editable');
view.set('selection', new window.wp.media.view.Selection({
controller: this,
collection: this.state().get('selection'),
priority: -40,
// If the selection is editable, pass the callback to
// switch the content mode.
editable: editable && function () {
this.controller.content.mode('edit-selection');
}
}).render());
},
mainInsertToolbar: function (view) {
var controller = this;
this.selectionStatusToolbar(view);
view.set('insert', {
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function () {
var state = controller.state();
var selection = state.get('selection');
controller.close();
state.trigger('insert', selection).reset();
}
});
},
mainEmbedToolbar: function (toolbar) {
var tbar = toolbar;
tbar.view = new window.wp.media.view.Toolbar.Embed({
controller: this,
text: 'Add images'
});
}
});
theFrame = new MediaManager({
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select'
}
});
this._mediaManager = theFrame;
this._mediaManager.on('insert', function () {
// Append media manager image selections to Images tab
var selection = theFrame.state().get('selection');
selection.each(function (attachment) {
var sizes = attachment.get('sizes');
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
var targetImageWidth = 1320;
// Pick the width that is closest to target width
var increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function (size) {
return Math.abs(targetImageWidth - sizes[size].width);
}
);
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
var imagesOfBestWidth = _.filter(
_.values(sizes),
function (size) { return size.width === bestWidth; }
);
// Maximize the height if there are multiple images with same width
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
if (that.options.onSelect) {
that.view[that.options.onSelect]({
height: mainSize.height + 'px',
width: mainSize.width + 'px',
src: mainSize.url,
alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
});
}
});
});
this._mediaManager.open();
},
onBeforeDestroy: function () {
if (typeof this._mediaManager === 'object') {
this._mediaManager.remove();
}
}
});
});

View File

@ -75,6 +75,11 @@ define([
});
Module.ImageBlockSettingsView = base.BlockSettingsView.extend({
behaviors: _.extend({}, base.BlockSettingsView.prototype.behaviors, {
MediaManagerBehavior: {
onSelect: 'onImageSelect'
}
}),
onRender: function () {
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-full-width'), {
tooltipId: 'tooltip-editor-full-width',
@ -89,11 +94,9 @@ define([
events: function () {
return {
'input .mailpoet_field_image_link': _.partial(this.changeField, 'link'),
'input .mailpoet_field_image_address': 'changeAddress',
'input .mailpoet_field_image_alt_text': _.partial(this.changeField, 'alt'),
'change .mailpoet_field_image_full_width': _.partial(this.changeBoolCheckboxField, 'fullWidth'),
'change .mailpoet_field_image_alignment': _.partial(this.changeField, 'styles.block.textAlign'),
'click .mailpoet_field_image_select_another_image': 'showMediaManager',
'click .mailpoet_done_editing': 'close',
'input .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
'change .mailpoet_field_image_width': _.partial(this.updateValueAndCall, '.mailpoet_field_image_width_input', _.partial(this.changePixelField, 'width').bind(this)),
@ -120,284 +123,10 @@ define([
this.$('.mailpoet_field_image_width').val(width);
this.$('.mailpoet_field_image_width_input').val(width);
},
initialize: function (options) {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
if (options.showImageManager) {
this.showMediaManager();
}
},
showMediaManager: function () {
var that = this;
var MediaManager;
var theFrame;
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
initialize: function () {
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
_.defaults(this.options, {
multiple: true,
editing: false,
state: 'insert'
});
this.createSelection();
this.createStates();
this.bindHandlers();
this.createIframeStates();
// Hide title
this.$el.addClass('hide-title');
},
resetSelections: function () {
this.state().get('selection').reset();
},
createQuery: function (options) {
var query = window.wp.media.query(options);
return query;
},
createStates: function () {
var options = this.options;
// Add the default states.
this.states.add([
// Main states.
new window.wp.media.controller.Library({
id: 'insert',
title: 'Add images',
priority: 20,
toolbar: 'main-insert',
filterable: 'image',
library: this.createQuery(options.library),
multiple: options.multiple ? 'reset' : false,
editable: false,
// If the user isn't allowed to edit fields,
// can they still edit it locally?
allowLocalEdits: false,
// Show the attachment display settings.
displaySettings: false,
// Update user settings when users adjust the
// attachment display settings.
displayUserSettings: false
})
]);
if (window.wp.media.view.settings.post.featuredImageId) {
this.states.add(new window.wp.media.controller.FeaturedImage());
}
},
bindHandlers: function () {
var handlers;
// from Select
this.on('router:create:browse', this.createRouter, this);
this.on('router:render:browse', this.browseRouter, this);
this.on('content:create:browse', this.browseContent, this);
this.on('content:render:upload', this.uploadContent, this);
this.on('toolbar:create:select', this.createSelectToolbar, this);
this.on('menu:create:gallery', this.createMenu, this);
this.on('toolbar:create:main-insert', this.createToolbar, this);
this.on('toolbar:create:main-gallery', this.createToolbar, this);
this.on('toolbar:create:main-embed', this.mainEmbedToolbar, this);
this.on('updateExcluded', this.browseContent, this);
handlers = {
content: {
embed: 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each(handlers, function (regionHandlers, region) {
_.each(regionHandlers, function (callback, handler) {
this.on(region + ':render:' + handler, this[callback], this);
}, this);
}, this);
},
uploadContent: function () {
window.wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
this.$el.addClass('hide-toolbar');
},
// Content
embedContent: function () {
var view = new window.wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
this.content.set(view);
view.url.focus();
},
editSelectionContent: function () {
var state = this.state();
var selection = state.get('selection');
var view;
view = new window.wp.media.view.AttachmentsBrowser({
controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: window.wp.media.view.Attachment.EditSelection
}).render();
view.toolbar.set('backToLibrary', {
text: 'Return to library',
priority: -100,
click: function () {
this.controller.content.mode('browse');
}
});
// Browse our library of attachments.
this.content.set(view);
},
// Toolbars
selectionStatusToolbar: function (view) {
var editable = this.state().get('editable');
view.set('selection', new window.wp.media.view.Selection({
controller: this,
collection: this.state().get('selection'),
priority: -40,
// If the selection is editable, pass the callback to
// switch the content mode.
editable: editable && function () {
this.controller.content.mode('edit-selection');
}
}).render());
},
mainInsertToolbar: function (view) {
var controller = this;
this.selectionStatusToolbar(view);
view.set('insert', {
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function () {
var state = controller.state();
var selection = state.get('selection');
controller.close();
state.trigger('insert', selection).reset();
}
});
},
mainEmbedToolbar: function (toolbar) {
var tbar = toolbar;
tbar.view = new window.wp.media.view.Toolbar.Embed({
controller: this,
text: 'Add images'
});
}
});
theFrame = new MediaManager({
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select'
}
});
this._mediaManager = theFrame;
this._mediaManager.on('insert', function () {
// Append media manager image selections to Images tab
var selection = theFrame.state().get('selection');
selection.each(function (attachment) {
var sizes = attachment.get('sizes');
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
var targetImageWidth = 1320;
// Pick the width that is closest to target width
var increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
);
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
var imagesOfBestWidth = _.filter(
_.values(sizes),
function (size) { return size.width === bestWidth; }
);
// Maximize the height if there are multiple images with same width
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
that.model.set({
height: mainSize.height + 'px',
width: mainSize.width + 'px',
src: mainSize.url,
alt: (attachment.get('alt') !== '' && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title')
});
onImageSelect: function (image) {
this.model.set(image);
// Rerender settings view due to changes from outside of settings view
that.render();
});
});
this._mediaManager.open();
},
changeAddress: function (event) {
var src = jQuery(event.target).val();
var image = new Image();
image.onload = function () {
this.model.set({
src: src,
width: image.naturalWidth + 'px',
height: image.naturalHeight + 'px'
});
}.bind(this);
image.src = src;
},
onBeforeDestroy: function () {
base.BlockSettingsView.prototype.onBeforeDestroy.apply(this, arguments);
if (typeof this._mediaManager === 'object') {
this._mediaManager.remove();
}
this.render();
}
});

View File

@ -80,7 +80,7 @@
</div>
<hr />
<div class="mailpoet_form_field">
<input type="button" name="select-another-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_another_image" value="<%= __('Select another image') | escape('html_attr') %>" />
<input type="button" name="select-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_image" value="<%= __('Select another image') | escape('html_attr') %>" />
</div>
<div class="mailpoet_form_field">

View File

@ -310,6 +310,7 @@ var adminConfig = {
'newsletter_editor/behaviors/DraggableBehavior.js',
'newsletter_editor/behaviors/HighlightContainerBehavior.js',
'newsletter_editor/behaviors/HighlightEditingBehavior.js',
'newsletter_editor/behaviors/MediaManagerBehavior.js',
'newsletter_editor/behaviors/ResizableBehavior.js',
'newsletter_editor/behaviors/SortableBehavior.js',
'newsletter_editor/behaviors/ShowSettingsBehavior.js',
@ -414,6 +415,7 @@ var testConfig = {
'newsletter_editor/behaviors/DraggableBehavior.js',
'newsletter_editor/behaviors/HighlightContainerBehavior.js',
'newsletter_editor/behaviors/HighlightEditingBehavior.js',
'newsletter_editor/behaviors/MediaManagerBehavior.js',
'newsletter_editor/behaviors/ResizableBehavior.js',
'newsletter_editor/behaviors/SortableBehavior.js',
'newsletter_editor/behaviors/ShowSettingsBehavior.js',