Files
piratepoet/assets/js/src/newsletter_editor/blocks/base.js
2017-11-29 13:43:35 +00:00

311 lines
9.3 KiB
JavaScript

/**
* Defines base classes for actual content blocks to extend.
* Extending content block modules need to at least extend
* a BlockModel and a BlockView.
* BlockToolsView, BlockSettingsView and BlockWidgetView are optional.
*/
define([
'newsletter_editor/App',
'backbone.marionette',
'backbone.supermodel',
'underscore',
'jquery',
'mailpoet',
'modal'
], function (App, Marionette, SuperModel, _, jQuery, MailPoet) {
'use strict';
var Module = {};
var AugmentedView = Marionette.View.extend({});
Module.BlockModel = SuperModel.extend({
stale: [], // Attributes to be removed upon saving
initialize: function () {
this.on('change', function () {
App.getChannel().trigger('autoSave');
});
},
_getDefaults: function (blockDefaults, configDefaults) {
var defaults = (_.isObject(configDefaults) && _.isFunction(configDefaults.toJSON)) ? configDefaults.toJSON() : configDefaults;
// Patch the resulting JSON object and fix it's constructors to be Object.
// Otherwise SuperModel interprets it not as a simpleObject
// and misbehaves
// TODO: Investigate for a better solution
return JSON.parse(JSON.stringify(jQuery.extend(blockDefaults, defaults || {})));
},
toJSON: function () {
// Remove stale attributes from resulting JSON object
return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
},
getChildren: function () {
return [];
}
});
Module.BlockView = AugmentedView.extend({
regions: {
toolsRegion: '> .mailpoet_tools'
},
modelEvents: {
change: 'render',
delete: 'deleteBlock',
duplicate: 'duplicateBlock'
},
events: {
mouseenter: 'showTools',
mouseleave: 'hideTools'
},
behaviors: {
DraggableBehavior: {
cloneOriginal: true,
hideOriginal: true,
onDrop: function (options) {
// After a clone of model has been dropped, cleanup
// and destroy self
options.dragBehavior.view.model.destroy();
},
onDragSubstituteBy: function (behavior) {
var WidgetView;
var node;
// When block is being dragged, display the widget icon instead.
// This will create an instance of block's widget view and
// use it's rendered DOM element instead of the content block
if (_.isFunction(behavior.view.onDragSubstituteBy)) {
WidgetView = new (behavior.view.onDragSubstituteBy())();
WidgetView.render();
node = WidgetView.$el.get(0).cloneNode(true);
WidgetView.destroy();
return node;
}
return undefined;
}
},
HighlightEditingBehavior: {}
},
templateContext: function () {
return {
model: this.model.toJSON(),
viewCid: this.cid
};
},
constructor: function () {
AugmentedView.apply(this, arguments);
this.$el.addClass('mailpoet_editor_view_' + this.cid);
},
initialize: function () {
this.on('showSettings', this.showSettings, this);
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
showTools: function () {
if (!this.showingToolsDisabled) {
this.$('> .mailpoet_tools').addClass('mailpoet_display_tools');
this.toolsView.triggerMethod('showTools');
}
},
hideTools: function () {
this.$('> .mailpoet_tools').removeClass('mailpoet_display_tools');
this.toolsView.triggerMethod('hideTools');
},
enableShowingTools: function () {
this.showingToolsDisabled = false;
},
disableShowingTools: function () {
this.showingToolsDisabled = true;
this.hideTools();
},
showSettings: function (options) {
this.toolsView.triggerMethod('showSettings', options);
},
/**
* Defines drop behavior of BlockView instance
*/
getDropFunc: function () {
return function () {
return this.model.clone();
}.bind(this);
},
disableDragging: function () {
this.$el.addClass('mailpoet_ignore_drag');
},
enableDragging: function () {
this.$el.removeClass('mailpoet_ignore_drag');
},
showBlock: function () {
if (this._isFirstRender) {
this.transitionIn();
this._isFirstRender = false;
}
},
deleteBlock: function () {
this.transitionOut().then(function () {
this.model.destroy();
}.bind(this));
},
duplicateBlock: function () {
this.model.collection.add(this.model.toJSON(), { at: this.model.collection.findIndex(this.model) });
},
transitionIn: function () {
return this._transition('slideDown', 'fadeIn', 'easeOut');
},
transitionOut: function () {
return this._transition('slideUp', 'fadeOut', 'easeIn');
},
_transition: function (slideDirection, fadeDirection, easing) {
var promise = jQuery.Deferred();
this.$el.velocity(
slideDirection,
{
duration: 250,
easing: easing,
complete: function () {
promise.resolve();
}.bind(this)
}
).velocity(
fadeDirection,
{
duration: 250,
easing: easing,
queue: false // Do not enqueue, trigger animation in parallel
}
);
return promise;
}
});
Module.BlockToolsView = AugmentedView.extend({
getTemplate: function () { return window.templates.genericBlockTools; },
events: {
'click .mailpoet_edit_block': 'changeSettings',
'click .mailpoet_delete_block_activate': 'showDeletionConfirmation',
'click .mailpoet_delete_block_cancel': 'hideDeletionConfirmation',
'click .mailpoet_delete_block_confirm': 'deleteBlock',
'click .mailpoet_duplicate_block': 'duplicateBlock'
},
// Markers of whether these particular tools will be used for this instance
tools: {
settings: true,
delete: true,
duplicate: true,
move: true
},
getSettingsView: function () { return Module.BlockSettingsView; },
initialize: function (opts) {
var options = opts || {};
if (!_.isUndefined(options.tools)) {
// Make a new block specific tool config object
this.tools = jQuery.extend({}, this.tools, options.tools || {});
}
// Automatically cancel deletion
this.on('hideTools', this.hideDeletionConfirmation, this);
this.on('showSettings', this.changeSettings);
},
templateContext: function () {
return {
model: this.model.toJSON(),
viewCid: this.cid,
tools: this.tools
};
},
changeSettings: function (options) {
var ViewType = this.getSettingsView();
(new ViewType(_.extend({ model: this.model }, options || {}))).render();
},
showDeletionConfirmation: function () {
this.$('.mailpoet_delete_block').addClass('mailpoet_delete_block_activated');
},
hideDeletionConfirmation: function () {
this.$('.mailpoet_delete_block').removeClass('mailpoet_delete_block_activated');
},
deleteBlock: function (event) {
event.preventDefault();
this.model.trigger('delete');
return false;
},
duplicateBlock: function (event) {
event.preventDefault();
this.model.trigger('duplicate');
return false;
}
});
Module.BlockSettingsView = Marionette.View.extend({
className: 'mailpoet_editor_settings',
behaviors: {
ColorPickerBehavior: {}
},
initialize: function (params) {
var panelParams;
this.model.trigger('startEditing');
panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
onCancel: function () {
this.destroy();
}.bind(this)
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateContext: function () {
return {
model: this.model.toJSON()
};
},
close: function () {
this.destroy();
},
changeField: function (field, event) {
this.model.set(field, jQuery(event.target).val());
},
changePixelField: function (field, event) {
this.changeFieldWithSuffix(field, event, 'px');
},
changeFieldWithSuffix: function (field, event, suffix) {
this.model.set(field, jQuery(event.target).val() + suffix);
},
changeBoolField: function (field, event) {
this.model.set(field, (jQuery(event.target).val() === 'true'));
},
changeBoolCheckboxField: function (field, event) {
this.model.set(field, (!!jQuery(event.target).prop('checked')));
},
changeColorField: function (field, event) {
var value = jQuery(event.target).val();
if (value === '') {
value = 'transparent';
}
this.model.set(field, value);
},
onBeforeDestroy: function () {
MailPoet.Modal.close();
this.model.trigger('stopEditing');
}
});
Module.WidgetView = Marionette.View.extend({
className: 'mailpoet_widget mailpoet_droppable_block mailpoet_droppable_widget',
behaviors: {
DraggableBehavior: {
drop: function () {
throw 'Unsupported operation';
}
}
}
});
return Module;
});