Wrap editor JS code in AMD modules and load them

This commit is contained in:
Tautvidas Sipavičius
2015-08-19 16:26:38 +03:00
parent da371e33f4
commit 40507d2cad
33 changed files with 4901 additions and 5236 deletions

View File

@ -1,142 +1,144 @@
define('handlebars_helpers', ['handlebars'], function(Handlebars) { define('handlebars_helpers', ['handlebars'], function(Handlebars) {
// Handlebars helpers // Handlebars helpers
Handlebars.registerHelper('concat', function() { Handlebars.registerHelper('concat', function() {
var size = (arguments.length - 1), var size = (arguments.length - 1),
output = ''; output = '';
for(var i = 0; i < size; i++) { for(var i = 0; i < size; i++) {
output += arguments[i]; output += arguments[i];
}; };
return output; return output;
}); });
Handlebars.registerHelper('number_format', function(value, block) { Handlebars.registerHelper('number_format', function(value, block) {
return Number(value).toLocaleString(); return Number(value).toLocaleString();
}); });
Handlebars.registerHelper('date_format', function(timestamp, block) { Handlebars.registerHelper('date_format', function(timestamp, block) {
if(window.moment) { if(window.moment) {
if(timestamp === undefined || isNaN(timestamp) || timestamp <= 0) { if(timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
return; return;
} }
// set date format // set date format
var f = block.hash.format || "MMM Do, YYYY"; var f = block.hash.format || "MMM Do, YYYY";
// check if we passed a timestamp // check if we passed a timestamp
if(parseInt(timestamp, 10) == timestamp) { if(parseInt(timestamp, 10) == timestamp) {
return moment.unix(timestamp).format(f); return moment.unix(timestamp).format(f);
} else { } else {
return moment.utc(timestamp).format(f); return moment.utc(timestamp).format(f);
} }
} else { } else {
return timestamp; return timestamp;
}; };
}); });
Handlebars.registerHelper('cycle', function(value, block) { Handlebars.registerHelper('cycle', function(value, block) {
var values = value.split(' '); var values = value.split(' ');
return values[block.data.index % (values.length + 1)]; return values[block.data.index % (values.length + 1)];
}); });
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) { Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
switch (operator) { switch (operator) {
case '==': case '==':
return (v1 == v2) ? options.fn(this) : options.inverse(this); return (v1 == v2) ? options.fn(this) : options.inverse(this);
case '===': case '===':
return (v1 === v2) ? options.fn(this) : options.inverse(this); return (v1 === v2) ? options.fn(this) : options.inverse(this);
case '!=': case '!=':
return (v1 != v2) ? options.fn(this) : options.inverse(this); return (v1 != v2) ? options.fn(this) : options.inverse(this);
case '!==': case '!==':
return (v1 !== v2) ? options.fn(this) : options.inverse(this); return (v1 !== v2) ? options.fn(this) : options.inverse(this);
case '<': case '<':
return (v1 < v2) ? options.fn(this) : options.inverse(this); return (v1 < v2) ? options.fn(this) : options.inverse(this);
case '<=': case '<=':
return (v1 <= v2) ? options.fn(this) : options.inverse(this); return (v1 <= v2) ? options.fn(this) : options.inverse(this);
case '>': case '>':
return (v1 > v2) ? options.fn(this) : options.inverse(this); return (v1 > v2) ? options.fn(this) : options.inverse(this);
case '>=': case '>=':
return (v1 >= v2) ? options.fn(this) : options.inverse(this); return (v1 >= v2) ? options.fn(this) : options.inverse(this);
case '&&': case '&&':
return (v1 && v2) ? options.fn(this) : options.inverse(this); return (v1 && v2) ? options.fn(this) : options.inverse(this);
case '||': case '||':
return (v1 || v2) ? options.fn(this) : options.inverse(this); return (v1 || v2) ? options.fn(this) : options.inverse(this);
case 'in': case 'in':
var values = v2.split(','); var values = v2.split(',');
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this); return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
default: default:
return options.inverse(this); return options.inverse(this);
} }
}); });
Handlebars.registerHelper('nl2br', function(value, block) { Handlebars.registerHelper('nl2br', function(value, block) {
return value.gsub("\n", "<br />"); return value.gsub("\n", "<br />");
}); });
Handlebars.registerHelper('json_encode', function(value, block) { Handlebars.registerHelper('json_encode', function(value, block) {
return JSON.stringify(value); return JSON.stringify(value);
}); });
Handlebars.registerHelper('json_decode', function(value, block) { Handlebars.registerHelper('json_decode', function(value, block) {
return JSON.parse(value); return JSON.parse(value);
}); });
Handlebars.registerHelper('url', function(value, block) { Handlebars.registerHelper('url', function(value, block) {
var url = window.location.protocol + "//" + window.location.host + window.location.pathname; var url = window.location.protocol + "//" + window.location.host + window.location.pathname;
return url + value; return url + value;
}); });
Handlebars.registerHelper('emailFromMailto', function(value) { Handlebars.registerHelper('emailFromMailto', function(value) {
var mailtoMatchingRegex = /^mailto\:/i; var mailtoMatchingRegex = /^mailto\:/i;
if (typeof value === 'string' && value.match(mailtoMatchingRegex)) { if (typeof value === 'string' && value.match(mailtoMatchingRegex)) {
return value.replace(mailtoMatchingRegex, ''); return value.replace(mailtoMatchingRegex, '');
} else { } else {
return value; return value;
} }
}); });
Handlebars.registerHelper('lookup', function(obj, field, options) { Handlebars.registerHelper('lookup', function(obj, field, options) {
return obj && obj[field]; return obj && obj[field];
}); });
Handlebars.registerHelper('rsa_key', function(value, block) { Handlebars.registerHelper('rsa_key', function(value, block) {
// extract all lines into an array // extract all lines into an array
if(value === undefined) return ''; if(value === undefined) return '';
var lines = value.trim().split("\n"); var lines = value.trim().split("\n");
// remove header & footer // remove header & footer
lines.shift(); lines.shift();
lines.pop(); lines.pop();
// return concatenated lines // return concatenated lines
return lines.join(''); return lines.join('');
}); });
Handlebars.registerHelper('trim', function(value, block) { Handlebars.registerHelper('trim', function(value, block) {
if(value === null || value === undefined) return ''; if(value === null || value === undefined) return '';
return value.trim(); return value.trim();
}); });
/** /**
* {{ellipsis}} * {{ellipsis}}
* From: https://github.com/assemble/handlebars-helpers * From: https://github.com/assemble/handlebars-helpers
* @author: Jon Schlinkert <http://github.com/jonschlinkert> * @author: Jon Schlinkert <http://github.com/jonschlinkert>
* Truncate the input string and removes all HTML tags * Truncate the input string and removes all HTML tags
* @param {String} str The input string. * @param {String} str The input string.
* @param {Number} limit The number of characters to limit the string. * @param {Number} limit The number of characters to limit the string.
* @param {String} append The string to append if charaters are omitted. * @param {String} append The string to append if charaters are omitted.
* @return {String} The truncated string. * @return {String} The truncated string.
*/ */
Handlebars.registerHelper('ellipsis', function (str, limit, append) { Handlebars.registerHelper('ellipsis', function (str, limit, append) {
if (append === undefined) { if (append === undefined) {
append = ''; append = '';
} }
var sanitized = str.replace(/(<([^>]+)>)/g, ''); var sanitized = str.replace(/(<([^>]+)>)/g, '');
if (sanitized.length > limit) { if (sanitized.length > limit) {
return sanitized.substr(0, limit - append.length) + append; return sanitized.substr(0, limit - append.length) + append;
} else { } else {
return sanitized; return sanitized;
} }
}); });
Handlebars.registerHelper('getNumber', function (string) { Handlebars.registerHelper('getNumber', function (string) {
return parseInt(string, 10); return parseInt(string, 10);
}); });
});
window.Handlebars = Handlebars;
});

View File

@ -0,0 +1,35 @@
define('newsletter_editor/App', [
'backbone',
'backbone.marionette',
'backbone.supermodel',
'jquery',
'underscore',
'handlebars',
'handlebars_helpers',
], function(Backbone, Marionette, SuperModel, jQuery, _, Handlebars) {
var app = new Marionette.Application(), AppView;
// Decoupled communication between application components
app.getChannel = function(channel) {
if (channel === undefined) return app.channel;
return Radio.channel(channel);
};
AppView = Marionette.LayoutView.extend({
el: '#mailpoet_editor',
regions: {
stylesRegion: '#mailpoet_editor_styles',
contentRegion: '#mailpoet_editor_content',
sidebarRegion: '#mailpoet_editor_sidebar',
bottomRegion: '#mailpoet_editor_bottom',
headingRegion: '#mailpoet_editor_heading',
},
});
app.on('start', function(options) {
app._appView = new AppView();
});
window.EditorApplication = app;
return app;
});

View File

@ -4,7 +4,16 @@
* *
* For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup * For more check: http://marionettejs.com/docs/marionette.behaviors.html#behaviorslookup
*/ */
BehaviorsLookup = {}; define('newsletter_editor/behaviors/behaviorsLookup', [
Backbone.Marionette.Behaviors.behaviorsLookup = function() { 'backbone.marionette',
], function(Marionette) {
var BehaviorsLookup = {};
Marionette.Behaviors.behaviorsLookup = function() {
return BehaviorsLookup; return BehaviorsLookup;
}; };
window.BehaviorsLookup = BehaviorsLookup;
return BehaviorsLookup;
});

View File

@ -3,14 +3,22 @@
* *
* Adds a color picker integration with the view * Adds a color picker integration with the view
*/ */
BehaviorsLookup.ColorPickerBehavior = Backbone.Marionette.Behavior.extend({ define('newsletter_editor/behaviors/ColorPickerBehavior', [
'backbone.marionette',
'newsletter_editor/behaviors/BehaviorsLookup',
'spectrum-colorpicker',
], function(Marionette, BehaviorsLookup) {
BehaviorsLookup.ColorPickerBehavior = Marionette.Behavior.extend({
onRender: function() { onRender: function() {
this.view.$('.mailpoet_color').spectrum({ this.view.$('.mailpoet_color').spectrum({
clickoutFiresChange: true, clickoutFiresChange: true,
showInput: true, showInput: true,
showInitial: true, showInitial: true,
preferredFormat: "hex6", preferredFormat: "hex6",
allowEmpty: true, allowEmpty: true,
}); });
}, },
});
}); });

View File

@ -5,415 +5,424 @@
* Allows CollectionView instances that use this behavior to act as drop zones and * Allows CollectionView instances that use this behavior to act as drop zones and
* accept droppables * accept droppables
*/ */
BehaviorsLookup.ContainerDropZoneBehavior = Backbone.Marionette.Behavior.extend({ define('newsletter_editor/behaviors/ContainerDropZoneBehavior', [
'backbone.marionette',
'underscore',
'newsletter_editor/behaviors/BehaviorsLookup',
'interact.js',
], function(Marionette, _, BehaviorsLookup, interact) {
BehaviorsLookup.ContainerDropZoneBehavior = Marionette.Behavior.extend({
defaults: { defaults: {
columnLimit: 3, columnLimit: 3,
}, },
onRender: function() { onRender: function() {
var dragAndDropDisabled = _.isObject(this.view.options.renderOptions) && this.view.options.renderOptions.disableDragAndDrop === true; var dragAndDropDisabled = _.isObject(this.view.options.renderOptions) && this.view.options.renderOptions.disableDragAndDrop === true;
if (!dragAndDropDisabled) { if (!dragAndDropDisabled) {
this.addDropZone(); this.addDropZone();
} }
}, },
addDropZone: function(_event) { addDropZone: function(_event) {
var that = this, var that = this,
view = this.view, view = this.view,
domElement = that.$el.get(0), domElement = that.$el.get(0),
acceptableElementSelector; acceptableElementSelector;
// TODO: Extract this limitation code to be controlled from containers // TODO: Extract this limitation code to be controlled from containers
if (this.view.renderOptions.depth === 0) { if (this.view.renderOptions.depth === 0) {
// Root level accept. Allow only layouts // Root level accept. Allow only layouts
acceptableElementSelector = '.mailpoet_droppable_block.mailpoet_droppable_layout_block'; acceptableElementSelector = '.mailpoet_droppable_block.mailpoet_droppable_layout_block';
} else if (this.view.renderOptions.depth === 2) { } else if (this.view.renderOptions.depth === 2) {
// Column level accept. Disallow layouts, allow only content blocks // Column level accept. Disallow layouts, allow only content blocks
acceptableElementSelector = '.mailpoet_droppable_block:not(.mailpoet_droppable_layout_block)'; acceptableElementSelector = '.mailpoet_droppable_block:not(.mailpoet_droppable_layout_block)';
} else { } else {
// Layout section container level. Allow nothing. // Layout section container level. Allow nothing.
return; return;
} }
// TODO: Simplify target block identification, remove special insertion support // TODO: Simplify target block identification, remove special insertion support
interact(domElement).dropzone({ interact(domElement).dropzone({
accept: acceptableElementSelector, accept: acceptableElementSelector,
overlap: 'pointer', // Mouse pointer denotes location of a droppable overlap: 'pointer', // Mouse pointer denotes location of a droppable
ondragenter: function(event) { ondragenter: function(event) {
// 1. Visually mark block as active for dropping // 1. Visually mark block as active for dropping
view.$el.addClass('mailpoet_drop_active'); view.$el.addClass('mailpoet_drop_active');
}, },
ondragleave: function(event) { ondragleave: function(event) {
// 1. Remove visual markings of active dropping container // 1. Remove visual markings of active dropping container
// 2. Remove visual markings of drop position visualization // 2. Remove visual markings of drop position visualization
that.cleanup(); that.cleanup();
}, },
ondropmove: function(event) { ondropmove: function(event) {
// 1. Compute actual location of the mouse within the container // 1. Compute actual location of the mouse within the container
// 2. Check if insertion is regular (between blocks) or special (with container insertion) // 2. Check if insertion is regular (between blocks) or special (with container insertion)
// 3a. If insertion is regular, compute position where insertion should happen // 3a. If insertion is regular, compute position where insertion should happen
// 3b. If insertion is special, compute position (which side) and which cell the insertion belongs to // 3b. If insertion is special, compute position (which side) and which cell the insertion belongs to
// 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container // 4. If insertion at that position is not visualized, display position visualization there, remove other visualizations from this container
var dropPosition = that.getDropPosition( var dropPosition = that.getDropPosition(
event.dragmove.pageX, event.dragmove.pageX,
event.dragmove.pageY, event.dragmove.pageY,
view.$el, view.$el,
view.model.get('orientation'), view.model.get('orientation'),
view.model.get('blocks').length view.model.get('blocks').length
), ),
element = view.$el, element = view.$el,
markerWidth = '', markerWidth = '',
markerHeight = '', markerHeight = '',
containerOffset = element.offset(), containerOffset = element.offset(),
marker, targetModel, targetView, targetElement, marker, targetModel, targetView, targetElement,
topOffset, leftOffset, isLastBlockInsertion, topOffset, leftOffset, isLastBlockInsertion,
$targetBlock, margin; $targetBlock, margin;
if (dropPosition === undefined) return; if (dropPosition === undefined) return;
element.find('.mailpoet_drop_marker').remove(); element.find('.mailpoet_drop_marker').remove();
// Allow empty collections to handle their own drop marking // Allow empty collections to handle their own drop marking
if (view.model.get('blocks').isEmpty()) return; if (view.model.get('blocks').isEmpty()) return;
if (view.collection.length === 0) { if (view.collection.length === 0) {
targetElement = element.find(view.childViewContainer); targetElement = element.find(view.childViewContainer);
topOffset = targetElement.offset().top - element.offset().top; topOffset = targetElement.offset().top - element.offset().top;
leftOffset = targetElement.offset().left - element.offset().left; leftOffset = targetElement.offset().left - element.offset().left;
markerWidth = targetElement.width(); markerWidth = targetElement.width();
markerHeight = targetElement.height(); markerHeight = targetElement.height();
} else {
isLastBlockInsertion = view.collection.length === dropPosition.index;
targetModel = isLastBlockInsertion ? view.collection.at(dropPosition.index - 1) : view.collection.at(dropPosition.index);
targetView = view.children.findByModel(targetModel);
targetElement = targetView.$el;
topOffset = targetElement.offset().top - containerOffset.top;
leftOffset = targetElement.offset().left - containerOffset.left;
if (dropPosition.insertionType === 'normal') {
if (dropPosition.position === 'after') {
// Move the marker to the opposite side of the block
if (view.model.get('orientation') === 'vertical') {
topOffset += targetElement.outerHeight(true);
} else { } else {
isLastBlockInsertion = view.collection.length === dropPosition.index; leftOffset += targetElement.outerWidth();
targetModel = isLastBlockInsertion ? view.collection.at(dropPosition.index - 1) : view.collection.at(dropPosition.index);
targetView = view.children.findByModel(targetModel);
targetElement = targetView.$el;
topOffset = targetElement.offset().top - containerOffset.top;
leftOffset = targetElement.offset().left - containerOffset.left;
if (dropPosition.insertionType === 'normal') {
if (dropPosition.position === 'after') {
// Move the marker to the opposite side of the block
if (view.model.get('orientation') === 'vertical') {
topOffset += targetElement.outerHeight(true);
} else {
leftOffset += targetElement.outerWidth();
}
}
if (view.model.get('orientation') === 'vertical') {
markerWidth = targetElement.outerWidth();
} else {
markerHeight = targetElement.outerHeight();
}
} else {
if (dropPosition.position === 'after') {
// Move the marker to the opposite side of the block
if (view.model.get('orientation') === 'vertical') {
leftOffset += targetElement.outerWidth();
} else {
topOffset += targetElement.outerHeight();
}
}
if (view.model.get('orientation') === 'vertical') {
markerHeight = targetElement.outerHeight(true);
} else {
markerWidth = targetElement.outerWidth(true);
}
}
} }
}
marker = jQuery('<div class="mailpoet_drop_marker"></div>'); if (view.model.get('orientation') === 'vertical') {
// Add apropriate CSS classes for position refinement with CSS markerWidth = targetElement.outerWidth();
if (dropPosition.index === 0) { } else {
marker.addClass('mailpoet_drop_marker_first'); markerHeight = targetElement.outerHeight();
} }
if (view.collection.length - 1 === dropPosition.index) { } else {
marker.addClass('mailpoet_drop_marker_last'); if (dropPosition.position === 'after') {
} // Move the marker to the opposite side of the block
if (dropPosition.index > 0 && view.collection.length - 1 > dropPosition.index) { if (view.model.get('orientation') === 'vertical') {
marker.addClass('mailpoet_drop_marker_middle'); leftOffset += targetElement.outerWidth();
}
marker.addClass('mailpoet_drop_marker_' + dropPosition.position);
// Compute margin (optional for each block) that needs to be
// compensated for to position marker right in the middle of two
// blocks
if (dropPosition.position === 'before') {
$targetBlock = view.children.findByModel(view.collection.at(dropPosition.index-1)).$el;
} else { } else {
$targetBlock = view.children.findByModel(view.collection.at(dropPosition.index)).$el; topOffset += targetElement.outerHeight();
} }
margin = $targetBlock.outerHeight(true) - $targetBlock.outerHeight(); }
marker.css('top', topOffset - (margin / 2)); if (view.model.get('orientation') === 'vertical') {
marker.css('left', leftOffset); markerHeight = targetElement.outerHeight(true);
marker.css('width', markerWidth); } else {
marker.css('height', markerHeight); markerWidth = targetElement.outerWidth(true);
}
}
}
element.append(marker); marker = jQuery('<div class="mailpoet_drop_marker"></div>');
}, // Add apropriate CSS classes for position refinement with CSS
ondrop: function(event) { if (dropPosition.index === 0) {
// 1. Compute actual location of the mouse marker.addClass('mailpoet_drop_marker_first');
// 2. Check if insertion is regular (between blocks) or special (with container insertion) }
// 3a. If insertion is regular if (view.collection.length - 1 === dropPosition.index) {
// 3a1. compute position where insertion should happen marker.addClass('mailpoet_drop_marker_last');
// 3a2. insert the drop model there }
// 3b. If insertion is special if (dropPosition.index > 0 && view.collection.length - 1 > dropPosition.index) {
// 3b1. compute position (which side) and which cell the insertion belongs to marker.addClass('mailpoet_drop_marker_middle');
// 3b2. remove element at that position from the collection }
// 3b3. create a new collection, insert the removed element to it marker.addClass('mailpoet_drop_marker_' + dropPosition.position);
// 3b4. insert the droppable model at the start or end of the new collection, depending on 3b1. position
// 3b5. insert the new collection into the old collection to cell from 3b1.
// 4. Perform cleanup actions
var dropPosition = that.getDropPosition( // Compute margin (optional for each block) that needs to be
event.dragEvent.pageX, // compensated for to position marker right in the middle of two
event.dragEvent.pageY, // blocks
view.$el, if (dropPosition.position === 'before') {
view.model.get('orientation'), $targetBlock = view.children.findByModel(view.collection.at(dropPosition.index-1)).$el;
view.model.get('blocks').length } else {
), $targetBlock = view.children.findByModel(view.collection.at(dropPosition.index)).$el;
droppableModel = event.draggable.getDropModel(), }
droppedView, droppedModel, index, tempCollection, tempCollection2; margin = $targetBlock.outerHeight(true) - $targetBlock.outerHeight();
if (dropPosition === undefined) return; marker.css('top', topOffset - (margin / 2));
marker.css('left', leftOffset);
marker.css('width', markerWidth);
marker.css('height', markerHeight);
if (dropPosition.insertionType === 'normal') { element.append(marker);
// Normal insertion of dropModel into existing collection },
index = (dropPosition.position === 'after') ? dropPosition.index + 1 : dropPosition.index; ondrop: function(event) {
// 1. Compute actual location of the mouse
// 2. Check if insertion is regular (between blocks) or special (with container insertion)
// 3a. If insertion is regular
// 3a1. compute position where insertion should happen
// 3a2. insert the drop model there
// 3b. If insertion is special
// 3b1. compute position (which side) and which cell the insertion belongs to
// 3b2. remove element at that position from the collection
// 3b3. create a new collection, insert the removed element to it
// 3b4. insert the droppable model at the start or end of the new collection, depending on 3b1. position
// 3b5. insert the new collection into the old collection to cell from 3b1.
// 4. Perform cleanup actions
if (view.model.get('orientation') === 'horizontal' && droppableModel.get('type') !== 'container') { var dropPosition = that.getDropPosition(
// Regular blocks always need to be inserted into columns - vertical containers event.dragEvent.pageX,
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({ event.dragEvent.pageY,
orientation: 'vertical', view.$el,
}); view.model.get('orientation'),
tempCollection.get('blocks').add(droppableModel); view.model.get('blocks').length
view.collection.add(tempCollection, {at: index}); ),
} else { droppableModel = event.draggable.getDropModel(),
view.collection.add(droppableModel, {at: index}); droppedView, droppedModel, index, tempCollection, tempCollection2;
}
droppedView = view.children.findByModel(droppableModel); if (dropPosition === undefined) return;
} else {
// Special insertion by replacing target block with collection
// and inserting dropModel into that
var tempModel = view.collection.at(dropPosition.index);
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({ if (dropPosition.insertionType === 'normal') {
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical', // Normal insertion of dropModel into existing collection
}); index = (dropPosition.position === 'after') ? dropPosition.index + 1 : dropPosition.index;
view.collection.remove(tempModel); if (view.model.get('orientation') === 'horizontal' && droppableModel.get('type') !== 'container') {
// Regular blocks always need to be inserted into columns - vertical containers
tempCollection = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection.get('blocks').add(droppableModel);
view.collection.add(tempCollection, {at: index});
} else {
view.collection.add(droppableModel, {at: index});
}
if (tempCollection.get('orientation') === 'horizontal') { droppedView = view.children.findByModel(droppableModel);
if (dropPosition.position === 'before') { } else {
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({ // Special insertion by replacing target block with collection
orientation: 'vertical', // and inserting dropModel into that
}); var tempModel = view.collection.at(dropPosition.index);
tempCollection2.get('blocks').add(droppableModel);
tempCollection.get('blocks').add(tempCollection2);
}
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection2.get('blocks').add(tempModel);
tempCollection.get('blocks').add(tempCollection2);
if (dropPosition.position === 'after') {
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection2.get('blocks').add(droppableModel);
tempCollection.get('blocks').add(tempCollection2);
}
} else {
if (dropPosition.position === 'before') {
tempCollection.get('blocks').add(droppableModel);
}
tempCollection.get('blocks').add(tempModel);
if (dropPosition.position === 'after') {
tempCollection.get('blocks').add(droppableModel);
}
}
view.collection.add(tempCollection, {at: dropPosition.index});
// Call post add actions tempCollection = new (EditorApplication.getBlockTypeModel('container'))({
droppedView = view.children.findByModel(tempCollection).children.findByModel(droppableModel); orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical',
}
// Call post add actions
event.draggable.onDrop({
dropBehavior: that,
droppedModel: droppableModel,
droppedView: droppedView,
});
that.cleanup();
},
});
},
cleanup: function() {
// 1. Remove visual markings of active dropping container
this.view.$el.removeClass('mailpoet_drop_active');
// 2. Remove visual markings of drop position visualization
this.view.$('.mailpoet_drop_marker').remove();
},
getDropPosition: function(eventX, eventY, unsafe) {
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
element = this.view.$el,
orientation = this.view.model.get('orientation'),
elementOffset = element.offset(),
elementPageX = elementOffset.left,
elementPageY = elementOffset.top,
elementWidth = element.outerWidth(true),
elementHeight = element.outerHeight(true),
relativeX = eventX - elementPageX,
relativeY = eventY - elementPageY,
relativeOffset, elementLength,
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
insertionType, index, position, indexAndPosition;
unsafe = !!unsafe;
if (this.view.collection.length === 0) {
return {
insertionType: 'normal',
index: 0,
position: 'inside',
};
}
if (orientation === 'vertical') {
relativeOffset = relativeX;
elementLength = elementWidth;
} else {
relativeOffset = relativeY;
elementLength = elementHeight;
}
if (canAcceptSpecialInsertion && !canAcceptNormalInsertion) {
// If normal insertion is not available, dedicate whole element area
// to special insertion
SPECIAL_AREA_INSERTION_WIDTH = 0.5;
}
if (relativeOffset <= elementLength * SPECIAL_AREA_INSERTION_WIDTH && (unsafe || canAcceptSpecialInsertion)) {
insertionType = 'special';
position = 'before';
index = this._computeSpecialIndex(eventX, eventY);
} else if (relativeOffset > elementLength * (1 - SPECIAL_AREA_INSERTION_WIDTH) && (unsafe || canAcceptSpecialInsertion)) {
insertionType = 'special';
position = 'after';
index = this._computeSpecialIndex(eventX, eventY);
} else {
indexAndPosition = this._computeNormalIndex(eventX, eventY);
insertionType = 'normal';
position = indexAndPosition.position;
index = indexAndPosition.index;
}
if (!unsafe && orientation === 'vertical' && insertionType === 'special' && this.view.collection.at(index).get('orientation') === 'horizontal') {
// Prevent placing horizontal container in another horizontal container,
// which would allow breaking the column limit.
// Switch that to normal insertion
indexAndPosition = this._computeNormalIndex(eventX, eventY);
insertionType = 'normal';
position = indexAndPosition.position;
index = indexAndPosition.index;
}
if (orientation === 'horizontal' && insertionType === 'special') {
// Disable special insertion for horizontal containers
return;
}
return {
insertionType: insertionType, // 'normal'|'special'
index: index,
position: position, // 'inside'|'before'|'after'
};
},
_computeNormalIndex: function(eventX, eventY) {
// Normal insertion inserts dropModel before target element if
// event happens on the first half of the element and after the
// target element if event happens on the second half of the element.
// Halves depend on orientation.
var index = this._computeCellIndex(eventX, eventY),
// TODO: Handle case when there are no children, container is empty
targetView = this.view.children.findByModel(this.view.collection.at(index)),
orientation = this.view.model.get('orientation'),
element = targetView.$el,
eventOffset, closeOffset, elementDimension;
if (orientation === 'vertical') {
eventOffset = eventY;
closeOffset = element.offset().top;
elementDimension = element.outerHeight(true);
} else {
eventOffset = eventX;
closeOffset = element.offset().left;
elementDimension = element.outerWidth(true);
}
if (eventOffset <= closeOffset + elementDimension / 2) {
// First half of the element
return {
index: index,
position: 'before',
};
} else {
// Second half of the element
return {
index: index,
position: 'after',
};
}
},
_computeSpecialIndex: function(eventX, eventY) {
return this._computeCellIndex(eventX, eventY);
},
_computeCellIndex: function(eventX, eventY) {
var orientation = this.view.model.get('orientation'),
eventOffset = (orientation === 'vertical') ? eventY : eventX,
resultView = this.view.children.find(function(view) {
var element = view.$el,
closeOffset, farOffset;
if (orientation === 'vertical') {
closeOffset = element.offset().top;
farOffset = element.outerHeight(true);
} else {
closeOffset = element.offset().left;
farOffset = element.outerWidth(true);
}
farOffset += closeOffset;
return closeOffset <= eventOffset && eventOffset <= farOffset;
}); });
var index = (typeof resultView === 'object') ? resultView._index : 0; view.collection.remove(tempModel);
return index; if (tempCollection.get('orientation') === 'horizontal') {
if (dropPosition.position === 'before') {
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection2.get('blocks').add(droppableModel);
tempCollection.get('blocks').add(tempCollection2);
}
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection2.get('blocks').add(tempModel);
tempCollection.get('blocks').add(tempCollection2);
if (dropPosition.position === 'after') {
tempCollection2 = new (EditorApplication.getBlockTypeModel('container'))({
orientation: 'vertical',
});
tempCollection2.get('blocks').add(droppableModel);
tempCollection.get('blocks').add(tempCollection2);
}
} else {
if (dropPosition.position === 'before') {
tempCollection.get('blocks').add(droppableModel);
}
tempCollection.get('blocks').add(tempModel);
if (dropPosition.position === 'after') {
tempCollection.get('blocks').add(droppableModel);
}
}
view.collection.add(tempCollection, {at: dropPosition.index});
// Call post add actions
droppedView = view.children.findByModel(tempCollection).children.findByModel(droppableModel);
}
// Call post add actions
event.draggable.onDrop({
dropBehavior: that,
droppedModel: droppableModel,
droppedView: droppedView,
});
that.cleanup();
},
});
},
cleanup: function() {
// 1. Remove visual markings of active dropping container
this.view.$el.removeClass('mailpoet_drop_active');
// 2. Remove visual markings of drop position visualization
this.view.$('.mailpoet_drop_marker').remove();
},
getDropPosition: function(eventX, eventY, unsafe) {
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
element = this.view.$el,
orientation = this.view.model.get('orientation'),
elementOffset = element.offset(),
elementPageX = elementOffset.left,
elementPageY = elementOffset.top,
elementWidth = element.outerWidth(true),
elementHeight = element.outerHeight(true),
relativeX = eventX - elementPageX,
relativeY = eventY - elementPageY,
relativeOffset, elementLength,
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
insertionType, index, position, indexAndPosition;
unsafe = !!unsafe;
if (this.view.collection.length === 0) {
return {
insertionType: 'normal',
index: 0,
position: 'inside',
};
}
if (orientation === 'vertical') {
relativeOffset = relativeX;
elementLength = elementWidth;
} else {
relativeOffset = relativeY;
elementLength = elementHeight;
}
if (canAcceptSpecialInsertion && !canAcceptNormalInsertion) {
// If normal insertion is not available, dedicate whole element area
// to special insertion
SPECIAL_AREA_INSERTION_WIDTH = 0.5;
}
if (relativeOffset <= elementLength * SPECIAL_AREA_INSERTION_WIDTH && (unsafe || canAcceptSpecialInsertion)) {
insertionType = 'special';
position = 'before';
index = this._computeSpecialIndex(eventX, eventY);
} else if (relativeOffset > elementLength * (1 - SPECIAL_AREA_INSERTION_WIDTH) && (unsafe || canAcceptSpecialInsertion)) {
insertionType = 'special';
position = 'after';
index = this._computeSpecialIndex(eventX, eventY);
} else {
indexAndPosition = this._computeNormalIndex(eventX, eventY);
insertionType = 'normal';
position = indexAndPosition.position;
index = indexAndPosition.index;
}
if (!unsafe && orientation === 'vertical' && insertionType === 'special' && this.view.collection.at(index).get('orientation') === 'horizontal') {
// Prevent placing horizontal container in another horizontal container,
// which would allow breaking the column limit.
// Switch that to normal insertion
indexAndPosition = this._computeNormalIndex(eventX, eventY);
insertionType = 'normal';
position = indexAndPosition.position;
index = indexAndPosition.index;
}
if (orientation === 'horizontal' && insertionType === 'special') {
// Disable special insertion for horizontal containers
return;
}
return {
insertionType: insertionType, // 'normal'|'special'
index: index,
position: position, // 'inside'|'before'|'after'
};
},
_computeNormalIndex: function(eventX, eventY) {
// Normal insertion inserts dropModel before target element if
// event happens on the first half of the element and after the
// target element if event happens on the second half of the element.
// Halves depend on orientation.
var index = this._computeCellIndex(eventX, eventY),
// TODO: Handle case when there are no children, container is empty
targetView = this.view.children.findByModel(this.view.collection.at(index)),
orientation = this.view.model.get('orientation'),
element = targetView.$el,
eventOffset, closeOffset, elementDimension;
if (orientation === 'vertical') {
eventOffset = eventY;
closeOffset = element.offset().top;
elementDimension = element.outerHeight(true);
} else {
eventOffset = eventX;
closeOffset = element.offset().left;
elementDimension = element.outerWidth(true);
}
if (eventOffset <= closeOffset + elementDimension / 2) {
// First half of the element
return {
index: index,
position: 'before',
};
} else {
// Second half of the element
return {
index: index,
position: 'after',
};
}
},
_computeSpecialIndex: function(eventX, eventY) {
return this._computeCellIndex(eventX, eventY);
},
_computeCellIndex: function(eventX, eventY) {
var orientation = this.view.model.get('orientation'),
eventOffset = (orientation === 'vertical') ? eventY : eventX,
resultView = this.view.children.find(function(view) {
var element = view.$el,
closeOffset, farOffset;
if (orientation === 'vertical') {
closeOffset = element.offset().top;
farOffset = element.outerHeight(true);
} else {
closeOffset = element.offset().left;
farOffset = element.outerWidth(true);
}
farOffset += closeOffset;
return closeOffset <= eventOffset && eventOffset <= farOffset;
});
var index = (typeof resultView === 'object') ? resultView._index : 0;
return index;
}, },
_canAcceptNormalInsertion: function() { _canAcceptNormalInsertion: function() {
var orientation = this.view.model.get('orientation'), var orientation = this.view.model.get('orientation'),
depth = this.view.renderOptions.depth, depth = this.view.renderOptions.depth,
childCount = this.view.children.length; childCount = this.view.children.length;
// Note that depth is zero indexed. Root container has depth=0 // Note that depth is zero indexed. Root container has depth=0
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit); return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
}, },
_canAcceptSpecialInsertion: function() { _canAcceptSpecialInsertion: function() {
var orientation = this.view.model.get('orientation'), var orientation = this.view.model.get('orientation'),
depth = this.view.renderOptions.depth, depth = this.view.renderOptions.depth,
childCount = this.view.children.length; childCount = this.view.children.length;
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit); return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
}, },
});
}); });

View File

@ -4,121 +4,129 @@
* Allows View instances to be draggable. * Allows View instances to be draggable.
* Part of the drag&drop behavior. * Part of the drag&drop behavior.
*/ */
BehaviorsLookup.DraggableBehavior = Backbone.Marionette.Behavior.extend({ define('newsletter_editor/behaviors/DraggableBehavior', [
defaults: { 'backbone.marionette',
cloneOriginal: false, 'newsletter_editor/behaviors/BehaviorsLookup',
hideOriginal: false, 'interact.js',
ignoreSelector: '.mailpoet_ignore_drag, .mailpoet_ignore_drag *', ], function(Marionette, BehaviorsLookup, interact) {
onDragSubstituteBy: undefined,
/**
* Constructs a model that will be passed to the receiver on drop
*
* @return Backbone.Model A model that will be passed to the receiver
*/
getDropModel: function() {
throw "Missing 'drop' function for DraggableBehavior";
},
onDrop: function(model, view) {}, BehaviorsLookup.DraggableBehavior = Marionette.Behavior.extend({
testAttachToInstance: function(model, view) { return true; }, defaults: {
}, cloneOriginal: false,
onRender: function() { hideOriginal: false,
var that = this, ignoreSelector: '.mailpoet_ignore_drag, .mailpoet_ignore_drag *',
interactable; onDragSubstituteBy: undefined,
/**
* Constructs a model that will be passed to the receiver on drop
*
* @return Backbone.Model A model that will be passed to the receiver
*/
getDropModel: function() {
throw "Missing 'drop' function for DraggableBehavior";
},
// Give instances more control over whether Draggable should be applied onDrop: function(model, view) {},
if (!this.options.testAttachToInstance(this.view.model, this.view)) return; testAttachToInstance: function(model, view) { return true; },
},
onRender: function() {
var that = this,
interactable;
interactable = interact(this.$el.get(0), { // Give instances more control over whether Draggable should be applied
ignoreFrom: this.options.ignoreSelector, if (!this.options.testAttachToInstance(this.view.model, this.view)) return;
}).draggable({
// allow dragging of multple elements at the same time
max: Infinity,
// Scroll when dragging near edges of a window interactable = interact(this.$el.get(0), {
autoScroll: true, ignoreFrom: this.options.ignoreSelector,
}).draggable({
// allow dragging of multple elements at the same time
max: Infinity,
onstart: function(event) { // Scroll when dragging near edges of a window
console.log('Drag start', event, this); autoScroll: true,
if (that.options.cloneOriginal === true) { onstart: function(event) {
// Use substitution instead of a clone console.log('Drag start', event, this);
var tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined,
// Or use a clone
clone = tempClone ? tempClone : event.target.cloneNode(true),
$original = jQuery(event.target), if (that.options.cloneOriginal === true) {
$clone = jQuery(clone), // Use substitution instead of a clone
centerXOffset, centerYOffset, parentOffset; var tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined,
// Or use a clone
clone = tempClone ? tempClone : event.target.cloneNode(true),
$clone.addClass('mailpoet_droppable_active'); $original = jQuery(event.target),
$clone.css('position', 'absolute'); $clone = jQuery(clone),
$clone.css('top', 0); centerXOffset, centerYOffset, parentOffset;
$clone.css('left', 0);
document.body.appendChild(clone);
// Position the clone over the target element with a slight $clone.addClass('mailpoet_droppable_active');
// offset to center the clone under the mouse cursor. $clone.css('position', 'absolute');
// Accurate dimensions can only be taken after insertion to document $clone.css('top', 0);
centerXOffset = $clone.width() / 2; $clone.css('left', 0);
centerYOffset = $clone.height() / 2; document.body.appendChild(clone);
$clone.css('top', event.pageY - centerYOffset);
$clone.css('left', event.pageX - centerXOffset);
event.interaction.element = clone; // Position the clone over the target element with a slight
// offset to center the clone under the mouse cursor.
// Accurate dimensions can only be taken after insertion to document
centerXOffset = $clone.width() / 2;
centerYOffset = $clone.height() / 2;
$clone.css('top', event.pageY - centerYOffset);
$clone.css('left', event.pageX - centerXOffset);
event.interaction.element = clone;
if (that.options.hideOriginal === true) { if (that.options.hideOriginal === true) {
that.view.$el.addClass('mailpoet_hidden'); that.view.$el.addClass('mailpoet_hidden');
} }
} }
}, },
// call this function on every dragmove event // call this function on every dragmove event
onmove: function (event) { onmove: function (event) {
var target = event.target, var target = event.target,
// keep the dragged position in the data-x/data-y attributes // keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx, x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy; y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element // translate the element
target.style.webkitTransform = target.style.webkitTransform =
target.style.transform = target.style.transform =
'translate(' + x + 'px, ' + y + 'px)'; 'translate(' + x + 'px, ' + y + 'px)';
// update the posiion attributes // update the posiion attributes
target.setAttribute('data-x', x); target.setAttribute('data-x', x);
target.setAttribute('data-y', y); target.setAttribute('data-y', y);
}, },
onend: function (event) { onend: function (event) {
var target = event.target; var target = event.target;
target.style.webkitTransform = target.style.transform = ''; target.style.webkitTransform = target.style.transform = '';
target.removeAttribute('data-x'); target.removeAttribute('data-x');
target.removeAttribute('data-y'); target.removeAttribute('data-y');
jQuery(event.interaction.element).addClass('mailpoet_droppable_active'); jQuery(event.interaction.element).addClass('mailpoet_droppable_active');
if (that.options.cloneOriginal === true) { if (that.options.cloneOriginal === true) {
jQuery(target).remove(); jQuery(target).remove();
if (that.options.hideOriginal === true) { if (that.options.hideOriginal === true) {
that.view.$el.removeClass('mailpoet_hidden'); that.view.$el.removeClass('mailpoet_hidden');
} }
} }
}, },
}).preventDefault('auto'); }).preventDefault('auto');
if (this.options.drop !== undefined) {
interactable.getDropModel = this.options.drop;
} else {
interactable.getDropModel = this.view.getDropFunc();
}
interactable.onDrop = function(options) {
if (_.isObject(options)) {
// Inject Draggable behavior if possible
options.dragBehavior = that;
}
// Delegate to view's event handler
that.options.onDrop.apply(that, [options]);
};
},
});
if (this.options.drop !== undefined) {
interactable.getDropModel = this.options.drop;
} else {
interactable.getDropModel = this.view.getDropFunc();
}
interactable.onDrop = function(options) {
if (_.isObject(options)) {
// Inject Draggable behavior if possible
options.dragBehavior = that;
}
// Delegate to view's event handler
that.options.onDrop.apply(that, [options]);
};
},
}); });

View File

@ -3,59 +3,67 @@
* *
* Allows resizing elements within a block * Allows resizing elements within a block
*/ */
BehaviorsLookup.ResizableBehavior = Backbone.Marionette.Behavior.extend({ define('newsletter_editor/behaviors/ResizableBehavior', [
defaults: { 'backbone.marionette',
elementSelector: null, 'newsletter_editor/behaviors/BehaviorsLookup',
resizeHandleSelector: true, // true will use edges of the element itself 'interact.js',
transformationFunction: function(y) { return y; }, ], function(Marionette, BehaviorsLookup, interact) {
minLength: 0,
modelField: 'styles.block.height',
},
events: {
"mouseenter": 'showResizeHandle',
"mouseleave": 'hideResizeHandle',
},
onRender: function() {
this.attachResize();
if (this.isBeingResized !== true) { BehaviorsLookup.ResizableBehavior = Marionette.Behavior.extend({
this.hideResizeHandle(); defaults: {
} elementSelector: null,
}, resizeHandleSelector: true, // true will use edges of the element itself
attachResize: function() { transformationFunction: function(y) { return y; },
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0), minLength: 0,
that = this; modelField: 'styles.block.height',
interact(domElement).resizable({ },
//axis: 'y', events: {
edges: { "mouseenter": 'showResizeHandle',
top: false, "mouseleave": 'hideResizeHandle',
left: false, },
right: false, onRender: function() {
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector, this.attachResize();
},
}).on('resizestart', function(event) {
that.isBeingResized = true;
that.$el.addClass('mailpoet_resize_active');
}).on('resizemove', function(event) {
var currentLength = parseFloat(that.view.model.get(that.options.modelField)),
newLength = currentLength + that.options.transformationFunction(event.dy);
if (newLength < that.options.minLength) newLength = that.options.minLength; if (this.isBeingResized !== true) {
this.hideResizeHandle();
}
},
attachResize: function() {
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0),
that = this;
interact(domElement).resizable({
//axis: 'y',
edges: {
top: false,
left: false,
right: false,
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector,
},
}).on('resizestart', function(event) {
that.isBeingResized = true;
that.$el.addClass('mailpoet_resize_active');
}).on('resizemove', function(event) {
var currentLength = parseFloat(that.view.model.get(that.options.modelField)),
newLength = currentLength + that.options.transformationFunction(event.dy);
if (newLength < that.options.minLength) newLength = that.options.minLength;
that.view.model.set(that.options.modelField, newLength + 'px');
}).on('resizeend', function(event) {
that.isBeingResized = null;
that.$el.removeClass('mailpoet_resize_active');
});
},
showResizeHandle: function() {
if (typeof this.options.resizeHandleSelector === 'string') {
this.view.$(this.options.resizeHandleSelector).removeClass('mailpoet_hidden');
}
},
hideResizeHandle: function() {
if (typeof this.options.resizeHandleSelector === 'string') {
this.view.$(this.options.resizeHandleSelector).addClass('mailpoet_hidden');
}
},
});
that.view.model.set(that.options.modelField, newLength + 'px');
}).on('resizeend', function(event) {
that.isBeingResized = null;
that.$el.removeClass('mailpoet_resize_active');
});
},
showResizeHandle: function() {
if (typeof this.options.resizeHandleSelector === 'string') {
this.view.$(this.options.resizeHandleSelector).removeClass('mailpoet_hidden');
}
},
hideResizeHandle: function() {
if (typeof this.options.resizeHandleSelector === 'string') {
this.view.$(this.options.resizeHandleSelector).addClass('mailpoet_hidden');
}
},
}); });

View File

@ -3,32 +3,38 @@
* *
* Allows sorting elements within a collection * Allows sorting elements within a collection
*/ */
BehaviorsLookup.SortableBehavior = Backbone.Marionette.Behavior.extend({ define('newsletter_editor/behaviors/SortableBehavior', [
onRender: function() { 'backbone.marionette',
var collection = this.view.collection; 'newsletter_editor/behaviors/BehaviorsLookup',
], function(Marionette, BehaviorsLookup) {
if (_.isFunction(this.$el.sortable)) { BehaviorsLookup.SortableBehavior = Marionette.Behavior.extend({
this.$el.sortable({ onRender: function() {
cursor: "move", var collection = this.view.collection;
start: function(event, ui) {
ui.item.data('previousIndex', ui.item.index()); if (_.isFunction(this.$el.sortable)) {
}, this.$el.sortable({
end: function(event, ui) { cursor: "move",
ui.item.removeData('previousIndex'); start: function(event, ui) {
}, ui.item.data('previousIndex', ui.item.index());
update: function(event, ui) { },
var previousIndex = ui.item.data('previousIndex'), end: function(event, ui) {
newIndex = ui.item.index(), ui.item.removeData('previousIndex');
model = collection.at(previousIndex); },
update: function(event, ui) {
var previousIndex = ui.item.data('previousIndex'),
newIndex = ui.item.index(),
model = collection.at(previousIndex);
// Replicate DOM changes. Move target model to a new position
// within the collection
collection.remove(model);
collection.add(model, { at: newIndex });
},
items: this.options.items,
});
}
}
});
// Replicate DOM changes. Move target model to a new position
// within the collection
collection.remove(model);
collection.add(model, { at: newIndex });
},
items: this.options.items,
});
}
}
}); });

View File

@ -6,325 +6,335 @@
* This block depends on blocks.button and blocks.divider for block model and * This block depends on blocks.button and blocks.divider for block model and
* block settings view. * block settings view.
*/ */
EditorApplication.module("blocks.automatedLatestContent", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/automatedLatestContent', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.automatedLatestContent", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
stale: ['_container'],
defaults: function() {
return this._getDefaults({
type: 'automatedLatestContent',
amount: '5',
contentType: 'post', // 'post'|'page'|'mailpoet_page'
terms: [], // List of category and tag objects
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:',
showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: 'Categories:',
readMoreType: 'button', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: 'Read more',
url: '[postLink]'
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {},
_container: new (App.getBlockTypeModel('container'))(),
}, EditorApplication.getConfig().get('blockDefaults.automatedLatestContent'));
},
relations: function() {
return {
readMoreButton: App.getBlockTypeModel('button'),
divider: App.getBlockTypeModel('divider'),
_container: App.getBlockTypeModel('container'),
};
},
initialize: function() {
base.BlockModel.prototype.initialize.apply(this);
this.fetchPosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._scheduleFetchPosts, this);
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
},
fetchPosts: function() {
var that = this;
mailpoet_post_wpi('automated_latest_content.php', this.toJSON(), function(response) {
console.log('ALC fetched', arguments);
that.get('_container').get('blocks').reset(response, {parse: true});
}, function() {
console.log('ALC fetchPosts error', arguments);
});
},
/**
* Batch more changes during a specific time, instead of fetching
* ALC posts on each model change
*/
_scheduleFetchPosts: function() {
var timeout = 2000,
that = this;
if (this._fetchPostsTimer !== undefined) {
clearTimeout(this._fetchPostsTimer);
}
this._fetchPostsTimer = setTimeout(function() {
that.fetchPosts();
that._fetchPostsTimer = undefined;
}, timeout);
},
});
Module.AutomatedLatestContentBlockView = base.BlockView.extend({ Module.AutomatedLatestContentBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_automated_latest_content_block mailpoet_droppable_block", stale: ['_container'],
getTemplate: function() { return templates.automatedLatestContentBlock; }, defaults: function() {
regions: { return this._getDefaults({
toolsRegion: '.mailpoet_tools', type: 'automatedLatestContent',
postsRegion: '.mailpoet_automated_latest_content_block_posts', amount: '5',
}, contentType: 'post', // 'post'|'page'|'mailpoet_page'
onDragSubstituteBy: function() { return Module.AutomatedLatestContentWidgetView; }, terms: [], // List of category and tag objects
onRender: function() { inclusionType: 'include', // 'include'|'exclude'
var ContainerView = App.getBlockTypeView('container'), displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
renderOptions = { titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
disableTextEditor: true, titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
disableDragAndDrop: true, titleAlignment: 'left', // 'left'|'center'|'right'
}; titleIsLink: false, // false|true
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model }); imagePadded: true, // true|false
this.toolsRegion.show(this.toolsView); //imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
this.postsRegion.show(new ContainerView({ model: this.model.get('_container'), renderOptions: renderOptions })); showAuthor: 'no', // 'no'|'aboveText'|'belowText'
}, authorPrecededBy: 'Author:',
}); showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: 'Categories:',
readMoreType: 'button', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: 'Read more',
url: '[postLink]'
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {},
_container: new (App.getBlockTypeModel('container'))(),
}, EditorApplication.getConfig().get('blockDefaults.automatedLatestContent'));
},
relations: function() {
return {
readMoreButton: App.getBlockTypeModel('button'),
divider: App.getBlockTypeModel('divider'),
_container: App.getBlockTypeModel('container'),
};
},
initialize: function() {
base.BlockModel.prototype.initialize.apply(this);
this.fetchPosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:displayType change:titleFormat change:titlePosition change:titleAlignment change:titleIsLink change:imagePadded change:showAuthor change:authorPrecededBy change:showCategories change:categoriesPrecededBy change:readMoreType change:readMoreText change:sortBy change:showDivider', this._scheduleFetchPosts, this);
this.listenTo(this.get('readMoreButton'), 'change', this._scheduleFetchPosts);
this.listenTo(this.get('divider'), 'change', this._scheduleFetchPosts);
},
fetchPosts: function() {
var that = this;
mailpoet_post_wpi('automated_latest_content.php', this.toJSON(), function(response) {
console.log('ALC fetched', arguments);
that.get('_container').get('blocks').reset(response, {parse: true});
}, function() {
console.log('ALC fetchPosts error', arguments);
});
},
/**
* Batch more changes during a specific time, instead of fetching
* ALC posts on each model change
*/
_scheduleFetchPosts: function() {
var timeout = 2000,
that = this;
if (this._fetchPostsTimer !== undefined) {
clearTimeout(this._fetchPostsTimer);
}
this._fetchPostsTimer = setTimeout(function() {
that.fetchPosts();
that._fetchPostsTimer = undefined;
}, timeout);
},
});
Module.AutomatedLatestContentBlockToolsView = base.BlockToolsView.extend({ Module.AutomatedLatestContentBlockView = base.BlockView.extend({
getSettingsView: function() { return Module.AutomatedLatestContentBlockSettingsView; }, className: "mailpoet_block mailpoet_automated_latest_content_block mailpoet_droppable_block",
}); getTemplate: function() { return templates.automatedLatestContentBlock; },
regions: {
toolsRegion: '.mailpoet_tools',
postsRegion: '.mailpoet_automated_latest_content_block_posts',
},
onDragSubstituteBy: function() { return Module.AutomatedLatestContentWidgetView; },
onRender: function() {
var ContainerView = App.getBlockTypeView('container'),
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
};
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
this.postsRegion.show(new ContainerView({ model: this.model.get('_container'), renderOptions: renderOptions }));
},
});
// Sidebar view container Module.AutomatedLatestContentBlockToolsView = base.BlockToolsView.extend({
Module.AutomatedLatestContentBlockSettingsView = base.BlockSettingsView.extend({ getSettingsView: function() { return Module.AutomatedLatestContentBlockSettingsView; },
getTemplate: function() { return templates.automatedLatestContentBlockSettings; }, });
events: function() {
return {
"click .mailpoet_automated_latest_content_hide_display_options": 'toggleDisplayOptions',
"click .mailpoet_automated_latest_content_show_display_options": 'toggleDisplayOptions',
"click .mailpoet_automated_latest_content_select_button": 'showButtonSettings',
"click .mailpoet_automated_latest_content_select_divider": 'showDividerSettings',
"change .mailpoet_automated_latest_content_read_more_type": 'changeReadMoreType',
"change .mailpoet_automated_latest_content_display_type": 'changeDisplayType',
"change .mailpoet_automated_latest_content_title_format": 'changeTitleFormat',
"change .mailpoet_automated_latest_content_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'),
"change .mailpoet_automated_latest_content_show_divider": _.partial(this.changeBoolField, 'showDivider'),
"keyup .mailpoet_automated_latest_content_show_amount": _.partial(this.changeField, "amount"),
"change .mailpoet_automated_latest_content_content_type": _.partial(this.changeField, "contentType"),
"change .mailpoet_automated_latest_content_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_automated_latest_content_title_position": _.partial(this.changeField, "titlePosition"),
"change .mailpoet_automated_latest_content_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_automated_latest_content_image_padded": _.partial(this.changeBoolField, "imagePadded"),
"change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"),
"keyup .mailpoet_automated_latest_content_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"keyup .mailpoet_automated_latest_content_read_more_text": _.partial(this.changeField, "readMoreText"),
"change .mailpoet_automated_latest_content_sort_by": _.partial(this.changeField, "sortBy"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
};
},
onRender: function() {
var that = this;
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({ // Sidebar view container
multiple: true, Module.AutomatedLatestContentBlockSettingsView = base.BlockSettingsView.extend({
allowClear: true, getTemplate: function() { return templates.automatedLatestContentBlockSettings; },
ajax: { events: function() {
url: App.getConfig().get('urls.termSearch'), return {
type: 'POST', "click .mailpoet_automated_latest_content_hide_display_options": 'toggleDisplayOptions',
dataType: 'json', "click .mailpoet_automated_latest_content_show_display_options": 'toggleDisplayOptions',
delay: 250, "click .mailpoet_automated_latest_content_select_button": 'showButtonSettings',
data: function(searchParameter, page) { "click .mailpoet_automated_latest_content_select_divider": 'showDividerSettings',
return JSON.stringify({ "change .mailpoet_automated_latest_content_read_more_type": 'changeReadMoreType',
postType: that.model.get('contentType'), "change .mailpoet_automated_latest_content_display_type": 'changeDisplayType',
search: searchParameter, "change .mailpoet_automated_latest_content_title_format": 'changeTitleFormat',
limit: 10, // TODO: Move this hardcoded limit to Config "change .mailpoet_automated_latest_content_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'),
page: page, "change .mailpoet_automated_latest_content_show_divider": _.partial(this.changeBoolField, 'showDivider'),
}); "keyup .mailpoet_automated_latest_content_show_amount": _.partial(this.changeField, "amount"),
}, "change .mailpoet_automated_latest_content_content_type": _.partial(this.changeField, "contentType"),
/** "change .mailpoet_automated_latest_content_include_or_exclude": _.partial(this.changeField, "inclusionType"),
* Parse results for select2. "change .mailpoet_automated_latest_content_title_position": _.partial(this.changeField, "titlePosition"),
* Returns object, where `results` key holds a list of "change .mailpoet_automated_latest_content_title_alignment": _.partial(this.changeField, "titleAlignment"),
* select item objects "change .mailpoet_automated_latest_content_image_padded": _.partial(this.changeBoolField, "imagePadded"),
*/ "change .mailpoet_automated_latest_content_show_author": _.partial(this.changeField, "showAuthor"),
results: function (data, page) { "keyup .mailpoet_automated_latest_content_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
return { "change .mailpoet_automated_latest_content_show_categories": _.partial(this.changeField, "showCategories"),
results: _.map( "keyup .mailpoet_automated_latest_content_categories": _.partial(this.changeField, "categoriesPrecededBy"),
data.results, "keyup .mailpoet_automated_latest_content_read_more_text": _.partial(this.changeField, "readMoreText"),
function(item) { "change .mailpoet_automated_latest_content_sort_by": _.partial(this.changeField, "sortBy"),
return _.defaults({ "click .mailpoet_done_editing": "close",
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name, };
id: item.term_id },
}, item); behaviors: {
} ColorPickerBehavior: {},
) },
}; templateHelpers: function() {
} return {
}, model: this.model.toJSON(),
initSelection: function(element, callback) { };
// On external data load tell select2 which terms to preselect },
onRender: function() {
var that = this;
callback(_.map( this.$('.mailpoet_automated_latest_content_categories_and_tags').select2({
that.model.get('terms').toJSON(), multiple: true,
function(item) { allowClear: true,
return { ajax: {
id: item.id, url: App.getConfig().get('urls.termSearch'),
text: item.text, type: 'POST',
}; dataType: 'json',
} delay: 250,
)); data: function(searchParameter, page) {
}, return JSON.stringify({
}).trigger( 'change' ).on({ postType: that.model.get('contentType'),
'change': function(e){ search: searchParameter,
var data = $(this).data('selected'); limit: 10, // TODO: Move this hardcoded limit to Config
page: page,
});
},
/**
* Parse results for select2.
* Returns object, where `results` key holds a list of
* select item objects
*/
results: function (data, page) {
return {
results: _.map(
data.results,
function(item) {
return _.defaults({
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
id: item.term_id
}, item);
}
)
};
}
},
initSelection: function(element, callback) {
// On external data load tell select2 which terms to preselect
if (typeof data === 'string') { callback(_.map(
if (data === '') { that.model.get('terms').toJSON(),
data = []; function(item) {
} else { return {
data = JSON.parse(data); id: item.id,
} text: item.text,
} };
}
));
},
}).trigger( 'change' ).on({
'change': function(e){
var data = $(this).data('selected');
if ( e.added ){ if (typeof data === 'string') {
data.push(e.added); if (data === '') {
} else { data = [];
data = _.filter(data, function(item) { } else {
return item.id !== e.removed.id; data = JSON.parse(data);
}); }
} }
// Update ALC model if ( e.added ){
that.model.set('terms', data); data.push(e.added);
} else {
data = _.filter(data, function(item) {
return item.id !== e.removed.id;
});
}
$(this).data('selected', JSON.stringify(data)); // Update ALC model
} that.model.set('terms', data);
});
},
onBeforeDestroy: function() {
// Force close select2 if it hasn't closed yet
this.$('.mailpoet_automated_latest_content_categories_and_tags').select2('close');
},
toggleDisplayOptions: function(event) {
var el = this.$('.mailpoet_automated_latest_content_display_options'),
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
if (el.hasClass('mailpoet_hidden')) {
el.removeClass('mailpoet_hidden');
showControl.addClass('mailpoet_hidden');
} else {
el.addClass('mailpoet_hidden');
showControl.removeClass('mailpoet_hidden');
}
},
showButtonSettings: function(event) {
var buttonModule = App.module('blocks.button');
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
renderOptions: {
displayFormat: 'subpanel',
hideLink: true,
hideApplyToAll: true,
},
})).render();
},
showDividerSettings: function(event) {
var dividerModule = App.module('blocks.divider');
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
renderOptions: {
displayFormat: 'subpanel',
hideApplyToAll: true,
},
})).render();
},
changeReadMoreType: function(event) {
var value = jQuery(event.target).val();
if (value == 'link') {
this.$('.mailpoet_automated_latest_content_read_more_text').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_select_button').addClass('mailpoet_hidden');
} else if (value == 'button') {
this.$('.mailpoet_automated_latest_content_read_more_text').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_select_button').removeClass('mailpoet_hidden');
}
this.changeField('readMoreType', event);
},
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_automated_latest_content_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_automated_latest_content_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').addClass('mailpoet_hidden');
// Reset titleFormat if it was set to List when switching away from displayType=titleOnly $(this).data('selected', JSON.stringify(data));
if (this.model.get('titleFormat') === 'ul') { }
this.model.set('titleFormat', 'h1'); });
this.$('.mailpoet_automated_latest_content_title_format').val(['h1']); },
this.$('.mailpoet_automated_latest_content_title_as_link').removeClass('mailpoet_hidden'); onBeforeDestroy: function() {
} // Force close select2 if it hasn't closed yet
} this.$('.mailpoet_automated_latest_content_categories_and_tags').select2('close');
this.changeField('displayType', event); },
}, toggleDisplayOptions: function(event) {
changeTitleFormat: function(event) { var el = this.$('.mailpoet_automated_latest_content_display_options'),
var value = jQuery(event.target).val(); showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
if (value == 'ul') { if (el.hasClass('mailpoet_hidden')) {
this.$('.mailpoet_automated_latest_content_non_title_list_options').addClass('mailpoet_hidden'); el.removeClass('mailpoet_hidden');
showControl.addClass('mailpoet_hidden');
} else {
el.addClass('mailpoet_hidden');
showControl.removeClass('mailpoet_hidden');
}
},
showButtonSettings: function(event) {
var buttonModule = App.module('blocks.button');
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
renderOptions: {
displayFormat: 'subpanel',
hideLink: true,
hideApplyToAll: true,
},
})).render();
},
showDividerSettings: function(event) {
var dividerModule = App.module('blocks.divider');
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
renderOptions: {
displayFormat: 'subpanel',
hideApplyToAll: true,
},
})).render();
},
changeReadMoreType: function(event) {
var value = jQuery(event.target).val();
if (value == 'link') {
this.$('.mailpoet_automated_latest_content_read_more_text').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_select_button').addClass('mailpoet_hidden');
} else if (value == 'button') {
this.$('.mailpoet_automated_latest_content_read_more_text').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_select_button').removeClass('mailpoet_hidden');
}
this.changeField('readMoreType', event);
},
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_automated_latest_content_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_automated_latest_content_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_list').addClass('mailpoet_hidden');
this.model.set('titleIsLink', true); // Reset titleFormat if it was set to List when switching away from displayType=titleOnly
this.$('.mailpoet_automated_latest_content_title_as_link').addClass('mailpoet_hidden'); if (this.model.get('titleFormat') === 'ul') {
this.$('.mailpoet_automated_latest_content_title_as_links').val(['true']); this.model.set('titleFormat', 'h1');
} else { this.$('.mailpoet_automated_latest_content_title_format').val(['h1']);
this.$('.mailpoet_automated_latest_content_non_title_list_options').removeClass('mailpoet_hidden'); this.$('.mailpoet_automated_latest_content_title_as_link').removeClass('mailpoet_hidden');
this.$('.mailpoet_automated_latest_content_title_as_link').removeClass('mailpoet_hidden'); }
} }
this.changeField('titleFormat', event); this.changeField('displayType', event);
}, },
}); changeTitleFormat: function(event) {
var value = jQuery(event.target).val();
if (value == 'ul') {
this.$('.mailpoet_automated_latest_content_non_title_list_options').addClass('mailpoet_hidden');
Module.AutomatedLatestContentWidgetView = base.WidgetView.extend({ this.model.set('titleIsLink', true);
getTemplate: function() { return templates.automatedLatestContentInsertion; }, this.$('.mailpoet_automated_latest_content_title_as_link').addClass('mailpoet_hidden');
behaviors: { this.$('.mailpoet_automated_latest_content_title_as_links').val(['true']);
DraggableBehavior: { } else {
cloneOriginal: true, this.$('.mailpoet_automated_latest_content_non_title_list_options').removeClass('mailpoet_hidden');
drop: function() { this.$('.mailpoet_automated_latest_content_title_as_link').removeClass('mailpoet_hidden');
return new Module.AutomatedLatestContentBlockModel({}, { parse: true }); }
} this.changeField('titleFormat', event);
} },
}, });
});
App.on('before:start', function() { Module.AutomatedLatestContentWidgetView = base.WidgetView.extend({
App.registerBlockType('automatedLatestContent', { getTemplate: function() { return templates.automatedLatestContentInsertion; },
blockModel: Module.AutomatedLatestContentBlockModel, behaviors: {
blockView: Module.AutomatedLatestContentBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.AutomatedLatestContentBlockModel({}, { parse: true });
}
}
},
});
App.on('before:start', function() {
App.registerBlockType('automatedLatestContent', {
blockModel: Module.AutomatedLatestContentBlockModel,
blockView: Module.AutomatedLatestContentBlockView,
});
App.registerWidget({
name: 'automatedLatestContent',
widgetView: Module.AutomatedLatestContentWidgetView,
priority: 97,
});
});
});
App.registerWidget({
name: 'automatedLatestContent',
widgetView: Module.AutomatedLatestContentWidgetView,
priority: 97,
});
});
}); });

View File

@ -4,206 +4,217 @@
* a BlockModel and a BlockView. * a BlockModel and a BlockView.
* BlockToolsView, BlockSettingsView and BlockWidgetView are optional. * BlockToolsView, BlockSettingsView and BlockWidgetView are optional.
*/ */
EditorApplication.module("blocks.base", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/base', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
'modal',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var AugmentedView = Marionette.LayoutView.extend({}); EditorApplication.module("blocks.base", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.BlockModel = Backbone.SuperModel.extend({ var AugmentedView = Marionette.LayoutView.extend({});
stale: [], // Attributes to be removed upon saving
initialize: function() {
var that = this;
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. Module.BlockModel = SuperModel.extend({
// Otherwise Backbone.SuperModel interprets it not as a simpleObject stale: [], // Attributes to be removed upon saving
// and misbehaves initialize: function() {
// TODO: Investigate for a better solution var that = this;
return JSON.parse(JSON.stringify(jQuery.extend(blockDefaults, defaults || {}))); this.on('change', function() {
}, App.getChannel().trigger('autoSave');
toJSON: function() { });
// Remove stale attributes from resulting JSON object },
return _.omit(Backbone.SuperModel.prototype.toJSON.call(this), this.stale); _getDefaults: function(blockDefaults, configDefaults) {
}, var defaults = (_.isObject(configDefaults) && _.isFunction(configDefaults.toJSON)) ? configDefaults.toJSON() : configDefaults;
});
Module.BlockView = AugmentedView.extend({ // Patch the resulting JSON object and fix it's constructors to be Object.
regions: { // Otherwise SuperModel interprets it not as a simpleObject
toolsRegion: '> .mailpoet_tools', // and misbehaves
}, // TODO: Investigate for a better solution
modelEvents: { return JSON.parse(JSON.stringify(jQuery.extend(blockDefaults, defaults || {})));
'change': 'render', },
}, toJSON: function() {
events: { // Remove stale attributes from resulting JSON object
"mouseenter": "showTools", return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
"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, 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;
}
},
},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
};
},
constructor: function() {
AugmentedView.apply(this, arguments);
this.$el.addClass('mailpoet_editor_view_' + this.cid);
},
showTools: function(_event) {
if (!this.showingToolsDisabled) {
this.$('> .mailpoet_tools').show();
this.toolsView.triggerMethod('showTools');
}
},
hideTools: function(e) {
this.$('> .mailpoet_tools').hide();
this.toolsView.triggerMethod('hideTools');
},
enableShowingTools: function() {
this.showingToolsDisabled = false;
},
disableShowingTools: function() {
this.showingToolsDisabled = true;
this.hideTools();
},
/**
* Defines drop behavior of BlockView instance
*/
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
},
});
Module.BlockToolsView = AugmentedView.extend({ Module.BlockView = AugmentedView.extend({
getTemplate: function() { return templates.genericBlockTools; }, regions: {
events: { toolsRegion: '> .mailpoet_tools',
"click .mailpoet_edit_block": "changeSettings", },
"click .mailpoet_delete_block_activate": "showDeletionConfirmation", modelEvents: {
"click .mailpoet_delete_block_cancel": "hideDeletionConfirmation", 'change': 'render',
"click .mailpoet_delete_block_confirm": "deleteBlock", },
}, events: {
// Markers of whether these particular tools will be used for this instance "mouseenter": "showTools",
tools: { "mouseleave": "hideTools",
settings: true, },
delete: true, behaviors: {
move: true, DraggableBehavior: {
}, cloneOriginal: true,
getSettingsView: function() { return Module.BlockSettingsView; }, hideOriginal: true,
initialize: function(options) { onDrop: function(options) {
options = options || {}; // After a clone of model has been dropped, cleanup
if (!_.isUndefined(options.tools)) { // and destroy self
// Make a new block specific tool config object options.dragBehavior.view.model.destroy();
this.tools = jQuery.extend({}, this.tools, options.tools || {}); },
} onDragSubstituteBy: function(behavior) {
var WidgetView, 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;
}
},
},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
};
},
constructor: function() {
AugmentedView.apply(this, arguments);
this.$el.addClass('mailpoet_editor_view_' + this.cid);
},
showTools: function(_event) {
if (!this.showingToolsDisabled) {
this.$('> .mailpoet_tools').show();
this.toolsView.triggerMethod('showTools');
}
},
hideTools: function(e) {
this.$('> .mailpoet_tools').hide();
this.toolsView.triggerMethod('hideTools');
},
enableShowingTools: function() {
this.showingToolsDisabled = false;
},
disableShowingTools: function() {
this.showingToolsDisabled = true;
this.hideTools();
},
/**
* Defines drop behavior of BlockView instance
*/
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
},
});
// Automatically cancel deletion Module.BlockToolsView = AugmentedView.extend({
this.on('hideTools', this.hideDeletionConfirmation, this); getTemplate: function() { return templates.genericBlockTools; },
}, events: {
templateHelpers: function() { "click .mailpoet_edit_block": "changeSettings",
return { "click .mailpoet_delete_block_activate": "showDeletionConfirmation",
model: this.model.toJSON(), "click .mailpoet_delete_block_cancel": "hideDeletionConfirmation",
viewCid: this.cid, "click .mailpoet_delete_block_confirm": "deleteBlock",
tools: this.tools, },
}; // Markers of whether these particular tools will be used for this instance
}, tools: {
changeSettings: function() { settings: true,
var ViewType = this.getSettingsView(); delete: true,
(new ViewType({ model: this.model })).render(); move: true,
}, },
showDeletionConfirmation: function() { getSettingsView: function() { return Module.BlockSettingsView; },
this.$('.mailpoet_delete_block').addClass('mailpoet_delete_block_activated'); initialize: function(options) {
}, options = options || {};
hideDeletionConfirmation: function() { if (!_.isUndefined(options.tools)) {
this.$('.mailpoet_delete_block').removeClass('mailpoet_delete_block_activated'); // Make a new block specific tool config object
}, this.tools = jQuery.extend({}, this.tools, options.tools || {});
deleteBlock: function(event) { }
event.preventDefault();
this.model.destroy();
return false;
}
});
Module.BlockSettingsView = Marionette.LayoutView.extend({ // Automatically cancel deletion
className: 'mailpoet_editor_settings', this.on('hideTools', this.hideDeletionConfirmation, this);
initialize: function() { },
var that = this; templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
tools: this.tools,
};
},
changeSettings: function() {
var ViewType = this.getSettingsView();
(new ViewType({ model: this.model })).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.destroy();
return false;
}
});
MailPoet.Modal.panel({ Module.BlockSettingsView = Marionette.LayoutView.extend({
element: this.$el, className: 'mailpoet_editor_settings',
template: '', initialize: function() {
position: 'right', var that = this;
width: App.getConfig().get('sidepanelWidth'),
onCancel: function() { MailPoet.Modal.panel({
that.destroy(); element: this.$el,
}, template: '',
}); position: 'right',
}, width: App.getConfig().get('sidepanelWidth'),
close: function(event) { onCancel: function() {
MailPoet.Modal.cancel(); that.destroy();
this.destroy(); },
}, });
changeField: function(field, event) { },
this.model.set(field, jQuery(event.target).val()); close: function(event) {
}, MailPoet.Modal.cancel();
changePixelField: function(field, event) { this.destroy();
this.changeFieldWithSuffix(field, event, 'px'); },
}, changeField: function(field, event) {
changeFieldWithSuffix: function(field, event, suffix) { this.model.set(field, jQuery(event.target).val());
this.model.set(field, jQuery(event.target).val() + suffix); },
}, changePixelField: function(field, event) {
changeBoolField: function(field, event) { this.changeFieldWithSuffix(field, event, 'px');
this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false); },
}, changeFieldWithSuffix: function(field, event, suffix) {
changeColorField: function(field, event) { this.model.set(field, jQuery(event.target).val() + suffix);
var value = jQuery(event.target).val(); },
if (value === '') { changeBoolField: function(field, event) {
value = 'transparent'; this.model.set(field, (jQuery(event.target).val() === 'true') ? true : false);
} },
this.model.set(field, value); changeColorField: function(field, event) {
}, var value = jQuery(event.target).val();
}); if (value === '') {
value = 'transparent';
}
this.model.set(field, value);
},
});
Module.WidgetView = Marionette.ItemView.extend({
className: 'mailpoet_widget mailpoet_droppable_block mailpoet_droppable_widget',
behaviors: {
DraggableBehavior: {
drop: function() {
throw "Unsupported operation";
}
}
},
});
});
Module.WidgetView = Marionette.ItemView.extend({
className: 'mailpoet_widget mailpoet_droppable_block mailpoet_droppable_widget',
behaviors: {
DraggableBehavior: {
drop: function() {
throw "Unsupported operation";
}
}
},
});
}); });

View File

@ -1,153 +1,163 @@
/** /**
* Button content block * Button content block
*/ */
EditorApplication.module("blocks.button", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/button', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.button", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.ButtonBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'button',
text: 'Button',
url: 'http://google.com',
styles: {
block: {
backgroundColor: '#ff0000',
borderColor: '#cccccc',
borderWidth: '1px',
borderRadius: '4px',
borderStyle: 'solid',
width: '200px',
lineHeight: '40px',
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '16px',
textAlign: 'center',
},
},
}, EditorApplication.getConfig().get('blockDefaults.button'));
},
});
Module.ButtonBlockView = base.BlockView.extend({ Module.ButtonBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.buttonBlock; }, return this._getDefaults({
modelEvents: { type: 'button',
'change': 'render', text: 'Button',
}, url: 'http://google.com',
onDragSubstituteBy: function() { return Module.ButtonWidgetView; }, styles: {
initialize: function() { block: {
base.BlockView.prototype.initialize.apply(this, arguments); backgroundColor: '#ff0000',
var that = this; borderColor: '#cccccc',
borderWidth: '1px',
borderRadius: '4px',
borderStyle: 'solid',
width: '200px',
lineHeight: '40px',
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '16px',
textAlign: 'center',
},
},
}, EditorApplication.getConfig().get('blockDefaults.button'));
},
});
// Listen for attempts to change all dividers in one go Module.ButtonBlockView = base.BlockView.extend({
this._replaceButtonStylesHandler = function(data) { that.model.set(data); }; className: "mailpoet_block mailpoet_button_block mailpoet_droppable_block",
App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler); getTemplate: function() { return templates.buttonBlock; },
}, modelEvents: {
onRender: function() { 'change': 'render',
this.toolsView = new Module.ButtonBlockToolsView({ model: this.model }); },
this.toolsRegion.show(this.toolsView); onDragSubstituteBy: function() { return Module.ButtonWidgetView; },
}, initialize: function() {
}); base.BlockView.prototype.initialize.apply(this, arguments);
var that = this;
Module.ButtonBlockToolsView = base.BlockToolsView.extend({ // Listen for attempts to change all dividers in one go
getSettingsView: function() { return Module.ButtonBlockSettingsView; }, this._replaceButtonStylesHandler = function(data) { that.model.set(data); };
}); App.getChannel().on('replaceAllButtonStyles', this._replaceButtonStylesHandler);
},
onRender: function() {
this.toolsView = new Module.ButtonBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
});
Module.ButtonBlockSettingsView = base.BlockSettingsView.extend({ Module.ButtonBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.buttonBlockSettings; }, getSettingsView: function() { return Module.ButtonBlockSettingsView; },
events: function() { });
return {
"keyup .mailpoet_field_button_text": _.partial(this.changeField, "text"),
"keyup .mailpoet_field_button_url": _.partial(this.changeField, "url"),
"change .mailpoet_field_button_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"change .mailpoet_field_button_font_color": _.partial(this.changeColorField, "styles.block.fontColor"),
"change .mailpoet_field_button_font_family": _.partial(this.changeField, "styles.block.fontFamily"),
"change .mailpoet_field_button_font_size": _.partial(this.changeField, "styles.block.fontSize"),
"change .mailpoet_field_button_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_button_border_color": _.partial(this.changeColorField, "styles.block.borderColor"),
"input .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), Module.ButtonBlockSettingsView = base.BlockSettingsView.extend({
"change .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), getTemplate: function() { return templates.buttonBlockSettings; },
"change .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), events: function() {
"keyup .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), return {
"keyup .mailpoet_field_button_text": _.partial(this.changeField, "text"),
"keyup .mailpoet_field_button_url": _.partial(this.changeField, "url"),
"change .mailpoet_field_button_alignment": _.partial(this.changeField, "styles.block.textAlign"),
"change .mailpoet_field_button_font_color": _.partial(this.changeColorField, "styles.block.fontColor"),
"change .mailpoet_field_button_font_family": _.partial(this.changeField, "styles.block.fontFamily"),
"change .mailpoet_field_button_font_size": _.partial(this.changeField, "styles.block.fontSize"),
"change .mailpoet_field_button_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_button_border_color": _.partial(this.changeColorField, "styles.block.borderColor"),
"input .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)), "input .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)), "change .mailpoet_field_button_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)), "change .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"keyup .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)), "keyup .mailpoet_field_button_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"input .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)), "input .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"change .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)), "change .mailpoet_field_button_border_radius": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius_input', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"change .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)), "change .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"keyup .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)), "keyup .mailpoet_field_button_border_radius_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_border_radius', _.partial(this.changePixelField, "styles.block.borderRadius").bind(this)),
"input .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)), "input .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"change .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)), "change .mailpoet_field_button_width": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width_input', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"change .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)), "change .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"keyup .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)), "keyup .mailpoet_field_button_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_width', _.partial(this.changePixelField, "styles.block.width").bind(this)),
"click .mailpoet_field_button_replace_all_styles": "applyToAll", "input .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
"click .mailpoet_done_editing": "close", "change .mailpoet_field_button_line_height": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height_input', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
}; "change .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
}, "keyup .mailpoet_field_button_line_height_input": _.partial(this.updateValueAndCall, '.mailpoet_field_button_line_height', _.partial(this.changePixelField, "styles.block.lineHeight").bind(this)),
behaviors: {
ColorPickerBehavior: {},
},
initialize: function(params) {
var panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
renderOptions: this.renderOptions,
};
},
applyToAll: function() {
App.getChannel().trigger('replaceAllButtonStyles', _.pick(this.model.toJSON(), 'styles', 'type'));
},
updateValueAndCall: function(fieldToUpdate, callable, event) {
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
});
Module.ButtonWidgetView = base.WidgetView.extend({ "click .mailpoet_field_button_replace_all_styles": "applyToAll",
getTemplate: function() { return templates.buttonInsertion; }, "click .mailpoet_done_editing": "close",
behaviors: { };
DraggableBehavior: { },
cloneOriginal: true, behaviors: {
drop: function() { ColorPickerBehavior: {},
return new Module.ButtonBlockModel(); },
}, initialize: function(params) {
} var panelParams = {
}, element: this.$el,
}); template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
renderOptions: this.renderOptions,
};
},
applyToAll: function() {
App.getChannel().trigger('replaceAllButtonStyles', _.pick(this.model.toJSON(), 'styles', 'type'));
},
updateValueAndCall: function(fieldToUpdate, callable, event) {
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
});
App.on('before:start', function() { Module.ButtonWidgetView = base.WidgetView.extend({
App.registerBlockType('button', { getTemplate: function() { return templates.buttonInsertion; },
blockModel: Module.ButtonBlockModel, behaviors: {
blockView: Module.ButtonBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.ButtonBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('button', {
blockModel: Module.ButtonBlockModel,
blockView: Module.ButtonBlockView,
});
App.registerWidget({
name: 'button',
widgetView: Module.ButtonWidgetView,
priority: 92,
});
});
});
App.registerWidget({
name: 'button',
widgetView: Module.ButtonWidgetView,
priority: 92,
});
});
}); });

View File

@ -3,337 +3,347 @@
* This is a special kind of block, as it can contain content blocks, as well * This is a special kind of block, as it can contain content blocks, as well
* as other containers. * as other containers.
*/ */
EditorApplication.module("blocks.container", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/container', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'), EditorApplication.module("blocks.container", function(Module, App, Backbone, Marionette, $, _) {
BlockCollection; "use strict";
Module.ContainerBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base'),
defaults: function() { BlockCollection;
return this._getDefaults({
type: 'container',
orientation: 'vertical',
styles: {
block: {
backgroundColor: 'transparent',
},
},
blocks: new BlockCollection(),
}, EditorApplication.getConfig().get('blockDefaults.container'));
},
parse: function(response) {
// If container has any blocks - add them to a collection
if (response.type === 'container' && _.has(response, 'blocks')) {
response.blocks = new BlockCollection(response.blocks, {
parse: true,
});
}
return response;
},
validate: function() {
// Recursively propagate validation checks to blocks in the tree
var invalidBlock = this.get('blocks').find(function(block) { return !block.isValid(); });
if (invalidBlock) {
return invalidBlock.validationError;
}
},
});
BlockCollection = Backbone.Collection.extend({ Module.ContainerBlockModel = base.BlockModel.extend({
model: base.BlockModel, defaults: function() {
initialize: function() { return this._getDefaults({
this.on('add change remove', function() { App.getChannel().trigger('autoSave'); }); type: 'container',
}, orientation: 'vertical',
parse: function(response) { styles: {
var self = this; block: {
return _.map(response, function(block) { backgroundColor: 'transparent',
var Type = App.getBlockTypeModel(block.type); },
// TODO: If type has no registered model, use a backup one },
return new Type(block, {parse: true}); blocks: new BlockCollection(),
}); }, EditorApplication.getConfig().get('blockDefaults.container'));
}, },
}); parse: function(response) {
// If container has any blocks - add them to a collection
if (response.type === 'container' && _.has(response, 'blocks')) {
response.blocks = new BlockCollection(response.blocks, {
parse: true,
});
}
return response;
},
validate: function() {
// Recursively propagate validation checks to blocks in the tree
var invalidBlock = this.get('blocks').find(function(block) { return !block.isValid(); });
if (invalidBlock) {
return invalidBlock.validationError;
}
},
});
Module.ContainerBlockView = Marionette.CompositeView.extend({ BlockCollection = Backbone.Collection.extend({
regionClass: Marionette.Region, model: base.BlockModel,
className: 'mailpoet_block mailpoet_container_block mailpoet_droppable_block mailpoet_droppable_layout_block', initialize: function() {
getTemplate: function() { return templates.containerBlock; }, this.on('add change remove', function() { App.getChannel().trigger('autoSave'); });
childViewContainer: '> .mailpoet_container', },
getEmptyView: function() { return Module.ContainerBlockEmptyView; }, parse: function(response) {
emptyViewOptions: function() { return { renderOptions: this.renderOptions }; }, var self = this;
modelEvents: { return _.map(response, function(block) {
'change': 'render' var Type = App.getBlockTypeModel(block.type);
}, // TODO: If type has no registered model, use a backup one
events: { return new Type(block, {parse: true});
"mouseenter": "showTools", });
"mouseleave": "hideTools", },
"click .mailpoet_newsletter_layer_selector": "toggleEditingLayer", });
},
regions: {
toolsRegion: '> .mailpoet_tools',
},
ui: {
tools: '> .mailpoet_tools'
},
behaviors: {
ContainerDropZoneBehavior: {},
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, 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;
}
},
testAttachToInstance: function(model, view) {
// Attach Draggable only to layout containers and disable it
// for root and column containers.
return view.renderOptions.depth === 1;
},
},
},
onDragSubstituteBy: function() {
// For two and three column layouts display their respective widgets,
// otherwise always default to one column layout widget
if (this.renderOptions.depth === 1) {
if (this.model.get('blocks').length === 3) return Module.ThreeColumnContainerWidgetView;
if (this.model.get('blocks').length === 2) return Module.TwoColumnContainerWidgetView;
}
return Module.OneColumnContainerWidgetView;
}, Module.ContainerBlockView = Marionette.CompositeView.extend({
constructor: function() { regionClass: Marionette.Region,
// Set the block collection to be handled by this view as well className: 'mailpoet_block mailpoet_container_block mailpoet_droppable_block mailpoet_droppable_layout_block',
arguments[0].collection = arguments[0].model.get('blocks'); getTemplate: function() { return templates.containerBlock; },
Marionette.CompositeView.apply(this, arguments); childViewContainer: '> .mailpoet_container',
this.$el.addClass('mailpoet_editor_view_' + this.cid); getEmptyView: function() { return Module.ContainerBlockEmptyView; },
}, emptyViewOptions: function() { return { renderOptions: this.renderOptions }; },
initialize: function(options) { modelEvents: {
this.renderOptions = _.defaults(options.renderOptions || {}, {}); 'change': 'render'
}, },
// Determines which view type should be used for a child events: {
getChildView: function(model) { "mouseenter": "showTools",
// TODO: If type does not have a type registered, use a generic one "mouseleave": "hideTools",
return App.getBlockTypeView(model.get('type')); "click .mailpoet_newsletter_layer_selector": "toggleEditingLayer",
}, },
childViewOptions: function() { regions: {
var newRenderOptions = _.clone(this.renderOptions); toolsRegion: '> .mailpoet_tools',
if (newRenderOptions.depth !== undefined) { },
newRenderOptions.depth += 1; ui: {
} tools: '> .mailpoet_tools'
return { },
renderOptions: newRenderOptions behaviors: {
}; ContainerDropZoneBehavior: {},
}, DraggableBehavior: {
templateHelpers: function() { cloneOriginal: true,
return { hideOriginal: true,
model: this.model.toJSON(), onDrop: function(options) {
viewCid: this.cid, // After a clone of model has been dropped, cleanup
}; // and destroy self
}, options.dragBehavior.view.model.destroy();
onRender: function() { },
this._rebuildRegions(); onDragSubstituteBy: function(behavior) {
this.toolsView = new Module.ContainerBlockToolsView({ var WidgetView, node;
model: this.model, // When block is being dragged, display the widget icon instead.
tools: { // This will create an instance of block's widget view and
settings: this.renderOptions.depth > 1, // use it's rendered DOM element instead of the content block
delete: this.renderOptions.depth === 1, if (_.isFunction(behavior.view.onDragSubstituteBy)) {
move: this.renderOptions.depth === 1, WidgetView = new (behavior.view.onDragSubstituteBy())();
layerSelector: this.renderOptions.depth === 1, WidgetView.render();
}, node = WidgetView.$el.get(0).cloneNode(true);
}); WidgetView.destroy();
this.toolsRegion.show(this.toolsView); return node;
}, }
onBeforeDestroy: function() { },
this.regionManager.destroy(); testAttachToInstance: function(model, view) {
}, // Attach Draggable only to layout containers and disable it
showTools: function() { // for root and column containers.
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) { return view.renderOptions.depth === 1;
this.$(this.ui.tools).show(); },
this.toolsView.triggerMethod('showTools'); },
} },
}, onDragSubstituteBy: function() {
hideTools: function() { // For two and three column layouts display their respective widgets,
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) { // otherwise always default to one column layout widget
this.$(this.ui.tools).hide(); if (this.renderOptions.depth === 1) {
this.toolsView.triggerMethod('hideTools'); if (this.model.get('blocks').length === 3) return Module.ThreeColumnContainerWidgetView;
} if (this.model.get('blocks').length === 2) return Module.TwoColumnContainerWidgetView;
}, }
toggleEditingLayer: function(event) { return Module.OneColumnContainerWidgetView;
var that = this,
$toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector'),
$overlay = jQuery('.mailpoet_layer_overlay'),
$container = this.$('> .mailpoet_container'),
enableContainerLayer = function() {
that.$el.addClass('mailpoet_container_layer_active');
$toggleButton.addClass('mailpoet_container_layer_active');
$container.addClass('mailpoet_layer_highlight');
$overlay.click(disableContainerLayer);
$overlay.show();
},
disableContainerLayer = function() {
that.$el.removeClass('mailpoet_container_layer_active');
$toggleButton.removeClass('mailpoet_container_layer_active');
$container.removeClass('mailpoet_layer_highlight');
$overlay.hide();
$overlay.off('click');
};
if ($toggleButton.hasClass('mailpoet_container_layer_active')) {
disableContainerLayer();
} else {
enableContainerLayer();
}
event.stopPropagation();
},
_buildRegions: function(regions) {
var that = this;
var defaults = { },
regionClass: this.getOption('regionClass'), constructor: function() {
parentEl: function() { return that.$el; } // Set the block collection to be handled by this view as well
}; arguments[0].collection = arguments[0].model.get('blocks');
Marionette.CompositeView.apply(this, arguments);
this.$el.addClass('mailpoet_editor_view_' + this.cid);
},
initialize: function(options) {
this.renderOptions = _.defaults(options.renderOptions || {}, {});
},
// Determines which view type should be used for a child
getChildView: function(model) {
// TODO: If type does not have a type registered, use a generic one
return App.getBlockTypeView(model.get('type'));
},
childViewOptions: function() {
var newRenderOptions = _.clone(this.renderOptions);
if (newRenderOptions.depth !== undefined) {
newRenderOptions.depth += 1;
}
return {
renderOptions: newRenderOptions
};
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
};
},
onRender: function() {
this._rebuildRegions();
this.toolsView = new Module.ContainerBlockToolsView({
model: this.model,
tools: {
settings: this.renderOptions.depth > 1,
delete: this.renderOptions.depth === 1,
move: this.renderOptions.depth === 1,
layerSelector: this.renderOptions.depth === 1,
},
});
this.toolsRegion.show(this.toolsView);
},
onBeforeDestroy: function() {
this.regionManager.destroy();
},
showTools: function() {
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) {
this.$(this.ui.tools).show();
this.toolsView.triggerMethod('showTools');
}
},
hideTools: function() {
if (this.renderOptions.depth === 1 && !this.$el.hasClass('mailpoet_container_layer_active')) {
this.$(this.ui.tools).hide();
this.toolsView.triggerMethod('hideTools');
}
},
toggleEditingLayer: function(event) {
var that = this,
$toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector'),
$overlay = jQuery('.mailpoet_layer_overlay'),
$container = this.$('> .mailpoet_container'),
enableContainerLayer = function() {
that.$el.addClass('mailpoet_container_layer_active');
$toggleButton.addClass('mailpoet_container_layer_active');
$container.addClass('mailpoet_layer_highlight');
$overlay.click(disableContainerLayer);
$overlay.show();
},
disableContainerLayer = function() {
that.$el.removeClass('mailpoet_container_layer_active');
$toggleButton.removeClass('mailpoet_container_layer_active');
$container.removeClass('mailpoet_layer_highlight');
$overlay.hide();
$overlay.off('click');
};
if ($toggleButton.hasClass('mailpoet_container_layer_active')) {
disableContainerLayer();
} else {
enableContainerLayer();
}
event.stopPropagation();
},
_buildRegions: function(regions) {
var that = this;
return this.regionManager.addRegions(regions, defaults); var defaults = {
}, regionClass: this.getOption('regionClass'),
_rebuildRegions: function() { parentEl: function() { return that.$el; }
if (this.regionManager === undefined) { };
this.regionManager = new Backbone.Marionette.RegionManager();
}
this.regionManager.destroy();
_.extend(this, this._buildRegions(this.regions));
},
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
that.model.destroy();
return newModel;
};
},
});
Module.ContainerBlockEmptyView = Marionette.ItemView.extend({ return this.regionManager.addRegions(regions, defaults);
getTemplate: function() { return templates.containerEmpty; }, },
initialize: function(options) { _rebuildRegions: function() {
this.renderOptions = _.defaults(options.renderOptions || {}, {}); if (this.regionManager === undefined) {
}, this.regionManager = new Backbone.Marionette.RegionManager();
templateHelpers: function() { }
return { this.regionManager.destroy();
isRoot: this.renderOptions.depth === 0, _.extend(this, this._buildRegions(this.regions));
}; },
}, getDropFunc: function() {
}); var that = this;
return function() {
var newModel = that.model.clone();
that.model.destroy();
return newModel;
};
},
});
Module.ContainerBlockToolsView = base.BlockToolsView.extend({ Module.ContainerBlockEmptyView = Marionette.ItemView.extend({
getSettingsView: function() { return Module.ContainerBlockSettingsView; }, getTemplate: function() { return templates.containerEmpty; },
}); initialize: function(options) {
this.renderOptions = _.defaults(options.renderOptions || {}, {});
},
templateHelpers: function() {
return {
isRoot: this.renderOptions.depth === 0,
};
},
});
Module.ContainerBlockSettingsView = base.BlockSettingsView.extend({ Module.ContainerBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.containerBlockSettings; }, getSettingsView: function() { return Module.ContainerBlockSettingsView; },
events: function() { });
return {
"change .mailpoet_field_container_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
});
Module.OneColumnContainerWidgetView = base.WidgetView.extend({ Module.ContainerBlockSettingsView = base.BlockSettingsView.extend({
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block', getTemplate: function() { return templates.containerBlockSettings; },
getTemplate: function() { return templates.oneColumnLayoutInsertion; }, events: function() {
behaviors: { return {
DraggableBehavior: { "change .mailpoet_field_container_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
cloneOriginal: true, "click .mailpoet_done_editing": "close",
drop: function() { };
return new Module.ContainerBlockModel({ },
orientation: 'horizontal', behaviors: {
blocks: [ ColorPickerBehavior: {},
new Module.ContainerBlockModel(), },
] });
});
}
}
},
});
Module.TwoColumnContainerWidgetView = base.WidgetView.extend({ Module.OneColumnContainerWidgetView = base.WidgetView.extend({
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block', className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
getTemplate: function() { return templates.twoColumnLayoutInsertion; }, getTemplate: function() { return templates.oneColumnLayoutInsertion; },
behaviors: { behaviors: {
DraggableBehavior: { DraggableBehavior: {
cloneOriginal: true, cloneOriginal: true,
drop: function() { drop: function() {
return new Module.ContainerBlockModel({ return new Module.ContainerBlockModel({
orientation: 'horizontal', orientation: 'horizontal',
blocks: [ blocks: [
new Module.ContainerBlockModel(), new Module.ContainerBlockModel(),
new Module.ContainerBlockModel(), ]
] });
}); }
} }
} },
}, });
});
Module.ThreeColumnContainerWidgetView = base.WidgetView.extend({ Module.TwoColumnContainerWidgetView = base.WidgetView.extend({
className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block', className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
getTemplate: function() { return templates.threeColumnLayoutInsertion; }, getTemplate: function() { return templates.twoColumnLayoutInsertion; },
behaviors: { behaviors: {
DraggableBehavior: { DraggableBehavior: {
cloneOriginal: true, cloneOriginal: true,
drop: function() { drop: function() {
return new Module.ContainerBlockModel({ return new Module.ContainerBlockModel({
orientation: 'horizontal', orientation: 'horizontal',
blocks: [ blocks: [
new Module.ContainerBlockModel(), new Module.ContainerBlockModel(),
new Module.ContainerBlockModel(), new Module.ContainerBlockModel(),
new Module.ContainerBlockModel(), ]
] });
}); }
} }
} },
}, });
});
App.on('before:start', function() { Module.ThreeColumnContainerWidgetView = base.WidgetView.extend({
App.registerBlockType('container', { className: base.WidgetView.prototype.className + ' mailpoet_droppable_layout_block',
blockModel: Module.ContainerBlockModel, getTemplate: function() { return templates.threeColumnLayoutInsertion; },
blockView: Module.ContainerBlockView, behaviors: {
}); DraggableBehavior: {
cloneOriginal: true,
drop: function() {
return new Module.ContainerBlockModel({
orientation: 'horizontal',
blocks: [
new Module.ContainerBlockModel(),
new Module.ContainerBlockModel(),
new Module.ContainerBlockModel(),
]
});
}
}
},
});
App.registerLayoutWidget({ App.on('before:start', function() {
name: 'oneColumnLayout', App.registerBlockType('container', {
priority: 100, blockModel: Module.ContainerBlockModel,
widgetView: Module.OneColumnContainerWidgetView, blockView: Module.ContainerBlockView,
}); });
App.registerLayoutWidget({ App.registerLayoutWidget({
name: 'twoColumnLayout', name: 'oneColumnLayout',
priority: 100, priority: 100,
widgetView: Module.TwoColumnContainerWidgetView, widgetView: Module.OneColumnContainerWidgetView,
}); });
App.registerLayoutWidget({
name: 'twoColumnLayout',
priority: 100,
widgetView: Module.TwoColumnContainerWidgetView,
});
App.registerLayoutWidget({
name: 'threeColumnLayout',
priority: 100,
widgetView: Module.ThreeColumnContainerWidgetView,
});
});
});
App.registerLayoutWidget({
name: 'threeColumnLayout',
priority: 100,
widgetView: Module.ThreeColumnContainerWidgetView,
});
});
}); });

View File

@ -1,164 +1,174 @@
/** /**
* Divider content block * Divider content block
*/ */
EditorApplication.module("blocks.divider", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/divider', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.divider", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.DividerBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'divider',
styles: {
block: {
backgroundColor: 'transparent',
padding: '12px',
borderStyle: 'solid',
borderWidth: '1px',
borderColor: '#000000',
},
},
}, EditorApplication.getConfig().get('blockDefaults.divider'));
},
});
Module.DividerBlockView = base.BlockView.extend({ Module.DividerBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_divider_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.dividerBlock; }, return this._getDefaults({
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), type: 'divider',
behaviors: _.defaults({ styles: {
ResizableBehavior: { block: {
elementSelector: '.mailpoet_content', backgroundColor: 'transparent',
resizeHandleSelector: '.mailpoet_resize_handle', padding: '12px',
transformationFunction: function(y) { return y / 2; }, borderStyle: 'solid',
minLength: 0, // TODO: Move this number to editor configuration borderWidth: '1px',
modelField: 'styles.block.padding', borderColor: '#000000',
}, },
}, base.BlockView.prototype.behaviors), },
onDragSubstituteBy: function() { return Module.DividerWidgetView; }, }, EditorApplication.getConfig().get('blockDefaults.divider'));
initialize: function() { },
base.BlockView.prototype.initialize.apply(this, arguments); });
var that = this;
// Listen for attempts to change all dividers in one go Module.DividerBlockView = base.BlockView.extend({
this._replaceDividerHandler = function(data) { that.model.set(data); that.model.trigger('applyToAll'); }; className: "mailpoet_block mailpoet_divider_block mailpoet_droppable_block",
App.getChannel().on('replaceAllDividers', this._replaceDividerHandler); getTemplate: function() { return templates.dividerBlock; },
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'),
behaviors: _.defaults({
ResizableBehavior: {
elementSelector: '.mailpoet_content',
resizeHandleSelector: '.mailpoet_resize_handle',
transformationFunction: function(y) { return y / 2; },
minLength: 0, // TODO: Move this number to editor configuration
modelField: 'styles.block.padding',
},
}, base.BlockView.prototype.behaviors),
onDragSubstituteBy: function() { return Module.DividerWidgetView; },
initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments);
var that = this;
this.listenTo(this.model, 'change:src change:styles.block.backgroundColor change:styles.block.borderStyle change:styles.block.borderWidth change:styles.block.borderColor applyToAll', this.render); // Listen for attempts to change all dividers in one go
this.listenTo(this.model, 'change:styles.block.padding', this.changePadding); this._replaceDividerHandler = function(data) { that.model.set(data); that.model.trigger('applyToAll'); };
}, App.getChannel().on('replaceAllDividers', this._replaceDividerHandler);
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px',
};
},
onRender: function() {
this.toolsView = new Module.DividerBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onBeforeDestroy: function() {
App.getChannel().off('replaceAllDividers', this._replaceDividerHandler);
this.stopListening(this.model);
},
changePadding: function() {
this.$('.mailpoet_content').css('padding-top', this.model.get('styles.block.padding'));
this.$('.mailpoet_content').css('padding-bottom', this.model.get('styles.block.padding'));
this.$('.mailpoet_resize_handle_text').text(parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px');
},
});
Module.DividerBlockToolsView = base.BlockToolsView.extend({ this.listenTo(this.model, 'change:src change:styles.block.backgroundColor change:styles.block.borderStyle change:styles.block.borderWidth change:styles.block.borderColor applyToAll', this.render);
getSettingsView: function() { return Module.DividerBlockSettingsView; }, this.listenTo(this.model, 'change:styles.block.padding', this.changePadding);
}); },
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
totalHeight: parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px',
};
},
onRender: function() {
this.toolsView = new Module.DividerBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onBeforeDestroy: function() {
App.getChannel().off('replaceAllDividers', this._replaceDividerHandler);
this.stopListening(this.model);
},
changePadding: function() {
this.$('.mailpoet_content').css('padding-top', this.model.get('styles.block.padding'));
this.$('.mailpoet_content').css('padding-bottom', this.model.get('styles.block.padding'));
this.$('.mailpoet_resize_handle_text').text(parseInt(this.model.get('styles.block.padding'), 10)*2 + parseInt(this.model.get('styles.block.borderWidth')) + 'px');
},
});
Module.DividerBlockSettingsView = base.BlockSettingsView.extend({ Module.DividerBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.dividerBlockSettings; }, getSettingsView: function() { return Module.DividerBlockSettingsView; },
events: function() { });
return {
"click .mailpoet_field_divider_style": 'changeStyle',
"input .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), Module.DividerBlockSettingsView = base.BlockSettingsView.extend({
"change .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), getTemplate: function() { return templates.dividerBlockSettings; },
"change .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), events: function() {
"keyup .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)), return {
"click .mailpoet_field_divider_style": 'changeStyle',
"change .mailpoet_field_divider_border_color": _.partial(this.changeColorField, "styles.block.borderColor"), "input .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"change .mailpoet_field_divider_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"), "change .mailpoet_field_divider_border_width": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width_input', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"click .mailpoet_button_divider_apply_to_all": "applyToAll", "change .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
"click .mailpoet_done_editing": "close", "keyup .mailpoet_field_divider_border_width_input": _.partial(this.updateValueAndCall, '.mailpoet_field_divider_border_width', _.partial(this.changePixelField, "styles.block.borderWidth").bind(this)),
};
},
modelEvents: function() {
return {
'change:styles.block.borderColor': 'repaintDividerStyleOptions',
};
},
behaviors: {
ColorPickerBehavior: {},
},
initialize: function(params) {
var panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
renderOptions: this.renderOptions,
};
},
changeStyle: function(event) {
var style = jQuery(event.currentTarget).data('style');
this.model.set('styles.block.borderStyle', style);
this.$('.mailpoet_field_divider_style').removeClass('mailpoet_active_divider_style');
this.$('.mailpoet_field_divider_style[data-style="' + style + '"]').addClass('mailpoet_active_divider_style');
},
repaintDividerStyleOptions: function() {
this.$('.mailpoet_field_divider_style > div').css('border-top-color', this.model.get('styles.block.borderColor'));
},
applyToAll: function(event) {
App.getChannel().trigger('replaceAllDividers', this.model.toJSON());
},
updateValueAndCall: function(fieldToUpdate, callable, event) {
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
});
Module.DividerWidgetView = base.WidgetView.extend({ "change .mailpoet_field_divider_border_color": _.partial(this.changeColorField, "styles.block.borderColor"),
getTemplate: function() { return templates.dividerInsertion; }, "change .mailpoet_field_divider_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
behaviors: { "click .mailpoet_button_divider_apply_to_all": "applyToAll",
DraggableBehavior: { "click .mailpoet_done_editing": "close",
cloneOriginal: true, };
drop: function() { },
return new Module.DividerBlockModel(); modelEvents: function() {
}, return {
} 'change:styles.block.borderColor': 'repaintDividerStyleOptions',
}, };
}); },
App.on('before:start', function() { behaviors: {
App.registerBlockType('divider', { ColorPickerBehavior: {},
blockModel: Module.DividerBlockModel, },
blockView: Module.DividerBlockView, initialize: function(params) {
}); var panelParams = {
element: this.$el,
template: '',
position: 'right',
width: App.getConfig().get('sidepanelWidth'),
};
this.renderOptions = params.renderOptions || {};
if (this.renderOptions.displayFormat === 'subpanel') {
MailPoet.Modal.subpanel(panelParams);
} else {
MailPoet.Modal.panel(panelParams);
}
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
renderOptions: this.renderOptions,
};
},
changeStyle: function(event) {
var style = jQuery(event.currentTarget).data('style');
this.model.set('styles.block.borderStyle', style);
this.$('.mailpoet_field_divider_style').removeClass('mailpoet_active_divider_style');
this.$('.mailpoet_field_divider_style[data-style="' + style + '"]').addClass('mailpoet_active_divider_style');
},
repaintDividerStyleOptions: function() {
this.$('.mailpoet_field_divider_style > div').css('border-top-color', this.model.get('styles.block.borderColor'));
},
applyToAll: function(event) {
App.getChannel().trigger('replaceAllDividers', this.model.toJSON());
},
updateValueAndCall: function(fieldToUpdate, callable, event) {
this.$(fieldToUpdate).val(jQuery(event.target).val());
callable(event);
},
});
Module.DividerWidgetView = base.WidgetView.extend({
getTemplate: function() { return templates.dividerInsertion; },
behaviors: {
DraggableBehavior: {
cloneOriginal: true,
drop: function() {
return new Module.DividerBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('divider', {
blockModel: Module.DividerBlockModel,
blockView: Module.DividerBlockView,
});
App.registerWidget({
name: 'divider',
widgetView: Module.DividerWidgetView,
priority: 93,
});
});
});
App.registerWidget({
name: 'divider',
widgetView: Module.DividerWidgetView,
priority: 93,
});
});
}); });

View File

@ -1,138 +1,148 @@
/** /**
* Footer content block * Footer content block
*/ */
EditorApplication.module("blocks.footer", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/footer', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.footer", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.FooterBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'footer',
text: '<a href="[unsubscribeUrl]">Unsubscribe</a> | <a href="[manageSubscriptionUrl]">Manage subscription</a><br /><b>Add your postal address here!</b>',
styles: {
block: {
backgroundColor: 'transparent',
},
text: {
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '12px',
textAlign: 'center',
},
link: {
fontColor: '#0000ff',
textDecoration: 'none',
},
},
}, EditorApplication.getConfig().get('blockDefaults.footer'));
},
});
Module.FooterBlockView = base.BlockView.extend({ Module.FooterBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.footerBlock; }, return this._getDefaults({
modelEvents: { type: 'footer',
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render', text: '<a href="[unsubscribeUrl]">Unsubscribe</a> | <a href="[manageSubscriptionUrl]">Manage subscription</a><br /><b>Add your postal address here!</b>',
}, styles: {
onDragSubstituteBy: function() { return Module.FooterWidgetView; }, block: {
onRender: function() { backgroundColor: 'transparent',
this.toolsView = new Module.FooterBlockToolsView({ model: this.model }); },
this.toolsRegion.show(this.toolsView); text: {
}, fontColor: '#000000',
onDomRefresh: function() { fontFamily: 'Arial',
this.attachTextEditor(); fontSize: '12px',
}, textAlign: 'center',
attachTextEditor: function() { },
var that = this; link: {
this.$('.mailpoet_content').tinymce({ fontColor: '#0000ff',
inline: true, textDecoration: 'none',
},
},
}, EditorApplication.getConfig().get('blockDefaults.footer'));
},
});
menubar: false, Module.FooterBlockView = base.BlockView.extend({
toolbar: "bold italic link unlink forecolor mailpoet_custom_fields", className: "mailpoet_block mailpoet_footer_block mailpoet_droppable_block",
getTemplate: function() { return templates.footerBlock; },
modelEvents: {
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
},
onDragSubstituteBy: function() { return Module.FooterWidgetView; },
onRender: function() {
this.toolsView = new Module.FooterBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onDomRefresh: function() {
this.attachTextEditor();
},
attachTextEditor: function() {
var that = this;
this.$('.mailpoet_content').tinymce({
inline: true,
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br", menubar: false,
invalid_elements: "script", toolbar: "bold italic link unlink forecolor mailpoet_custom_fields",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
plugins: "wplink textcolor mailpoet_custom_fields", valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br",
invalid_elements: "script",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
setup: function(editor) { plugins: "wplink textcolor mailpoet_custom_fields",
editor.on('change', function(e) {
that.model.set('text', editor.getContent());
});
editor.on('focus', function(e) { setup: function(editor) {
that.disableShowingTools(); editor.on('change', function(e) {
}); that.model.set('text', editor.getContent());
});
editor.on('blur', function(e) { editor.on('focus', function(e) {
that.enableShowingTools(); that.disableShowingTools();
}); });
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(), editor.on('blur', function(e) {
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'), that.enableShowingTools();
}); });
}, },
});
Module.FooterBlockToolsView = base.BlockToolsView.extend({ mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
getSettingsView: function() { return Module.FooterBlockSettingsView; }, mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
}); });
},
});
Module.FooterBlockSettingsView = base.BlockSettingsView.extend({ Module.FooterBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.footerBlockSettings; }, getSettingsView: function() { return Module.FooterBlockSettingsView; },
events: function() { });
return {
"change .mailpoet_field_footer_text_color": _.partial(this.changeColorField, "styles.text.fontColor"),
"change .mailpoet_field_footer_text_font_family": _.partial(this.changeField, "styles.text.fontFamily"),
"change .mailpoet_field_footer_text_size": _.partial(this.changeField, "styles.text.fontSize"),
"change #mailpoet_field_footer_link_color": _.partial(this.changeColorField, "styles.link.fontColor"),
"change #mailpoet_field_footer_link_underline": function(event) {
this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
},
"change .mailpoet_field_footer_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_footer_alignment": _.partial(this.changeField, "styles.text.textAlign"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
};
},
});
Module.FooterWidgetView = base.WidgetView.extend({ Module.FooterBlockSettingsView = base.BlockSettingsView.extend({
getTemplate: function() { return templates.footerInsertion; }, getTemplate: function() { return templates.footerBlockSettings; },
behaviors: { events: function() {
DraggableBehavior: { return {
cloneOriginal: true, "change .mailpoet_field_footer_text_color": _.partial(this.changeColorField, "styles.text.fontColor"),
drop: function() { "change .mailpoet_field_footer_text_font_family": _.partial(this.changeField, "styles.text.fontFamily"),
return new Module.FooterBlockModel(); "change .mailpoet_field_footer_text_size": _.partial(this.changeField, "styles.text.fontSize"),
}, "change #mailpoet_field_footer_link_color": _.partial(this.changeColorField, "styles.link.fontColor"),
} "change #mailpoet_field_footer_link_underline": function(event) {
}, this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
}); },
"change .mailpoet_field_footer_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_footer_alignment": _.partial(this.changeField, "styles.text.textAlign"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
};
},
});
App.on('before:start', function() { Module.FooterWidgetView = base.WidgetView.extend({
App.registerBlockType('footer', { getTemplate: function() { return templates.footerInsertion; },
blockModel: Module.FooterBlockModel, behaviors: {
blockView: Module.FooterBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.FooterBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('footer', {
blockModel: Module.FooterBlockModel,
blockView: Module.FooterBlockView,
});
App.registerWidget({
name: 'footer',
widgetView: Module.FooterWidgetView,
priority: 99,
});
});
});
App.registerWidget({
name: 'footer',
widgetView: Module.FooterWidgetView,
priority: 99,
});
});
}); });

View File

@ -1,138 +1,148 @@
/** /**
* Header content block * Header content block
*/ */
EditorApplication.module("blocks.header", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/header', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.header", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.HeaderBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'header',
text: 'Display problems? <a href="[viewInBrowserUrl]">View it in your browser</a>',
styles: {
block: {
backgroundColor: 'transparent',
},
text: {
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '12px',
textAlign: 'center',
},
link: {
fontColor: '#0000ff',
textDecoration: 'underline',
},
},
}, EditorApplication.getConfig().get('blockDefaults.header'));
},
});
Module.HeaderBlockView = base.BlockView.extend({ Module.HeaderBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.headerBlock; }, return this._getDefaults({
modelEvents: { type: 'header',
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render', text: 'Display problems? <a href="[viewInBrowserUrl]">View it in your browser</a>',
}, styles: {
onDragSubstituteBy: function() { return Module.HeaderWidgetView; }, block: {
onRender: function() { backgroundColor: 'transparent',
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model }); },
this.toolsRegion.show(this.toolsView); text: {
}, fontColor: '#000000',
onDomRefresh: function() { fontFamily: 'Arial',
this.attachTextEditor(); fontSize: '12px',
}, textAlign: 'center',
attachTextEditor: function() { },
var that = this; link: {
this.$('.mailpoet_content').tinymce({ fontColor: '#0000ff',
inline: true, textDecoration: 'underline',
},
},
}, EditorApplication.getConfig().get('blockDefaults.header'));
},
});
menubar: false, Module.HeaderBlockView = base.BlockView.extend({
toolbar: "bold italic link unlink forecolor mailpoet_custom_fields", className: "mailpoet_block mailpoet_header_block mailpoet_droppable_block",
getTemplate: function() { return templates.headerBlock; },
modelEvents: {
'change:styles.block.backgroundColor change:styles.text.fontColor change:styles.text.fontFamily change:styles.text.fontSize change:styles.text.textAlign change:styles.link.fontColor change:styles.link.textDecoration': 'render',
},
onDragSubstituteBy: function() { return Module.HeaderWidgetView; },
onRender: function() {
this.toolsView = new Module.HeaderBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onDomRefresh: function() {
this.attachTextEditor();
},
attachTextEditor: function() {
var that = this;
this.$('.mailpoet_content').tinymce({
inline: true,
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br", menubar: false,
invalid_elements: "script", toolbar: "bold italic link unlink forecolor mailpoet_custom_fields",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
plugins: "wplink textcolor mailpoet_custom_fields", valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],strong[class|style],em[class|style],strike,br",
invalid_elements: "script",
style_formats: [
{title: 'Paragraph', block: 'p'},
],
setup: function(editor) { plugins: "wplink textcolor mailpoet_custom_fields",
editor.on('change', function(e) {
that.model.set('text', editor.getContent());
});
editor.on('focus', function(e) { setup: function(editor) {
that.disableShowingTools(); editor.on('change', function(e) {
}); that.model.set('text', editor.getContent());
});
editor.on('blur', function(e) { editor.on('focus', function(e) {
that.enableShowingTools(); that.disableShowingTools();
}); });
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(), editor.on('blur', function(e) {
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'), that.enableShowingTools();
}); });
}, },
});
Module.HeaderBlockToolsView = base.BlockToolsView.extend({ mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
getSettingsView: function() { return Module.HeaderBlockSettingsView; }, mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
}); });
},
});
Module.HeaderBlockSettingsView = base.BlockSettingsView.extend({ Module.HeaderBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.headerBlockSettings; }, getSettingsView: function() { return Module.HeaderBlockSettingsView; },
events: function() { });
return {
"change .mailpoet_field_header_text_color": _.partial(this.changeColorField, "styles.text.fontColor"),
"change .mailpoet_field_header_text_font_family": _.partial(this.changeField, "styles.text.fontFamily"),
"change .mailpoet_field_header_text_size": _.partial(this.changeField, "styles.text.fontSize"),
"change #mailpoet_field_header_link_color": _.partial(this.changeColorField, "styles.link.fontColor"),
"change #mailpoet_field_header_link_underline": function(event) {
this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
},
"change .mailpoet_field_header_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_header_alignment": _.partial(this.changeField, "styles.text.textAlign"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
};
},
});
Module.HeaderWidgetView = base.WidgetView.extend({ Module.HeaderBlockSettingsView = base.BlockSettingsView.extend({
getTemplate: function() { return templates.headerInsertion; }, getTemplate: function() { return templates.headerBlockSettings; },
behaviors: { events: function() {
DraggableBehavior: { return {
cloneOriginal: true, "change .mailpoet_field_header_text_color": _.partial(this.changeColorField, "styles.text.fontColor"),
drop: function() { "change .mailpoet_field_header_text_font_family": _.partial(this.changeField, "styles.text.fontFamily"),
return new Module.HeaderBlockModel(); "change .mailpoet_field_header_text_size": _.partial(this.changeField, "styles.text.fontSize"),
}, "change #mailpoet_field_header_link_color": _.partial(this.changeColorField, "styles.link.fontColor"),
} "change #mailpoet_field_header_link_underline": function(event) {
}, this.model.set('styles.link.textDecoration', (event.target.checked) ? event.target.value : 'none');
}); },
"change .mailpoet_field_header_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"change .mailpoet_field_header_alignment": _.partial(this.changeField, "styles.text.textAlign"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: App.getAvailableStyles().toJSON(),
};
},
});
App.on('before:start', function() { Module.HeaderWidgetView = base.WidgetView.extend({
App.registerBlockType('header', { getTemplate: function() { return templates.headerInsertion; },
blockModel: Module.HeaderBlockModel, behaviors: {
blockView: Module.HeaderBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.HeaderBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('header', {
blockModel: Module.HeaderBlockModel,
blockView: Module.HeaderBlockView,
});
App.registerWidget({
name: 'header',
widgetView: Module.HeaderWidgetView,
priority: 98,
});
});
});
App.registerWidget({
name: 'header',
widgetView: Module.HeaderWidgetView,
priority: 98,
});
});
}); });

View File

@ -1,380 +1,390 @@
/** /**
* Image content block * Image content block
*/ */
EditorApplication.module("blocks.image", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/image', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'), EditorApplication.module("blocks.image", function(Module, App, Backbone, Marionette, $, _) {
ImageWidgetView; "use strict";
Module.ImageBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base'),
defaults: function() { ImageWidgetView;
return this._getDefaults({
type: 'image',
link: 'http://example.org',
src: 'no-image.png',
alt: 'An image of...',
padded: true, // true | false - Padded or full width
width: '64px',
height: '64px',
styles: {
block: {
textAlign: 'center',
},
},
}, EditorApplication.getConfig().get('blockDefaults.image'));
},
});
Module.ImageBlockView = base.BlockView.extend({ Module.ImageBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_image_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.imageBlock; }, return this._getDefaults({
initialize: function() { type: 'image',
this.on('showSettings', this.showSettings); link: 'http://example.org',
}, src: 'no-image.png',
onDragSubstituteBy: function() { return Module.ImageWidgetView; }, alt: 'An image of...',
templateHelpers: function() { padded: true, // true | false - Padded or full width
return { width: '64px',
model: this.model.toJSON(), height: '64px',
viewCid: this.cid, styles: {
imageMissingSrc: App.getConfig().get('urls.imageMissing'), block: {
}; textAlign: 'center',
}, },
onRender: function() { },
this.toolsView = new Module.ImageBlockToolsView({ model: this.model }); }, EditorApplication.getConfig().get('blockDefaults.image'));
this.toolsRegion.show(this.toolsView); },
});
if (this.model.get('padded')) { Module.ImageBlockView = base.BlockView.extend({
this.$el.removeClass('mailpoet_full_image'); className: "mailpoet_block mailpoet_image_block mailpoet_droppable_block",
} else { getTemplate: function() { return templates.imageBlock; },
this.$el.addClass('mailpoet_full_image'); initialize: function() {
} this.on('showSettings', this.showSettings);
}, },
showSettings: function(options) { onDragSubstituteBy: function() { return Module.ImageWidgetView; },
this.toolsView.triggerMethod('showSettings', options); templateHelpers: function() {
}, return {
onBeforeDestroy: function() { model: this.model.toJSON(),
this.off('showSettings'); viewCid: this.cid,
}, imageMissingSrc: App.getConfig().get('urls.imageMissing'),
}); };
},
onRender: function() {
this.toolsView = new Module.ImageBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
Module.ImageBlockToolsView = base.BlockToolsView.extend({ if (this.model.get('padded')) {
getSettingsView: function() { return Module.ImageBlockSettingsView; }, this.$el.removeClass('mailpoet_full_image');
initialize: function() { } else {
base.BlockToolsView.prototype.initialize.apply(this, arguments); this.$el.addClass('mailpoet_full_image');
this.on('showSettings', this.changeSettings); }
}, },
changeSettings: function(options) { showSettings: function(options) {
(new Module.ImageBlockSettingsView({ this.toolsView.triggerMethod('showSettings', options);
model: this.model, },
showImageManager: (options.showImageManager === true), onBeforeDestroy: function() {
})).render(); this.off('showSettings');
}, },
onBeforeDestroy: function() { });
this.off('showSettings');
},
});
Module.ImageBlockSettingsView = base.BlockSettingsView.extend({ Module.ImageBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.imageBlockSettings; }, getSettingsView: function() { return Module.ImageBlockSettingsView; },
events: function() { initialize: function() {
return { base.BlockToolsView.prototype.initialize.apply(this, arguments);
"keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"), this.on('showSettings', this.changeSettings);
"keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"), },
"keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"), changeSettings: function(options) {
"change .mailpoet_field_image_padded": _.partial(this.changeBoolField, "padded"), (new Module.ImageBlockSettingsView({
"change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"), model: this.model,
"click .mailpoet_field_image_select_another_image": "showMediaManager", showImageManager: (options.showImageManager === true),
"click .mailpoet_done_editing": "close", })).render();
}; },
}, onBeforeDestroy: function() {
initialize: function(options) { this.off('showSettings');
base.BlockSettingsView.prototype.initialize.apply(this, arguments); },
});
if (options.showImageManager) { Module.ImageBlockSettingsView = base.BlockSettingsView.extend({
this.showMediaManager(); getTemplate: function() { return templates.imageBlockSettings; },
} events: function() {
}, return {
showMediaManager: function() { "keyup .mailpoet_field_image_link": _.partial(this.changeField, "link"),
if (this._mediaManager) { "keyup .mailpoet_field_image_address": _.partial(this.changeField, "src"),
this._mediaManager.resetSelections(); "keyup .mailpoet_field_image_alt_text": _.partial(this.changeField, "alt"),
this._mediaManager.open(); "change .mailpoet_field_image_padded": _.partial(this.changeBoolField, "padded"),
return; "change .mailpoet_field_image_alignment": _.partial(this.changeField, "styles.block.textAlign"),
} "click .mailpoet_field_image_select_another_image": "showMediaManager",
"click .mailpoet_done_editing": "close",
};
},
initialize: function(options) {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
var MediaManager = wp.media.view.MediaFrame.Select.extend({ if (options.showImageManager) {
this.showMediaManager();
}
},
showMediaManager: function() {
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
initialize: function() { var MediaManager = wp.media.view.MediaFrame.Select.extend({
wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
_.defaults(this.options, { initialize: function() {
multiple: true, wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
editing: false,
state: 'insert'
});
this.createSelection(); _.defaults(this.options, {
this.createStates(); multiple: true,
this.bindHandlers(); editing: false,
this.createIframeStates(); state: 'insert'
});
// Hide title this.createSelection();
this.$el.addClass('hide-title'); this.createStates();
}, this.bindHandlers();
this.createIframeStates();
resetSelections: function() { // Hide title
this.state().get('selection').reset(); this.$el.addClass('hide-title');
}, },
createQuery: function(options) { resetSelections: function() {
var query = wp.media.query(options); this.state().get('selection').reset();
return query; },
},
createStates: function() { createQuery: function(options) {
var options = this.options; var query = wp.media.query(options);
return query;
},
// Add the default states. createStates: function() {
this.states.add([ var options = this.options;
// Main states.
new 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, // Add the default states.
// can they still edit it locally? this.states.add([
allowLocalEdits: false, // Main states.
new 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,
// Show the attachment display settings. // If the user isn't allowed to edit fields,
displaySettings: false, // can they still edit it locally?
// Update user settings when users adjust the allowLocalEdits: false,
// attachment display settings.
displayUserSettings: false
}),
]);
if(wp.media.view.settings.post.featuredImageId) { // Show the attachment display settings.
this.states.add(new wp.media.controller.FeaturedImage()); displaySettings: false,
} // Update user settings when users adjust the
}, // attachment display settings.
displayUserSettings: false
}),
]);
bindHandlers: function() { if(wp.media.view.settings.post.featuredImageId) {
// from Select this.states.add(new wp.media.controller.FeaturedImage());
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); bindHandlers: function() {
this.on('toolbar:create:main-insert', this.createToolbar, this); // from Select
this.on('toolbar:create:main-gallery', this.createToolbar, this); this.on('router:create:browse', this.createRouter, this);
this.on('toolbar:create:main-embed', this.mainEmbedToolbar, 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('updateExcluded', this.browseContent, 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);
var handlers = { this.on('updateExcluded', this.browseContent, this);
content: {
'embed': 'embedContent',
'edit-selection': 'editSelectionContent'
},
toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
_.each(handlers, function(regionHandlers, region) { var handlers = {
_.each(regionHandlers, function(callback, handler) { content: {
this.on(region + ':render:' + handler, this[callback], this); 'embed': 'embedContent',
}, this); 'edit-selection': 'editSelectionContent'
}, this); },
}, toolbar: {
'main-insert': 'mainInsertToolbar'
}
};
uploadContent: function() { _.each(handlers, function(regionHandlers, region) {
wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments); _.each(regionHandlers, function(callback, handler) {
this.$el.addClass('hide-toolbar'); this.on(region + ':render:' + handler, this[callback], this);
}, }, this);
}, this);
},
// Content uploadContent: function() {
embedContent: function() { wp.media.view.MediaFrame.Select.prototype.uploadContent.apply(this, arguments);
var view = new wp.media.view.Embed({ this.$el.addClass('hide-toolbar');
controller: this, },
model: this.state()
}).render();
this.content.set(view); // Content
view.url.focus(); embedContent: function() {
}, var view = new wp.media.view.Embed({
controller: this,
model: this.state()
}).render();
editSelectionContent: function() { this.content.set(view);
var state = this.state(), view.url.focus();
selection = state.get('selection'), },
view;
view = new wp.media.view.AttachmentsBrowser({ editSelectionContent: function() {
controller: this, var state = this.state(),
collection: selection, selection = state.get('selection'),
selection: selection, view;
model: state,
sortable: true,
search: false,
dragInfo: true,
AttachmentView: wp.media.view.Attachment.EditSelection view = new wp.media.view.AttachmentsBrowser({
}).render(); controller: this,
collection: selection,
selection: selection,
model: state,
sortable: true,
search: false,
dragInfo: true,
view.toolbar.set('backToLibrary', { AttachmentView: wp.media.view.Attachment.EditSelection
text: 'Return to library', }).render();
priority: -100,
click: function() { view.toolbar.set('backToLibrary', {
this.controller.content.mode('browse'); text: 'Return to library',
} priority: -100,
});
// Browse our library of attachments. click: function() {
this.content.set(view); this.controller.content.mode('browse');
}, }
});
// Toolbars // Browse our library of attachments.
selectionStatusToolbar: function(view) { this.content.set(view);
var editable = this.state().get('editable'); },
view.set('selection', new wp.media.view.Selection({ // Toolbars
controller: this, selectionStatusToolbar: function(view) {
collection: this.state().get('selection'), var editable = this.state().get('editable');
priority: -40,
// If the selection is editable, pass the callback to view.set('selection', new wp.media.view.Selection({
// switch the content mode. controller: this,
editable: editable && function() { collection: this.state().get('selection'),
this.controller.content.mode('edit-selection'); priority: -40,
}
}).render() );
},
mainInsertToolbar: function(view) { // If the selection is editable, pass the callback to
var controller = this; // switch the content mode.
editable: editable && function() {
this.controller.content.mode('edit-selection');
}
}).render() );
},
this.selectionStatusToolbar(view); mainInsertToolbar: function(view) {
var controller = this;
view.set('insert', { this.selectionStatusToolbar(view);
style: 'primary',
priority: 80,
text: 'Select Image',
requires: { selection: true },
click: function() { view.set('insert', {
var state = controller.state(), style: 'primary',
selection = state.get('selection'); priority: 80,
text: 'Select Image',
requires: { selection: true },
controller.close(); click: function() {
state.trigger('insert', selection).reset(); var state = controller.state(),
} selection = state.get('selection');
});
},
mainEmbedToolbar: function(toolbar) { controller.close();
toolbar.view = new wp.media.view.Toolbar.Embed({ state.trigger('insert', selection).reset();
controller: this, }
text: 'Add images' });
}); },
}
}); mainEmbedToolbar: function(toolbar) {
toolbar.view = new wp.media.view.Toolbar.Embed({
controller: this,
text: 'Add images'
});
}
var theFrame = this._mediaManager = new MediaManager({ });
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select',
},
}),
that = this;
this._mediaManager.on('insert', function() { var theFrame = this._mediaManager = new MediaManager({
// Append media manager image selections to Images tab id: 'mailpoet-media-manager',
var selection = theFrame.state().get('selection'); frame: 'select',
selection.each(function(attachment) { title: 'Select image',
var sizes = attachment.get('sizes'), editing: false,
// Following advice from Becs, the target width should multiple: false,
// be a double of one column width to render well on library: {
// retina screen devices type: 'image'
targetImageWidth = 1200, },
displaySettings: false,
button: {
text: 'Select',
},
}),
that = this;
// For main image use the size, that's closest to being 600px in width this._mediaManager.on('insert', function() {
sizeKeys = _.keys(sizes), // 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
targetImageWidth = 1200,
// Pick the width that is closest to target width // For main image use the size, that's closest to being 600px in width
increasingByWidthDifference = _.sortBy( sizeKeys = _.keys(sizes),
_.keys(sizes),
function(size) { return Math.abs(targetImageWidth - sizes[size].width); }
),
bestWidth = sizes[_.first(increasingByWidthDifference)].width,
imagesOfBestWidth = _.filter(_.values(sizes), function(size) { return size.width === bestWidth; }),
// Maximize the height if there are multiple images with same width // Pick the width that is closest to target width
mainSize = _.max(imagesOfBestWidth, function(size) { return size.height; }); increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function(size) { return Math.abs(targetImageWidth - sizes[size].width); }
),
bestWidth = sizes[_.first(increasingByWidthDifference)].width,
imagesOfBestWidth = _.filter(_.values(sizes), function(size) { return size.width === bestWidth; }),
that.model.set({ // Maximize the height if there are multiple images with same width
height: mainSize.height + 'px', mainSize = _.max(imagesOfBestWidth, function(size) { return size.height; });
width: mainSize.width + 'px',
src: mainSize.url,
alt: (attachment.get('alt') !== "" && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title'),
});
// Rerender settings view due to changes from outside of settings view
that.render();
});
});
this._mediaManager.open(); that.model.set({
}, height: mainSize.height + 'px',
onBeforeDestroy: function() { width: mainSize.width + 'px',
if (typeof this._mediaManager === 'object') { src: mainSize.url,
this._mediaManager.remove(); alt: (attachment.get('alt') !== "" && attachment.get('alt') !== undefined) ? attachment.get('alt') : attachment.get('title'),
} });
}, // Rerender settings view due to changes from outside of settings view
}); that.render();
});
});
ImageWidgetView = base.WidgetView.extend({ this._mediaManager.open();
getTemplate: function() { return templates.imageInsertion; }, },
behaviors: { onBeforeDestroy: function() {
DraggableBehavior: { if (typeof this._mediaManager === 'object') {
cloneOriginal: true, this._mediaManager.remove();
drop: function() { }
return new Module.ImageBlockModel(); },
}, });
onDrop: function(options) {
options.droppedView.triggerMethod('showSettings', { showImageManager: true });
},
}
},
});
Module.ImageWidgetView = ImageWidgetView;
App.on('before:start', function() { ImageWidgetView = base.WidgetView.extend({
App.registerBlockType('image', { getTemplate: function() { return templates.imageInsertion; },
blockModel: Module.ImageBlockModel, behaviors: {
blockView: Module.ImageBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.ImageBlockModel();
},
onDrop: function(options) {
options.droppedView.triggerMethod('showSettings', { showImageManager: true });
},
}
},
});
Module.ImageWidgetView = ImageWidgetView;
App.on('before:start', function() {
App.registerBlockType('image', {
blockModel: Module.ImageBlockModel,
blockView: Module.ImageBlockView,
});
App.registerWidget({
name: 'image',
widgetView: Module.ImageWidgetView,
priority: 91,
});
});
});
App.registerWidget({
name: 'image',
widgetView: Module.ImageWidgetView,
priority: 91,
});
});
}); });

View File

@ -10,460 +10,470 @@
* This block depends on blocks.button and blocks.divider for block model and * This block depends on blocks.button and blocks.divider for block model and
* block settings view. * block settings view.
*/ */
EditorApplication.module("blocks.posts", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/posts', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.posts", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.PostsBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
stale: ['_selectedPosts', '_availablePosts'],
defaults: function() {
return this._getDefaults({
type: 'posts',
amount: '10',
contentType: 'post', // 'post'|'page'|'mailpoet_page'
postStatus: 'publish', // 'draft'|'pending'|'private'|'publish'|'future'
terms: [], // List of category and tag objects
search: '', // Search keyword term
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
//imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: 'Author:',
showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: 'Categories:',
readMoreType: 'link', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: 'Read more',
url: '[postLink]'
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {},
_selectedPosts: [],
_availablePosts: [],
}, EditorApplication.getConfig().get('blockDefaults.posts'));
},
relations: function() {
return {
readMoreButton: App.getBlockTypeModel('button'),
divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection,
};
},
initialize: function() {
var that = this;
// Attach Radio.Requests API primarily for highlighting
_.extend(this, Backbone.Radio.Requests);
this.fetchAvailablePosts(); Module.PostsBlockModel = base.BlockModel.extend({
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this); stale: ['_selectedPosts', '_availablePosts'],
this.on('insertSelectedPosts', this._insertSelectedPosts, this); defaults: function() {
}, return this._getDefaults({
fetchAvailablePosts: function() { type: 'posts',
var that = this; amount: '10',
mailpoet_post_wpi('posts.php', this.toJSON(), function(response) { contentType: 'post', // 'post'|'page'|'mailpoet_page'
console.log('Posts fetched', arguments); postStatus: 'publish', // 'draft'|'pending'|'private'|'publish'|'future'
that.get('_availablePosts').reset(response); terms: [], // List of category and tag objects
that.get('_selectedPosts').reset(); // Empty out the collection search: '', // Search keyword term
that.trigger('change:_availablePosts'); inclusionType: 'include', // 'include'|'exclude'
}, function() { displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
console.log('Posts fetchPosts error', arguments); titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
}); titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
}, titleAlignment: 'left', // 'left'|'center'|'right'
/** titleIsLink: false, // false|true
* Batch more changes during a specific time, instead of fetching imagePadded: true, // true|false
* ALC posts on each model change //imageAlignment: 'centerPadded', // 'centerFull'|'centerPadded'|'left'|'right'|'alternate'|'none'
*/ showAuthor: 'no', // 'no'|'aboveText'|'belowText'
_scheduleFetchAvailablePosts: function() { authorPrecededBy: 'Author:',
var timeout = 500, showCategories: 'no', // 'no'|'aboveText'|'belowText'
that = this; categoriesPrecededBy: 'Categories:',
if (this._fetchPostsTimer !== undefined) { readMoreType: 'link', // 'link'|'button'
clearTimeout(this._fetchPostsTimer); readMoreText: 'Read more', // 'link'|'button'
} readMoreButton: {
this._fetchPostsTimer = setTimeout(function() { text: 'Read more',
that.fetchAvailablePosts(); url: '[postLink]'
that._fetchPostsTimer = undefined; },
}, timeout); sortBy: 'newest', // 'newest'|'oldest',
}, showDivider: true, // true|false
_insertSelectedPosts: function() { divider: {},
var that = this, _selectedPosts: [],
data = this.toJSON(), _availablePosts: [],
index = this.collection.indexOf(this), }, EditorApplication.getConfig().get('blockDefaults.posts'));
collection = this.collection; },
relations: function() {
return {
readMoreButton: App.getBlockTypeModel('button'),
divider: App.getBlockTypeModel('divider'),
_selectedPosts: Backbone.Collection,
_availablePosts: Backbone.Collection,
};
},
initialize: function() {
var that = this;
// Attach Radio.Requests API primarily for highlighting
_.extend(this, Backbone.Radio.Requests);
data.posts = this.get('_selectedPosts').pluck('ID'); this.fetchAvailablePosts();
this.on('change:amount change:contentType change:terms change:inclusionType change:postStatus change:search change:sortBy', this._scheduleFetchAvailablePosts, this);
this.on('insertSelectedPosts', this._insertSelectedPosts, this);
},
fetchAvailablePosts: function() {
var that = this;
mailpoet_post_wpi('posts.php', this.toJSON(), function(response) {
console.log('Posts fetched', arguments);
that.get('_availablePosts').reset(response);
that.get('_selectedPosts').reset(); // Empty out the collection
that.trigger('change:_availablePosts');
}, function() {
console.log('Posts fetchPosts error', arguments);
});
},
/**
* Batch more changes during a specific time, instead of fetching
* ALC posts on each model change
*/
_scheduleFetchAvailablePosts: function() {
var timeout = 500,
that = this;
if (this._fetchPostsTimer !== undefined) {
clearTimeout(this._fetchPostsTimer);
}
this._fetchPostsTimer = setTimeout(function() {
that.fetchAvailablePosts();
that._fetchPostsTimer = undefined;
}, timeout);
},
_insertSelectedPosts: function() {
var that = this,
data = this.toJSON(),
index = this.collection.indexOf(this),
collection = this.collection;
if (data.posts.length === 0) return; data.posts = this.get('_selectedPosts').pluck('ID');
mailpoet_post_wpi('automated_latest_content.php', data, function(response) { if (data.posts.length === 0) return;
console.log('Available posts fetched', arguments);
collection.add(response, { at: index });
}, function() {
console.log('Posts fetchPosts error', arguments);
});
},
});
Module.PostsBlockView = base.BlockView.extend({ mailpoet_post_wpi('automated_latest_content.php', data, function(response) {
className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block", console.log('Available posts fetched', arguments);
getTemplate: function() { return templates.postsBlock; }, collection.add(response, { at: index });
modelEvents: {}, }, function() {
onDragSubstituteBy: function() { return Module.PostsWidgetView; }, console.log('Posts fetchPosts error', arguments);
initialize: function() { });
this.toolsView = new Module.PostsBlockToolsView({ model: this.model }); },
this.on('showSettings', this.showSettings); });
this.model.reply('blockView', this.notifyAboutSelf, this);
},
onRender: function() {
if (!this.toolsRegion.hasView()) {
this.toolsRegion.show(this.toolsView);
}
this.trigger('showSettings');
},
showSettings: function(options) {
this.toolsView.triggerMethod('showSettings', options);
},
notifyAboutSelf: function() {
return this;
},
onBeforeDestroy: function() {
this.model.stopReplying('blockView', this.notifyAboutSelf, this);
},
});
Module.PostsBlockToolsView = base.BlockToolsView.extend({ Module.PostsBlockView = base.BlockView.extend({
getSettingsView: function() { return Module.PostsBlockSettingsView; }, className: "mailpoet_block mailpoet_posts_block mailpoet_droppable_block",
initialize: function() { getTemplate: function() { return templates.postsBlock; },
base.BlockToolsView.prototype.initialize.apply(this, arguments); modelEvents: {},
this.on('showSettings', this.changeSettings); onDragSubstituteBy: function() { return Module.PostsWidgetView; },
this.settingsView = new Module.PostsBlockSettingsView({ model: this.model }); initialize: function() {
}, this.toolsView = new Module.PostsBlockToolsView({ model: this.model });
changeSettings: function() { this.on('showSettings', this.showSettings);
this.settingsView.render(); this.model.reply('blockView', this.notifyAboutSelf, this);
}, },
onBeforeDestroy: function() { onRender: function() {
this.settingsView.destroy(); if (!this.toolsRegion.hasView()) {
this.off('showSettings'); this.toolsRegion.show(this.toolsView);
MailPoet.Modal.close(); }
}, this.trigger('showSettings');
}); },
showSettings: function(options) {
this.toolsView.triggerMethod('showSettings', options);
},
notifyAboutSelf: function() {
return this;
},
onBeforeDestroy: function() {
this.model.stopReplying('blockView', this.notifyAboutSelf, this);
},
});
Module.PostsBlockSettingsView = base.BlockSettingsView.extend({ Module.PostsBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.postsBlockSettings; }, getSettingsView: function() { return Module.PostsBlockSettingsView; },
regions: { initialize: function() {
selectionRegion: '.mailpoet_settings_posts_selection', base.BlockToolsView.prototype.initialize.apply(this, arguments);
displayOptionsRegion: '.mailpoet_settings_posts_display_options', this.on('showSettings', this.changeSettings);
}, this.settingsView = new Module.PostsBlockSettingsView({ model: this.model });
events: { },
'click .mailpoet_settings_posts_show_display_options': 'switchToDisplayOptions', changeSettings: function() {
'click .mailpoet_settings_posts_show_post_selection': 'switchToPostSelection', this.settingsView.render();
'click .mailpoet_settings_posts_insert_selected': 'insertPosts', },
}, onBeforeDestroy: function() {
templateHelpers: function() { this.settingsView.destroy();
return { this.off('showSettings');
model: this.model.toJSON(), MailPoet.Modal.close();
}; },
}, });
initialize: function() {
this.selectionView = new PostSelectionSettingsView({ model: this.model });
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
},
onRender: function() {
var that = this,
blockView = this.model.request('blockView');
this.selectionRegion.show(this.selectionView); Module.PostsBlockSettingsView = base.BlockSettingsView.extend({
this.displayOptionsRegion.show(this.displayOptionsView); getTemplate: function() { return templates.postsBlockSettings; },
regions: {
selectionRegion: '.mailpoet_settings_posts_selection',
displayOptionsRegion: '.mailpoet_settings_posts_display_options',
},
events: {
'click .mailpoet_settings_posts_show_display_options': 'switchToDisplayOptions',
'click .mailpoet_settings_posts_show_post_selection': 'switchToPostSelection',
'click .mailpoet_settings_posts_insert_selected': 'insertPosts',
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
};
},
initialize: function() {
this.selectionView = new PostSelectionSettingsView({ model: this.model });
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
},
onRender: function() {
var that = this,
blockView = this.model.request('blockView');
MailPoet.Modal.panel({ this.selectionRegion.show(this.selectionView);
element: this.$el, this.displayOptionsRegion.show(this.displayOptionsView);
template: '',
position: 'right',
overlay: true,
highlight: blockView.$el,
width: App.getConfig().get('sidepanelWidth'),
onCancel: function() {
// Self destroy the block if the user closes settings modal
that.model.destroy();
},
});
},
switchToDisplayOptions: function() {
// Switch content view
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_hidden');
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_hidden');
// Switch controls MailPoet.Modal.panel({
this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden'); element: this.$el,
this.$('.mailpoet_settings_posts_show_post_selection').removeClass('mailpoet_hidden'); template: '',
}, position: 'right',
switchToPostSelection: function() { overlay: true,
// Switch content view highlight: blockView.$el,
this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_hidden'); width: App.getConfig().get('sidepanelWidth'),
this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_hidden'); onCancel: function() {
// Self destroy the block if the user closes settings modal
that.model.destroy();
},
});
},
switchToDisplayOptions: function() {
// Switch content view
this.$('.mailpoet_settings_posts_selection').addClass('mailpoet_hidden');
this.$('.mailpoet_settings_posts_display_options').removeClass('mailpoet_hidden');
// Switch controls // Switch controls
this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_show_display_options').addClass('mailpoet_hidden');
this.$('.mailpoet_settings_posts_show_display_options').removeClass('mailpoet_hidden'); this.$('.mailpoet_settings_posts_show_post_selection').removeClass('mailpoet_hidden');
}, },
insertPosts: function() { switchToPostSelection: function() {
this.model.trigger('insertSelectedPosts'); // Switch content view
this.model.destroy(); this.$('.mailpoet_settings_posts_display_options').addClass('mailpoet_hidden');
}, this.$('.mailpoet_settings_posts_selection').removeClass('mailpoet_hidden');
});
var PostSelectionSettingsView = Marionette.CompositeView.extend({ // Switch controls
getTemplate: function() { return templates.postSelectionPostsBlockSettings; }, this.$('.mailpoet_settings_posts_show_post_selection').addClass('mailpoet_hidden');
getChildView: function() { return SinglePostSelectionSettingsView; }, this.$('.mailpoet_settings_posts_show_display_options').removeClass('mailpoet_hidden');
childViewContainer: '.mailpoet_post_selection_container', },
getEmptyView: function() { return EmptyPostSelectionSettingsView; }, insertPosts: function() {
childViewOptions: function() { this.model.trigger('insertSelectedPosts');
return { this.model.destroy();
blockModel: this.model, },
}; });
},
events: function() {
return {
'change .mailpoet_settings_posts_content_type': _.partial(this.changeField, 'contentType'),
'change .mailpoet_posts_post_status': _.partial(this.changeField, 'postStatus'),
'keyup .mailpoet_posts_search_term': _.partial(this.changeField, 'search'),
};
},
constructor: function() {
// Set the block collection to be handled by this view as well
arguments[0].collection = arguments[0].model.get('_availablePosts');
Marionette.CompositeView.apply(this, arguments);
},
onRender: function() {
var that = this;
this.$('.mailpoet_posts_categories_and_tags').select2({ var PostSelectionSettingsView = Marionette.CompositeView.extend({
multiple: true, getTemplate: function() { return templates.postSelectionPostsBlockSettings; },
allowClear: true, getChildView: function() { return SinglePostSelectionSettingsView; },
ajax: { childViewContainer: '.mailpoet_post_selection_container',
url: App.getConfig().get('urls.termSearch'), getEmptyView: function() { return EmptyPostSelectionSettingsView; },
type: 'POST', childViewOptions: function() {
dataType: 'json', return {
delay: 250, blockModel: this.model,
data: function(searchParameter, page) { };
return JSON.stringify({ },
postType: that.model.get('contentType'), events: function() {
search: searchParameter, return {
limit: 10, // TODO: Move this hardcoded limit to Config 'change .mailpoet_settings_posts_content_type': _.partial(this.changeField, 'contentType'),
page: page, 'change .mailpoet_posts_post_status': _.partial(this.changeField, 'postStatus'),
}); 'keyup .mailpoet_posts_search_term': _.partial(this.changeField, 'search'),
}, };
/** },
* Parse results for select2. constructor: function() {
* Returns object, where `results` key holds a list of // Set the block collection to be handled by this view as well
* select item objects arguments[0].collection = arguments[0].model.get('_availablePosts');
*/ Marionette.CompositeView.apply(this, arguments);
results: function (data, page) { },
return { onRender: function() {
results: _.map( var that = this;
data.results,
function(item) {
return _.defaults({
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
id: item.term_id
}, item);
}
)
};
}
},
}).trigger( 'change' ).on({
'change': function(e){
var data = [];
if (typeof data === 'string') { this.$('.mailpoet_posts_categories_and_tags').select2({
if (data === '') { multiple: true,
data = []; allowClear: true,
} else { ajax: {
data = JSON.parse(data); url: App.getConfig().get('urls.termSearch'),
} type: 'POST',
} dataType: 'json',
delay: 250,
data: function(searchParameter, page) {
return JSON.stringify({
postType: that.model.get('contentType'),
search: searchParameter,
limit: 10, // TODO: Move this hardcoded limit to Config
page: page,
});
},
/**
* Parse results for select2.
* Returns object, where `results` key holds a list of
* select item objects
*/
results: function (data, page) {
return {
results: _.map(
data.results,
function(item) {
return _.defaults({
text: data.taxonomies[item.taxonomy].labels.singular_name + ': ' + item.name,
id: item.term_id
}, item);
}
)
};
}
},
}).trigger( 'change' ).on({
'change': function(e){
var data = [];
if ( e.added ){ if (typeof data === 'string') {
data.push(e.added); if (data === '') {
} data = [];
} else {
data = JSON.parse(data);
}
}
// Update ALC model if ( e.added ){
that.model.set('terms', data); data.push(e.added);
}
$(this).data('selected', JSON.stringify(data)); // Update ALC model
} that.model.set('terms', data);
});
},
onBeforeDestroy: function() {
// Force close select2 if it hasn't closed yet
this.$('.mailpoet_posts_categories_and_tags').select2('close');
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
});
var EmptyPostSelectionSettingsView = Marionette.ItemView.extend({ $(this).data('selected', JSON.stringify(data));
getTemplate: function() { return templates.emptyPostPostsBlockSettings; }, }
}); });
},
onBeforeDestroy: function() {
// Force close select2 if it hasn't closed yet
this.$('.mailpoet_posts_categories_and_tags').select2('close');
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
});
var SinglePostSelectionSettingsView = Marionette.ItemView.extend({ var EmptyPostSelectionSettingsView = Marionette.ItemView.extend({
getTemplate: function() { return templates.singlePostPostsBlockSettings; }, getTemplate: function() { return templates.emptyPostPostsBlockSettings; },
events: function() { });
return {
'change .mailpoet_select_post_checkbox': 'postSelectionChange',
};
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
index: this._index,
};
},
initialize: function(options) {
this.blockModel = options.blockModel;
},
postSelectionChange: function(event) {
var checkBox = jQuery(event.target),
selectedPostsCollection = this.blockModel.get('_selectedPosts');
if (checkBox.prop('checked')) {
selectedPostsCollection.add(this.model);
} else {
selectedPostsCollection.remove(this.model);
}
},
});
var PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({ var SinglePostSelectionSettingsView = Marionette.ItemView.extend({
getTemplate: function() { return templates.displayOptionsPostsBlockSettings; }, getTemplate: function() { return templates.singlePostPostsBlockSettings; },
events: function() { events: function() {
return { return {
"click .mailpoet_posts_select_button": 'showButtonSettings', 'change .mailpoet_select_post_checkbox': 'postSelectionChange',
"click .mailpoet_posts_select_divider": 'showDividerSettings', };
"change .mailpoet_posts_read_more_type": 'changeReadMoreType', },
"change .mailpoet_posts_display_type": 'changeDisplayType', templateHelpers: function() {
"change .mailpoet_posts_title_format": 'changeTitleFormat', return {
"change .mailpoet_posts_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'), model: this.model.toJSON(),
"change .mailpoet_posts_show_divider": _.partial(this.changeBoolField, 'showDivider'), index: this._index,
"keyup .mailpoet_posts_show_amount": _.partial(this.changeField, "amount"), };
"change .mailpoet_posts_content_type": _.partial(this.changeField, "contentType"), },
"change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"), initialize: function(options) {
"change .mailpoet_posts_title_position": _.partial(this.changeField, "titlePosition"), this.blockModel = options.blockModel;
"change .mailpoet_posts_title_alignment": _.partial(this.changeField, "titleAlignment"), },
"change .mailpoet_posts_image_padded": _.partial(this.changeBoolField, "imagePadded"), postSelectionChange: function(event) {
"change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"), var checkBox = jQuery(event.target),
"keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"), selectedPostsCollection = this.blockModel.get('_selectedPosts');
"change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"), if (checkBox.prop('checked')) {
"keyup .mailpoet_posts_categories": _.partial(this.changeField, "categoriesPrecededBy"), selectedPostsCollection.add(this.model);
"keyup .mailpoet_posts_read_more_text": _.partial(this.changeField, "readMoreText"), } else {
"change .mailpoet_posts_sort_by": _.partial(this.changeField, "sortBy"), selectedPostsCollection.remove(this.model);
}; }
}, },
behaviors: { });
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
};
},
showButtonSettings: function(event) {
var buttonModule = App.module('blocks.button');
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
renderOptions: {
displayFormat: 'subpanel',
hideLink: true,
hideApplyToAll: true,
},
})).render();
},
showDividerSettings: function(event) {
var dividerModule = App.module('blocks.divider');
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
renderOptions: {
displayFormat: 'subpanel',
hideApplyToAll: true,
},
})).render();
},
changeReadMoreType: function(event) {
var value = jQuery(event.target).val();
if (value == 'link') {
this.$('.mailpoet_posts_read_more_text').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').addClass('mailpoet_hidden');
} else if (value == 'button') {
this.$('.mailpoet_posts_read_more_text').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').removeClass('mailpoet_hidden');
}
this.changeField('readMoreType', event);
},
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_posts_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').addClass('mailpoet_hidden');
// Reset titleFormat if it was set to List when switching away from displayType=titleOnly var PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
if (this.model.get('titleFormat') === 'ul') { getTemplate: function() { return templates.displayOptionsPostsBlockSettings; },
this.model.set('titleFormat', 'h1'); events: function() {
this.$('.mailpoet_posts_title_format').val(['h1']); return {
this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden'); "click .mailpoet_posts_select_button": 'showButtonSettings',
} "click .mailpoet_posts_select_divider": 'showDividerSettings',
} "change .mailpoet_posts_read_more_type": 'changeReadMoreType',
this.changeField('displayType', event); "change .mailpoet_posts_display_type": 'changeDisplayType',
}, "change .mailpoet_posts_title_format": 'changeTitleFormat',
changeTitleFormat: function(event) { "change .mailpoet_posts_title_as_links": _.partial(this.changeBoolField, 'titleIsLink'),
var value = jQuery(event.target).val(); "change .mailpoet_posts_show_divider": _.partial(this.changeBoolField, 'showDivider'),
if (value == 'ul') { "keyup .mailpoet_posts_show_amount": _.partial(this.changeField, "amount"),
this.$('.mailpoet_posts_non_title_list_options').addClass('mailpoet_hidden'); "change .mailpoet_posts_content_type": _.partial(this.changeField, "contentType"),
"change .mailpoet_posts_include_or_exclude": _.partial(this.changeField, "inclusionType"),
"change .mailpoet_posts_title_position": _.partial(this.changeField, "titlePosition"),
"change .mailpoet_posts_title_alignment": _.partial(this.changeField, "titleAlignment"),
"change .mailpoet_posts_image_padded": _.partial(this.changeBoolField, "imagePadded"),
"change .mailpoet_posts_show_author": _.partial(this.changeField, "showAuthor"),
"keyup .mailpoet_posts_author_preceded_by": _.partial(this.changeField, "authorPrecededBy"),
"change .mailpoet_posts_show_categories": _.partial(this.changeField, "showCategories"),
"keyup .mailpoet_posts_categories": _.partial(this.changeField, "categoriesPrecededBy"),
"keyup .mailpoet_posts_read_more_text": _.partial(this.changeField, "readMoreText"),
"change .mailpoet_posts_sort_by": _.partial(this.changeField, "sortBy"),
};
},
behaviors: {
ColorPickerBehavior: {},
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
};
},
showButtonSettings: function(event) {
var buttonModule = App.module('blocks.button');
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
renderOptions: {
displayFormat: 'subpanel',
hideLink: true,
hideApplyToAll: true,
},
})).render();
},
showDividerSettings: function(event) {
var dividerModule = App.module('blocks.divider');
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
renderOptions: {
displayFormat: 'subpanel',
hideApplyToAll: true,
},
})).render();
},
changeReadMoreType: function(event) {
var value = jQuery(event.target).val();
if (value == 'link') {
this.$('.mailpoet_posts_read_more_text').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').addClass('mailpoet_hidden');
} else if (value == 'button') {
this.$('.mailpoet_posts_read_more_text').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_select_button').removeClass('mailpoet_hidden');
}
this.changeField('readMoreType', event);
},
changeDisplayType: function(event) {
var value = jQuery(event.target).val();
if (value == 'titleOnly') {
this.$('.mailpoet_posts_title_position_container').addClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').removeClass('mailpoet_hidden');
} else {
this.$('.mailpoet_posts_title_position_container').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_list').addClass('mailpoet_hidden');
this.model.set('titleIsLink', true); // Reset titleFormat if it was set to List when switching away from displayType=titleOnly
this.$('.mailpoet_posts_title_as_link').addClass('mailpoet_hidden'); if (this.model.get('titleFormat') === 'ul') {
this.$('.mailpoet_posts_title_as_links').val(['true']); this.model.set('titleFormat', 'h1');
} else { this.$('.mailpoet_posts_title_format').val(['h1']);
this.$('.mailpoet_posts_non_title_list_options').removeClass('mailpoet_hidden'); this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden');
this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden'); }
} }
this.changeField('titleFormat', event); this.changeField('displayType', event);
}, },
}); changeTitleFormat: function(event) {
var value = jQuery(event.target).val();
if (value == 'ul') {
this.$('.mailpoet_posts_non_title_list_options').addClass('mailpoet_hidden');
Module.PostsWidgetView = base.WidgetView.extend({ this.model.set('titleIsLink', true);
getTemplate: function() { return templates.postsInsertion; }, this.$('.mailpoet_posts_title_as_link').addClass('mailpoet_hidden');
behaviors: { this.$('.mailpoet_posts_title_as_links').val(['true']);
DraggableBehavior: { } else {
cloneOriginal: true, this.$('.mailpoet_posts_non_title_list_options').removeClass('mailpoet_hidden');
drop: function() { this.$('.mailpoet_posts_title_as_link').removeClass('mailpoet_hidden');
return new Module.PostsBlockModel({}, { parse: true }); }
} this.changeField('titleFormat', event);
} },
}, });
});
App.on('before:start', function() { Module.PostsWidgetView = base.WidgetView.extend({
App.registerBlockType('posts', { getTemplate: function() { return templates.postsInsertion; },
blockModel: Module.PostsBlockModel, behaviors: {
blockView: Module.PostsBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.PostsBlockModel({}, { parse: true });
}
}
},
});
App.on('before:start', function() {
App.registerBlockType('posts', {
blockModel: Module.PostsBlockModel,
blockView: Module.PostsBlockView,
});
App.registerWidget({
name: 'posts',
widgetView: Module.PostsWidgetView,
priority: 96,
});
});
});
App.registerWidget({
name: 'posts',
widgetView: Module.PostsWidgetView,
priority: 96,
});
});
}); });

View File

@ -1,365 +1,375 @@
/** /**
* Social icons content block * Social icons content block
*/ */
EditorApplication.module("blocks.social", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/social', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'), EditorApplication.module("blocks.social", function(Module, App, Backbone, Marionette, $, _) {
SocialBlockSettingsIconSelectorView, SocialBlockSettingsIconView, SocialBlockSettingsStylesView; "use strict";
Module.SocialIconModel = Backbone.SuperModel.extend({ var base = App.module('blocks.base'),
defaults: function() { SocialBlockSettingsIconSelectorView, SocialBlockSettingsIconView, SocialBlockSettingsStylesView;
var defaultValues = App.getConfig().get('socialIcons.custom');
return {
type: 'socialIcon',
iconType: 'custom',
link: defaultValues.get('defaultLink'),
image: App.getAvailableStyles().get('socialIconSets.default.custom'),
height: '32px',
width: '32px',
text: defaultValues.get('title'),
};
},
initialize: function(options) {
var that = this;
// Make model swap to default values for that type when iconType changes
this.on('change:iconType', function() {
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType')),
iconSet = that.collection.iconBlockModel.getIconSet();
this.set({
link: defaultValues.get('defaultLink'),
image: iconSet.get(that.get('iconType')),
text: defaultValues.get('title'),
});
}, this);
this.on('change', function() { App.getChannel().trigger('autoSave'); });
},
});
Module.SocialIconCollectionModel = Backbone.Collection.extend({ Module.SocialIconModel = SuperModel.extend({
model: Module.SocialIconModel defaults: function() {
}); var defaultValues = App.getConfig().get('socialIcons.custom');
return {
type: 'socialIcon',
iconType: 'custom',
link: defaultValues.get('defaultLink'),
image: App.getAvailableStyles().get('socialIconSets.default.custom'),
height: '32px',
width: '32px',
text: defaultValues.get('title'),
};
},
initialize: function(options) {
var that = this;
// Make model swap to default values for that type when iconType changes
this.on('change:iconType', function() {
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType')),
iconSet = that.collection.iconBlockModel.getIconSet();
this.set({
link: defaultValues.get('defaultLink'),
image: iconSet.get(that.get('iconType')),
text: defaultValues.get('title'),
});
}, this);
this.on('change', function() { App.getChannel().trigger('autoSave'); });
},
});
Module.SocialBlockModel = base.BlockModel.extend({ Module.SocialIconCollectionModel = Backbone.Collection.extend({
name: 'iconBlockModel', model: Module.SocialIconModel
defaults: function() { });
return this._getDefaults({
type: 'social',
iconSet: 'default',
icons: new Module.SocialIconCollectionModel(),
}, EditorApplication.getConfig().get('blockDefaults.social'));
},
relations: {
icons: Module.SocialIconCollectionModel,
},
initialize: function() {
this.get('icons').on('add remove change', this._iconsChanged, this);
this.on('change:iconSet', this.changeIconSet, this);
},
getIconSet: function() {
return App.getAvailableStyles().get('socialIconSets').get(this.get('iconSet'));
},
changeIconSet: function() {
var iconSet = this.getIconSet();
_.each(this.get('icons').models, function(model) {
model.set('image', iconSet.get(model.get('iconType')));
});
},
_iconsChanged: function() {
App.getChannel().trigger('autoSave');
},
});
var SocialIconView = Marionette.ItemView.extend({ Module.SocialBlockModel = base.BlockModel.extend({
tagName: 'span', name: 'iconBlockModel',
getTemplate: function() { return templates.socialIconBlock; }, defaults: function() {
modelEvents: { return this._getDefaults({
'change': 'render', type: 'social',
}, iconSet: 'default',
templateHelpers: function() { icons: new Module.SocialIconCollectionModel(),
var allIconSets = App.getAvailableStyles().get('socialIconSets'); }, EditorApplication.getConfig().get('blockDefaults.social'));
return { },
model: this.model.toJSON(), relations: {
allIconSets: allIconSets.toJSON(), icons: Module.SocialIconCollectionModel,
}; },
}, initialize: function() {
}); this.get('icons').on('add remove change', this._iconsChanged, this);
this.on('change:iconSet', this.changeIconSet, this);
},
getIconSet: function() {
return App.getAvailableStyles().get('socialIconSets').get(this.get('iconSet'));
},
changeIconSet: function() {
var iconSet = this.getIconSet();
_.each(this.get('icons').models, function(model) {
model.set('image', iconSet.get(model.get('iconType')));
});
},
_iconsChanged: function() {
App.getChannel().trigger('autoSave');
},
});
Module.SocialBlockView = Marionette.CompositeView.extend({ var SocialIconView = Marionette.ItemView.extend({
regionClass: Marionette.Region, tagName: 'span',
className: 'mailpoet_block mailpoet_social_block mailpoet_droppable_block', getTemplate: function() { return templates.socialIconBlock; },
getTemplate: function() { return templates.socialBlock; }, modelEvents: {
childViewContainer: '.mailpoet_social', 'change': 'render',
modelEvents: { },
'change': 'render' templateHelpers: function() {
}, var allIconSets = App.getAvailableStyles().get('socialIconSets');
events: { return {
"mouseover": "showTools", model: this.model.toJSON(),
"mouseout": "hideTools", allIconSets: allIconSets.toJSON(),
}, };
regions: { },
toolsRegion: '> .mailpoet_tools', });
},
ui: {
tools: '> .mailpoet_tools'
},
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, 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;
}
},
},
},
onDragSubstituteBy: function() { return Module.SocialWidgetView; },
constructor: function() {
// Set the block collection to be handled by this view as well
arguments[0].collection = arguments[0].model.get('icons');
Marionette.CompositeView.apply(this, arguments);
},
// Determines which view type should be used for a child
childView: SocialIconView,
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
};
},
onRender: function() {
this._rebuildRegions();
this.toolsView = new Module.SocialBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onBeforeDestroy: function() {
this.regionManager.destroy();
},
showTools: function(_event) {
this.$(this.ui.tools).show();
_event.stopPropagation();
},
hideTools: function(_event) {
this.$(this.ui.tools).hide();
_event.stopPropagation();
},
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
},
_buildRegions: function(regions) {
var that = this;
var defaults = { Module.SocialBlockView = Marionette.CompositeView.extend({
regionClass: this.getOption('regionClass'), regionClass: Marionette.Region,
parentEl: function() { return that.$el; } className: 'mailpoet_block mailpoet_social_block mailpoet_droppable_block',
}; getTemplate: function() { return templates.socialBlock; },
childViewContainer: '.mailpoet_social',
modelEvents: {
'change': 'render'
},
events: {
"mouseover": "showTools",
"mouseout": "hideTools",
},
regions: {
toolsRegion: '> .mailpoet_tools',
},
ui: {
tools: '> .mailpoet_tools'
},
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, 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;
}
},
},
},
onDragSubstituteBy: function() { return Module.SocialWidgetView; },
constructor: function() {
// Set the block collection to be handled by this view as well
arguments[0].collection = arguments[0].model.get('icons');
Marionette.CompositeView.apply(this, arguments);
},
// Determines which view type should be used for a child
childView: SocialIconView,
templateHelpers: function() {
return {
model: this.model.toJSON(),
viewCid: this.cid,
};
},
onRender: function() {
this._rebuildRegions();
this.toolsView = new Module.SocialBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
onBeforeDestroy: function() {
this.regionManager.destroy();
},
showTools: function(_event) {
this.$(this.ui.tools).show();
_event.stopPropagation();
},
hideTools: function(_event) {
this.$(this.ui.tools).hide();
_event.stopPropagation();
},
getDropFunc: function() {
var that = this;
return function() {
var newModel = that.model.clone();
//that.model.destroy();
return newModel;
};
},
_buildRegions: function(regions) {
var that = this;
return this.regionManager.addRegions(regions, defaults); var defaults = {
}, regionClass: this.getOption('regionClass'),
_rebuildRegions: function() { parentEl: function() { return that.$el; }
if (this.regionManager === undefined) { };
this.regionManager = new Backbone.Marionette.RegionManager();
}
this.regionManager.destroy();
_.extend(this, this._buildRegions(this.regions));
},
});
Module.SocialBlockToolsView = base.BlockToolsView.extend({ return this.regionManager.addRegions(regions, defaults);
getSettingsView: function() { return Module.SocialBlockSettingsView; }, },
}); _rebuildRegions: function() {
if (this.regionManager === undefined) {
this.regionManager = new Backbone.Marionette.RegionManager();
}
this.regionManager.destroy();
_.extend(this, this._buildRegions(this.regions));
},
});
// Sidebar view container Module.SocialBlockToolsView = base.BlockToolsView.extend({
Module.SocialBlockSettingsView = base.BlockSettingsView.extend({ getSettingsView: function() { return Module.SocialBlockSettingsView; },
getTemplate: function() { return templates.socialBlockSettings; }, });
regions: {
iconRegion: '#mailpoet_social_icons_selection',
stylesRegion: '#mailpoet_social_icons_styles',
},
events: function() {
return {
"click .mailpoet_done_editing": "close",
};
},
initialize: function() {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
this._iconSelectorView = new SocialBlockSettingsIconSelectorView({ model: this.model }); // Sidebar view container
this._stylesView = new SocialBlockSettingsStylesView({ model: this.model }); Module.SocialBlockSettingsView = base.BlockSettingsView.extend({
}, getTemplate: function() { return templates.socialBlockSettings; },
onRender: function() { regions: {
this.iconRegion.show(this._iconSelectorView); iconRegion: '#mailpoet_social_icons_selection',
this.stylesRegion.show(this._stylesView); stylesRegion: '#mailpoet_social_icons_styles',
} },
}); events: function() {
return {
"click .mailpoet_done_editing": "close",
};
},
initialize: function() {
base.BlockSettingsView.prototype.initialize.apply(this, arguments);
// Single icon settings view, used by the selector view this._iconSelectorView = new SocialBlockSettingsIconSelectorView({ model: this.model });
SocialBlockSettingsIconView = Marionette.ItemView.extend({ this._stylesView = new SocialBlockSettingsStylesView({ model: this.model });
getTemplate: function() { return templates.socialSettingsIcon; }, },
events: function() { onRender: function() {
return { this.iconRegion.show(this._iconSelectorView);
"click .mailpoet_delete_block": "deleteIcon", this.stylesRegion.show(this._stylesView);
"change .mailpoet_social_icon_field_type": _.partial(this.changeField, "iconType"), }
"keyup .mailpoet_social_icon_field_image": _.partial(this.changeField, "image"), });
"keyup .mailpoet_social_icon_field_link": this.changeLink,
"keyup .mailpoet_social_icon_field_text": _.partial(this.changeField, "text"),
};
},
modelEvents: {
'change:iconType': 'render',
'change:image': function() {
this.$('.mailpoet_social_icon_image').attr('src', this.model.get('image'));
},
'change:text': function() {
this.$('.mailpoet_social_icon_image').attr('alt', this.model.get('text'));
},
},
templateHelpers: function() {
var icons = App.getConfig().get('socialIcons'),
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
availableIconTypes = _.map(_.keys(icons.attributes), function(key) { return { iconType: key, title: icons.get(key).get('title') }; }),
allIconSets = App.getAvailableStyles().get('socialIconSets');
return {
model: this.model.toJSON(),
iconTypes: availableIconTypes,
currentType: icons.get(this.model.get('iconType')).toJSON(),
allIconSets: allIconSets.toJSON(),
};
},
deleteIcon: function() {
this.model.destroy();
},
changeLink: function(event) {
if (this.model.get('iconType') === 'email') {
this.model.set('link', 'mailto:' + jQuery(event.target).val());
} else {
return this.changeField('link', event);
}
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
});
// Select icons section container view // Single icon settings view, used by the selector view
SocialBlockSettingsIconSelectorView = Marionette.CompositeView.extend({ SocialBlockSettingsIconView = Marionette.ItemView.extend({
getTemplate: function() { return templates.socialSettingsIconSelector; }, getTemplate: function() { return templates.socialSettingsIcon; },
childView: SocialBlockSettingsIconView, events: function() {
childViewContainer: '#mailpoet_social_icon_selector_contents', return {
events: { "click .mailpoet_delete_block": "deleteIcon",
'click .mailpoet_add_social_icon': 'addSocialIcon', "change .mailpoet_social_icon_field_type": _.partial(this.changeField, "iconType"),
}, "keyup .mailpoet_social_icon_field_image": _.partial(this.changeField, "image"),
modelEvents: { "keyup .mailpoet_social_icon_field_link": this.changeLink,
'change:iconSet': 'render', "keyup .mailpoet_social_icon_field_text": _.partial(this.changeField, "text"),
}, };
behaviors: { },
SortableBehavior: { modelEvents: {
items: '#mailpoet_social_icon_selector_contents > div', 'change:iconType': 'render',
}, 'change:image': function() {
}, this.$('.mailpoet_social_icon_image').attr('src', this.model.get('image'));
constructor: function() { },
// Set the icon collection to be handled by this view as well 'change:text': function() {
arguments[0].collection = arguments[0].model.get('icons'); this.$('.mailpoet_social_icon_image').attr('alt', this.model.get('text'));
Marionette.CompositeView.apply(this, arguments); },
}, },
addSocialIcon: function() { templateHelpers: function() {
// Add a social icon with default values var icons = App.getConfig().get('socialIcons'),
this.collection.add({}); // Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
} availableIconTypes = _.map(_.keys(icons.attributes), function(key) { return { iconType: key, title: icons.get(key).get('title') }; }),
}); allIconSets = App.getAvailableStyles().get('socialIconSets');
return {
model: this.model.toJSON(),
iconTypes: availableIconTypes,
currentType: icons.get(this.model.get('iconType')).toJSON(),
allIconSets: allIconSets.toJSON(),
};
},
deleteIcon: function() {
this.model.destroy();
},
changeLink: function(event) {
if (this.model.get('iconType') === 'email') {
this.model.set('link', 'mailto:' + jQuery(event.target).val());
} else {
return this.changeField('link', event);
}
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
});
SocialBlockSettingsStylesView = Marionette.ItemView.extend({ // Select icons section container view
getTemplate: function() { return templates.socialSettingsStyles; }, SocialBlockSettingsIconSelectorView = Marionette.CompositeView.extend({
modelEvents: { getTemplate: function() { return templates.socialSettingsIconSelector; },
'change': 'render', childView: SocialBlockSettingsIconView,
}, childViewContainer: '#mailpoet_social_icon_selector_contents',
events: { events: {
'click .mailpoet_social_icon_set': 'changeSocialIconSet', 'click .mailpoet_add_social_icon': 'addSocialIcon',
}, },
initialize: function() { modelEvents: {
this.listenTo(this.model.get('icons'), 'add remove change', this.render); 'change:iconSet': 'render',
}, },
templateHelpers: function() { behaviors: {
var allIconSets = App.getAvailableStyles().get('socialIconSets'); SortableBehavior: {
return { items: '#mailpoet_social_icon_selector_contents > div',
activeSet: this.model.get('iconSet'), },
socialIconSets: allIconSets.toJSON(), },
availableSets: _.keys(allIconSets.toJSON()), constructor: function() {
availableSocialIcons: this.model.get('icons').pluck('iconType'), // Set the icon collection to be handled by this view as well
}; arguments[0].collection = arguments[0].model.get('icons');
}, Marionette.CompositeView.apply(this, arguments);
changeSocialIconSet: function(event) { },
this.model.set('iconSet', jQuery(event.currentTarget).data('setname')); addSocialIcon: function() {
}, // Add a social icon with default values
onBeforeDestroy: function() { this.collection.add({});
this.model.get('icons').off('add remove', this.render, this); }
}, });
});
Module.SocialWidgetView = base.WidgetView.extend({ SocialBlockSettingsStylesView = Marionette.ItemView.extend({
getTemplate: function() { return templates.socialInsertion; }, getTemplate: function() { return templates.socialSettingsStyles; },
behaviors: { modelEvents: {
DraggableBehavior: { 'change': 'render',
cloneOriginal: true, },
drop: function() { events: {
return new Module.SocialBlockModel({ 'click .mailpoet_social_icon_set': 'changeSocialIconSet',
type: 'social', },
iconSet: 'default', initialize: function() {
icons: [ this.listenTo(this.model.get('icons'), 'add remove change', this.render);
{ },
type: 'socialIcon', templateHelpers: function() {
iconType: 'facebook', var allIconSets = App.getAvailableStyles().get('socialIconSets');
link: 'http://example.com', return {
image: App.getAvailableStyles().get('socialIconSets.default.facebook'), activeSet: this.model.get('iconSet'),
height: '32px', socialIconSets: allIconSets.toJSON(),
width: '32px', availableSets: _.keys(allIconSets.toJSON()),
text: 'Facebook', availableSocialIcons: this.model.get('icons').pluck('iconType'),
}, };
{ },
type: 'socialIcon', changeSocialIconSet: function(event) {
iconType: 'twitter', this.model.set('iconSet', jQuery(event.currentTarget).data('setname'));
link: 'http://example.com', },
image: App.getAvailableStyles().get('socialIconSets.default.twitter'), onBeforeDestroy: function() {
height: '32px', this.model.get('icons').off('add remove', this.render, this);
width: '32px', },
text: 'Twitter', });
},
],
}, { parse: true });
}
}
},
});
App.on('before:start', function() { Module.SocialWidgetView = base.WidgetView.extend({
App.registerBlockType('social', { getTemplate: function() { return templates.socialInsertion; },
blockModel: Module.SocialBlockModel, behaviors: {
blockView: Module.SocialBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.SocialBlockModel({
type: 'social',
iconSet: 'default',
icons: [
{
type: 'socialIcon',
iconType: 'facebook',
link: 'http://example.com',
image: App.getAvailableStyles().get('socialIconSets.default.facebook'),
height: '32px',
width: '32px',
text: 'Facebook',
},
{
type: 'socialIcon',
iconType: 'twitter',
link: 'http://example.com',
image: App.getAvailableStyles().get('socialIconSets.default.twitter'),
height: '32px',
width: '32px',
text: 'Twitter',
},
],
}, { parse: true });
}
}
},
});
App.on('before:start', function() {
App.registerBlockType('social', {
blockModel: Module.SocialBlockModel,
blockView: Module.SocialBlockView,
});
App.registerWidget({
name: 'social',
widgetView: Module.SocialWidgetView,
priority: 95,
});
});
});
App.registerWidget({
name: 'social',
widgetView: Module.SocialWidgetView,
priority: 95,
});
});
}); });

View File

@ -1,96 +1,106 @@
/** /**
* Spacer content block * Spacer content block
*/ */
EditorApplication.module("blocks.spacer", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/spacer', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.spacer", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.SpacerBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'spacer',
styles: {
block: {
backgroundColor: 'transparent',
height: '40px',
},
},
}, EditorApplication.getConfig().get('blockDefaults.spacer'));
},
});
Module.SpacerBlockView = base.BlockView.extend({ Module.SpacerBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_spacer_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.spacerBlock; }, return this._getDefaults({
behaviors: _.defaults({ type: 'spacer',
ResizableBehavior: { styles: {
elementSelector: '.mailpoet_spacer', block: {
resizeHandleSelector: '.mailpoet_resize_handle', backgroundColor: 'transparent',
minLength: 20, // TODO: Move this number to editor configuration height: '40px',
modelField: 'styles.block.height', },
}, },
}, base.BlockView.prototype.behaviors), }, EditorApplication.getConfig().get('blockDefaults.spacer'));
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), },
onDragSubstituteBy: function() { return Module.SpacerWidgetView; }, });
initialize: function() {
base.BlockView.prototype.initialize.apply(this, arguments);
this.listenTo(this.model, 'change:styles.block.backgroundColor', this.render); Module.SpacerBlockView = base.BlockView.extend({
this.listenTo(this.model, 'change:styles.block.height', this.changeHeight); className: "mailpoet_block mailpoet_spacer_block mailpoet_droppable_block",
}, getTemplate: function() { return templates.spacerBlock; },
onRender: function() { behaviors: _.defaults({
this.toolsView = new Module.SpacerBlockToolsView({ model: this.model }); ResizableBehavior: {
this.toolsRegion.show(this.toolsView); elementSelector: '.mailpoet_spacer',
}, resizeHandleSelector: '.mailpoet_resize_handle',
changeHeight: function() { minLength: 20, // TODO: Move this number to editor configuration
this.$('.mailpoet_spacer').css('height', this.model.get('styles.block.height')); modelField: 'styles.block.height',
this.$('.mailpoet_resize_handle_text').text(this.model.get('styles.block.height')); },
}, }, base.BlockView.prototype.behaviors),
onBeforeDestroy: function() { modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'),
this.stopListening(this.model); onDragSubstituteBy: function() { return Module.SpacerWidgetView; },
}, initialize: function() {
}); base.BlockView.prototype.initialize.apply(this, arguments);
Module.SpacerBlockToolsView = base.BlockToolsView.extend({ this.listenTo(this.model, 'change:styles.block.backgroundColor', this.render);
getSettingsView: function() { return Module.SpacerBlockSettingsView; }, this.listenTo(this.model, 'change:styles.block.height', this.changeHeight);
}); },
onRender: function() {
this.toolsView = new Module.SpacerBlockToolsView({ model: this.model });
this.toolsRegion.show(this.toolsView);
},
changeHeight: function() {
this.$('.mailpoet_spacer').css('height', this.model.get('styles.block.height'));
this.$('.mailpoet_resize_handle_text').text(this.model.get('styles.block.height'));
},
onBeforeDestroy: function() {
this.stopListening(this.model);
},
});
Module.SpacerBlockSettingsView = base.BlockSettingsView.extend({ Module.SpacerBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.spacerBlockSettings; }, getSettingsView: function() { return Module.SpacerBlockSettingsView; },
events: function() { });
return {
"change .mailpoet_field_spacer_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
"click .mailpoet_done_editing": "close",
};
},
behaviors: {
ColorPickerBehavior: {},
},
});
Module.SpacerWidgetView = base.WidgetView.extend({ Module.SpacerBlockSettingsView = base.BlockSettingsView.extend({
getTemplate: function() { return templates.spacerInsertion; }, getTemplate: function() { return templates.spacerBlockSettings; },
behaviors: { events: function() {
DraggableBehavior: { return {
cloneOriginal: true, "change .mailpoet_field_spacer_background_color": _.partial(this.changeColorField, "styles.block.backgroundColor"),
drop: function() { "click .mailpoet_done_editing": "close",
return new Module.SpacerBlockModel(); };
}, },
} behaviors: {
}, ColorPickerBehavior: {},
}); },
});
App.on('before:start', function() { Module.SpacerWidgetView = base.WidgetView.extend({
App.registerBlockType('spacer', { getTemplate: function() { return templates.spacerInsertion; },
blockModel: Module.SpacerBlockModel, behaviors: {
blockView: Module.SpacerBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.SpacerBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('spacer', {
blockModel: Module.SpacerBlockModel,
blockView: Module.SpacerBlockView,
});
App.registerWidget({
name: 'spacer',
widgetView: Module.SpacerWidgetView,
priority: 94,
});
});
});
App.registerWidget({
name: 'spacer',
widgetView: Module.SpacerWidgetView,
priority: 94,
});
});
}); });

View File

@ -1,116 +1,129 @@
/** /**
* Text content block * Text content block
*/ */
EditorApplication.module("blocks.text", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/blocks/text', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'mailpoet',
'jquery',
//'tinymce',
//'jquery.tinymce',
], function(EditorApplication, Backbone, SuperModel, Marionette, MailPoet, jQuery, TinyMCE) {
var base = App.module('blocks.base'); EditorApplication.module("blocks.text", function(Module, App, Backbone, Marionette, $, _) {
"use strict";
Module.TextBlockModel = base.BlockModel.extend({ var base = App.module('blocks.base');
defaults: function() {
return this._getDefaults({
type: 'text',
text: 'Edit this to insert text',
}, EditorApplication.getConfig().get('blockDefaults.text'));
},
});
Module.TextBlockView = base.BlockView.extend({ Module.TextBlockModel = base.BlockModel.extend({
className: "mailpoet_block mailpoet_text_block mailpoet_droppable_block", defaults: function() {
getTemplate: function() { return templates.textBlock; }, return this._getDefaults({
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing type: 'text',
initialize: function(options) { text: 'Edit this to insert text',
this.renderOptions = _.defaults(options.renderOptions || {}, { }, EditorApplication.getConfig().get('blockDefaults.text'));
disableTextEditor: false, },
}); });
},
onDragSubstituteBy: function() { return Module.TextWidgetView; },
onRender: function() {
this.toolsView = new Module.TextBlockToolsView({
model: this.model,
tools: {
settings: false,
},
});
this.toolsRegion.show(this.toolsView);
},
onDomRefresh: function() {
this.attachTextEditor();
},
attachTextEditor: function() {
var that = this;
if (!this.renderOptions.disableTextEditor) {
this.$('.mailpoet_content').tinymce({
inline: true,
menubar: false, Module.TextBlockView = base.BlockView.extend({
toolbar1: "styleselect bold italic forecolor | link unlink", className: "mailpoet_block mailpoet_text_block mailpoet_droppable_block",
toolbar2: "alignleft aligncenter alignright alignjustify | bullist numlist blockquote | code mailpoet_custom_fields", getTemplate: function() { return templates.textBlock; },
modelEvents: _.omit(base.BlockView.prototype.modelEvents, 'change'), // Prevent rerendering on model change due to text editor redrawing
initialize: function(options) {
this.renderOptions = _.defaults(options.renderOptions || {}, {
disableTextEditor: false,
});
},
onDragSubstituteBy: function() { return Module.TextWidgetView; },
onRender: function() {
this.toolsView = new Module.TextBlockToolsView({
model: this.model,
tools: {
settings: false,
},
});
this.toolsRegion.show(this.toolsView);
},
onDomRefresh: function() {
this.attachTextEditor();
},
attachTextEditor: function() {
var that = this;
if (!this.renderOptions.disableTextEditor) {
this.$('.mailpoet_content').tinymce({
inline: true,
//forced_root_block: 'p', menubar: false,
valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],h1[class|style],h2[class|style],h3[class|style],ol[class|style],ul[class|style],li[class|style],strong[class|style],em[class|style],strike,br,blockquote[class|style],table[class|style],tr[class|style],th[class|style],td[class|style]", toolbar1: "styleselect bold italic forecolor | link unlink",
invalid_elements: "script", toolbar2: "alignleft aligncenter alignright alignjustify | bullist numlist blockquote | code mailpoet_custom_fields",
style_formats: [
{title: 'Heading 1', block: 'h1'},
{title: 'Heading 2', block: 'h2'},
{title: 'Heading 3', block: 'h3'},
{title: 'Paragraph', block: 'p'}, //forced_root_block: 'p',
], valid_elements: "p[class|style],span[class|style],a[href|class|title|target|style],h1[class|style],h2[class|style],h3[class|style],ol[class|style],ul[class|style],li[class|style],strong[class|style],em[class|style],strike,br,blockquote[class|style],table[class|style],tr[class|style],th[class|style],td[class|style]",
invalid_elements: "script",
style_formats: [
{title: 'Heading 1', block: 'h1'},
{title: 'Heading 2', block: 'h2'},
{title: 'Heading 3', block: 'h3'},
plugins: "wplink code textcolor mailpoet_custom_fields", {title: 'Paragraph', block: 'p'},
],
setup: function(editor) { plugins: "wplink code textcolor mailpoet_custom_fields",
editor.on('change', function(e) {
that.model.set('text', editor.getContent());
});
editor.on('focus', function(e) { setup: function(editor) {
that.disableShowingTools(); editor.on('change', function(e) {
}); that.model.set('text', editor.getContent());
});
editor.on('blur', function(e) { editor.on('focus', function(e) {
that.enableShowingTools(); that.disableShowingTools();
}); });
},
mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(), editor.on('blur', function(e) {
mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'), that.enableShowingTools();
}); });
} },
},
});
Module.TextBlockToolsView = base.BlockToolsView.extend({ mailpoet_custom_fields: App.getConfig().get('customFields').toJSON(),
getSettingsView: function() { return Module.TextBlockSettingsView; }, mailpoet_custom_fields_window_title: App.getConfig().get('translations.customFieldsWindowTitle'),
}); });
}
},
});
Module.TextBlockSettingsView = base.BlockSettingsView.extend({ Module.TextBlockToolsView = base.BlockToolsView.extend({
getTemplate: function() { return templates.textBlockSettings; }, getSettingsView: function() { return Module.TextBlockSettingsView; },
}); });
Module.TextWidgetView = base.WidgetView.extend({ Module.TextBlockSettingsView = base.BlockSettingsView.extend({
getTemplate: function() { return templates.textInsertion; }, getTemplate: function() { return templates.textBlockSettings; },
behaviors: { });
DraggableBehavior: {
cloneOriginal: true,
drop: function() {
return new Module.TextBlockModel();
},
}
},
});
App.on('before:start', function() { Module.TextWidgetView = base.WidgetView.extend({
App.registerBlockType('text', { getTemplate: function() { return templates.textInsertion; },
blockModel: Module.TextBlockModel, behaviors: {
blockView: Module.TextBlockView, DraggableBehavior: {
}); cloneOriginal: true,
drop: function() {
return new Module.TextBlockModel();
},
}
},
});
App.on('before:start', function() {
App.registerBlockType('text', {
blockModel: Module.TextBlockModel,
blockView: Module.TextBlockView,
});
App.registerWidget({
name: 'text',
widgetView: Module.TextWidgetView,
priority: 90,
});
});
});
App.registerWidget({
name: 'text',
widgetView: Module.TextWidgetView,
priority: 90,
});
});
}); });

View File

@ -1,28 +1,36 @@
EditorApplication.module("components.config", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/config', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
], function(EditorApplication, Backbone, SuperModel) {
Module.ConfigModel = Backbone.SuperModel.extend({ EditorApplication.module("components.config", function(Module, App, Backbone, Marionette, $, _) {
defaults: { "use strict";
availableStyles: {},
socialIcons: {},
blockDefaults: {},
translations: {},
sidepanelWidth: '331px',
validation: {},
urls: {},
},
});
// Global and available styles for access in blocks and their settings Module.ConfigModel = SuperModel.extend({
Module._config = {}; defaults: {
Module.getConfig = function() { return Module._config; }; availableStyles: {},
Module.setConfig = function(options) { Module._config = new Module.ConfigModel(options, { parse: true }); return Module._config; }; socialIcons: {},
blockDefaults: {},
translations: {},
sidepanelWidth: '331px',
validation: {},
urls: {},
},
});
App.on('before:start', function(options) { // Global and available styles for access in blocks and their settings
// Expose config methods globally Module._config = {};
App.getConfig = Module.getConfig; Module.getConfig = function() { return Module._config; };
App.setConfig = Module.setConfig; Module.setConfig = function(options) { Module._config = new Module.ConfigModel(options, { parse: true }); return Module._config; };
App.on('before:start', function(options) {
// Expose config methods globally
App.getConfig = Module.getConfig;
App.setConfig = Module.setConfig;
App.setConfig(options.config);
});
});
App.setConfig(options.config);
});
}); });

View File

@ -1,72 +1,81 @@
EditorApplication.module("components.content", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/content', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
], function(EditorApplication, Backbone, SuperModel, Marionette) {
// Holds newsletter entry fields, such as subject or creation datetime. EditorApplication.module("components.content", function(Module, App, Backbone, Marionette, $, _) {
// Does not hold newsletter content nor newsletter styles, those are "use strict";
// handled by other components.
Module.NewsletterModel = Backbone.SuperModel.extend({
stale: ['data', 'styles'],
initialize: function(options) {
this.on('change', function() {
App.getChannel().trigger('autoSave');
});
},
toJSON: function() {
// Remove stale attributes from resulting JSON object
return _.omit(Backbone.SuperModel.prototype.toJSON.call(this), this.stale);
},
});
// Content block view and model handlers for different content types // Holds newsletter entry fields, such as subject or creation datetime.
Module._blockTypes = {}; // Does not hold newsletter content nor newsletter styles, those are
Module.registerBlockType = function(type, data) { // handled by other components.
Module._blockTypes[type] = data; Module.NewsletterModel = SuperModel.extend({
}; stale: ['data', 'styles'],
Module.getBlockTypeModel = function(type) { initialize: function(options) {
if (type in Module._blockTypes) { this.on('change', function() {
return Module._blockTypes[type].blockModel; App.getChannel().trigger('autoSave');
} else { });
throw "Block type not supported: " + type; },
} toJSON: function() {
}; // Remove stale attributes from resulting JSON object
Module.getBlockTypeView = function(type) { return _.omit(SuperModel.prototype.toJSON.call(this), this.stale);
if (type in Module._blockTypes) { },
return Module._blockTypes[type].blockView; });
} else {
throw "Block type not supported: " + type;
}
};
Module.toJSON = function() { // Content block view and model handlers for different content types
return _.extend({ Module._blockTypes = {};
data: App._contentContainer.toJSON(), Module.registerBlockType = function(type, data) {
styles: App.getGlobalStyles().toJSON(), Module._blockTypes[type] = data;
}, App.getNewsletter().toJSON()); };
}; Module.getBlockTypeModel = function(type) {
if (type in Module._blockTypes) {
return Module._blockTypes[type].blockModel;
} else {
throw "Block type not supported: " + type;
}
};
Module.getBlockTypeView = function(type) {
if (type in Module._blockTypes) {
return Module._blockTypes[type].blockView;
} else {
throw "Block type not supported: " + type;
}
};
Module.getNewsletter = function() { Module.toJSON = function() {
return Module.newsletter; return _.extend({
}; data: App._contentContainer.toJSON(),
styles: App.getGlobalStyles().toJSON(),
}, App.getNewsletter().toJSON());
};
App.on('before:start', function(options) { Module.getNewsletter = function() {
// Expose block methods globally return Module.newsletter;
App.registerBlockType = Module.registerBlockType; };
App.getBlockTypeModel = Module.getBlockTypeModel;
App.getBlockTypeView = Module.getBlockTypeView;
App.toJSON = Module.toJSON;
App.getNewsletter = Module.getNewsletter;
Module.newsletter = new Module.NewsletterModel(options.newsletter); App.on('before:start', function(options) {
}); // Expose block methods globally
App.registerBlockType = Module.registerBlockType;
App.getBlockTypeModel = Module.getBlockTypeModel;
App.getBlockTypeView = Module.getBlockTypeView;
App.toJSON = Module.toJSON;
App.getNewsletter = Module.getNewsletter;
App.on('start', function(options) { Module.newsletter = new Module.NewsletterModel(options.newsletter);
// TODO: Other newsletter information will be needed as well. });
App._contentContainer = new (this.getBlockTypeModel('container'))(options.newsletter.data, {parse: true});
App._contentContainerView = new (this.getBlockTypeView('container'))({ App.on('start', function(options) {
model: App._contentContainer, // TODO: Other newsletter information will be needed as well.
renderOptions: { depth: 0 }, App._contentContainer = new (this.getBlockTypeModel('container'))(options.newsletter.data, {parse: true});
}); App._contentContainerView = new (this.getBlockTypeView('container'))({
model: App._contentContainer,
renderOptions: { depth: 0 },
});
App._appView.contentRegion.show(App._contentContainerView);
});
});
App._appView.contentRegion.show(App._contentContainerView);
});
}); });

View File

@ -1,26 +1,33 @@
EditorApplication.module("components.heading", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/heading', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.marionette',
], function(EditorApplication, Backbone, Marionette) {
Module.HeadingView = Marionette.ItemView.extend({ EditorApplication.module("components.heading", function(Module, App, Backbone, Marionette, $, _) {
getTemplate: function() { return templates.heading; }, "use strict";
templateHelpers: function() {
return { Module.HeadingView = Marionette.ItemView.extend({
model: this.model.toJSON(), getTemplate: function() { return templates.heading; },
}; templateHelpers: function() {
}, return {
events: function() { model: this.model.toJSON(),
return { };
'keyup .mailpoet_input_title': _.partial(this.changeField, "newsletter_subject"), },
'keyup .mailpoet_input_preheader': _.partial(this.changeField, "newsletter_preheader"), events: function() {
}; return {
}, 'keyup .mailpoet_input_title': _.partial(this.changeField, "newsletter_subject"),
changeField: function(field, event) { 'keyup .mailpoet_input_preheader': _.partial(this.changeField, "newsletter_preheader"),
this.model.set(field, jQuery(event.target).val()); };
}, },
}); changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
});
App.on('start', function(options) {
App._appView.headingRegion.show(new Module.HeadingView({ model: App.getNewsletter() }));
});
});
App.on('start', function(options) {
App._appView.headingRegion.show(new Module.HeadingView({ model: App.getNewsletter() }));
});
}); });

View File

@ -1,169 +1,177 @@
EditorApplication.module("components.save", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/save', [
"use strict"; 'newsletter_editor/App',
var saveTimeout; 'backbone',
'backbone.marionette',
], function(EditorApplication, Backbone, Marionette) {
// Save editor contents to server EditorApplication.module("components.save", function(Module, App, Backbone, Marionette, $, _) {
Module.save = function() { "use strict";
App.getChannel().trigger('beforeEditorSave'); var saveTimeout;
var json = App.toJSON(); // Save editor contents to server
Module.save = function() {
App.getChannel().trigger('beforeEditorSave');
var json = App.toJSON();
// save newsletter // save newsletter
mailpoet_post_wpi('newsletter_save.php', json, function(response) { mailpoet_post_wpi('newsletter_save.php', json, function(response) {
if(response.success !== undefined && response.success === true) { if(response.success !== undefined && response.success === true) {
//MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>"); //MailPoet.Notice.success("<?php _e('Newsletter has been saved.'); ?>");
} else if(response.error !== undefined) { } else if(response.error !== undefined) {
if(response.error.length === 0) { if(response.error.length === 0) {
// TODO: Handle translations // TODO: Handle translations
MailPoet.Notice.error("<?php _e('An unknown error occurred, please check your settings.'); ?>"); MailPoet.Notice.error("<?php _e('An unknown error occurred, please check your settings.'); ?>");
} else { } else {
$(response.error).each(function(i, error) { $(response.error).each(function(i, error) {
MailPoet.Notice.error(error); MailPoet.Notice.error(error);
}); });
} }
} }
App.getChannel().trigger('afterEditorSave', json, response); App.getChannel().trigger('afterEditorSave', json, response);
}, function(error) { }, function(error) {
// TODO: Handle saving errors // TODO: Handle saving errors
App.getChannel().trigger('afterEditorSave', {}, error); App.getChannel().trigger('afterEditorSave', {}, error);
}); });
}; };
Module.SaveView = Marionette.LayoutView.extend({ Module.SaveView = Marionette.LayoutView.extend({
getTemplate: function() { return templates.save; }, getTemplate: function() { return templates.save; },
events: { events: {
'click .mailpoet_save_button': 'save', 'click .mailpoet_save_button': 'save',
'click .mailpoet_save_show_options': 'toggleSaveOptions', 'click .mailpoet_save_show_options': 'toggleSaveOptions',
'click .mailpoet_save_next': 'next', 'click .mailpoet_save_next': 'next',
/* Save as template */ /* Save as template */
'click .mailpoet_save_template': 'toggleSaveAsTemplate', 'click .mailpoet_save_template': 'toggleSaveAsTemplate',
'click .mailpoet_save_as_template': 'saveAsTemplate', 'click .mailpoet_save_as_template': 'saveAsTemplate',
/* Export template */ /* Export template */
'click .mailpoet_save_export': 'exportTemplate', 'click .mailpoet_save_export': 'exportTemplate',
}, },
initialize: function(options) { initialize: function(options) {
App.getChannel().on('beforeEditorSave', this.beforeSave, this); App.getChannel().on('beforeEditorSave', this.beforeSave, this);
App.getChannel().on('afterEditorSave', this.afterSave, this); App.getChannel().on('afterEditorSave', this.afterSave, this);
this.validateNewsletter(App.toJSON()); this.validateNewsletter(App.toJSON());
}, },
save: function() { save: function() {
this.hideOptionContents(); this.hideOptionContents();
App.getChannel().trigger('save'); App.getChannel().trigger('save');
}, },
beforeSave: function() { beforeSave: function() {
// TODO: Add a loading animation instead // TODO: Add a loading animation instead
this.$('.mailpoet_autosaved_at').text('Saving...'); this.$('.mailpoet_autosaved_at').text('Saving...');
}, },
afterSave: function(json, response) { afterSave: function(json, response) {
this.validateNewsletter(json); this.validateNewsletter(json);
// Update 'Last saved timer' // Update 'Last saved timer'
this.$('.mailpoet_editor_last_saved').removeClass('mailpoet_hidden'); this.$('.mailpoet_editor_last_saved').removeClass('mailpoet_hidden');
this.$('.mailpoet_autosaved_at').text(response.time); this.$('.mailpoet_autosaved_at').text(response.time);
}, },
toggleSaveOptions: function() { toggleSaveOptions: function() {
this.$('.mailpoet_save_options').toggleClass('mailpoet_hidden'); this.$('.mailpoet_save_options').toggleClass('mailpoet_hidden');
this.$('.mailpoet_save_show_options').toggleClass('mailpoet_save_show_options_active'); this.$('.mailpoet_save_show_options').toggleClass('mailpoet_save_show_options_active');
}, },
toggleSaveAsTemplate: function() { toggleSaveAsTemplate: function() {
this.$('.mailpoet_save_as_template_container').toggleClass('mailpoet_hidden'); this.$('.mailpoet_save_as_template_container').toggleClass('mailpoet_hidden');
this.toggleSaveOptions(); this.toggleSaveOptions();
}, },
showSaveAsTemplate: function() { showSaveAsTemplate: function() {
this.$('.mailpoet_save_as_template_container').removeClass('mailpoet_hidden'); this.$('.mailpoet_save_as_template_container').removeClass('mailpoet_hidden');
this.toggleSaveOptions(); this.toggleSaveOptions();
}, },
hideSaveAsTemplate: function() { hideSaveAsTemplate: function() {
this.$('.mailpoet_save_as_template_container').addClass('mailpoet_hidden'); this.$('.mailpoet_save_as_template_container').addClass('mailpoet_hidden');
}, },
saveAsTemplate: function() { saveAsTemplate: function() {
var templateName = this.$('.mailpoet_save_as_template_name').val(), var templateName = this.$('.mailpoet_save_as_template_name').val(),
templateDescription = this.$('.mailpoet_save_as_template_description').val(); templateDescription = this.$('.mailpoet_save_as_template_description').val();
console.log('Saving template with ', templateName, templateDescription); console.log('Saving template with ', templateName, templateDescription);
this.hideOptionContents(); this.hideOptionContents();
}, },
exportTemplate: function() { exportTemplate: function() {
console.log('Exporting template'); console.log('Exporting template');
this.hideOptionContents(); this.hideOptionContents();
}, },
hideOptionContents: function() { hideOptionContents: function() {
this.hideSaveAsTemplate(); this.hideSaveAsTemplate();
this.$('.mailpoet_save_options').addClass('mailpoet_hidden'); this.$('.mailpoet_save_options').addClass('mailpoet_hidden');
}, },
next: function() { next: function() {
this.hideOptionContents(); this.hideOptionContents();
console.log('Next'); console.log('Next');
window.location.href = App.getConfig().get('urls.send'); window.location.href = App.getConfig().get('urls.send');
}, },
validateNewsletter: function(jsonObject) { validateNewsletter: function(jsonObject) {
if (!App._contentContainer.isValid()) { if (!App._contentContainer.isValid()) {
this.showValidationError(App._contentContainer.validationError); this.showValidationError(App._contentContainer.validationError);
return; return;
} }
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') && if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
JSON.stringify(jsonObject).indexOf("[unsubscribeUrl]") < 0) { JSON.stringify(jsonObject).indexOf("[unsubscribeUrl]") < 0) {
this.showValidationError(App.getConfig().get('translations.unsubscribeLinkMissing')); this.showValidationError(App.getConfig().get('translations.unsubscribeLinkMissing'));
return; return;
} }
this.hideValidationError(); this.hideValidationError();
}, },
showValidationError: function(message) { showValidationError: function(message) {
var $el = this.$('.mailpoet_save_error'); var $el = this.$('.mailpoet_save_error');
$el.text(message); $el.text(message);
$el.removeClass('mailpoet_hidden'); $el.removeClass('mailpoet_hidden');
this.$('.mailpoet_save_next').addClass('button-disabled'); this.$('.mailpoet_save_next').addClass('button-disabled');
}, },
hideValidationError: function() { hideValidationError: function() {
this.$('.mailpoet_save_error').addClass('mailpoet_hidden'); this.$('.mailpoet_save_error').addClass('mailpoet_hidden');
this.$('.mailpoet_save_next').removeClass('button-disabled'); this.$('.mailpoet_save_next').removeClass('button-disabled');
}, },
}); });
Module.autoSave = function() { Module.autoSave = function() {
// Delay in saving editor contents, during which a new autosave // Delay in saving editor contents, during which a new autosave
// may be requested // may be requested
var AUTOSAVE_DELAY_DURATION = 1000; var AUTOSAVE_DELAY_DURATION = 1000;
// Cancel save timer if another change happens before it completes // Cancel save timer if another change happens before it completes
if (saveTimeout) clearTimeout(saveTimeout); if (saveTimeout) clearTimeout(saveTimeout);
saveTimeout = setTimeout(function() { saveTimeout = setTimeout(function() {
App.getChannel().trigger('save'); App.getChannel().trigger('save');
clearTimeout(saveTimeout); clearTimeout(saveTimeout);
saveTimeout = undefined; saveTimeout = undefined;
}, AUTOSAVE_DELAY_DURATION); }, AUTOSAVE_DELAY_DURATION);
}; };
Module.beforeExitWithUnsavedChanges = function(e) { Module.beforeExitWithUnsavedChanges = function(e) {
if (saveTimeout) { if (saveTimeout) {
// TODO: Translate this message // TODO: Translate this message
var message = "There are unsaved changes which will be lost if you leave this page."; var message = "There are unsaved changes which will be lost if you leave this page.";
e = e || window.event; e = e || window.event;
if (e) { if (e) {
e.returnValue = message; e.returnValue = message;
} }
return message; return message;
} }
}; };
App.on('before:start', function(options) { App.on('before:start', function(options) {
App.save = Module.save; App.save = Module.save;
App.getChannel().on('autoSave', Module.autoSave); App.getChannel().on('autoSave', Module.autoSave);
window.onbeforeunload = Module.beforeExitWithUnsavedChanges; window.onbeforeunload = Module.beforeExitWithUnsavedChanges;
App.getChannel().on('save', function() { App.save(); }); App.getChannel().on('save', function() { App.save(); });
}); });
App.on('start', function(options) {
var saveView = new Module.SaveView();
App._appView.bottomRegion.show(saveView);
});
});
App.on('start', function(options) {
var saveView = new Module.SaveView();
App._appView.bottomRegion.show(saveView);
});
}); });

View File

@ -1,249 +1,259 @@
EditorApplication.module("components.sidebar", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/sidebar', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
'sticky-kit',
], function(EditorApplication, Backbone, SuperModel, Marionette) {
// Widget handlers for use to create new content blocks via drag&drop EditorApplication.module("components.sidebar", function(Module, App, Backbone, Marionette, $, _) {
Module._contentWidgets = new (Backbone.Collection.extend({ "use strict";
model: Backbone.SuperModel.extend({
defaults: {
name: '',
priority: 100,
widgetView: undefined,
},
}),
comparator: 'priority',
}))();
Module.registerWidget = function(widget) { return Module._contentWidgets.add(widget); };
Module.getWidgets = function() { return Module._contentWidgets; };
// Layout widget handlers for use to create new layout blocks via drag&drop // Widget handlers for use to create new content blocks via drag&drop
Module._layoutWidgets = new (Backbone.Collection.extend({ Module._contentWidgets = new (Backbone.Collection.extend({
model: Backbone.SuperModel.extend({ model: SuperModel.extend({
defaults: { defaults: {
name: '', name: '',
priority: 100, priority: 100,
widgetView: undefined, widgetView: undefined,
}, },
}), }),
comparator: 'priority', comparator: 'priority',
}))(); }))();
Module.registerLayoutWidget = function(widget) { return Module._layoutWidgets.add(widget); }; Module.registerWidget = function(widget) { return Module._contentWidgets.add(widget); };
Module.getLayoutWidgets = function() { return Module._layoutWidgets; }; Module.getWidgets = function() { return Module._contentWidgets; };
var SidebarView = Backbone.Marionette.LayoutView.extend({ // Layout widget handlers for use to create new layout blocks via drag&drop
getTemplate: function() { return templates.sidebar; }, Module._layoutWidgets = new (Backbone.Collection.extend({
regions: { model: SuperModel.extend({
contentRegion: '.mailpoet_content_region', defaults: {
layoutRegion: '.mailpoet_layout_region', name: '',
stylesRegion: '.mailpoet_styles_region', priority: 100,
previewRegion: '.mailpoet_preview_region', widgetView: undefined,
}, },
events: { }),
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) { comparator: 'priority',
this.$el.find('.mailpoet_sidebar_region').addClass('closed'); }))();
this.$el.find(event.target).parent().parent().removeClass('closed'); Module.registerLayoutWidget = function(widget) { return Module._layoutWidgets.add(widget); };
}, Module.getLayoutWidgets = function() { return Module._layoutWidgets; };
},
initialize: function(options) {
$(window)
.on('resize', this.updateHorizontalScroll.bind(this))
.on('scroll', this.updateHorizontalScroll.bind(this));
},
onRender: function() {
this.contentRegion.show(new Module.SidebarWidgetsView({
collection: App.getWidgets(),
}));
this.layoutRegion.show(new Module.SidebarLayoutWidgetsView({
collection: App.getLayoutWidgets(),
}));
this.stylesRegion.show(new Module.SidebarStylesView({
model: App.getGlobalStyles(),
availableStyles: App.getAvailableStyles(),
}));
this.previewRegion.show(new Module.SidebarPreviewView());
},
updateHorizontalScroll: function() {
// Fixes the sidebar so that on narrower screens the horizontal
// position of the sidebar would be scrollable and not fixed
// partially out of visible screen
this.$el.parent().each(function () {
var calculated_left, self;
self = $(this); var SidebarView = Backbone.Marionette.LayoutView.extend({
getTemplate: function() { return templates.sidebar; },
regions: {
contentRegion: '.mailpoet_content_region',
layoutRegion: '.mailpoet_layout_region',
stylesRegion: '.mailpoet_styles_region',
previewRegion: '.mailpoet_preview_region',
},
events: {
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function(event) {
this.$el.find('.mailpoet_sidebar_region').addClass('closed');
this.$el.find(event.target).parent().parent().removeClass('closed');
},
},
initialize: function(options) {
$(window)
.on('resize', this.updateHorizontalScroll.bind(this))
.on('scroll', this.updateHorizontalScroll.bind(this));
},
onRender: function() {
this.contentRegion.show(new Module.SidebarWidgetsView({
collection: App.getWidgets(),
}));
this.layoutRegion.show(new Module.SidebarLayoutWidgetsView({
collection: App.getLayoutWidgets(),
}));
this.stylesRegion.show(new Module.SidebarStylesView({
model: App.getGlobalStyles(),
availableStyles: App.getAvailableStyles(),
}));
this.previewRegion.show(new Module.SidebarPreviewView());
},
updateHorizontalScroll: function() {
// Fixes the sidebar so that on narrower screens the horizontal
// position of the sidebar would be scrollable and not fixed
// partially out of visible screen
this.$el.parent().each(function () {
var calculated_left, self;
if (self.css('position') === 'fixed') { self = $(this);
calculated_left = self.parent().offset().left - $(window).scrollLeft();
self.css('left', calculated_left + 'px');
} else {
self.css('left', '');
}
});
},
onDomRefresh: function() {
var that = this;
this.$el.parent().stick_in_parent({
offset_top: 32,
});
this.$el.parent().on('sticky_kit:stick', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:unstick', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:bottom', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:unbottom', this.updateHorizontalScroll.bind(this));
},
});
/** if (self.css('position') === 'fixed') {
* Responsible for rendering draggable content widgets calculated_left = self.parent().offset().left - $(window).scrollLeft();
*/ self.css('left', calculated_left + 'px');
Module.SidebarWidgetsView = Backbone.Marionette.CompositeView.extend({ } else {
getTemplate: function() { return templates.sidebarContent; }, self.css('left', '');
getChildView: function(model) { }
return model.get('widgetView'); });
}, },
childViewContainer: '.mailpoet_region_content', onDomRefresh: function() {
}); var that = this;
this.$el.parent().stick_in_parent({
offset_top: 32,
});
this.$el.parent().on('sticky_kit:stick', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:unstick', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:bottom', this.updateHorizontalScroll.bind(this));
this.$el.parent().on('sticky_kit:unbottom', this.updateHorizontalScroll.bind(this));
},
});
/** /**
* Responsible for rendering draggable layout widgets * Responsible for rendering draggable content widgets
*/ */
Module.SidebarLayoutWidgetsView = Module.SidebarWidgetsView.extend({ Module.SidebarWidgetsView = Backbone.Marionette.CompositeView.extend({
getTemplate: function() { return templates.sidebarLayout; }, getTemplate: function() { return templates.sidebarContent; },
}); getChildView: function(model) {
/** return model.get('widgetView');
* Responsible for managing global styles },
*/ childViewContainer: '.mailpoet_region_content',
Module.SidebarStylesView = Backbone.Marionette.LayoutView.extend({ });
getTemplate: function() { return templates.sidebarStyles; },
events: function() {
return {
"change #mailpoet_text_font_color": _.partial(this.changeColorField, 'text.fontColor'),
"change #mailpoet_text_font_family": function(event) {
this.model.set('text.fontFamily', event.target.value);
},
"change #mailpoet_text_font_size": function(event) {
this.model.set('text.fontSize', event.target.value);
},
"change #mailpoet_h1_font_color": _.partial(this.changeColorField, 'h1.fontColor'),
"change #mailpoet_h1_font_family": function(event) {
this.model.set('h1.fontFamily', event.target.value);
},
"change #mailpoet_h1_font_size": function(event) {
this.model.set('h1.fontSize', event.target.value);
},
"change #mailpoet_h2_font_color": _.partial(this.changeColorField, 'h2.fontColor'),
"change #mailpoet_h2_font_family": function(event) {
this.model.set('h2.fontFamily', event.target.value);
},
"change #mailpoet_h2_font_size": function(event) {
this.model.set('h2.fontSize', event.target.value);
},
"change #mailpoet_h3_font_color": _.partial(this.changeColorField, 'h3.fontColor'),
"change #mailpoet_h3_font_family": function(event) {
this.model.set('h3.fontFamily', event.target.value);
},
"change #mailpoet_h3_font_size": function(event) {
this.model.set('h3.fontSize', event.target.value);
},
"change #mailpoet_a_font_color": _.partial(this.changeColorField, 'link.fontColor'),
"change #mailpoet_a_font_underline": function(event) {
this.model.set('link.textDecoration', (event.target.checked) ? event.target.value : 'none');
},
"change #mailpoet_newsletter_background_color": _.partial(this.changeColorField, 'newsletter.backgroundColor'),
"change #mailpoet_background_color": _.partial(this.changeColorField, 'background.backgroundColor'),
};
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: this.availableStyles.toJSON(),
};
},
initialize: function(options) {
this.availableStyles = options.availableStyles;
var that = this;
},
onRender: function() {
var that = this;
this.$('.mailpoet_color').spectrum({
clickoutFiresChange: true,
showInput: true,
showInitial: true,
preferredFormat: "hex6",
allowEmpty: true,
});
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
changeColorField: function(field, event) {
var value = jQuery(event.target).val();
if (value === '') {
value = 'transparent';
}
this.model.set(field, value);
},
});
Module.SidebarPreviewView = Backbone.Marionette.LayoutView.extend({ /**
getTemplate: function() { return templates.sidebarPreview; }, * Responsible for rendering draggable layout widgets
events: { */
'click .mailpoet_show_preview': 'showPreview', Module.SidebarLayoutWidgetsView = Module.SidebarWidgetsView.extend({
'click #mailpoet_send_preview': 'sendPreview', getTemplate: function() { return templates.sidebarLayout; },
}, });
showPreview: function() { /**
var json = App.toJSON(); * Responsible for managing global styles
*/
Module.SidebarStylesView = Backbone.Marionette.LayoutView.extend({
getTemplate: function() { return templates.sidebarStyles; },
events: function() {
return {
"change #mailpoet_text_font_color": _.partial(this.changeColorField, 'text.fontColor'),
"change #mailpoet_text_font_family": function(event) {
this.model.set('text.fontFamily', event.target.value);
},
"change #mailpoet_text_font_size": function(event) {
this.model.set('text.fontSize', event.target.value);
},
"change #mailpoet_h1_font_color": _.partial(this.changeColorField, 'h1.fontColor'),
"change #mailpoet_h1_font_family": function(event) {
this.model.set('h1.fontFamily', event.target.value);
},
"change #mailpoet_h1_font_size": function(event) {
this.model.set('h1.fontSize', event.target.value);
},
"change #mailpoet_h2_font_color": _.partial(this.changeColorField, 'h2.fontColor'),
"change #mailpoet_h2_font_family": function(event) {
this.model.set('h2.fontFamily', event.target.value);
},
"change #mailpoet_h2_font_size": function(event) {
this.model.set('h2.fontSize', event.target.value);
},
"change #mailpoet_h3_font_color": _.partial(this.changeColorField, 'h3.fontColor'),
"change #mailpoet_h3_font_family": function(event) {
this.model.set('h3.fontFamily', event.target.value);
},
"change #mailpoet_h3_font_size": function(event) {
this.model.set('h3.fontSize', event.target.value);
},
"change #mailpoet_a_font_color": _.partial(this.changeColorField, 'link.fontColor'),
"change #mailpoet_a_font_underline": function(event) {
this.model.set('link.textDecoration', (event.target.checked) ? event.target.value : 'none');
},
"change #mailpoet_newsletter_background_color": _.partial(this.changeColorField, 'newsletter.backgroundColor'),
"change #mailpoet_background_color": _.partial(this.changeColorField, 'background.backgroundColor'),
};
},
templateHelpers: function() {
return {
model: this.model.toJSON(),
availableStyles: this.availableStyles.toJSON(),
};
},
initialize: function(options) {
this.availableStyles = options.availableStyles;
var that = this;
},
onRender: function() {
var that = this;
this.$('.mailpoet_color').spectrum({
clickoutFiresChange: true,
showInput: true,
showInitial: true,
preferredFormat: "hex6",
allowEmpty: true,
});
},
changeField: function(field, event) {
this.model.set(field, jQuery(event.target).val());
},
changeColorField: function(field, event) {
var value = jQuery(event.target).val();
if (value === '') {
value = 'transparent';
}
this.model.set(field, value);
},
});
mailpoet_post_json('newsletter_render.php', { data: json }, function(response) { Module.SidebarPreviewView = Backbone.Marionette.LayoutView.extend({
console.log('Should open a new window'); getTemplate: function() { return templates.sidebarPreview; },
window.open('data:text/html,' + encodeURIComponent(response), '_blank'); events: {
}, function(error) { 'click .mailpoet_show_preview': 'showPreview',
console.log('Preview error', json); 'click #mailpoet_send_preview': 'sendPreview',
alert('Something went wrong, check console'); },
}); showPreview: function() {
}, var json = App.toJSON();
sendPreview: function() {
// testing sending method
console.log('trying to send a preview');
// get form data
var data = {
from_name: this.$('#mailpoet_preview_from_name').val(),
from_email: this.$('#mailpoet_preview_from_email').val(),
to_email: this.$('#mailpoet_preview_to_email').val(),
newsletter: App.newsletterId,
};
// send test email mailpoet_post_json('newsletter_render.php', { data: json }, function(response) {
MailPoet.Modal.loading(true); console.log('Should open a new window');
window.open('data:text/html,' + encodeURIComponent(response), '_blank');
}, function(error) {
console.log('Preview error', json);
alert('Something went wrong, check console');
});
},
sendPreview: function() {
// testing sending method
console.log('trying to send a preview');
// get form data
var data = {
from_name: this.$('#mailpoet_preview_from_name').val(),
from_email: this.$('#mailpoet_preview_from_email').val(),
to_email: this.$('#mailpoet_preview_to_email').val(),
newsletter: App.newsletterId,
};
mailpoet_post_wpi('newsletter_preview.php', data, function(response) { // send test email
if(response.success !== undefined && response.success === true) { MailPoet.Modal.loading(true);
MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent'));
} else if(response.error !== undefined) {
if(response.error.length === 0) {
MailPoet.Notice.error(App.getConfig().get('translations.unknownErrorOccurred'));
} else {
$(response.error).each(function(i, error) {
MailPoet.Notice.error(error);
});
}
}
MailPoet.Modal.loading(false);
}, function(error) {
// an error occurred
MailPoet.Modal.loading(false);
});
},
});
App.on('before:start', function(options) { mailpoet_post_wpi('newsletter_preview.php', data, function(response) {
App.registerWidget = Module.registerWidget; if(response.success !== undefined && response.success === true) {
App.getWidgets = Module.getWidgets; MailPoet.Notice.success(App.getConfig().get('translations.testEmailSent'));
App.registerLayoutWidget = Module.registerLayoutWidget; } else if(response.error !== undefined) {
App.getLayoutWidgets = Module.getLayoutWidgets; if(response.error.length === 0) {
}); MailPoet.Notice.error(App.getConfig().get('translations.unknownErrorOccurred'));
} else {
$(response.error).each(function(i, error) {
MailPoet.Notice.error(error);
});
}
}
MailPoet.Modal.loading(false);
}, function(error) {
// an error occurred
MailPoet.Modal.loading(false);
});
},
});
App.on('start', function(options) { App.on('before:start', function(options) {
var stylesModel = App.getGlobalStyles(), App.registerWidget = Module.registerWidget;
sidebarView = new SidebarView(); App.getWidgets = Module.getWidgets;
App.registerLayoutWidget = Module.registerLayoutWidget;
App.getLayoutWidgets = Module.getLayoutWidgets;
});
App.on('start', function(options) {
var stylesModel = App.getGlobalStyles(),
sidebarView = new SidebarView();
App._appView.sidebarRegion.show(sidebarView);
});
});
App._appView.sidebarRegion.show(sidebarView);
});
}); });

View File

@ -1,75 +1,84 @@
EditorApplication.module("components.styles", function(Module, App, Backbone, Marionette, $, _) { define('newsletter_editor/components/styles', [
"use strict"; 'newsletter_editor/App',
'backbone',
'backbone.supermodel',
'backbone.marionette',
], function(EditorApplication, Backbone, SuperModel, Marionette) {
Module.StylesModel = Backbone.SuperModel.extend({ EditorApplication.module("components.styles", function(Module, App, Backbone, Marionette, $, _) {
defaults: { "use strict";
text: {
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '16px',
},
h1: {
fontColor: '#111111',
fontFamily: 'Arial Black',
fontSize: '40px'
},
h2: {
fontColor: '#222222',
fontFamily: 'Tahoma',
fontSize: '32px',
},
h3: {
fontColor: '#333333',
fontFamily: 'Verdana',
fontSize: '24px',
},
link: {
fontColor: '#21759B',
textDecoration: 'underline',
},
newsletter: {
backgroundColor: '#ffffff',
},
background: {
backgroundColor: '#cccccc',
},
},
initialize: function() {
this.on('change', function() { App.getChannel().trigger('autoSave'); });
},
});
Module.StylesView = Marionette.ItemView.extend({ Module.StylesModel = SuperModel.extend({
getTemplate: function() { return templates.styles; }, defaults: {
modelEvents: { text: {
'change': 'render', fontColor: '#000000',
}, fontFamily: 'Arial',
}); fontSize: '16px',
},
h1: {
fontColor: '#111111',
fontFamily: 'Arial Black',
fontSize: '40px'
},
h2: {
fontColor: '#222222',
fontFamily: 'Tahoma',
fontSize: '32px',
},
h3: {
fontColor: '#333333',
fontFamily: 'Verdana',
fontSize: '24px',
},
link: {
fontColor: '#21759B',
textDecoration: 'underline',
},
newsletter: {
backgroundColor: '#ffffff',
},
background: {
backgroundColor: '#cccccc',
},
},
initialize: function() {
this.on('change', function() { App.getChannel().trigger('autoSave'); });
},
});
Module._globalStyles = new Backbone.SuperModel(); Module.StylesView = Marionette.ItemView.extend({
Module.getGlobalStyles = function() { getTemplate: function() { return templates.styles; },
return Module._globalStyles; modelEvents: {
}; 'change': 'render',
Module.setGlobalStyles = function(options) { },
Module._globalStyles = new Module.StylesModel(options); });
return Module._globalStyles;
};
Module.getAvailableStyles = function() {
return App.getConfig().get('availableStyles');
};
App.on('before:start', function(options) { Module._globalStyles = new SuperModel();
// Expose style methods to global application Module.getGlobalStyles = function() {
App.getGlobalStyles = Module.getGlobalStyles; return Module._globalStyles;
App.setGlobalStyles = Module.setGlobalStyles; };
Module.setGlobalStyles = function(options) {
Module._globalStyles = new Module.StylesModel(options);
return Module._globalStyles;
};
Module.getAvailableStyles = function() {
return App.getConfig().get('availableStyles');
};
App.getAvailableStyles = Module.getAvailableStyles; App.on('before:start', function(options) {
// Expose style methods to global application
App.getGlobalStyles = Module.getGlobalStyles;
App.setGlobalStyles = Module.setGlobalStyles;
this.setGlobalStyles(options.newsletter.styles); App.getAvailableStyles = Module.getAvailableStyles;
});
this.setGlobalStyles(options.newsletter.styles);
});
App.on('start', function(options) {
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
App._appView.stylesRegion.show(stylesView);
});
});
App.on('start', function(options) {
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
App._appView.stylesRegion.show(stylesView);
});
}); });

File diff suppressed because it is too large Load Diff

View File

@ -1,28 +0,0 @@
var EditorApplication = (function() {
"use strict";
var app = new Backbone.Marionette.Application(), AppView;
// Decoupled communication between application components
app.getChannel = function(channel) {
if (channel === undefined) return app.channel;
return Radio.channel(channel);
};
AppView = Marionette.LayoutView.extend({
el: '#mailpoet_editor',
regions: {
stylesRegion: '#mailpoet_editor_styles',
contentRegion: '#mailpoet_editor_content',
sidebarRegion: '#mailpoet_editor_sidebar',
bottomRegion: '#mailpoet_editor_bottom',
headingRegion: '#mailpoet_editor_heading',
},
});
app.on('start', function(options) {
app._appView = new AppView();
});
return app;
})();

View File

@ -10,50 +10,51 @@
/*jshint unused:false */ /*jshint unused:false */
/*global tinymce:true */ /*global tinymce:true */
define('mailpoet_custom_fields', ['jquery', 'tinymce'], function(jQuery, tinymce) {
tinymce.PluginManager.add('mailpoet_custom_fields', function(editor, url) {
var appendLabelAndClose = function(text) {
editor.insertContent('[' + text + ']');
editor.windowManager.close();
},
generateOnClickFunc = function(id) {
return function() {
appendLabelAndClose(id);
};
};
tinymce.PluginManager.add('mailpoet_custom_fields', function(editor, url) { editor.addButton('mailpoet_custom_fields', {
var appendLabelAndClose = function(text) { icon: 'mailpoet_custom_fields',
editor.insertContent('[' + text + ']'); onclick: function() {
editor.windowManager.close(); var customFields = [],
}, configCustomFields = editor.settings.mailpoet_custom_fields;
generateOnClickFunc = function(id) {
return function() {
appendLabelAndClose(id);
};
};
editor.addButton('mailpoet_custom_fields', { for (var segment in configCustomFields) {
icon: 'mailpoet_custom_fields', if (configCustomFields.hasOwnProperty(segment)) {
onclick: function() { customFields.push({
var customFields = [], type: 'label',
configCustomFields = editor.settings.mailpoet_custom_fields; text: segment,
});
for (var segment in configCustomFields) { for (var i = 0; i < configCustomFields[segment].length; i += 1) {
if (configCustomFields.hasOwnProperty(segment)) { customFields.push({
customFields.push({ type: 'button',
type: 'label', text: configCustomFields[segment][i].text,
text: segment, onClick: generateOnClickFunc(configCustomFields[segment][i].shortcode)
}); });
}
}
}
for (var i = 0; i < configCustomFields[segment].length; i += 1) { // Open window
customFields.push({ editor.windowManager.open({
type: 'button', height: parseInt(editor.getParam("plugin_mailpoet_custom_fields_height", 400)),
text: configCustomFields[segment][i].text, width: parseInt(editor.getParam("plugin_mailpoet_custom_fields_width", 450)),
onClick: generateOnClickFunc(configCustomFields[segment][i].shortcode) autoScroll: true,
}); title: editor.settings.mailpoet_custom_fields_window_title,
} body: customFields,
} buttons: [],
} });
},
// Open window });
editor.windowManager.open({ });
height: parseInt(editor.getParam("plugin_mailpoet_custom_fields_height", 400)),
width: parseInt(editor.getParam("plugin_mailpoet_custom_fields_width", 450)),
autoScroll: true,
title: editor.settings.mailpoet_custom_fields_window_title,
body: customFields,
buttons: [],
});
},
});
}); });

View File

@ -82,6 +82,7 @@ class Menu {
function newsletterEditor() { function newsletterEditor() {
$data = array(); $data = array();
wp_enqueue_media();
echo $this->renderer->render('newsletter/editor.html', $data); echo $this->renderer->render('newsletter/editor.html', $data);
} }
} }

View File

@ -97,12 +97,13 @@
<%= partial('newsletter_editor_template_automated_latest_content_block', 'newsletter/templates/blocks/automatedLatestContent/block.hbs') %> <%= partial('newsletter_editor_template_automated_latest_content_block', 'newsletter/templates/blocks/automatedLatestContent/block.hbs') %>
<%= partial('newsletter_editor_template_automated_latest_content_widget', 'newsletter/templates/blocks/automatedLatestContent/widget.hbs') %> <%= partial('newsletter_editor_template_automated_latest_content_widget', 'newsletter/templates/blocks/automatedLatestContent/widget.hbs') %>
<%= partial('newsletter_editor_template_automated_latest_content_settings', 'newsletter/templates/blocks/automatedLatestContent/settings.hbs') %> <%= partial('newsletter_editor_template_automated_latest_content_settings', 'newsletter/templates/blocks/automatedLatestContent/settings.hbs') %>
<%= partial('newsletter_editor_template_button_block', 'newsletter/templates/blocks/button/block.hbs') %> <%= partial('newsletter_editor_template_button_block', 'newsletter/templates/blocks/button/block.hbs') %>
<%= partial('newsletter_editor_template_button_widget', 'newsletter/templates/blocks/button/widget.hbs') %> <%= partial('newsletter_editor_template_button_widget', 'newsletter/templates/blocks/button/widget.hbs') %>
<%= partial('newsletter_editor_template_button_settings', 'newsletter/templates/blocks/button/settings.hbs') %> <%= partial('newsletter_editor_template_button_settings', 'newsletter/templates/blocks/button/settings.hbs') %>
<%= partial('newsletter_editor_template_container_block', 'newsletter/templates/blocks/container/block.hbs') %> <%= partial('newsletter_editor_template_container_block', 'newsletter/templates/blocks/container/block.hbs') %>
<%= partial('newsletter_editor_template_container_block', 'newsletter/templates/blocks/container/emptyBlock.hbs') %> <%= partial('newsletter_editor_template_container_block_empty', 'newsletter/templates/blocks/container/emptyBlock.hbs') %>
<%= partial('newsletter_editor_template_container_one_column_widget', 'newsletter/templates/blocks/container/oneColumnLayoutWidget.hbs') %> <%= partial('newsletter_editor_template_container_one_column_widget', 'newsletter/templates/blocks/container/oneColumnLayoutWidget.hbs') %>
<%= partial('newsletter_editor_template_container_two_column_widget', 'newsletter/templates/blocks/container/twoColumnLayoutWidget.hbs') %> <%= partial('newsletter_editor_template_container_two_column_widget', 'newsletter/templates/blocks/container/twoColumnLayoutWidget.hbs') %>
<%= partial('newsletter_editor_template_container_three_column_widget', 'newsletter/templates/blocks/container/threeColumnLayoutWidget.hbs') %> <%= partial('newsletter_editor_template_container_three_column_widget', 'newsletter/templates/blocks/container/threeColumnLayoutWidget.hbs') %>
@ -133,6 +134,7 @@
<%= partial('newsletter_editor_template_posts_settings_single_post', 'newsletter/templates/blocks/posts/settingsSinglePost.hbs') %> <%= partial('newsletter_editor_template_posts_settings_single_post', 'newsletter/templates/blocks/posts/settingsSinglePost.hbs') %>
<%= partial('newsletter_editor_template_social_block', 'newsletter/templates/blocks/social/block.hbs') %> <%= partial('newsletter_editor_template_social_block', 'newsletter/templates/blocks/social/block.hbs') %>
<%= partial('newsletter_editor_template_social_block_icon', 'newsletter/templates/blocks/social/blockIcon.hbs') %>
<%= partial('newsletter_editor_template_social_widget', 'newsletter/templates/blocks/social/widget.hbs') %> <%= partial('newsletter_editor_template_social_widget', 'newsletter/templates/blocks/social/widget.hbs') %>
<%= partial('newsletter_editor_template_social_settings', 'newsletter/templates/blocks/social/settings.hbs') %> <%= partial('newsletter_editor_template_social_settings', 'newsletter/templates/blocks/social/settings.hbs') %>
<%= partial('newsletter_editor_template_social_settings_icon', 'newsletter/templates/blocks/social/settingsIcon.hbs') %> <%= partial('newsletter_editor_template_social_settings_icon', 'newsletter/templates/blocks/social/settingsIcon.hbs') %>
@ -152,10 +154,670 @@
<%= partial('newsletter_editor_template_styles', 'newsletter/templates/components/styles.hbs') %> <%= partial('newsletter_editor_template_styles', 'newsletter/templates/components/styles.hbs') %>
<%= partial('newsletter_editor_template_sidebar', 'newsletter/templates/components/sidebar/sidebar.hbs') %> <%= partial('newsletter_editor_template_sidebar', 'newsletter/templates/components/sidebar/sidebar.hbs') %>
<%= partial('newsletter_editor_template_content', 'newsletter/templates/components/sidebar/content.hbs') %> <%= partial('newsletter_editor_template_sidebar_content', 'newsletter/templates/components/sidebar/content.hbs') %>
<%= partial('newsletter_editor_template_layout', 'newsletter/templates/components/sidebar/layout.hbs') %> <%= partial('newsletter_editor_template_sidebar_layout', 'newsletter/templates/components/sidebar/layout.hbs') %>
<%= partial('newsletter_editor_template_preview', 'newsletter/templates/components/sidebar/preview.hbs') %> <%= partial('newsletter_editor_template_sidebar_preview', 'newsletter/templates/components/sidebar/preview.hbs') %>
<%= partial('newsletter_editor_template_styles', 'newsletter/templates/components/sidebar/styles.hbs') %> <%= partial('newsletter_editor_template_sidebar_styles', 'newsletter/templates/components/sidebar/styles.hbs') %>
<%= stylesheet('newsletter_editor.css') %> <%= stylesheet(
'lib/select2/select2.css',
'lib/spectrum.css',
'newsletter_editor.css'
) %>
<%= javascript(
'vendor.js',
'newsletter_editor.js'
) %>
<script type="text/javascript">
var templates = {
styles: Handlebars.compile(jQuery('#newsletter_editor_template_styles').html()),
save: Handlebars.compile(jQuery('#newsletter_editor_template_save').html()),
heading: Handlebars.compile(jQuery('#newsletter_editor_template_heading').html()),
sidebar: Handlebars.compile(jQuery('#newsletter_editor_template_sidebar').html()),
sidebarContent: Handlebars.compile(jQuery('#newsletter_editor_template_sidebar_content').html()),
sidebarLayout: Handlebars.compile(jQuery('#newsletter_editor_template_sidebar_layout').html()),
sidebarStyles: Handlebars.compile(jQuery('#newsletter_editor_template_sidebar_styles').html()),
sidebarPreview: Handlebars.compile(jQuery('#newsletter_editor_template_sidebar_preview').html()),
genericBlockTools: Handlebars.compile(jQuery('#newsletter_editor_template_tools_generic').html()),
containerBlock: Handlebars.compile(jQuery('#newsletter_editor_template_container_block').html()),
containerEmpty: Handlebars.compile(jQuery('#newsletter_editor_template_container_block_empty').html()),
oneColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_one_column_widget').html()),
twoColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_two_column_widget').html()),
threeColumnLayoutInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_container_three_column_widget').html()),
containerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_container_settings').html()),
buttonBlock: Handlebars.compile(jQuery('#newsletter_editor_template_button_block').html()),
buttonInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_button_widget').html()),
buttonBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_button_settings').html()),
dividerBlock: Handlebars.compile(jQuery('#newsletter_editor_template_divider_block').html()),
dividerInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_divider_widget').html()),
dividerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_divider_settings').html()),
footerBlock: Handlebars.compile(jQuery('#newsletter_editor_template_footer_block').html()),
footerInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_footer_widget').html()),
footerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_footer_settings').html()),
headerBlock: Handlebars.compile(jQuery('#newsletter_editor_template_header_block').html()),
headerInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_header_widget').html()),
headerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_header_settings').html()),
imageBlock: Handlebars.compile(jQuery('#newsletter_editor_template_image_block').html()),
imageInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_image_widget').html()),
imageBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_image_settings').html()),
socialBlock: Handlebars.compile(jQuery('#newsletter_editor_template_social_block').html()),
socialIconBlock: Handlebars.compile(jQuery('#newsletter_editor_template_social_block_icon').html()),
socialInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_social_widget').html()),
socialBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_social_settings').html()),
socialSettingsIconSelector: Handlebars.compile(jQuery('#newsletter_editor_template_social_settings_icon_selector').html()),
socialSettingsIcon: Handlebars.compile(jQuery('#newsletter_editor_template_social_settings_icon').html()),
socialSettingsStyles: Handlebars.compile(jQuery('#newsletter_editor_template_social_settings_styles').html()),
spacerBlock: Handlebars.compile(jQuery('#newsletter_editor_template_spacer_block').html()),
spacerInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_spacer_widget').html()),
spacerBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_spacer_settings').html()),
automatedLatestContentBlock: Handlebars.compile(jQuery('#newsletter_editor_template_automated_latest_content_block').html()),
automatedLatestContentInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_automated_latest_content_widget').html()),
automatedLatestContentBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_automated_latest_content_settings').html()),
postsBlock: Handlebars.compile(jQuery('#newsletter_editor_template_posts_block').html()),
postsInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_posts_widget').html()),
postsBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_posts_settings').html()),
postSelectionPostsBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_posts_settings_selection').html()),
emptyPostPostsBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_posts_settings_selection_empty').html()),
singlePostPostsBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_posts_settings_single_post').html()),
displayOptionsPostsBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_posts_settings_display_options').html()),
textBlock: Handlebars.compile(jQuery('#newsletter_editor_template_text_block').html()),
textInsertion: Handlebars.compile(jQuery('#newsletter_editor_template_text_widget').html()),
textBlockSettings: Handlebars.compile(jQuery('#newsletter_editor_template_text_settings').html()),
};
</script>
<script type="text/javascript">
var newsletter = {
data: {},
styles: {
text: {
fontColor: "#565656",
fontFamily: "Arial",
fontSize: "16px"
},
h1: {
fontColor: "#565656",
fontFamily: "Arial",
fontSize: "36px"
},
h2: {
fontColor: "#565656",
fontFamily: "Arial",
fontSize: "26px"
},
h3: {
fontColor: "#565656",
fontFamily: "Arial",
fontSize: "18px"
},
link: {
fontColor: "#a86b6b",
textDecoration: "underline"
},
newsletter: {
backgroundColor: "#999999"
},
background: {
backgroundColor: "#333333"
}
},
}, config = {
availableStyles: {
textSizes: [
'9px', '10px', '11px', '12px', '13px', '14px', '15px', '16px', '17px', '18px', '19px', '20px', '21px', '22px', '23px', '24px',
],
headingSizes: [
'10px', '12px', '14px', '16px', '18px', '20px', '22px', '24px', '26px', '30px', '36px', '40px',
],
fonts: [
'Arial',
'Comic Sans',
'Courier New',
'Georgia',
'Lucida',
'Tahoma',
'Times New Roman',
'Trebuchet MS',
'Verdana',
],
socialIconSets: {
'default': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/01-social/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/01-social/LinkedIn.png' %>',
},
'grey': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/02-grey/LinkedIn.png' %>',
},
'circles': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/03-circles/LinkedIn.png' %>',
},
'full-flat-roundrect': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/04-full-flat-roundrect/LinkedIn.png' %>',
},
'full-gradient-square': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/05-full-gradient-square/LinkedIn.png' %>',
},
'full-symbol-color': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/06-full-symbol-color/LinkedIn.png' %>',
},
'full-symbol-black': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/07-full-symbol-black/LinkedIn.png' %>',
},
'full-symbol-grey': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/08-full-symbol-grey/LinkedIn.png' %>',
},
'line-roundrect': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/09-line-roundrect/LinkedIn.png' %>',
},
'line-square': {
'custom': '<%= 'assets/img/newsletter_editor/social-icons/custom.png' %>',
'facebook': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Facebook.png' %>',
'twitter': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Twitter.png' %>',
'google-plus': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Google-Plus.png' %>',
'youtube': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Youtube.png' %>',
'website': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Website.png' %>',
'email': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Email.png' %>',
'instagram': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Instagram.png' %>',
'pinterest': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/Pinterest.png' %>',
'linkedin': '<%= 'assets/img/newsletter_editor/social-icons/10-line-square/LinkedIn.png' %>',
},
},
dividers: [
'hidden',
'dotted',
'dashed',
'solid',
'double',
'groove',
'ridge',
],
},
socialIcons: {
'facebook': {
title: 'Facebook',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://www.facebook.com',
},
'twitter': {
title: 'Twitter',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://www.twitter.com',
},
'google-plus': {
title: 'Google Plus',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://plus.google.com',
},
'youtube': {
title: 'Youtube',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://www.youtube.com',
},
'website': {
title: '<%= __('Website') %>',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://example.org',
},
'email': {
title: '<%= __('Email') %>',
linkFieldName: '<%= __('Email') %>',
defaultLink: 'mailto:mail@example.org',
},
'instagram': {
title: 'Instagram',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://instagram.com',
},
'pinterest': {
title: 'Pinterest',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://www.pinterest.com',
},
'linkedin': {
title: 'LinkedIn',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://www.linkedin.com',
},
'custom': {
title: '<%= __('Custom') %>',
linkFieldName: '<%= __('Link') %>',
defaultLink: 'http://example.org',
},
},
blockDefaults: {
automatedLatestContent: {
amount: '5',
contentType: 'post', // 'post'|'page'|'mailpoet_page'
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: '<%= __('Author:') %>',
showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: '<%= __('Categories:') %>',
readMoreType: 'button', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: '<%= __('Read more') %>',
url: '[postLink]',
styles: {
block: {
backgroundColor: '#ffffff',
borderColor: '#00ddff',
borderWidth: '1px',
borderRadius: '5px',
borderStyle: 'solid',
width: '120px',
lineHeight: '30px',
fontColor: '#00ddff',
fontFamily: 'Arial',
fontSize: '20px',
textAlign: 'center',
},
},
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {
styles: {
block: {
backgroundColor: 'transparent',
padding: '13px',
borderStyle: 'solid',
borderWidth: '3px',
borderColor: '#aaaaaa',
},
},
},
backgroundColor: '#ffffff',
backgroundColorAlternate: '#eeeeee',
},
button: {
text: '<%= __('Button') %>',
url: 'http://example.org',
styles: {
block: {
backgroundColor: '#2ea1cd',
borderColor: '#0074a2',
borderWidth: '1px',
borderRadius: '7px',
borderStyle: 'solid',
width: '120px',
lineHeight: '35px',
fontColor: '#ffffff',
fontFamily: 'Verdana',
fontSize: '18px',
textAlign: 'center',
},
},
},
container: {
styles: {
block: {
backgroundColor: 'transparent',
},
},
},
divider: {
styles: {
block: {
backgroundColor: 'transparent',
padding: '13px',
borderStyle: 'solid',
borderWidth: '3px',
borderColor: '#aaaaaa',
},
},
},
footer: {
text: '<a href="[unsubscribeUrl]"><%= __('Unsubscribe') %></a> | <a href="[manageSubscriptionUrl]"><%= __('Manage subscription') %></a><br /><b><%= __('Add your postal address here!') %></b>',
styles: {
block: {
backgroundColor: 'transparent',
},
text: {
fontColor: '#222222',
fontFamily: 'Arial',
fontSize: '12px',
textAlign: 'center',
},
link: {
fontColor: '#6cb7d4',
textDecoration: 'none',
},
},
},
image: {
link: 'http://example.org',
src: '<%= 'assets/img/newsletter_editor/pigeon.png' %>',
alt: '<%= __('An image of...') %>',
padded: true,
width: '281px',
height: '190px',
styles: {
block: {
textAlign: 'center',
},
},
},
posts: {
amount: '10',
contentType: 'post', // 'post'|'page'|'mailpoet_page'
postStatus: 'publish', // 'draft'|'pending'|'private'|'publish'|'future'
inclusionType: 'include', // 'include'|'exclude'
displayType: 'excerpt', // 'excerpt'|'full'|'titleOnly'
titleFormat: 'h1', // 'h1'|'h2'|'h3'|'ul'
titlePosition: 'inTextBlock', // 'inTextBlock'|'aboveBlock',
titleAlignment: 'left', // 'left'|'center'|'right'
titleIsLink: false, // false|true
imagePadded: true, // true|false
showAuthor: 'no', // 'no'|'aboveText'|'belowText'
authorPrecededBy: '<%= __('Author:') %>',
showCategories: 'no', // 'no'|'aboveText'|'belowText'
categoriesPrecededBy: '<%= __('Categories:') %>',
readMoreType: 'link', // 'link'|'button'
readMoreText: 'Read more', // 'link'|'button'
readMoreButton: {
text: '<%= __('Read more') %>',
url: '[postLink]',
styles: {
block: {
backgroundColor: '#ffffff',
borderColor: '#000000',
borderWidth: '1px',
borderRadius: '5px',
borderStyle: 'solid',
width: '120px',
lineHeight: '30px',
fontColor: '#000000',
fontFamily: 'Arial',
fontSize: '20px',
textAlign: 'center',
},
},
},
sortBy: 'newest', // 'newest'|'oldest',
showDivider: true, // true|false
divider: {
styles: {
block: {
backgroundColor: 'transparent',
padding: '13px',
borderStyle: 'solid',
borderWidth: '3px',
borderColor: '#aaaaaa',
},
},
},
backgroundColor: '#ffffff',
backgroundColorAlternate: '#eeeeee',
},
social: {
iconSet: 'default',
icons: [
{
type: 'socialIcon',
iconType: 'facebook',
link: 'http://example.com',
image: '<%= 'assets/img/newsletter_editor/social-icons/01-social/Facebook.png' %>',
height: '32px',
width: '32px',
text: '<%= __('Facebook') %>',
},
{
type: 'socialIcon',
iconType: 'twitter',
link: 'http://example.com',
image: '<%= 'assets/img/newsletter_editor/social-icons/01-social/Twitter.png' %>',
height: '32px',
width: '32px',
text: '<%= __('Twitter') %>',
},
],
},
spacer: {
styles: {
block: {
backgroundColor: 'transparent',
height: '40px',
},
},
},
text: {
text: '<%= __('Edit this to insert text') %>',
},
header: {
text: '<%= __('Display problems?') %> <a href="[viewInBrowserUrl]"><%= __('View it in your browser') %></a>',
styles: {
block: {
backgroundColor: 'transparent',
},
text: {
fontColor: '#222222',
fontFamily: 'Arial',
fontSize: '12px',
textAlign: 'center',
},
link: {
fontColor: '#6cb7d4',
textDecoration: 'underline',
},
},
},
},
customFields: {
'<%= __('Subscriber') %>': [
{
text: '<%= __('First Name') %>',
shortcode: 'user:firstname | default:reader',
},
{
text: '<%= __('Last Name') %>',
shortcode: 'user:lastname | default:reader',
},
{
text: '<%= __('Email Address') %>',
shortcode: 'user:email',
},
{
text: '<%= __('Wordpress user display name') %>',
shortcode: 'user:displayname | default:member',
},
{
text: '<%= __('Total of subscribers') %>',
shortcode: 'user:count',
},
],
'<%= __('Newsletter') %>': [
{
text: '<%= __('Newsletter Subject') %>',
shortcode: 'newsletter:subject',
},
],
'<%= __('Post Notifications') %>': [
{
text: '<%= __('Total number of posts or pages') %>',
shortcode: 'newsletter:total',
},
{
text: '<%= __('Latest post title') %>',
shortcode: 'newsletter:post_title',
},
{
text: '<%= __('Issue number') %>',
shortcode: 'newsletter:number',
},
],
'<%= __('Date') %>': [
{
text: '<%= __('Current day of the month number') %>',
shortcode: 'date:d',
},
{
text: '<%= __('Current day of the month in ordinal, ie. 2nd, 3rd, etc.') %>',
shortcode: 'date:dordinal',
},
{
text: '<%= __('Full name of current day') %>',
shortcode: 'date:dtext',
},
{
text: '<%= __('Current month number') %>',
shortcode: 'date:m',
},
{
text: '<%= __('Full name of current month') %>',
shortcode: 'date:mtext',
},
{
text: '<%= __('Year') %>',
shortcode: 'date:y',
},
],
'<%= __('Links') %>': [
{
text: '<%= __('Unsubscribe link') %>',
shortcode: 'global:unsubscribe',
},
{
text: '<%= __('Edit subscription page link') %>',
shortcode: 'global:manage',
},
{
text: '<%= __('View in browser link') %>',
shortcode: 'global:browser',
},
],
'<%= __('Custom fields') %>': [
{
text: '<%= __('Temporary sample custom field') %>',
shortcode: 'custom:samplefield',
},
],
},
translations: {
customFieldsWindowTitle: '<%= __('Select a shortcode') %>',
unsubscribeLinkMissing: '<%= __('Please include an unsubscribe link to continue.') %>',
testEmailSent: '<%= __('Test email succesfully sent!') %>',
unknownErrorOccurred: '<%= __('An unknown error occurred, please check your settings.') %>',
},
sidepanelWidth: '331px',
validation: {
validateUnsubscribeLinkPresent: true, // TODO: Add validation based on whether Mailpoet MTA is used or not
},
urls: {
termSearch: ajaxurl + '?action=mailpoet_ajax&mailpoet_file=search_terms.php',
send: '?page=mailpoet-newsletters&action=send&newsletter=1', // TODO: Add saving path based on newsletter id
imageMissing: '<%= 'assets/img/newsletter_editor/image-missing.svg' %>',
},
};
// start editor
var editor = EditorApplication.start({
newsletter: newsletter,
config: config,
});
</script>
<% endblock %> <% endblock %>

View File

@ -1,2 +1,2 @@
<div class="mailpoet_container_empty">{{#if isRoot}}<?php _e('Drop layout here'); ?>{{else}}<?php _e('Drop content here'); ?>{{/if}}</div> <div class="mailpoet_container_empty">{{#if isRoot}}<%= __('Drop layout here') %>{{else}}<%= __('Drop content here') %>{{/if}}</div>
{{debug}} {{debug}}

View File

@ -17,7 +17,11 @@ baseConfig = {
], ],
alias: { alias: {
'handlebars': 'handlebars/dist/handlebars.js', 'handlebars': 'handlebars/dist/handlebars.js',
}, 'backbone.marionette': 'backbone.marionette/lib/backbone.marionette',
'sticky-kit': 'sticky-kit/jquery.sticky-kit',
//'tinymce': 'tinymce/tinymce.jquery',
//'jquery.tinymce': 'tinymce/jquery.tinymce.min.js',
}
}, },
node: { node: {
fs: 'empty' fs: 'empty'
@ -45,7 +49,54 @@ config.push(_.extend({}, baseConfig, {
'newsletters/newsletters.jsx', 'newsletters/newsletters.jsx',
'newsletters/list.jsx', 'newsletters/list.jsx',
'newsletters/form.jsx' 'newsletters/form.jsx'
] ],
newsletter_editor: [
'underscore',
'backbone',
'backbone.marionette',
'backbone.supermodel/build/backbone.supermodel.amd',
'interact.js',
'backbone.radio',
//'moment-with-locales',
'tinymce/tinymce.jquery.js',
'tinymce/jquery.tinymce.min.js',
//'tinymce',
//'jquery.tinymce',
'select2',
'spectrum-colorpicker',
'sticky-kit',
//'newsletter_editor/tinymce/wplink.js',
//'newsletter_editor/tinymce/mailpoet_custom_fields/plugin.js',
'newsletter_editor/communicationsFix.js',
'newsletter_editor/App',
'newsletter_editor/components/config.js',
'newsletter_editor/components/styles.js',
'newsletter_editor/components/sidebar.js',
'newsletter_editor/components/content.js',
'newsletter_editor/components/heading.js',
'newsletter_editor/components/save.js',
'newsletter_editor/behaviors/BehaviorsLookup.js',
'newsletter_editor/behaviors/ColorPickerBehavior.js',
'newsletter_editor/behaviors/ContainerDropZoneBehavior.js',
'newsletter_editor/behaviors/DraggableBehavior.js',
'newsletter_editor/behaviors/ResizableBehavior.js',
'newsletter_editor/behaviors/SortableBehavior.js',
'newsletter_editor/blocks/base.js',
'newsletter_editor/blocks/container.js',
'newsletter_editor/blocks/button.js',
'newsletter_editor/blocks/image.js',
'newsletter_editor/blocks/divider.js',
'newsletter_editor/blocks/text.js',
'newsletter_editor/blocks/spacer.js',
'newsletter_editor/blocks/footer.js',
'newsletter_editor/blocks/header.js',
'newsletter_editor/blocks/automatedLatestContent.js',
'newsletter_editor/blocks/posts.js',
'newsletter_editor/blocks/social.js',
],
}, },
plugins: [ plugins: [
new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'), new webpack.optimize.CommonsChunkPlugin('vendor', 'vendor.js'),