Compare commits

...

28 Commits
3.0.3 ... 3.0.6

Author SHA1 Message Date
ca157fc91d Release 3.0.6 2017-10-10 16:46:37 +03:00
1fbe5d7bc6 Merge pull request #1138 from mailpoet/parsley
fixing missing parsley method [MAILPOET-1157]
2017-10-10 16:34:18 +03:00
71c031ccf9 fixing missing parsley method 2017-10-10 13:10:49 +00:00
6a956472fe Release 3.0.5 2017-10-10 12:27:16 +03:00
0c4407f43a Merge pull request #1126 from mailpoet/eslint3
Eslint rules [MAILPOET-1084]
2017-10-10 11:46:45 +03:00
b6c864e7a1 Change the JSON API error message
[MAILPOET-1103]
2017-10-10 09:45:50 +01:00
3d9dc6465d Merge pull request #1132 from mailpoet/image_alignment
Add image alignment option to newsletter editor [MAILPOET-1124]
2017-10-10 11:37:11 +03:00
9a6fec094a fixing divider and spacer resize 2017-10-10 08:35:26 +00:00
61af224d7d Fixing image resize 2017-10-09 12:12:47 +00:00
9b41641e97 Tests vars-on-top 2017-10-09 11:17:23 +00:00
4e2e9f6f8f rebasing on master 2017-10-09 11:17:23 +00:00
c29dc8b4c7 fixing tests 2017-10-09 11:15:58 +00:00
98a3c6b156 Tests no-unused-vars 2017-10-09 11:15:58 +00:00
69c540288b Tests one-var 2017-10-09 11:15:58 +00:00
651c9f5692 Tests one-var-declaration-per-line 2017-10-09 11:15:58 +00:00
9ad3778cf7 ES6 rules 2017-10-09 11:15:58 +00:00
c90e0e9f64 ES5 no-unused-vars 2017-10-09 11:15:58 +00:00
cb1730c4e2 ES5 no-var 2017-10-09 11:15:58 +00:00
3dd8a973fd ES5 vars-on-top 2017-10-09 11:15:58 +00:00
c3ea088fca ES5 one-var 2017-10-09 11:15:58 +00:00
a11d6d7868 ES5 one-var-declaration-per-line 2017-10-09 11:15:58 +00:00
a596add838 ES5 block-scoped-var 2017-10-09 11:15:58 +00:00
7e7103ddab ES5 block-scoped-var 2017-10-09 11:15:58 +00:00
0064970ed7 Add a test for an alignment setting of an image block in the editor [MAILPOET-1124] 2017-10-09 13:00:39 +03:00
9d93f3ea95 Add a test for image alignment rendering [MAILPOET-1124] 2017-10-09 09:37:31 +03:00
ff2a3cd19e Release 3.0.4 2017-10-05 12:09:28 +00:00
8419d95ea1 Fix spacer and divider resizing [MAILPOET-1131] 2017-10-05 10:48:52 +01:00
26241afb86 Add image alignment option to newsletter editor [MAILPOET-1124] 2017-10-02 15:30:43 +03:00
71 changed files with 1197 additions and 983 deletions

View File

@ -12,7 +12,6 @@
"prefer-arrow-callback": 0,
"radix": 0,
"no-alert": 0,
"block-scoped-var": 0,
"guard-for-in": 0,
"no-prototype-builtins": 0,
"no-restricted-syntax": 0,
@ -38,20 +37,16 @@
"global-require": 0,
"no-throw-literal": 0,
"no-extra-bind": 0,
"one-var-declaration-per-line": 0,
"consistent-return": 0,
"no-shadow": 0,
"no-underscore-dangle": 0,
"brace-style": 0,
"no-else-return": 0,
"no-use-before-define": 0,
"one-var": 0,
"camelcase": 0,
"padded-blocks": 0,
"strict": 0,
"vars-on-top": 0,
"no-var": 0,
"no-unused-vars": 0,
"space-infix-ops": 0,
"object-shorthand": 0,
"new-parens": 0,
"eol-last": 0,

View File

@ -48,7 +48,7 @@
"camelcase": 0,
"eqeqeq": 0,
"no-lonely-if": 0,
"block-scoped-var": 0,
"space-unary-ops": 0,
"no-extra-bind": 0,
"class-methods-use-this": 0,
"no-case-declarations": 0,
@ -59,12 +59,11 @@
"no-extra-boolean-cast": 0,
"dot-notation": 0,
"no-shadow": 0,
"one-var": 0,
"no-alert": 0,
"one-var-declaration-per-line": 0,
"no-script-url": 0,
"wrap-iife": 0,
"vars-on-top": 0,
"space-infix-ops": 0,
"no-irregular-whitespace": 0,
"padded-blocks": 0,
"no-underscore-dangle": 0
}

View File

@ -9,7 +9,6 @@
},
"rules": {
"import/no-amd": 0,
"one-var": 0,
"no-whitespace-before-property": 0,
"global-require": 0,
"keyword-spacing": 0,
@ -18,14 +17,11 @@
"func-call-spacing": 0,
"max-len": 0,
"space-unary-ops": 0,
"no-unused-vars": 0,
"no-underscore-dangle": 0,
"no-shadow": 0,
"padded-blocks": 0,
"vars-on-top": 0,
"space-before-blocks": 0,
"object-curly-spacing": 0,
"one-var-declaration-per-line": 0,
"func-names": 0,
"space-before-function-paren": 0
}

View File

@ -2,11 +2,10 @@ function requestFailed(errorMessage, xhr) {
if (xhr.responseJSON) {
return xhr.responseJSON;
}
var message = errorMessage.replace('%d', xhr.status);
return {
errors: [
{
message: message
message: errorMessage.replace('%d', xhr.status)
}
]
};
@ -54,21 +53,23 @@ define('ajax', ['mailpoet', 'jquery', 'underscore'], function (mp, jQuery, _) {
};
},
request: function (method, options) {
// set options
var params;
var deferred;
// set options
this.init(options);
// set request params
var params = this.getParams();
// set request params
params = this.getParams();
// remove null values from the data object
// remove null values from the data object
if (_.isObject(params.data)) {
params.data = _.pick(params.data, function (value) {
return (value !== null);
});
}
// ajax request
var deferred = jQuery.post(
// ajax request
deferred = jQuery.post(
this.options.url,
params,
null,

View File

@ -43,9 +43,10 @@ define('date',
},
format: function (date, opts) {
var options = opts || {};
var momentDate;
this.init(options);
var momentDate = Moment(date, this.convertFormat(options.parseFormat));
momentDate = Moment(date, this.convertFormat(options.parseFormat));
if (options.offset === 0) momentDate = momentDate.utc();
return momentDate.format(this.convertFormat(this.options.format));
},
@ -71,6 +72,11 @@ define('date',
});
},
convertFormat: function (format) {
var replacements;
var convertedFormat;
var escapeToken;
var index;
var token;
var format_mappings = {
date: {
d: 'DD',
@ -140,12 +146,11 @@ define('date',
if (!format || format.length <= 0) return format;
var replacements = format_mappings['date'];
replacements = format_mappings['date'];
convertedFormat = [];
escapeToken = false;
var convertedFormat = [];
var escapeToken = false;
for (var index = 0, token = ''; format.charAt(index); index += 1) {
for (index = 0, token = ''; format.charAt(index); index += 1) {
token = format.charAt(index);
if (escapeToken === true) {
convertedFormat.push('[' + token + ']');

View File

@ -7,6 +7,10 @@
'use strict';
var Observable;
var WysijaHistory;
var WysijaForm;
Event.cacheDelegated = {};
Object.extend(document, (function () {
var cache = Event.cacheDelegated;
@ -30,18 +34,21 @@ Object.extend(document, (function () {
}
function destroyWrapper(selector, eventName, handler) {
var wrapper;
var c = getCacheForSelector(selector);
if (!c[eventName]) return false;
var wrapper = findWrapper(selector, eventName, handler);
wrapper = findWrapper(selector, eventName, handler);
c[eventName] = c[eventName].without(wrapper);
return wrapper;
}
function createWrapper(selector, eventName, handler, context) {
var wrapper, c = getWrappersForSelector(selector, eventName);
var wrapper;
var element;
var c = getWrappersForSelector(selector, eventName);
if (c.pluck('handler').include(handler)) return false;
wrapper = function (event) {
var element = event.findElement(selector);
element = event.findElement(selector);
if (element) handler.call(context || element, event, element);
};
wrapper.handler = handler;
@ -49,13 +56,14 @@ Object.extend(document, (function () {
return wrapper;
}
return {
delegate: function (selector, eventName, handler, context) {
delegate: function (selector, eventName) {
var wrapper = createWrapper.apply(null, arguments);
if (wrapper) document.observe(eventName, wrapper);
return document;
},
stopDelegating: function (selector, eventName, handler) {
stopDelegating: function (selector, eventName) {
var length = arguments.length;
var wrapper;
switch (length) {
case 2:
getWrappersForSelector(selector, eventName).each(function (wrapper) {
@ -73,7 +81,7 @@ Object.extend(document, (function () {
});
break;
default:
var wrapper = destroyWrapper.apply(null, arguments);
wrapper = destroyWrapper.apply(null, arguments);
if (wrapper) document.stopObserving(eventName, wrapper);
}
return document;
@ -81,7 +89,7 @@ Object.extend(document, (function () {
};
})());
var Observable = (function () {
Observable = (function () {
function getEventName(nameA, namespace) {
var name = nameA.substring(2);
if (namespace) name = namespace + ':' + name;
@ -89,8 +97,8 @@ var Observable = (function () {
}
function getHandlers(klass) {
var proto = klass.prototype,
namespace = proto.namespace;
var proto = klass.prototype;
var namespace = proto.namespace;
return Object.keys(proto).grep(/^on/).inject(window.$H(), function (handlers, name) {
if (name === 'onDomLoaded') return handlers;
handlers.set(getEventName(name, namespace), getWrapper(proto[name], klass));
@ -111,9 +119,9 @@ var Observable = (function () {
}
return {
observe: function (selector) {
var klass = this;
if (!this.handlers) this.handlers = {};
if (this.handlers[selector]) return;
var klass = this;
if (this.prototype.onDomLoaded) {
if (document.loaded) {
onDomLoad(selector, klass);
@ -146,8 +154,9 @@ Object.extend(window.Droppables, {
return proceed(drop);
}),
show: function (point, element) {
var drop;
var affected = [];
if (!this.drops.length) return;
var drop, affected = [];
this.drops.each(function (drop) {
if (window.Droppables.isAffected(point, element, drop)) affected.push(drop);
});
@ -159,13 +168,13 @@ Object.extend(window.Droppables, {
if (drop !== this.last_active) window.Droppables.activate(drop, element);
}
},
displayArea: function (draggable) {
displayArea: function () {
if (!this.drops.length) return;
// hide controls when displaying drop areas.
WysijaForm.hideBlockControls();
this.drops.each(function (drop, iterator) {
this.drops.each(function (drop) {
if (drop.element.hasClassName('block_placeholder')) {
drop.element.addClassName('active');
}
@ -173,7 +182,7 @@ Object.extend(window.Droppables, {
},
hideArea: function () {
if (!this.drops.length) return;
this.drops.each(function (drop, iterator) {
this.drops.each(function (drop) {
if (drop.element.hasClassName('block_placeholder')) {
drop.element.removeClassName('active');
} else if (drop.element.hasClassName('image_placeholder')) {
@ -195,7 +204,7 @@ Object.extend(window.Droppables, {
- set a maximum number of items to be stored
*/
var WysijaHistory = {
WysijaHistory = {
container: 'mailpoet_form_history',
size: 30,
enqueue: function (element) {
@ -239,7 +248,7 @@ var WysijaHistory = {
};
/* MailPoet Form */
var WysijaForm = {
WysijaForm = {
version: '0.7',
options: {
container: 'mailpoet_form_container',
@ -292,6 +301,7 @@ var WysijaForm = {
});
},
load: function (data) {
var settings_elements;
if (data === undefined) return;
// load body
@ -302,7 +312,7 @@ var WysijaForm = {
});
// load settings
var settings_elements = window.$('mailpoet_form_settings').getElements();
settings_elements = window.$('mailpoet_form_settings').getElements();
settings_elements.each(function (setting) {
// skip lists
if (setting.name === 'segments') {
@ -324,13 +334,13 @@ var WysijaForm = {
}
},
save: function () {
var position = 1,
data = {
name: window.$F('mailpoet_form_name'),
settings: window.$('mailpoet_form_settings').serialize(true),
body: [],
styles: (window.MailPoet.CodeEditor !== undefined) ? window.MailPoet.CodeEditor.getValue() : null
};
var position = 1;
var data = {
name: window.$F('mailpoet_form_name'),
settings: window.$('mailpoet_form_settings').serialize(true),
body: [],
styles: (window.MailPoet.CodeEditor !== undefined) ? window.MailPoet.CodeEditor.getValue() : null
};
// body
WysijaForm.getBlocks().each(function (b) {
var block_data = (typeof (b.block['save']) === 'function') ? b.block.save() : null;
@ -401,6 +411,7 @@ var WysijaForm = {
return data;
},
toggleWidgets: function () {
var hasSegmentSelection;
window.$$('a[wysija_unique="1"]').invoke('removeClassName', 'disabled');
// loop through each unique field already inserted in the editor and disable its toolbar equivalent
@ -411,7 +422,7 @@ var WysijaForm = {
}
});
var hasSegmentSelection = WysijaForm.hasSegmentSelection();
hasSegmentSelection = WysijaForm.hasSegmentSelection();
if (hasSegmentSelection) {
window.$('mailpoet_form_segments').writeAttribute('required', false).disable();
@ -426,8 +437,9 @@ var WysijaForm = {
},
isSegmentSelectionValid: function () {
var segment_selection = window.$$('#' + WysijaForm.options.editor + ' [wysija_id="segments"]')[0];
var block;
if (segment_selection !== undefined) {
var block = WysijaForm.get(segment_selection).block.getData();
block = WysijaForm.get(segment_selection).block.getData();
return (
(block.params.values !== undefined)
&&
@ -437,10 +449,12 @@ var WysijaForm = {
return false;
},
setBlockPositions: function (event, target) {
var index = 1;
var block_placeholder;
var previous_placeholder;
// release dragging lock
WysijaForm.locks.dragging = false;
var index = 1;
WysijaForm.getBlocks().each(function (container) {
container.setPosition(index++);
// remove z-index value to avoid issues when resizing images
@ -453,8 +467,8 @@ var WysijaForm = {
if (target !== undefined) {
// get placeholders (previous placeholder matches the placeholder linked to the next block)
var block_placeholder = window.$(target.element.readAttribute('wysija_placeholder')),
previous_placeholder = target.element.previous('.block_placeholder');
block_placeholder = window.$(target.element.readAttribute('wysija_placeholder'));
previous_placeholder = target.element.previous('.block_placeholder');
if (block_placeholder !== null) {
// put block placeholder before the current block
@ -479,23 +493,17 @@ var WysijaForm = {
},
setSettingsPosition: function () {
// get viewport offsets and dimensions
var viewportHeight = document.viewport.getHeight(),
blockPadding = 5;
var viewportHeight = document.viewport.getHeight();
window.$(WysijaForm.options.container).select('.wysija_settings').each(function (element) {
// get parent dimensions and position
var parentDim = element.up('.mailpoet_form_block').getDimensions(),
parentPos = element.up('.mailpoet_form_block').cumulativeOffset(),
is_visible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight)),
buttonMargin = 5,
relativeTop = buttonMargin;
var parentDim = element.up('.mailpoet_form_block').getDimensions();
var parentPos = element.up('.mailpoet_form_block').cumulativeOffset();
var is_visible = (parentPos.top <= (WysijaForm.scroll.top + viewportHeight));
var buttonMargin = 5;
var relativeTop = buttonMargin;
if (is_visible) {
// desired position is set to center of viewport
var absoluteTop = parseInt(WysijaForm.scroll.top + ((viewportHeight / 2) - (element.getHeight() / 2)), 10),
parentTop = parseInt(parentPos.top - blockPadding, 10),
parentBottom = parseInt(parentPos.top + parentDim.height - blockPadding, 10);
// always center
relativeTop = parseInt((parentDim.height / 2) - (element.getHeight() / 2), 10);
}
@ -519,9 +527,10 @@ var WysijaForm = {
},
setToolbarPosition: function () {
var position;
WysijaForm.initToolbarPosition();
var position = {
position = {
top: WysijaForm.toolbar.y + 'px',
visibility: 'visible'
};
@ -585,10 +594,12 @@ var WysijaForm = {
instances: {},
get: function (element, typ) {
var type = typ;
var id;
var instance;
if (type === undefined) type = 'block';
// identify element
var id = element.identify();
var instance = WysijaForm.instances[id] || new WysijaForm[type.capitalize().camelize()](id);
id = element.identify();
instance = WysijaForm.instances[id] || new WysijaForm[type.capitalize().camelize()](id);
WysijaForm.instances[id] = instance;
return instance;
@ -636,8 +647,8 @@ var WysijaForm = {
},
encodeURIComponent: function (str) {
// check if it's a url and if so, prevent encoding of protocol
var regexp = new RegExp(/^http[s]?:\/\//),
protocol = regexp.exec(str);
var regexp = new RegExp(/^http[s]?:\/\//);
var protocol = regexp.exec(str);
if (protocol === null) {
// this is not a url so encode the whole thing
@ -680,13 +691,13 @@ WysijaForm.DraggableItem = window.Class.create({
},
STYLES: new window.Template('position: absolute; top: #{top}px; left: #{left}px;'),
cloneElement: function () {
var clone = this.element.clone(),
offset = this.element.cumulativeOffset(),
list = this.getList(),
styles = this.STYLES.evaluate({
top: offset.top - list.scrollTop,
left: offset.left - list.scrollLeft
});
var clone = this.element.clone();
var offset = this.element.cumulativeOffset();
var list = this.getList();
var styles = this.STYLES.evaluate({
top: offset.top - list.scrollTop,
left: offset.left - list.scrollLeft
});
clone.setStyle(styles);
clone.addClassName('mailpoet_form_widget');
@ -767,15 +778,17 @@ WysijaForm.Block = window.Class.create({
}
},
makeBlockDroppable: function () {
var block_placeholder;
if (this.isBlockDroppableEnabled() === false) {
var block_placeholder = this.getBlockDroppable();
block_placeholder = this.getBlockDroppable();
window.Droppables.add(block_placeholder.identify(), WysijaForm.blockDropOptions);
block_placeholder.addClassName('enabled');
}
},
removeBlockDroppable: function () {
var block_placeholder;
if (this.isBlockDroppableEnabled()) {
var block_placeholder = this.getBlockDroppable();
block_placeholder = this.getBlockDroppable();
window.Droppables.remove(block_placeholder.identify());
block_placeholder.removeClassName('enabled');
}
@ -807,6 +820,7 @@ WysijaForm.Block = window.Class.create({
return this.element.down('.wysija_controls');
},
setupControls: function () {
var block;
// enable controls
this.controls = this.getControls();
@ -857,9 +871,8 @@ WysijaForm.Block = window.Class.create({
if (this.settingsButton !== null) {
this.settingsButton.observe('click', function (event) {
// TODO: refactor
var block = window.$(event.target).up('.mailpoet_form_block') || null;
block = window.$(event.target).up('.mailpoet_form_block') || null;
if (block !== null) {
var field = WysijaForm.getFieldData(block);
this.editSettings();
}
}.bind(this));
@ -905,18 +918,23 @@ WysijaForm.Block = window.Class.create({
/* Invoked on item dropped */
WysijaForm.Block.create = function (createBlock, target) {
var block = createBlock;
var body;
var block_template;
var template;
var output;
var settings_segments;
if (window.$('form_template_' + block.type) === null) {
return false;
}
var body = window.$(WysijaForm.options.body),
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML),
template = window.Handlebars.compile(window.$('form_template_' + block.type).innerHTML),
output = '';
body = window.$(WysijaForm.options.body);
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
template = window.Handlebars.compile(window.$('form_template_' + block.type).innerHTML);
output = '';
if (block.type === 'segment') {
if (block.params.values === undefined) {
var settings_segments = window.jQuery('#mailpoet_form_segments').val();
settings_segments = window.jQuery('#mailpoet_form_segments').val();
if (settings_segments !== null && settings_segments.length > 0) {
block.params.values = window.mailpoet_segments.filter(function (segment) {
return (settings_segments.indexOf(segment.id) !== -1);
@ -936,14 +954,13 @@ WysijaForm.Block.create = function (createBlock, target) {
}
// if the drop target was the bottom placeholder
var element = null;
if (target.identify() === 'block_placeholder') {
// insert block at the bottom
element = body.insert(output);
body.insert(output);
// block = body.childElements().last();
} else {
// insert block before the drop target
element = target.insert({
target.insert({
before: output
});
// block = target.previous('.mailpoet_form_block');
@ -984,8 +1001,8 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
this.setupControls();
},
save: function () {
info('widget -> save');
var data = this.getData();
info('widget -> save');
if (data.element !== undefined) {
delete data.element;
@ -994,8 +1011,8 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
return data;
},
setData: function (data) {
var current_data = this.getData(),
params = window.$H(current_data.params).merge(data.params).toObject();
var current_data = this.getData();
var params = window.$H(current_data.params).merge(data.params).toObject();
// update type if it changed
if (data.type !== undefined && data.type !== current_data.type) {
@ -1020,16 +1037,20 @@ WysijaForm.Widget = window.Class.create(WysijaForm.Block, {
this.removeBlock();
},
redraw: function (data) {
var options;
var block_template;
var template;
var params;
// set parameters
this.setData(data);
var options = this.getData();
options = this.getData();
// redraw block
var block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML),
template = window.Handlebars.compile(window.$('form_template_' + options.type).innerHTML),
data = window.$H(options).merge({
template: template(options)
}).toObject();
this.element.replace(block_template(data));
block_template = window.Handlebars.compile(window.$('form_template_block').innerHTML);
template = window.Handlebars.compile(window.$('form_template_' + options.type).innerHTML);
params = window.$H(options).merge({
template: template(options)
}).toObject();
this.element.replace(block_template(params));
WysijaForm.init();
},
@ -1061,8 +1082,8 @@ function info(value) {
var noop = function () {};
var methods = ['assert', 'clear', 'count', 'debug', 'dir', 'dirxml', 'error', 'exception', 'group', 'groupCollapsed', 'groupEnd', 'info', 'log', 'markTimeline', 'profile', 'profileEnd', 'markTimeline', 'table', 'time', 'timeEnd', 'timeStamp', 'trace', 'warn'];
var length = methods.length;
window.console = {};
var console = {};
window.console = {};
while (length--) {
console[methods[length]] = noop;
}

View File

@ -1,26 +1,28 @@
define('handlebars_helpers', ['handlebars'], function (Handlebars) {
// Handlebars helpers
Handlebars.registerHelper('concat', function () {
var size = (arguments.length - 1),
output = '';
for (var i = 0; i < size; i++) {
var size = (arguments.length - 1);
var output = '';
var i;
for (i = 0; i < size; i++) {
output += arguments[i];
}
return output;
});
Handlebars.registerHelper('number_format', function (value, block) {
Handlebars.registerHelper('number_format', function (value) {
return Number(value).toLocaleString();
});
Handlebars.registerHelper('date_format', function (timestamp, block) {
var f;
if (window.moment) {
if (timestamp === undefined || isNaN(timestamp) || timestamp <= 0) {
return;
}
// set date format
var f = block.hash.format || 'MMM Do, YYYY';
// check if we passed a timestamp
// set date format
f = block.hash.format || 'MMM Do, YYYY';
// check if we passed a timestamp
if (parseInt(timestamp, 10) == timestamp) {
return window.moment.unix(timestamp).format(f);
} else {
@ -59,25 +61,24 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
case '||':
return (v1 || v2) ? options.fn(this) : options.inverse(this);
case 'in':
var values = v2.split(',');
return (v2.indexOf(v1) !== -1) ? options.fn(this) : options.inverse(this);
default:
return options.inverse(this);
}
});
Handlebars.registerHelper('nl2br', function (value, block) {
Handlebars.registerHelper('nl2br', function (value) {
return value.gsub('\n', '<br />');
});
Handlebars.registerHelper('json_encode', function (value, block) {
Handlebars.registerHelper('json_encode', function (value) {
return JSON.stringify(value);
});
Handlebars.registerHelper('json_decode', function (value, block) {
Handlebars.registerHelper('json_decode', function (value) {
return JSON.parse(value);
});
Handlebars.registerHelper('url', function (value, block) {
Handlebars.registerHelper('url', function (value) {
var url = window.location.protocol + '//' + window.location.host + window.location.pathname;
return url + value;
@ -90,16 +91,17 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
return value;
}
});
Handlebars.registerHelper('lookup', function (obj, field, options) {
Handlebars.registerHelper('lookup', function (obj, field) {
return obj && obj[field];
});
Handlebars.registerHelper('rsa_key', function (value, block) {
// extract all lines into an array
Handlebars.registerHelper('rsa_key', function (value) {
var lines;
// extract all lines into an array
if (value === undefined) return '';
var lines = value.trim().split('\n');
lines = value.trim().split('\n');
// remove header & footer
lines.shift();
@ -109,7 +111,7 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
return lines.join('');
});
Handlebars.registerHelper('trim', function (value, block) {
Handlebars.registerHelper('trim', function (value) {
if (value === null || value === undefined) return '';
return value.trim();
});
@ -126,10 +128,10 @@ define('handlebars_helpers', ['handlebars'], function (Handlebars) {
*/
Handlebars.registerHelper('ellipsis', function (str, limit, append) {
var strAppend = append;
var sanitized = str.replace(/(<([^>]+)>)/g, '');
if (strAppend === undefined) {
strAppend = '';
}
var sanitized = str.replace(/(<([^>]+)>)/g, '');
if (sanitized.length > limit) {
return sanitized.substr(0, limit - strAppend.length) + strAppend;
} else {

View File

@ -24,20 +24,20 @@ define(
* http://benalman.com/about/license/
*/
$.fn.serializeObject = function (coerce) {
var obj = {},
coerce_types = { true: !0, false: !1, null: null };
var obj = {};
var coerce_types = { true: !0, false: !1, null: null };
// Iterate over all name=value pairs.
$.each(this.serializeArray(), function (j, v) {
var key = v.name,
val = v.value,
cur = obj,
i = 0,
var key = v.name;
var val = v.value;
var cur = obj;
var i = 0;
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
// into its component parts.
keys = key.split(']['),
keys_last = keys.length - 1;
// If key is more complex than 'foo', like 'a[]' or 'a[b][c]', split it
// into its component parts.
var keys = key.split('][');
var keys_last = keys.length - 1;
// If the first keys part contains [ and the last ends with ], then []
// are correctly balanced.
@ -106,4 +106,4 @@ define(
return $;
}
);
);

View File

@ -658,8 +658,8 @@ const Listing = React.createClass({
});
},
handleSelectItem: function (id, is_checked) {
let selected_ids = this.state.selected_ids,
selection = false;
let selected_ids = this.state.selected_ids;
let selection = false;
if (is_checked) {
selected_ids = jQuery.merge(selected_ids, [id]);

View File

@ -116,6 +116,7 @@ define('modal', ['mailpoet', 'jquery'],
}
},
init: function (options) {
var modal;
if (this.initialized === true) {
this.close();
}
@ -134,7 +135,7 @@ define('modal', ['mailpoet', 'jquery'],
if (this.options.type !== null) {
// insert modal depending on its type
if (this.options.type === 'popup') {
var modal = this.compileTemplate(
modal = this.compileTemplate(
this.templates[this.options.type]
);
// create modal
@ -178,7 +179,7 @@ define('modal', ['mailpoet', 'jquery'],
return this;
},
initOverlay: function (toggle) {
initOverlay: function () {
if (jQuery('#mailpoet_modal_overlay').length === 0) {
// insert overlay into the DOM
jQuery('body').append(this.templates.overlay);
@ -343,20 +344,21 @@ define('modal', ['mailpoet', 'jquery'],
return this;
},
setPosition: function () {
var screenWidth;
var screenHeight;
var modalWidth;
var modalHeight;
switch (this.options.type) {
case 'popup':
var screenWidth = jQuery(window).width(),
screenHeight = jQuery(window).height(),
modalWidth = jQuery('.mailpoet_' + this.options.type + '_wrapper').width(),
modalHeight = jQuery('.mailpoet_' + this.options.type + '_wrapper').height();
screenWidth = jQuery(window).width();
screenHeight = jQuery(window).height();
modalWidth = jQuery('.mailpoet_'+ this.options.type +'_wrapper').width();
modalHeight = jQuery('.mailpoet_'+ this.options.type +'_wrapper').height();
var top = Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
left = Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)));
// set position of popup depending on screen dimensions.
// set position of popup depending on screen dimensions.
jQuery('#mailpoet_popup').css({
top: top,
left: left
top: Math.max(48, parseInt((screenHeight / 2) - (modalHeight / 2))),
left: Math.max(0, parseInt((screenWidth / 2) - (modalWidth / 2)))
});
break;
case 'panel':
@ -436,7 +438,7 @@ define('modal', ['mailpoet', 'jquery'],
.removeClass('mailpoet_modal_highlight');
return this;
},
hideModal: function (callback) {
hideModal: function () {
// set modal as closed
this.opened = false;
@ -451,7 +453,7 @@ define('modal', ['mailpoet', 'jquery'],
return this;
},
showOverlay: function (force) {
showOverlay: function () {
jQuery('#mailpoet_modal_overlay').show();
return this;
},

View File

@ -1,12 +1,8 @@
define([
'backbone',
'backbone.marionette',
'backbone.radio',
'jquery',
'underscore',
'handlebars',
'handlebars_helpers'
], function (Backbone, Marionette, BackboneRadio, jQuery, _, Handlebars) {
'backbone.radio'
], function (Backbone, Marionette, BackboneRadio) {
var Radio = BackboneRadio;
var AppView = Marionette.View.extend({

View File

@ -8,13 +8,13 @@ define([
'newsletter_editor/behaviors/BehaviorsLookup',
'mailpoet',
'spectrum'
], function (Marionette, BehaviorsLookup, MailPoet, Spectrum) {
], function (Marionette, BehaviorsLookup, MailPoet) {
var BL = BehaviorsLookup;
BL.ColorPickerBehavior = Marionette.Behavior.extend({
onRender: function () {
var that = this,
preferredFormat = 'hex6';
var that = this;
var preferredFormat = 'hex6';
this.view.$('.mailpoet_color').each(function () {
var $input = that.view.$(this);
var updateColorInput = function (color) {

View File

@ -25,11 +25,11 @@ define([
this.addDropZone();
}
},
addDropZone: function (_event) {
var that = this,
view = this.view,
domElement = that.$el.get(0),
acceptableElementSelector;
addDropZone: function () {
var that = this;
var view = this.view;
var domElement = that.$el.get(0);
var acceptableElementSelector;
// TODO: Extract this limitation code to be controlled from containers
if (this.view.renderOptions.depth === 0) {
@ -47,11 +47,11 @@ define([
interact(domElement).dropzone({
accept: acceptableElementSelector,
overlap: 'pointer', // Mouse pointer denotes location of a droppable
ondragenter: function (event) {
ondragenter: function () {
// 1. Visually mark block as active for dropping
view.$el.addClass('mailpoet_drop_active');
},
ondragleave: function (event) {
ondragleave: function () {
// 1. Remove visual markings of active dropping container
// 2. Remove visual markings of drop position visualization
that.cleanup();
@ -63,20 +63,26 @@ define([
// 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
var dropPosition = that.getDropPosition(
event.dragmove.pageX,
event.dragmove.pageY,
view.$el,
view.model.get('orientation'),
view.model.get('blocks').length
),
element = view.$el,
markerWidth = '',
markerHeight = '',
containerOffset = element.offset(),
viewCollection = that.getCollection(),
marker, targetModel, targetView, targetElement,
topOffset, leftOffset, isLastBlockInsertion,
$targetBlock, margin;
event.dragmove.pageX,
event.dragmove.pageY,
view.$el,
view.model.get('orientation'),
view.model.get('blocks').length
);
var element = view.$el;
var markerWidth = '';
var markerHeight = '';
var containerOffset = element.offset();
var viewCollection = that.getCollection();
var marker;
var targetModel;
var targetView;
var targetElement;
var topOffset;
var leftOffset;
var isLastBlockInsertion;
var $targetBlock;
var margin;
if (dropPosition === undefined) return;
@ -178,15 +184,19 @@ define([
// 4. Perform cleanup actions
var dropPosition = that.getDropPosition(
event.dragEvent.pageX,
event.dragEvent.pageY,
view.$el,
view.model.get('orientation'),
view.model.get('blocks').length
),
droppableModel = event.draggable.getDropModel(),
viewCollection = that.getCollection(),
droppedView, droppedModel, index, tempCollection, tempCollection2;
event.dragEvent.pageX,
event.dragEvent.pageY,
view.$el,
view.model.get('orientation'),
view.model.get('blocks').length
);
var droppableModel = event.draggable.getDropModel();
var viewCollection = that.getCollection();
var droppedView;
var index;
var tempCollection;
var tempCollection2;
var tempModel;
if (dropPosition === undefined) return;
@ -210,7 +220,7 @@ define([
} else {
// Special insertion by replacing target block with collection
// and inserting dropModel into that
var tempModel = viewCollection.at(dropPosition.index);
tempModel = viewCollection.at(dropPosition.index);
tempCollection = new (window.EditorApplication.getBlockTypeModel('container'))({
orientation: (view.model.get('orientation') === 'vertical') ? 'horizontal' : 'vertical'
@ -272,26 +282,30 @@ define([
this.view.$('.mailpoet_drop_marker').remove();
},
getDropPosition: function (eventX, eventY, is_unsafe) {
var SPECIAL_AREA_INSERTION_WIDTH = 0.00, // Disable special insertion. Default: 0.3
var SPECIAL_AREA_INSERTION_WIDTH = 0.00; // Disable special insertion. Default: 0.3
element = this.view.$el,
orientation = this.view.model.get('orientation'),
var element = this.view.$el;
var orientation = this.view.model.get('orientation');
elementOffset = element.offset(),
elementPageX = elementOffset.left,
elementPageY = elementOffset.top,
elementWidth = element.outerWidth(true),
elementHeight = element.outerHeight(true),
var elementOffset = element.offset();
var elementPageX = elementOffset.left;
var elementPageY = elementOffset.top;
var elementWidth = element.outerWidth(true);
var elementHeight = element.outerHeight(true);
relativeX = eventX - elementPageX,
relativeY = eventY - elementPageY,
var relativeX = eventX - elementPageX;
var relativeY = eventY - elementPageY;
relativeOffset, elementLength,
var relativeOffset;
var elementLength;
canAcceptNormalInsertion = this._canAcceptNormalInsertion(),
canAcceptSpecialInsertion = this._canAcceptSpecialInsertion(),
var canAcceptNormalInsertion = this._canAcceptNormalInsertion();
var canAcceptSpecialInsertion = this._canAcceptSpecialInsertion();
insertionType, index, position, indexAndPosition;
var insertionType;
var index;
var position;
var indexAndPosition;
var unsafe = !!is_unsafe;
@ -359,12 +373,14 @@ define([
// 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.getChildren().findByModel(this.getCollection().at(index)),
orientation = this.view.model.get('orientation'),
element = targetView.$el,
eventOffset, closeOffset, elementDimension;
var index = this._computeCellIndex(eventX, eventY);
// TODO: Handle case when there are no children, container is empty
var targetView = this.getChildren().findByModel(this.getCollection().at(index));
var orientation = this.view.model.get('orientation');
var element = targetView.$el;
var eventOffset;
var closeOffset;
var elementDimension;
if (orientation === 'vertical') {
eventOffset = eventY;
@ -394,39 +410,40 @@ define([
return this._computeCellIndex(eventX, eventY);
},
_computeCellIndex: function (eventX, eventY) {
var orientation = this.view.model.get('orientation'),
eventOffset = (orientation === 'vertical') ? eventY : eventX,
resultView = this.getChildren().find(function (view) {
var element = view.$el,
closeOffset, farOffset;
var orientation = this.view.model.get('orientation');
var eventOffset = (orientation === 'vertical') ? eventY : eventX;
var resultView = this.getChildren().find(function (view) {
var element = view.$el;
var closeOffset;
var farOffset;
if (orientation === 'vertical') {
closeOffset = element.offset().top;
farOffset = element.outerHeight(true);
} else {
closeOffset = element.offset().left;
farOffset = element.outerWidth(true);
}
farOffset += closeOffset;
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;
});
return closeOffset <= eventOffset && eventOffset <= farOffset;
});
var index = (typeof resultView === 'object') ? resultView._index : 0;
return index;
},
_canAcceptNormalInsertion: function () {
var orientation = this.view.model.get('orientation'),
depth = this.view.renderOptions.depth,
childCount = this.getChildren().length;
var orientation = this.view.model.get('orientation');
var depth = this.view.renderOptions.depth;
var childCount = this.getChildren().length;
// Note that depth is zero indexed. Root container has depth=0
return orientation === 'vertical' || (orientation === 'horizontal' && depth === 1 && childCount < this.options.columnLimit);
},
_canAcceptSpecialInsertion: function () {
var orientation = this.view.model.get('orientation'),
depth = this.view.renderOptions.depth,
childCount = this.getChildren().length;
var orientation = this.view.model.get('orientation');
var depth = this.view.renderOptions.depth;
var childCount = this.getChildren().length;
return depth === 0 || (depth === 1 && orientation === 'horizontal' && childCount <= this.options.columnLimit);
},
getCollectionView: function () {

View File

@ -28,12 +28,12 @@ define([
throw "Missing 'drop' function for DraggableBehavior";
},
onDrop: function (model, view) {},
testAttachToInstance: function (model, view) { return true; }
onDrop: function () {},
testAttachToInstance: function () { return true; }
},
onRender: function () {
var that = this,
interactable;
var that = this;
var interactable;
// Give instances more control over whether Draggable should be applied
if (!this.options.testAttachToInstance(this.view.model, this.view)) return;
@ -49,16 +49,19 @@ define([
onstart: function (startEvent) {
var event = startEvent;
var centerXOffset;
var centerYOffset;
var tempClone;
var clone;
var $clone;
if (that.options.cloneOriginal === true) {
// Use substitution instead of a clone
var tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined,
// Or use a clone
clone = tempClone || event.target.cloneNode(true),
$original = jQuery(event.target),
$clone = jQuery(clone),
centerXOffset, centerYOffset, parentOffset;
tempClone = (_.isFunction(that.options.onDragSubstituteBy)) ? that.options.onDragSubstituteBy(that) : undefined;
// Or use a clone
clone = tempClone || event.target.cloneNode(true);
jQuery(event.target);
$clone = jQuery(clone);
$clone.addClass('mailpoet_droppable_active');
$clone.css('position', 'absolute');
@ -85,10 +88,10 @@ define([
},
// call this function on every dragmove event
onmove: function (event) {
var target = event.target,
// keep the dragged position in the data-x/data-y attributes
x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx,
y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
var target = event.target;
// keep the dragged position in the data-x/data-y attributes
var x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
var y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
// translate the element
target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';

View File

@ -14,12 +14,13 @@ define([
defaults: {
elementSelector: null,
resizeHandleSelector: true, // true will use edges of the element itself
transformationFunction: function (y) { return y; }, // for blocks that use the default onResize function
minLength: 0,
maxLength: Infinity,
modelField: 'styles.block.height',
onResize: function (event) {
var currentLength = parseFloat(this.view.model.get(this.options.modelField)),
newLength = currentLength + event.y;
var currentLength = parseFloat(this.view.model.get(this.options.modelField));
var newLength = currentLength + this.options.transformationFunction(event.dy);
newLength = Math.min(this.options.maxLength, Math.max(this.options.minLength, newLength));
this.view.model.set(this.options.modelField, newLength + 'px');
}
@ -36,8 +37,8 @@ define([
}
},
attachResize: function () {
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0),
that = this;
var domElement = (this.options.elementSelector === null) ? this.view.$el.get(0) : this.view.$(this.options.elementSelector).get(0);
var that = this;
interact(domElement).resizable({
// axis: 'y',
edges: {
@ -47,7 +48,7 @@ define([
bottom: (typeof this.options.resizeHandleSelector === 'string') ? this.view.$(this.options.resizeHandleSelector).get(0) : this.options.resizeHandleSelector
}
})
.on('resizestart', function (event) {
.on('resizestart', function () {
that.isBeingResized = true;
that.$el.addClass('mailpoet_resize_active');
})
@ -55,7 +56,7 @@ define([
var onResize = that.options.onResize.bind(that);
return onResize(event);
})
.on('resizeend', function (event) {
.on('resizeend', function () {
that.isBeingResized = null;
that.$el.removeClass('mailpoet_resize_active');
});

View File

@ -24,9 +24,9 @@ define([
ui.item.removeData('previousIndex');
},
update: function (event, ui) {
var previousIndex = ui.item.data('previousIndex'),
newIndex = ui.item.index(),
model = collection.at(previousIndex);
var previousIndex = ui.item.data('previousIndex');
var newIndex = ui.item.index();
var model = collection.at(previousIndex);
// Replicate DOM changes. Move target model to a new position
// within the collection

View File

@ -42,7 +42,7 @@ define([
relative_urls: false,
remove_script_host: false,
convert_urls: true,
urlconverter_callback: function (url, node, on_save, name) {
urlconverter_callback: function (url) {
if (url.match(/\[.+\]/g)) {
// Do not convert URLs with shortcodes
return url;
@ -57,7 +57,7 @@ define([
plugins: this.options.plugins,
setup: function (editor) {
editor.on('change', function (e) {
editor.on('change', function () {
that.view.triggerMethod('text:editor:change', editor.getContent());
});
@ -71,12 +71,12 @@ define([
}
});
editor.on('focus', function (e) {
editor.on('focus', function () {
that.view.triggerMethod('text:editor:focus');
that._isActivationClick = true;
});
editor.on('blur', function (e) {
editor.on('blur', function () {
that.view.triggerMethod('text:editor:blur');
});
}

View File

@ -30,8 +30,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.ALCSupervisor = SuperModel.extend({
initialize: function () {
@ -43,12 +43,13 @@ define([
);
},
refresh: function () {
var blocks;
var models = App.findModels(function (model) {
return model.get('type') === 'automatedLatestContent';
}) || [];
if (models.length === 0) return;
var blocks = _.map(models, function (model) {
blocks = _.map(models, function (model) {
return model.toJSON();
});
@ -60,8 +61,8 @@ define([
_.each(
_.zip(models, renderedBlocks),
function (args) {
var model = args[0],
contents = args[1];
var model = args[0];
var contents = args[1];
model.trigger('refreshPosts', contents);
}
);
@ -150,12 +151,12 @@ define([
}),
onDragSubstituteBy: function () { return Module.AutomatedLatestContentWidgetView; },
onRender: function () {
var ContainerView = App.getBlockTypeView('container'),
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
};
var ContainerView = App.getBlockTypeView('container');
var renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
};
this.toolsView = new Module.AutomatedLatestContentBlockToolsView({ model: this.model });
this.showChildView('toolsRegion', this.toolsView);
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_container'), renderOptions: renderOptions }));
@ -213,12 +214,13 @@ define([
},
transport: function (options, success, failure) {
var taxonomies;
var termsPromise;
var promise = CommunicationComponent.getTaxonomies(
that.model.get('contentType')
).then(function (tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
var promise = CommunicationComponent.getTerms({
termsPromise = CommunicationComponent.getTerms({
search: options.data.term,
taxonomies: _.keys(taxonomies)
}).then(function (terms) {
@ -227,7 +229,7 @@ define([
terms: terms
};
});
return promise;
return termsPromise;
});
promise.then(success);
@ -264,9 +266,9 @@ define([
}
}).trigger('change');
},
toggleDisplayOptions: function (event) {
var el = this.$('.mailpoet_automated_latest_content_display_options'),
showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
toggleDisplayOptions: function () {
var el = this.$('.mailpoet_automated_latest_content_display_options');
var showControl = this.$('.mailpoet_automated_latest_content_show_display_options');
if (el.hasClass('mailpoet_closed')) {
el.removeClass('mailpoet_closed');
showControl.addClass('mailpoet_hidden');
@ -275,7 +277,7 @@ define([
showControl.removeClass('mailpoet_hidden');
}
},
showButtonSettings: function (event) {
showButtonSettings: function () {
var buttonModule = ButtonBlock;
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
@ -286,7 +288,7 @@ define([
}
})).render();
},
showDividerSettings: function (event) {
showDividerSettings: function () {
var dividerModule = DividerBlock;
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
@ -349,8 +351,8 @@ define([
this.changeField('titleFormat', event);
},
_updateContentTypes: function (postTypes) {
var select = this.$('.mailpoet_automated_latest_content_content_type'),
selectedValue = this.model.get('contentType');
var select = this.$('.mailpoet_automated_latest_content_content_type');
var selectedValue = this.model.get('contentType');
select.find('option').remove();
_.each(postTypes, function (type) {
@ -378,7 +380,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('automatedLatestContent', {
blockModel: Module.AutomatedLatestContentBlockModel,
blockView: Module.AutomatedLatestContentBlockView
@ -391,7 +393,7 @@ define([
});
});
App.on('start', function (App, options) {
App.on('start', function (App) {
var Application = App;
Application._ALCSupervisor = new Module.ALCSupervisor();
Application._ALCSupervisor.refresh();

View File

@ -12,17 +12,16 @@ define([
'jquery',
'mailpoet',
'modal'
], function (App, Marionette, SuperModel, _, jQuery, MailPoet, Modal) {
], function (App, Marionette, SuperModel, _, jQuery, MailPoet) {
'use strict';
var Module = {},
AugmentedView = Marionette.View.extend({});
var Module = {};
var AugmentedView = Marionette.View.extend({});
Module.BlockModel = SuperModel.extend({
stale: [], // Attributes to be removed upon saving
initialize: function () {
var that = this;
this.on('change', function () {
App.getChannel().trigger('autoSave');
});
@ -68,7 +67,8 @@ define([
options.dragBehavior.view.model.destroy();
},
onDragSubstituteBy: function (behavior) {
var WidgetView, node;
var WidgetView;
var node;
// When block is being dragged, display the widget icon instead.
// This will create an instance of block's widget view and
// use it's rendered DOM element instead of the content block
@ -98,13 +98,13 @@ define([
this.on('dom:refresh', this.showBlock, this);
this._isFirstRender = true;
},
showTools: function (_event) {
showTools: function () {
if (!this.showingToolsDisabled) {
this.$('> .mailpoet_tools').addClass('mailpoet_display_tools');
this.toolsView.triggerMethod('showTools');
}
},
hideTools: function (e) {
hideTools: function () {
this.$('> .mailpoet_tools').removeClass('mailpoet_display_tools');
this.toolsView.triggerMethod('hideTools');
},
@ -240,8 +240,9 @@ define([
ColorPickerBehavior: {}
},
initialize: function (params) {
var panelParams;
this.model.trigger('startEditing');
var panelParams = {
panelParams = {
element: this.$el,
template: '',
position: 'right',
@ -262,7 +263,7 @@ define([
model: this.model.toJSON()
};
},
close: function (event) {
close: function () {
this.destroy();
},
changeField: function (field, event) {

View File

@ -11,8 +11,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.ButtonBlockModel = base.BlockModel.extend({
defaults: function () {
@ -132,7 +132,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('button', {
blockModel: Module.ButtonBlockModel,
blockView: Module.ButtonBlockView

View File

@ -14,9 +14,9 @@ define([
'use strict';
var Module = {},
base = BaseBlock,
BlockCollection;
var Module = {};
var base = BaseBlock;
var BlockCollection;
BlockCollection = Backbone.Collection.extend({
model: base.BlockModel,
@ -24,7 +24,6 @@ define([
this.on('add change remove', function () { App.getChannel().trigger('autoSave'); });
},
parse: function (response) {
var self = this;
return _.map(response, function (block) {
var Type = App.getBlockTypeModel(block.type);
// TODO: If type has no registered model, use a backup one
@ -66,7 +65,7 @@ define([
return response;
},
getChildren: function () {
var models = this.get('blocks').map(function (model, index, list) {
var models = this.get('blocks').map(function (model) {
return [model, model.getChildren()];
});
@ -121,7 +120,8 @@ define([
options.dragBehavior.view.model.destroy();
},
onDragSubstituteBy: function (behavior) {
var WidgetView, node;
var WidgetView;
var node;
// When block is being dragged, display the widget icon instead.
// This will create an instance of block's widget view and
// use it's rendered DOM element instead of the content block
@ -190,24 +190,24 @@ define([
}
},
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');
};
var that = this;
var $toggleButton = this.$('> .mailpoet_tools .mailpoet_newsletter_layer_selector');
var $overlay = jQuery('.mailpoet_layer_overlay');
var $container = this.$('> .mailpoet_container');
var 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();
};
var 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 {
@ -336,7 +336,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('container', {
blockModel: Module.ContainerBlockModel,
blockView: Module.ContainerBlockView

View File

@ -5,14 +5,13 @@ define([
'newsletter_editor/App',
'newsletter_editor/blocks/base',
'underscore',
'jquery',
'mailpoet'
], function (App, BaseBlock, _, jQuery, MailPoet) {
'jquery'
], function (App, BaseBlock, _, jQuery) {
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.DividerBlockModel = base.BlockModel.extend({
defaults: function () {
@ -49,8 +48,8 @@ define([
}, base.BlockView.prototype.behaviors),
onDragSubstituteBy: function () { return Module.DividerWidgetView; },
initialize: function () {
base.BlockView.prototype.initialize.apply(this, arguments);
var that = this;
base.BlockView.prototype.initialize.apply(this, arguments);
// Listen for attempts to change all dividers in one go
this._replaceDividerHandler = function (data) { that.model.set(data); that.model.trigger('applyToAll'); };
@ -119,7 +118,7 @@ define([
repaintDividerStyleOptions: function () {
this.$('.mailpoet_field_divider_style > div').css('border-top-color', this.model.get('styles.block.borderColor'));
},
applyToAll: function (event) {
applyToAll: function () {
App.getChannel().trigger('replaceAllDividers', this.model.toJSON());
},
updateValueAndCall: function (fieldToUpdate, callable, event) {
@ -139,7 +138,7 @@ define([
}
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('divider', {
blockModel: Module.DividerBlockModel,
blockView: Module.DividerBlockView

View File

@ -10,8 +10,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.FooterBlockModel = base.BlockModel.extend({
defaults: function () {
@ -110,7 +110,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('footer', {
blockModel: Module.FooterBlockModel,
blockView: Module.FooterBlockView

View File

@ -10,8 +10,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.HeaderBlockModel = base.BlockModel.extend({
defaults: function () {
@ -110,7 +110,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('header', {
blockModel: Module.HeaderBlockModel,
blockView: Module.HeaderBlockView

View File

@ -11,9 +11,9 @@ define([
'use strict';
var Module = {},
base = BaseBlock,
ImageWidgetView;
var Module = {};
var base = BaseBlock;
var ImageWidgetView;
Module.ImageBlockModel = base.BlockModel.extend({
defaults: function () {
@ -48,8 +48,8 @@ define([
elementSelector: '.mailpoet_image',
resizeHandleSelector: '.mailpoet_image_resize_handle',
onResize: function (event) {
var corner = this.$('.mailpoet_image').offset(),
width = event.pageX - corner.left;
var corner = this.$('.mailpoet_image').offset();
var width = event.pageX - corner.left;
this.view.model.set('width', width + 'px');
}
},
@ -127,13 +127,16 @@ define([
}
},
showMediaManager: function () {
var that = this;
var MediaManager;
var theFrame;
if (this._mediaManager) {
this._mediaManager.resetSelections();
this._mediaManager.open();
return;
}
var MediaManager = window.wp.media.view.MediaFrame.Select.extend({
MediaManager = window.wp.media.view.MediaFrame.Select.extend({
initialize: function () {
window.wp.media.view.MediaFrame.prototype.initialize.apply(this, arguments);
@ -196,6 +199,7 @@ define([
},
bindHandlers: function () {
var handlers;
// from Select
this.on('router:create:browse', this.createRouter, this);
this.on('router:render:browse', this.browseRouter, this);
@ -210,7 +214,7 @@ define([
this.on('updateExcluded', this.browseContent, this);
var handlers = {
handlers = {
content: {
embed: 'embedContent',
'edit-selection': 'editSelectionContent'
@ -244,9 +248,9 @@ define([
},
editSelectionContent: function () {
var state = this.state(),
selection = state.get('selection'),
view;
var state = this.state();
var selection = state.get('selection');
var view;
view = new window.wp.media.view.AttachmentsBrowser({
controller: this,
@ -302,8 +306,8 @@ define([
requires: { selection: true },
click: function () {
var state = controller.state(),
selection = state.get('selection');
var state = controller.state();
var selection = state.get('selection');
controller.close();
state.trigger('insert', selection).reset();
@ -321,46 +325,42 @@ define([
});
var theFrame = 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;
theFrame = new MediaManager({
id: 'mailpoet-media-manager',
frame: 'select',
title: 'Select image',
editing: false,
multiple: false,
library: {
type: 'image'
},
displaySettings: false,
button: {
text: 'Select'
}
});
this._mediaManager = theFrame;
this._mediaManager.on('insert', function () {
// Append media manager image selections to Images tab
var selection = theFrame.state().get('selection');
selection.each(function (attachment) {
var sizes = attachment.get('sizes'),
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
targetImageWidth = 1320,
var sizes = attachment.get('sizes');
// Following advice from Becs, the target width should
// be a double of one column width to render well on
// retina screen devices
var targetImageWidth = 1320;
// For main image use the size, that's closest to being 660px in width
sizeKeys = _.keys(sizes),
// Pick the width that is closest to target width
var increasingByWidthDifference = _.sortBy(
_.keys(sizes),
function (size) { return Math.abs(targetImageWidth - sizes[size].width); }
);
var bestWidth = sizes[_.first(increasingByWidthDifference)].width;
var imagesOfBestWidth = _.filter(_.values(sizes), function (size) { return size.width === bestWidth; });
// Pick the width that is closest to target width
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; }),
// Maximize the height if there are multiple images with same width
mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
// Maximize the height if there are multiple images with same width
var mainSize = _.max(imagesOfBestWidth, function (size) { return size.height; });
that.model.set({
height: mainSize.height + 'px',
@ -413,7 +413,7 @@ define([
});
Module.ImageWidgetView = ImageWidgetView;
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('image', {
blockModel: Module.ImageBlockModel,
blockView: Module.ImageBlockView

View File

@ -39,8 +39,13 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
var PostsDisplayOptionsSettingsView;
var SinglePostSelectionSettingsView;
var EmptyPostSelectionSettingsView;
var PostSelectionSettingsView;
var PostsSelectionCollectionView;
Module.PostsBlockModel = base.BlockModel.extend({
stale: ['_selectedPosts', '_availablePosts', '_transformedPosts'],
@ -89,10 +94,9 @@ define([
};
},
initialize: function () {
var that = this,
POST_REFRESH_DELAY_MS = 500,
refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS),
refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
var POST_REFRESH_DELAY_MS = 500;
var refreshAvailablePosts = _.debounce(this.fetchAvailablePosts.bind(this), POST_REFRESH_DELAY_MS);
var refreshTransformedPosts = _.debounce(this._refreshTransformedPosts.bind(this), POST_REFRESH_DELAY_MS);
// Attach Radio.Requests API primarily for highlighting
_.extend(this, Radio.Requests);
@ -120,9 +124,9 @@ define([
});
},
_loadMorePosts: function () {
var that = this,
postCount = this.get('_availablePosts').length,
nextOffset = this.get('offset') + Number(this.get('amount'));
var that = this;
var postCount = this.get('_availablePosts').length;
var nextOffset = this.get('offset') + Number(this.get('amount'));
if (postCount === 0 || postCount < nextOffset) {
// No more posts to load
@ -141,8 +145,8 @@ define([
});
},
_refreshTransformedPosts: function () {
var that = this,
data = this.toJSON();
var that = this;
var data = this.toJSON();
data.posts = this.get('_selectedPosts').pluck('ID');
@ -158,10 +162,9 @@ define([
});
},
_insertSelectedPosts: function () {
var that = this,
data = this.toJSON(),
index = this.collection.indexOf(this),
collection = this.collection;
var data = this.toJSON();
var index = this.collection.indexOf(this);
var collection = this.collection;
data.posts = this.get('_selectedPosts').pluck('ID');
@ -190,17 +193,19 @@ define([
this.model.reply('blockView', this.notifyAboutSelf, this);
},
onRender: function () {
var ContainerView;
var renderOptions;
if (!this.getRegion('toolsRegion').hasView()) {
this.showChildView('toolsRegion', this.toolsView);
}
this.trigger('showSettings');
var ContainerView = App.getBlockTypeView('container'),
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
};
ContainerView = App.getBlockTypeView('container');
renderOptions = {
disableTextEditor: true,
disableDragAndDrop: true,
emptyContainerMessage: MailPoet.I18n.t('noPostsToDisplay')
};
this.showChildView('postsRegion', new ContainerView({ model: this.model.get('_transformedPosts'), renderOptions: renderOptions }));
},
notifyAboutSelf: function () {
@ -237,8 +242,8 @@ define([
this.displayOptionsView = new PostsDisplayOptionsSettingsView({ model: this.model });
},
onRender: function () {
var that = this,
blockView = this.model.request('blockView');
var that = this;
this.model.request('blockView');
this.showChildView('selectionRegion', this.selectionView);
this.showChildView('displayOptionsRegion', this.displayOptionsView);
@ -283,7 +288,7 @@ define([
}
});
var PostsSelectionCollectionView = Marionette.CollectionView.extend({
PostsSelectionCollectionView = Marionette.CollectionView.extend({
className: 'mailpoet_post_scroll_container',
childView: function () { return SinglePostSelectionSettingsView; },
emptyView: function () { return EmptyPostSelectionSettingsView; },
@ -307,7 +312,7 @@ define([
}
});
var PostSelectionSettingsView = Marionette.View.extend({
PostSelectionSettingsView = Marionette.View.extend({
getTemplate: function () { return window.templates.postSelectionPostsBlockSettings; },
regions: {
posts: '.mailpoet_post_selection_container'
@ -334,9 +339,10 @@ define([
}
},
onRender: function () {
var postsView;
// Dynamically update available post types
CommunicationComponent.getPostTypes().done(_.bind(this._updateContentTypes, this));
var postsView = new PostsSelectionCollectionView({
postsView = new PostsSelectionCollectionView({
collection: this.model.get('_availablePosts'),
blockModel: this.model
});
@ -358,12 +364,13 @@ define([
},
transport: function (options, success, failure) {
var taxonomies;
var termsPromise;
var promise = CommunicationComponent.getTaxonomies(
that.model.get('contentType')
).then(function (tax) {
taxonomies = tax;
// Fetch available terms based on the list of taxonomies already fetched
var promise = CommunicationComponent.getTerms({
termsPromise = CommunicationComponent.getTerms({
search: options.data.term,
taxonomies: _.keys(taxonomies)
}).then(function (terms) {
@ -372,7 +379,7 @@ define([
terms: terms
};
});
return promise;
return termsPromise;
});
promise.then(success);
@ -413,8 +420,8 @@ define([
this.model.set(field, jQuery(event.target).val());
},
_updateContentTypes: function (postTypes) {
var select = this.$('.mailpoet_settings_posts_content_type'),
selectedValue = this.model.get('contentType');
var select = this.$('.mailpoet_settings_posts_content_type');
var selectedValue = this.model.get('contentType');
select.find('option').remove();
_.each(postTypes, function (type) {
@ -427,11 +434,11 @@ define([
}
});
var EmptyPostSelectionSettingsView = Marionette.View.extend({
EmptyPostSelectionSettingsView = Marionette.View.extend({
getTemplate: function () { return window.templates.emptyPostPostsBlockSettings; }
});
var SinglePostSelectionSettingsView = Marionette.View.extend({
SinglePostSelectionSettingsView = Marionette.View.extend({
getTemplate: function () { return window.templates.singlePostPostsBlockSettings; },
events: function () {
return {
@ -448,8 +455,8 @@ define([
this.blockModel = options.blockModel;
},
postSelectionChange: function (event) {
var checkBox = jQuery(event.target),
selectedPostsCollection = this.blockModel.get('_selectedPosts');
var checkBox = jQuery(event.target);
var selectedPostsCollection = this.blockModel.get('_selectedPosts');
if (checkBox.prop('checked')) {
selectedPostsCollection.add(this.model);
} else {
@ -458,7 +465,7 @@ define([
}
});
var PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
PostsDisplayOptionsSettingsView = base.BlockSettingsView.extend({
getTemplate: function () { return window.templates.displayOptionsPostsBlockSettings; },
events: function () {
return {
@ -488,7 +495,7 @@ define([
model: this.model.toJSON()
};
},
showButtonSettings: function (event) {
showButtonSettings: function () {
var buttonModule = ButtonBlock;
(new buttonModule.ButtonBlockSettingsView({
model: this.model.get('readMoreButton'),
@ -499,7 +506,7 @@ define([
}
})).render();
},
showDividerSettings: function (event) {
showDividerSettings: function () {
var dividerModule = DividerBlock;
(new dividerModule.DividerBlockSettingsView({
model: this.model.get('divider'),
@ -575,7 +582,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('posts', {
blockModel: Module.PostsBlockModel,
blockView: Module.PostsBlockView

View File

@ -13,12 +13,13 @@ define([
'use strict';
var Module = {},
base = BaseBlock,
SocialBlockSettingsIconSelectorView,
SocialBlockSettingsIconView,
SocialBlockSettingsIconCollectionView,
SocialBlockSettingsStylesView;
var Module = {};
var base = BaseBlock;
var SocialBlockSettingsIconSelectorView;
var SocialBlockSettingsIconView;
var SocialBlockSettingsIconCollectionView;
var SocialBlockSettingsStylesView;
var SocialIconView;
Module.SocialIconModel = SuperModel.extend({
defaults: function () {
@ -33,12 +34,12 @@ define([
text: defaultValues.get('title')
};
},
initialize: function (options) {
initialize: function () {
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();
var defaultValues = App.getConfig().get('socialIcons').get(that.get('iconType'));
var iconSet = that.collection.iconBlockModel.getIconSet();
this.set({
link: defaultValues.get('defaultLink'),
image: iconSet.get(that.get('iconType')),
@ -83,7 +84,7 @@ define([
}
});
var SocialIconView = Marionette.View.extend({
SocialIconView = Marionette.View.extend({
tagName: 'span',
getTemplate: function () { return window.templates.socialIconBlock; },
modelEvents: {
@ -175,10 +176,10 @@ define([
}
},
templateContext: 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');
var icons = App.getConfig().get('socialIcons');
// Construct icon type list of format [{iconType: 'type', title: 'Title'}, ...]
var availableIconTypes = _.map(_.keys(icons.attributes), function (key) { return { iconType: key, title: icons.get(key).get('title') }; });
var allIconSets = App.getAvailableStyles().get('socialIconSets');
return _.extend({}, base.BlockView.prototype.templateContext.apply(this, arguments), {
iconTypes: availableIconTypes,
currentType: icons.get(this.model.get('iconType')).toJSON(),
@ -297,7 +298,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('social', {
blockModel: Module.SocialBlockModel,
blockView: Module.SocialBlockView

View File

@ -9,8 +9,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.SpacerBlockModel = base.BlockModel.extend({
defaults: function () {
@ -87,7 +87,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('spacer', {
blockModel: Module.SpacerBlockModel,
blockView: Module.SpacerBlockView

View File

@ -10,8 +10,8 @@ define([
'use strict';
var Module = {},
base = BaseBlock;
var Module = {};
var base = BaseBlock;
Module.TextBlockModel = base.BlockModel.extend({
defaults: function () {
@ -94,7 +94,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
App.registerBlockType('text', {
blockModel: Module.TextBlockModel,
blockView: Module.TextBlockView

View File

@ -7,15 +7,15 @@
*/
(function (root, factory) {
var Marionette = require('backbone.marionette');
var Radio = require('backbone.radio');
var _ = require('underscore');
if (typeof define === 'function' && define.amd) {
define(['backbone.marionette', 'backbone.radio', 'underscore'], function (Marionette, Radio, _) {
return factory(Marionette, Radio, _);
});
}
else if (typeof exports !== 'undefined') {
var Marionette = require('backbone.marionette');
var Radio = require('backbone.radio');
var _ = require('underscore');
module.exports = factory(Marionette, Radio, _);
}
else {

View File

@ -98,7 +98,7 @@ define([
});
};
App.on('start', function (App, options) {
App.on('start', function () {
// Prefetch post types
Module.getPostTypes();
});

View File

@ -13,7 +13,7 @@ define([
// handled by other components.
Module.NewsletterModel = SuperModel.extend({
whitelisted: ['id', 'subject', 'preheader'],
initialize: function (options) {
initialize: function () {
this.on('change', function () {
App.getChannel().trigger('autoSave');
});

View File

@ -29,7 +29,7 @@ define([
}
});
App.on('start', function (App, options) {
App.on('start', function (App) {
App._appView.showChildView('headingRegion', new Module.HeadingView({ model: App.getNewsletter() }));
MailPoet.helpTooltip.show(document.getElementById('tooltip-designer-subject-line'), {
tooltipId: 'tooltip-designer-subject-line-ti',

View File

@ -28,8 +28,8 @@ define([
'use strict';
var Module = {},
saveTimeout;
var Module = {};
var saveTimeout;
// Save editor contents to server
Module.save = function () {
@ -76,10 +76,9 @@ define([
// Temporary workaround for html2canvas-alpha2.
// Removes 1px left transparent border from resulting canvas.
var oldContext = oldCanvas.getContext('2d'),
newCanvas = document.createElement('canvas'),
newContext = newCanvas.getContext('2d'),
leftBorderWidth = 1;
var newCanvas = document.createElement('canvas');
var newContext = newCanvas.getContext('2d');
var leftBorderWidth = 1;
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
@ -95,8 +94,7 @@ define([
};
Module.saveTemplate = function (options) {
var that = this,
promise = jQuery.Deferred();
var promise = jQuery.Deferred();
promise.then(function (thumbnail) {
var data = _.extend(options || {}, {
@ -122,7 +120,6 @@ define([
};
Module.exportTemplate = function (options) {
var that = this;
return Module.getThumbnail(
jQuery('#mailpoet_editor_content > .mailpoet_block').get(0)
).then(function (thumbnail) {
@ -155,7 +152,7 @@ define([
'click .mailpoet_save_export': 'toggleExportTemplate',
'click .mailpoet_export_template': 'exportTemplate'
},
initialize: function (options) {
initialize: function () {
App.getChannel().on('beforeEditorSave', this.beforeSave, this);
App.getChannel().on('afterEditorSave', this.afterSave, this);
},
@ -170,7 +167,7 @@ define([
// TODO: Add a loading animation instead
this.$('.mailpoet_autosaved_at').text(MailPoet.I18n.t('saving'));
},
afterSave: function (json, response) {
afterSave: function (json) {
this.validateNewsletter(json);
// Update 'Last saved timer'
this.$('.mailpoet_editor_last_saved').removeClass('mailpoet_hidden');
@ -192,9 +189,9 @@ define([
this.$('.mailpoet_save_as_template_container').addClass('mailpoet_hidden');
},
saveAsTemplate: function () {
var templateName = this.$('.mailpoet_save_as_template_name').val(),
templateDescription = this.$('.mailpoet_save_as_template_description').val(),
that = this;
var templateName = this.$('.mailpoet_save_as_template_name').val();
var templateDescription = this.$('.mailpoet_save_as_template_description').val();
var that = this;
if (templateName === '') {
MailPoet.Notice.error(
@ -248,9 +245,9 @@ define([
this.$('.mailpoet_export_template_container').addClass('mailpoet_hidden');
},
exportTemplate: function () {
var templateName = this.$('.mailpoet_export_template_name').val(),
templateDescription = this.$('.mailpoet_export_template_description').val(),
that = this;
var templateName = this.$('.mailpoet_export_template_name').val();
var templateDescription = this.$('.mailpoet_export_template_description').val();
var that = this;
if (templateName === '') {
MailPoet.Notice.error(
@ -285,18 +282,19 @@ define([
this.hideOptionContents();
if (!this.$('.mailpoet_save_next').hasClass('button-disabled')) {
Module._cancelAutosave();
Module.save().done(function (response) {
Module.save().done(function () {
window.location.href = App.getConfig().get('urls.send');
});
}
},
validateNewsletter: function (jsonObject) {
var contents;
if (!App._contentContainer.isValid()) {
this.showValidationError(App._contentContainer.validationError);
return;
}
var contents = JSON.stringify(jsonObject);
contents = JSON.stringify(jsonObject);
if (App.getConfig().get('validation.validateUnsubscribeLinkPresent') &&
contents.indexOf('[link:subscription_unsubscribe_url]') < 0 &&
contents.indexOf('[link:subscription_unsubscribe]') < 0) {
@ -340,9 +338,11 @@ define([
};
Module.beforeExitWithUnsavedChanges = function (e) {
var message;
var event;
if (saveTimeout) {
var message = MailPoet.I18n.t('unsavedChangesWillBeLost');
var event = e || window.event;
message = MailPoet.I18n.t('unsavedChangesWillBeLost');
event = e || window.event;
if (event) {
event.returnValue = message;
@ -352,7 +352,7 @@ define([
}
};
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
var Application = App;
Application.save = Module.save;
Application.getChannel().on('autoSave', Module.autoSave);
@ -362,7 +362,7 @@ define([
Application.getChannel().reply('save', Application.save);
});
App.on('start', function (App, options) {
App.on('start', function (App) {
var saveView = new Module.SaveView();
App._appView.showChildView('bottomRegion', saveView);
});

View File

@ -6,8 +6,7 @@ define([
'backbone.marionette',
'backbone.supermodel',
'underscore',
'jquery',
'sticky-kit'
'jquery'
], function (
App,
CommunicationComponent,
@ -16,14 +15,13 @@ define([
Marionette,
SuperModel,
_,
jQuery,
StickyKit
jQuery
) {
'use strict';
var Module = {};
var SidebarView;
// Widget handlers for use to create new content blocks via drag&drop
Module._contentWidgets = new (Backbone.Collection.extend({
model: SuperModel.extend({
@ -52,7 +50,7 @@ define([
Module.registerLayoutWidget = function (widget) { return Module._layoutWidgets.add(widget); };
Module.getLayoutWidgets = function () { return Module._layoutWidgets; };
var SidebarView = Marionette.View.extend({
SidebarView = Marionette.View.extend({
getTemplate: function () { return window.templates.sidebar; },
regions: {
contentRegion: '.mailpoet_content_region',
@ -62,8 +60,8 @@ define([
},
events: {
'click .mailpoet_sidebar_region h3, .mailpoet_sidebar_region .handlediv': function (event) {
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)'),
$targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
var $openRegion = this.$el.find('.mailpoet_sidebar_region:not(.closed)');
var $targetRegion = this.$el.find(event.target).closest('.mailpoet_sidebar_region');
$openRegion.find('.mailpoet_region_content').velocity(
'slideUp',
@ -90,7 +88,7 @@ define([
}
}
},
initialize: function (options) {
initialize: function () {
jQuery(window)
.on('resize', this.updateHorizontalScroll.bind(this))
.on('scroll', this.updateHorizontalScroll.bind(this));
@ -113,9 +111,8 @@ define([
// 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 = jQuery(this);
var calculated_left;
var self = jQuery(this);
if (self.css('position') === 'fixed') {
calculated_left = self.parent().offset().left - jQuery(window).scrollLeft();
@ -271,7 +268,7 @@ define([
previewUrl: response.meta.preview_url
});
var view = this.previewView.render();
this.previewView.render();
this.previewView.$el.css('height', '100%');
MailPoet.Modal.popup({
@ -324,7 +321,7 @@ define([
App.getChannel().request('save').always(function () {
CommunicationComponent.previewNewsletter(data).always(function () {
MailPoet.Modal.loading(false);
}).done(function (response) {
}).done(function () {
MailPoet.Notice.success(
MailPoet.I18n.t('newsletterPreviewSent'),
{ scroll: true }
@ -363,7 +360,7 @@ define([
}
});
App.on('before:start', function (App, options) {
App.on('before:start', function (App) {
var Application = App;
Application.registerWidget = Module.registerWidget;
Application.getWidgets = Module.getWidgets;
@ -371,9 +368,8 @@ define([
Application.getLayoutWidgets = Module.getLayoutWidgets;
});
App.on('start', function (App, options) {
var stylesModel = App.getGlobalStyles(),
sidebarView = new SidebarView();
App.on('start', function (App) {
var sidebarView = new SidebarView();
App._appView.showChildView('sidebarRegion', sidebarView);

View File

@ -71,17 +71,19 @@ define([
App.on('before:start', function (App, options) {
var Application = App;
var body;
var globalStyles;
// Expose style methods to global application
Application.getGlobalStyles = Module.getGlobalStyles;
Application.setGlobalStyles = Module.setGlobalStyles;
Application.getAvailableStyles = Module.getAvailableStyles;
var body = options.newsletter.body;
var globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
body = options.newsletter.body;
globalStyles = (_.has(body, 'globalStyles')) ? body.globalStyles : {};
this.setGlobalStyles(globalStyles);
});
App.on('start', function (App, options) {
App.on('start', function (App) {
var stylesView = new Module.StylesView({ model: App.getGlobalStyles() });
App._appView.showChildView('stylesRegion', stylesView);
});

View File

@ -10,31 +10,33 @@
/* jshint unused:false */
/* global tinymce:true */
tinymce.PluginManager.add('mailpoet_shortcodes', function (editor, url) {
tinymce.PluginManager.add('mailpoet_shortcodes', function (editor) {
var appendLabelAndClose = function (shortcode) {
editor.insertContent(shortcode);
editor.windowManager.close();
},
generateOnClickFunc = function (shortcode) {
return function () {
appendLabelAndClose(shortcode);
};
editor.insertContent(shortcode);
editor.windowManager.close();
};
var generateOnClickFunc = function (shortcode) {
return function () {
appendLabelAndClose(shortcode);
};
};
editor.addButton('mailpoet_shortcodes', {
icon: 'mailpoet_shortcodes',
onclick: function () {
var shortcodes = [],
configShortcodes = editor.settings.mailpoet_shortcodes;
var shortcodes = [];
var configShortcodes = editor.settings.mailpoet_shortcodes;
var segment;
var i;
for (var segment in configShortcodes) {
for (segment in configShortcodes) {
if (configShortcodes.hasOwnProperty(segment)) {
shortcodes.push({
type: 'label',
text: segment
});
for (var i = 0; i < configShortcodes[segment].length; i += 1) {
for (i = 0; i < configShortcodes[segment].length; i += 1) {
shortcodes.push({
type: 'button',
text: configShortcodes[segment][i].text,

View File

@ -179,11 +179,11 @@ const _StatisticsMixin = {
const percentage_opened_display = MailPoet.Num.toLocaleFixed(percentage_opened, 1);
const percentage_unsubscribed_display = MailPoet.Num.toLocaleFixed(percentage_unsubscribed, 1);
let show_stats_timeout,
newsletter_date,
sent_hours_ago,
too_early_for_stats,
show_kb_link;
let show_stats_timeout;
let newsletter_date;
let sent_hours_ago;
let too_early_for_stats;
let show_kb_link;
if (current_time !== undefined) {
// standard emails and post notifications:
// display green box for newsletters that were just sent

View File

@ -47,6 +47,8 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
return this;
},
createNotice: function () {
var onClose;
var positionAfter;
// clone element
this.element = jQuery('#mailpoet_notice_' + this.options.type).clone();
@ -62,7 +64,6 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
this.element.removeAttr('id');
// insert notice after its parent
var positionAfter;
if (typeof this.options.positionAfter === 'object') {
positionAfter = this.options.positionAfter;
} else if (typeof this.options.positionAfter === 'string') {
@ -73,7 +74,7 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
positionAfter.after(this.element);
// setup onClose callback
var onClose = null;
onClose = null;
if (this.options.onClose !== null) {
onClose = this.options.onClose;
}
@ -177,12 +178,13 @@ define('notice', ['mailpoet', 'jquery'], function (mp, jQuery) {
}
},
hide: function (all) {
var id;
if (all !== undefined && all === true) {
// all notices
jQuery('.mailpoet_notice:not([id])').trigger('close');
} else if (all !== undefined && jQuery.isArray(all)) {
// array of ids
for (var id in all) {
for (id in all) {
jQuery('[data-id="' + all[id] + '"]').trigger('close');
}
} if (all !== undefined) {

View File

@ -5,8 +5,7 @@ define([
],
function (
MailPoet,
jQuery,
Parsley
jQuery
) {
jQuery(function ($) {
function isSameDomain(url) {
@ -20,7 +19,7 @@ function (
$('form.mailpoet_form').each(function () {
var form = $(this);
form.parsley().on('form:validated', function (parsley) {
form.parsley().on('form:validated', function () {
// clear messages
form.find('.mailpoet_message > p').hide();
@ -84,4 +83,4 @@ function (
});
});
});
});
});

View File

@ -5,7 +5,7 @@ define(
function (
MailPoet
) {
var element;
function eventHandler() {
if (confirm(MailPoet.I18n.t('reinstallConfirmation'))) {
MailPoet.trackEvent(
@ -36,8 +36,8 @@ define(
return false;
}
var element = document.getElementById('mailpoet_reinstall');
element = document.getElementById('mailpoet_reinstall');
if (element) {
element.addEventListener('click', eventHandler, false);
}
});
});

View File

@ -51,7 +51,7 @@ define(
jQuery('#mailpoet_sending_method_setup').fadeIn();
}
},
tabs: function (tabStr, section) {
tabs: function (tabStr) {
// set default tab
var tab = tabStr || 'mta';
@ -86,4 +86,4 @@ define(
if (!Backbone.History.started) Backbone.history.start();
});
}
);
);

View File

@ -3,98 +3,104 @@ define(
'underscore',
'jquery',
'mailpoet',
'handlebars',
'select2'
'handlebars'
],
function (
_,
jQuery,
MailPoet,
Handlebars,
select2
Handlebars
) {
if (!jQuery('#mailpoet_subscribers_export').length) {
return;
}
jQuery(document).ready(function () {
var segmentsContainerElement;
var subscriberFieldsContainerElement;
var exportConfirmedOptionElement;
var groupBySegmentOptionElement;
var nextStepButton;
var renderSegmentsAndFields;
var subscribers_export_template;
if (!window.exportData.segments) {
return;
}
var subscribers_export_template =
subscribers_export_template =
Handlebars.compile(jQuery('#mailpoet_subscribers_export_template').html());
// render template
jQuery('#mailpoet_subscribers_export > div.inside').html(subscribers_export_template(window.exportData));
// define reusable variables
var segmentsContainerElement = jQuery('#export_lists'),
subscriberFieldsContainerElement = jQuery('#export_columns'),
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]'),
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]'),
nextStepButton = jQuery('a.mailpoet_export_process'),
renderSegmentsAndFields = function (container, data) {
if (container.data('select2')) {
container
.html('')
.select2('destroy');
}
segmentsContainerElement = jQuery('#export_lists');
subscriberFieldsContainerElement = jQuery('#export_columns');
exportConfirmedOptionElement = jQuery(':radio[name="option_confirmed"]');
groupBySegmentOptionElement = jQuery(':checkbox[name="option_group_by_list"]');
nextStepButton = jQuery('a.mailpoet_export_process');
renderSegmentsAndFields = function (container, data) {
if (container.data('select2')) {
container
.select2({
data: data,
width: '20em',
templateResult: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
: item.name;
},
templateSelection: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
: item.name;
}
})
.on('select2:selecting', function (selectEvent) {
var selectElement = this,
selectedOptionId = selectEvent.params.args.data.id,
fieldsToExclude = [
'select',
'deselect'
];
if (_.contains(fieldsToExclude, selectedOptionId)) {
selectEvent.preventDefault();
if (selectedOptionId === 'deselect') {
jQuery(selectElement).val('').trigger('change');
} else {
var allOptions = [];
_.each(container.find('option'), function (field) {
if (!_.contains(fieldsToExclude, field.value)) {
allOptions.push(field.value);
}
});
jQuery(selectElement).val(allOptions).trigger('change');
}
jQuery(selectElement).select2('close');
}
})
.on('change', function () {
if ((window.exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
||
(!window.exportData.segments && subscriberFieldsContainerElement.select2('data').length)
) {
toggleNextStepButton('on');
}
else {
toggleNextStepButton('off');
.html('')
.select2('destroy');
}
container
.select2({
data: data,
width: '20em',
templateResult: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
: item.name;
},
templateSelection: function (item) {
return (item.subscriberCount > 0)
? item.name + ' (' + parseInt(item.subscriberCount).toLocaleString() + ')'
: item.name;
}
})
.on('select2:selecting', function (selectEvent) {
var selectElement = this;
var selectedOptionId = selectEvent.params.args.data.id;
var fieldsToExclude = [
'select',
'deselect'
];
var allOptions;
if (_.contains(fieldsToExclude, selectedOptionId)) {
selectEvent.preventDefault();
if (selectedOptionId === 'deselect') {
jQuery(selectElement).val('').trigger('change');
} else {
allOptions = [];
_.each(container.find('option'), function (field) {
if (!_.contains(fieldsToExclude, field.value)) {
allOptions.push(field.value);
}
});
jQuery(selectElement).val(allOptions).trigger('change');
}
jQuery(selectElement).select2('close');
}
})
.on('change', function () {
if ((window.exportData.segments && segmentsContainerElement.select2('data').length && subscriberFieldsContainerElement.select2('data').length)
||
(!window.exportData.segments && subscriberFieldsContainerElement.select2('data').length)
) {
toggleNextStepButton('on');
}
else {
toggleNextStepButton('off');
}
if (segmentsContainerElement.select2('data').length > 1 && window.exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').show();
}
else if (window.exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').hide();
}
});
};
if (segmentsContainerElement.select2('data').length > 1 && window.exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').show();
}
else if (window.exportData.groupBySegmentOption) {
jQuery('.mailpoet_group_by_list').hide();
}
});
};
// set confirmed subscribers export option to false
window.exportData.exportConfirmedOption = false;
@ -133,11 +139,12 @@ define(
}
nextStepButton.click(function () {
var exportFormat;
if (jQuery(this).hasClass('button-disabled')) {
return;
}
MailPoet.Modal.loading(true);
var exportFormat = jQuery(':radio[name="option_format"]:checked').val();
exportFormat = jQuery(':radio[name="option_format"]:checked').val();
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
endpoint: 'ImportExport',
@ -149,7 +156,7 @@ define(
segments: (window.exportData.segments) ? segmentsContainerElement.val() : false,
subscriber_fields: subscriberFieldsContainerElement.val()
})
}).always(function (response) {
}).always(function () {
MailPoet.Modal.loading(false);
}).done(function (response) {
var resultMessage = MailPoet.I18n.t('exportMessage')

View File

@ -7,8 +7,7 @@ define(
'handlebars',
'papaparse',
'asyncqueue',
'moment',
'select2'
'moment'
],
function (
Backbone,
@ -18,16 +17,16 @@ define(
Handlebars,
Papa,
AsyncQueue,
Moment,
select2
Moment
) {
if (!jQuery('#mailpoet_subscribers_import').length) {
return;
}
jQuery(document).ready(function () {
var router;
jQuery('input[name="select_method"]').attr('checked', false);
// configure router
var router = new (Backbone.Router.extend({
router = new (Backbone.Router.extend({
routes: {
'': 'home',
step1: 'step1',
@ -50,8 +49,20 @@ define(
* STEP 1 (upload or copy/paste)
*/
router.on('route:step1', function () {
var methodProcessContainerTemplate;
var currentStepE;
var methodSelectionElement;
var pasteInputElement;
var pasteInputPlaceholderElement;
var pasteProcessButtonElement;
var mailChimpKeyInputElement;
var mailChimpKeyVerifyButtonElement;
var mailChimpListsContainerElement;
var mailChimpProcessButtonElement;
var uploadElement;
var uploadProcessButtonElement;
// set or reset temporary validation rule on all columns
window.mailpoetColumns = jQuery.map(window.mailpoetColumns, function (column, columnIndex) {
window.mailpoetColumns = jQuery.map(window.mailpoetColumns, function (column) {
var col = column;
col.validation_rule = false;
return col;
@ -63,36 +74,34 @@ define(
}
// render process button for each method
var methodProcessContainerTemplate =
methodProcessContainerTemplate =
Handlebars.compile(jQuery('#method_process_template').html());
jQuery('.mailpoet_method_process').html(methodProcessContainerTemplate());
// define reusable variables
var currentStepE = jQuery(location.hash),
methodSelectionElement = jQuery('#select_method'),
pasteInputElement = jQuery('#paste_input'),
pasteInputPlaceholderElement =
pasteInputElement.data('placeholder').replace(/\\n/g, '\n'),
pasteProcessButtonElement =
jQuery('#method_paste > div.mailpoet_method_process')
.find('a.mailpoet_process'),
mailChimpKeyInputElement = jQuery('#mailchimp_key'),
mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify'),
mailChimpListsContainerElement = jQuery('#mailchimp_lists'),
mailChimpProcessButtonElement =
jQuery('#method_mailchimp > div.mailpoet_method_process')
.find('a.mailpoet_process'),
uploadElement = jQuery('#file_local'),
uploadProcessButtonElement =
jQuery('#method_file > div.mailpoet_method_process')
.find('a.mailpoet_process');
currentStepE = jQuery(location.hash);
methodSelectionElement = jQuery('#select_method');
pasteInputElement = jQuery('#paste_input');
pasteInputPlaceholderElement =
pasteInputElement.data('placeholder').replace(/\\n/g, '\n');
pasteProcessButtonElement =
jQuery('#method_paste > div.mailpoet_method_process')
.find('a.mailpoet_process');
mailChimpKeyInputElement = jQuery('#mailchimp_key');
mailChimpKeyVerifyButtonElement = jQuery('#mailchimp_key_verify');
mailChimpListsContainerElement = jQuery('#mailchimp_lists');
mailChimpProcessButtonElement = jQuery('#method_mailchimp > div.mailpoet_method_process')
.find('a.mailpoet_process');
uploadElement = jQuery('#file_local');
uploadProcessButtonElement =
jQuery('#method_file > div.mailpoet_method_process')
.find('a.mailpoet_process');
// define method change behavior
methodSelectionElement.change(function () {
var available_methods = jQuery(':radio[name="select_method"]');
var selected_method = available_methods.index(available_methods.filter(':checked'));
MailPoet.Notice.hide();
var available_methods = jQuery(':radio[name="select_method"]'),
selected_method =
available_methods.index(available_methods.filter(':checked'));
// hide all methods
currentStepE.find('.inside')
.children('div[id^="method_"]')
@ -131,9 +140,9 @@ define(
});
pasteProcessButtonElement.click(function () {
var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
MailPoet.Notice.hide();
// get an approximate size of textarea paste in bytes
var pasteSize = encodeURI(pasteInputElement.val()).split(/%..|./).length - 1;
if (pasteSize > window.maxPostSizeBytes) {
MailPoet.Notice.error(MailPoet.I18n.t('maxPostSizeNotice'));
return;
@ -149,8 +158,8 @@ define(
* CSV file
*/
uploadElement.change(function () {
MailPoet.Notice.hide();
var ext = this.value.match(/\.(.+)$/);
MailPoet.Notice.hide();
if (ext === null || ext[1].toLowerCase() !== 'csv') {
this.value = '';
MailPoet.Notice.error(MailPoet.I18n.t('wrongFileFormat'));
@ -234,7 +243,7 @@ define(
api_key: mailChimpKeyInputElement.val(),
lists: mailChimpListsContainerElement.find('select').val()
}
}).always(function (response) {
}).always(function () {
MailPoet.Modal.loading(false);
}).done(function (response) {
window.importData.step1 = response.data;
@ -294,48 +303,48 @@ define(
}
function parseCSV(isFile) {
var processedSubscribers = [],
parsedEmails = [],
duplicateEmails = [],
invalidEmails = [],
emailColumnPosition = null,
columnCount = null,
isHeaderFound = false,
advancedOptionHeader = true,
advancedOptionDelimiter = '',
advancedOptionNewline = '',
advancedOptionComments = false,
// trim spaces, commas, periods,
// single/double quotes and convert to lowercase
detectAndCleanupEmail = function (emailString) {
var test;
// decode HTML entities
var email = jQuery('<div />').html(emailString).text();
email = email
.toLowerCase()
// left/right trim spaces, punctuation (e.g., " 'email@email.com'; ")
// right trim non-printable characters (e.g., "email@email.com<6F>")
.replace(/^["';.,\s]+|[^\x20-\x7E]+$|["';.,_\s]+$/g, '')
// remove spaces (e.g., "email @ email . com")
// remove urlencoded characters
.replace(/\s+|%\d+|,+/g, '');
// detect e-mails that will be otherwise rejected by email regex
test = /<(.*?)>/.exec(email);
if (test) {
// is the email inside angle brackets (e.g., 'some@email.com <some@email.com>')?
email = test[1].trim();
}
test = /mailto:(?:\s+)?(.*)/.exec(email);
if (test) {
// is the email in 'mailto:email' format?
email = test[1].trim();
}
// test for valid characters using WP's rule (https://core.trac.wordpress.org/browser/tags/4.7.3/src/wp-includes/formatting.php#L2902)
if (!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.\-@]+$/.test(email)) {
return false;
}
return email;
};
var processedSubscribers = [];
var parsedEmails = [];
var duplicateEmails = [];
var invalidEmails = [];
var emailColumnPosition = null;
var columnCount = null;
var isHeaderFound = false;
var advancedOptionHeader = true;
var advancedOptionDelimiter = '';
var advancedOptionNewline = '';
var advancedOptionComments = false;
// trim spaces, commas, periods,
// single/double quotes and convert to lowercase
var detectAndCleanupEmail = function (emailString) {
var test;
// decode HTML entities
var email = jQuery('<div />').html(emailString).text();
email = email
.toLowerCase()
// left/right trim spaces, punctuation (e.g., " 'email@email.com'; ")
// right trim non-printable characters (e.g., "email@email.com<6F>")
.replace(/^["';.,\s]+|[^\x20-\x7E]+$|["';.,_\s]+$/g, '')
// remove spaces (e.g., "email @ email . com")
// remove urlencoded characters
.replace(/\s+|%\d+|,+/g, '');
// detect e-mails that will be otherwise rejected by email regex
test = /<(.*?)>/.exec(email);
if (test) {
// is the email inside angle brackets (e.g., 'some@email.com <some@email.com>')?
email = test[1].trim();
}
test = /mailto:(?:\s+)?(.*)/.exec(email);
if (test) {
// is the email in 'mailto:email' format?
email = test[1].trim();
}
// test for valid characters using WP's rule (https://core.trac.wordpress.org/browser/tags/4.7.3/src/wp-includes/formatting.php#L2902)
if (!/^[a-zA-Z0-9!#$%&\'*+\/=?^_`{|}~\.\-@]+$/.test(email)) {
return false;
}
return email;
};
return {
skipEmptyLines: true,
@ -347,11 +356,18 @@ define(
MailPoet.Notice.error(MailPoet.I18n.t('dataProcessingError'));
},
complete: function (CSV) {
for (var rowCount in CSV.data) {
var rowData = CSV.data[rowCount].map(function (el) {
var email;
var emailAddress;
var column;
var rowCount;
var rowData;
var rowColumnCount;
var errorNotice;
for (rowCount in CSV.data) {
rowData = CSV.data[rowCount].map(function (el) {
return el.trim();
});
var rowColumnCount = rowData.length;
rowColumnCount = rowData.length;
// set the number of row elements based on the first non-empty row
if (columnCount === null) {
columnCount = rowColumnCount;
@ -365,14 +381,14 @@ define(
// determine position of email address inside an array; this is
// done once and then email regex is run just on that element for each row
if (emailColumnPosition === null) {
for (var column in rowData) {
var email = detectAndCleanupEmail(rowData[column]);
for (column in rowData) {
emailAddress = detectAndCleanupEmail(rowData[column]);
if (emailColumnPosition === null
&& window.emailRegex.test(email)) {
&& window.emailRegex.test(emailAddress)) {
emailColumnPosition = column;
parsedEmails[email] = true; // add current e-mail to an object index
rowData[column] = email;
processedSubscribers[email] = rowData;
parsedEmails[emailAddress] = true; // add current e-mail to an object index
rowData[column] = emailAddress;
processedSubscribers[emailAddress] = rowData;
}
}
if (emailColumnPosition === null
@ -383,7 +399,7 @@ define(
}
}
else if (rowData[emailColumnPosition] !== '') {
var email = detectAndCleanupEmail(rowData[emailColumnPosition]);
email = detectAndCleanupEmail(rowData[emailColumnPosition]);
if (_.has(parsedEmails, email)) {
duplicateEmails.push(email);
}
@ -427,7 +443,7 @@ define(
}
else {
MailPoet.Modal.loading(false);
var errorNotice = MailPoet.I18n.t('noValidRecords');
errorNotice = MailPoet.I18n.t('noValidRecords');
errorNotice = errorNotice.replace('[link]', MailPoet.I18n.t('csvKBLink'));
errorNotice = errorNotice.replace('[/link]', '</a>');
MailPoet.Notice.error(errorNotice);
@ -438,37 +454,41 @@ define(
});
router.on('route:step2', function () {
var nextStepButton;
var previousStepButton;
var subscribers;
var subscribersDataTemplate;
var subscribersDataTemplatePartial;
var subscribersDataParseResultsTemplate;
var segmentSelectElement;
var maxRowsToShow;
var filler;
var fillerArray;
var fillerPosition;
var import_results;
var duplicates;
var email;
if (typeof (window.importData.step1) === 'undefined') {
router.navigate('step1', { trigger: true });
return;
}
// define reusable variables
var nextStepButton = jQuery('#step2_process'),
previousStepButton = jQuery('#return_to_step1'),
nextStepButton = jQuery('#step2_process');
previousStepButton = jQuery('#return_to_step1');
// create a copy of subscribers object for further manipulation
subscribers = jQuery.extend(true, {}, window.importData.step1),
subscribersDataTemplate =
Handlebars
.compile(jQuery('#subscribers_data_template')
.html()),
subscribersDataTemplatePartial =
Handlebars
.compile(jQuery('#subscribers_data_template_partial')
.html()),
subscribersDataParseResultsTemplate =
Handlebars
.compile(jQuery('#subscribers_data_parse_results_template')
.html()),
segmentSelectElement = jQuery('#mailpoet_segments_select'),
maxRowsToShow = 10,
filler = '. . .',
subscribers = jQuery.extend(true, {}, window.importData.step1);
subscribersDataTemplate = Handlebars.compile(jQuery('#subscribers_data_template').html());
subscribersDataTemplatePartial = Handlebars.compile(jQuery('#subscribers_data_template_partial').html());
subscribersDataParseResultsTemplate = Handlebars.compile(jQuery('#subscribers_data_parse_results_template').html());
segmentSelectElement = jQuery('#mailpoet_segments_select');
maxRowsToShow = 10;
filler = '. . .';
// create an array of filler data with the same number of
// elements as in the subscribers' data row
fillerArray = Array.apply(
null,
new Array(subscribers.subscribers[0].length)
).map(String.prototype.valueOf, filler),
fillerPosition;
fillerArray = Array.apply(
null,
new Array(subscribers.subscribers[0].length)
).map(String.prototype.valueOf, filler);
showCurrentStep();
@ -480,12 +500,12 @@ define(
if (subscribers.invalid.length || subscribers.duplicate.length) {
// count repeating e-mails inside duplicate array and present them in
// 'email (xN)' format
var duplicates = {};
duplicates = {};
subscribers.duplicate.forEach(function (email) {
duplicates[email] = (duplicates[email] || 0) + 1;
});
subscribers.duplicate = [];
for (var email in duplicates) {
for (email in duplicates) {
if (duplicates[email] > 1) {
subscribers.duplicate.push(email + ' (x' + duplicates[email] + ')');
}
@ -494,7 +514,7 @@ define(
}
}
var import_results = {
import_results = {
notice: MailPoet.I18n.t('importNoticeSkipped').replace(
'%1$s',
'<strong>' + (subscribers.invalid.length + subscribers.duplicate.length) + '</strong>'
@ -599,13 +619,14 @@ define(
description: segmentDescription
}
}).done(function (response) {
var selected_values;
window.mailpoetSegments.push({
id: response.data.id,
name: response.data.name,
subscriberCount: 0
});
var selected_values = segmentSelectElement.val();
selected_values = segmentSelectElement.val();
if (selected_values === null) {
selected_values = [response.data.id];
} else {
@ -642,20 +663,25 @@ define(
Handlebars.registerHelper(
'show_and_match_columns',
function (subscribers, options) {
var displayedColumns = [],
displayedColumnsIds = [];
var displayedColumns = [];
var displayedColumnsIds = [];
var i;
var columnData;
var columnId;
var headerName;
var headerNameMatch;
// go through all elements of the first row in subscribers data
for (var i in subscribers.subscribers[0]) {
var columnData = subscribers.subscribers[0][i],
columnId = 'ignore'; // set default column type
for (i in subscribers.subscribers[0]) {
columnData = subscribers.subscribers[0][i];
columnId = 'ignore'; // set default column type
// if the column is not undefined and has a valid e-mail, set type as email
if (columnData % 1 !== 0 && window.emailRegex.test(columnData)) {
columnId = 'email';
} else if (subscribers.header) {
var headerName = subscribers.header[i],
headerNameMatch = window.mailpoetColumns.map(function (el) {
return el.name;
}).indexOf(headerName);
headerName = subscribers.header[i];
headerNameMatch = window.mailpoetColumns.map(function (el) {
return el.name;
}).indexOf(headerName);
// set column type using header
if (headerNameMatch !== -1) {
columnId = window.mailpoetColumns[headerNameMatch].id;
@ -733,8 +759,8 @@ define(
}
})
.on('select2:selecting', function (selectEvent) {
var selectElement = this,
selectedOptionId = selectEvent.params.args.data.id;
var selectElement = this;
var selectedOptionId = selectEvent.params.args.data.id;
// CREATE CUSTOM FIELD
if (selectedOptionId === 'create') {
selectEvent.preventDefault();
@ -743,7 +769,7 @@ define(
title: MailPoet.I18n.t('addNewField'),
template: jQuery('#form_template_field_form').html()
});
jQuery('#form_field_new').parsley().on('form:submit', function (parsley) {
jQuery('#form_field_new').parsley().on('form:submit', function () {
// get data
var data = jQuery(this.$element).serializeObject();
@ -807,8 +833,8 @@ define(
// check for duplicate values in all select options
jQuery('select.mailpoet_subscribers_column_data_match')
.each(function () {
var element = this,
elementId = jQuery(element).val();
var element = this;
var elementId = jQuery(element).val();
// if another column has the same value and it's not an 'ignore', prompt user
if (elementId === selectedOptionId
&& elementId !== 'ignore') {
@ -824,28 +850,34 @@ define(
}
})
.on('select2:select', function (selectEvent) {
var selectElement = this,
selectedOptionId = selectEvent.params.data.id;
var selectElement = this;
var selectedOptionId = selectEvent.params.data.id;
jQuery(selectElement).data('column-id', selectedOptionId);
filterSubscribers();
});
// filter subscribers' data to detect dates, emails, etc.
function filterSubscribers() {
var subscribersClone = jQuery.extend(true, {}, subscribers);
var preventNextStep = false;
var displayedColumns;
jQuery(
'[data-id="notice_invalidEmail"], [data-id="notice_invalidDate"]')
.remove();
var subscribersClone = jQuery.extend(true, {}, subscribers),
preventNextStep = false,
displayedColumns = jQuery.map(
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
var columnId = jQuery(element).data('column-id');
var validationRule = jQuery(element).data('validation-rule');
jQuery(element).val(columnId).trigger('change');
return { id: columnId, index: elementIndex, validationRule: validationRule, element: element };
});
displayedColumns = jQuery.map(
jQuery('.mailpoet_subscribers_column_data_match'), function (element, elementIndex) {
var columnId = jQuery(element).data('column-id');
var validationRule = jQuery(element).data('validation-rule');
jQuery(element).val(columnId).trigger('change');
return { id: columnId, index: elementIndex, validationRule: validationRule, element: element };
});
// iterate through the object of mailpoet columns
jQuery.map(window.mailpoetColumns, function (column, columnIndex) {
jQuery.map(window.mailpoetColumns, function (column) {
var firstRowData;
var validationRule;
var testedFormat;
var format;
var allowedDateFormats;
// check if the column id matches the selected id of one of the
// subscriber's data columns
var matchedColumn = _.find(displayedColumns, function (data) { return data.id === column.id; });
@ -869,7 +901,7 @@ define(
}
// DATE filter: if column type is date, check if we can recognize it
if (column.type === 'date' && matchedColumn) {
var allowedDateFormats = [
allowedDateFormats = [
Moment.ISO_8601,
'YYYY/MM/DD',
'MM/DD/YYYY',
@ -880,8 +912,8 @@ define(
'YYYY/MM',
'YYYY'
];
var firstRowData = subscribersClone.subscribers[0][matchedColumn.index];
var validationRule = false;
firstRowData = subscribersClone.subscribers[0][matchedColumn.index];
validationRule = false;
// check if date exists
if (firstRowData.trim() === '') {
subscribersClone.subscribers[0][matchedColumn.index] =
@ -892,10 +924,10 @@ define(
preventNextStep = true;
}
else {
for (var format in allowedDateFormats) {
var testedFormat = allowedDateFormats[format];
for (format in allowedDateFormats) {
testedFormat = allowedDateFormats[format];
if (Moment(firstRowData, testedFormat, true).isValid()) {
var validationRule = (typeof (testedFormat) === 'function') ?
validationRule = (typeof (testedFormat) === 'function') ?
'datetime' :
testedFormat;
// set validation on the column element
@ -910,8 +942,8 @@ define(
jQuery.map(subscribersClone.subscribers, function (dataSubscribers, index) {
var data = dataSubscribers;
var rowData = data[matchedColumn.index];
if (index === fillerPosition || rowData.trim() === '') return;
var date = Moment(rowData, testedFormat, true);
if (index === fillerPosition || rowData.trim() === '') return;
// validate date
if (date.isValid()) {
data[matchedColumn.index] = new Handlebars.SafeString(
@ -970,33 +1002,35 @@ define(
});
nextStepButton.off().on('click', function () {
var columns = {};
var queue = new jQuery.AsyncQueue();
var batchNumber = 0;
var batchSize = 2000;
var timestamp = Date.now() / 1000;
var subscribers = [];
var importResults = {
created: 0,
updated: 0,
errors: [],
segments: []
};
var subscribers;
var splitSubscribers;
if (jQuery(this).hasClass('button-disabled')) {
return;
}
MailPoet.Modal.loading(true);
var columns = {},
queue = new jQuery.AsyncQueue(),
batchNumber = 0,
batchSize = 2000,
timestamp = Date.now() / 1000,
subscribers = [],
importResults = {
created: 0,
updated: 0,
errors: [],
segments: []
},
splitSubscribers = function (subscribers, size) {
return subscribers.reduce(function (res, item, index) {
if (index % size === 0) {
res.push([]);
}
res[res.length - 1].push(item);
return res;
}, []);
},
subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
splitSubscribers = function (subscribers, size) {
return subscribers.reduce(function (res, item, index) {
if (index % size === 0) {
res.push([]);
}
res[res.length - 1].push(item);
return res;
}, []);
};
subscribers = splitSubscribers(window.importData.step1.subscribers, batchSize);
_.each(jQuery('select.mailpoet_subscribers_column_data_match'),
function (column, columnIndex) {
@ -1067,6 +1101,9 @@ define(
});
router.on('route:step3', function () {
var subscribersDataImportResultsTemplate;
var exportMenuElement;
var importResults;
if (typeof (window.importData.step2) === 'undefined') {
router.navigate('step2', { trigger: true });
return;
@ -1085,25 +1122,23 @@ define(
});
// display statistics
var subscribersDataImportResultsTemplate =
Handlebars
.compile(jQuery('#subscribers_data_import_results_template')
.html()),
exportMenuElement = jQuery('span.mailpoet_export'),
importResults = {
created: (window.importData.step2.created)
? MailPoet.I18n.t('subscribersCreated')
.replace('%1$s', '<strong>' + window.importData.step2.created.toLocaleString() + '</strong>')
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
: false,
updated: (window.importData.step2.updated)
? MailPoet.I18n.t('subscribersUpdated')
.replace('%1$s', '<strong>' + window.importData.step2.updated.toLocaleString() + '</strong>')
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
: false,
no_action: (!window.importData.step2.created && !window.importData.step2.updated),
added_to_segment_with_welcome_notification: window.importData.step2.added_to_segment_with_welcome_notification
};
subscribersDataImportResultsTemplate =
Handlebars.compile(jQuery('#subscribers_data_import_results_template').html());
exportMenuElement = jQuery('span.mailpoet_export');
importResults = {
created: (window.importData.step2.created)
? MailPoet.I18n.t('subscribersCreated')
.replace('%1$s', '<strong>' + window.importData.step2.created.toLocaleString() + '</strong>')
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
: false,
updated: (window.importData.step2.updated)
? MailPoet.I18n.t('subscribersUpdated')
.replace('%1$s', '<strong>' + window.importData.step2.updated.toLocaleString() + '</strong>')
.replace('%2$s', '"' + window.importData.step2.segments.join('", "') + '"')
: false,
no_action: (!window.importData.step2.created && !window.importData.step2.updated),
added_to_segment_with_welcome_notification: window.importData.step2.added_to_segment_with_welcome_notification
};
jQuery('#subscribers_data_import_results')
.html(subscribersDataImportResultsTemplate(importResults))

View File

@ -57,7 +57,7 @@ class API {
$this->setRequestData($_POST);
if($this->checkToken() === false) {
$error_message = __('Invalid API request.', 'mailpoet');
$error_message = __('Sorry, but we couldn\'t connect to the MailPoet server. Please refresh the web page and try again.', 'mailpoet');
$error_response = $this->createErrorResponse(Error::UNAUTHORIZED, $error_message, Response::STATUS_UNAUTHORIZED);
return $error_response->send();
}
@ -200,4 +200,4 @@ class API {
);
return $error_response;
}
}
}

View File

@ -20,9 +20,13 @@ class Image {
if(!empty($element['link'])) {
$image_template = '<a href="' . $element['link'] . '">' . $image_template . '</a>';
}
$align = 'center';
if(!empty($element['styles']['block']['textAlign']) && in_array($element['styles']['block']['textAlign'], array('left', 'right'))) {
$align = $element['styles']['block']['textAlign'];
}
$template = '
<tr>
<td class="mailpoet_image ' . (($element['fullWidth'] === false) ? 'mailpoet_padded_bottom mailpoet_padded_side' : '') . '" align="center" valign="top">
<td class="mailpoet_image ' . (($element['fullWidth'] === false) ? 'mailpoet_padded_bottom mailpoet_padded_side' : '') . '" align="' . $align . '" valign="top">
' . $image_template . '
</td>
</tr>';

View File

@ -4,7 +4,7 @@ if(!defined('ABSPATH')) exit;
/*
* Plugin Name: MailPoet 3 (new)
* Version: 3.0.3
* Version: 3.0.6
* Plugin URI: http://www.mailpoet.com
* Description: Create and send newsletters, post notifications and welcome emails from your WordPress.
* Author: MailPoet
@ -20,7 +20,7 @@ if(!defined('ABSPATH')) exit;
*/
$mailpoet_plugin = array(
'version' => '3.0.3',
'version' => '3.0.6',
'filename' => __FILE__,
'path' => dirname(__FILE__),
'autoloader' => dirname(__FILE__) . '/vendor/autoload.php',

View File

@ -4,7 +4,7 @@ Tags: newsletter, email, welcome email, post notification, autoresponder, signup
Requires at least: 4.6
Tested up to: 4.8
Requires PHP: 5.3
Stable tag: 3.0.3
Stable tag: 3.0.5
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -114,6 +114,15 @@ Stop by our [support site](https://www.mailpoet.com/support).
== Changelog ==
= 3.0.6 - 2017-10-10 =
* Fixed: subscription forms to not throw form validation engine errors;
= 3.0.5 - 2017-10-10 =
* Added: images can now be aligned left, center or right in email designer;
= 3.0.4 - 2017-10-05 =
* Fixed: dividers and spacers' height can be changed on mouse drag again;
= 3.0.3 - 2017-10-03 =
* Fixed: mixed collation error in WordPress user synchronization. Thanks Chris, Till, Robin, Robero, @Seph, @kaiwen and others for the reports!

View File

@ -1,8 +1,8 @@
var fs = require('fs');
module.exports = {
loadFileToContainer: function (path, window, containerTagName, opts) {
var contents = fs.readFileSync(path),
container = window.document.createElement(containerTagName);
var contents = fs.readFileSync(path);
var container = window.document.createElement(containerTagName);
var options = opts || {};
container.innerHTML = contents;

View File

@ -3,6 +3,7 @@ var sinon = require('sinon');
var sinonChai = require('sinon-chai');
var chaiJq = require('chai-jq');
var _ = require('underscore');
var jsdom = require('jsdom').jsdom;
chai.use(sinonChai);
chai.use(chaiJq);
@ -11,7 +12,6 @@ global.expect = chai.expect;
global.sinon = sinon;
if (!global.document || !global.window) {
var jsdom = require('jsdom').jsdom;
global.document = jsdom('<html><head><script></script></head><body></body></html>', {}, {
FetchExternalResources: ['script'],

View File

@ -21,6 +21,8 @@ define([
describe('Automated Latest Content Supervisor', function() {
var model;
var mock;
var module;
beforeEach(function() {
model = new AutomatedLatestContentBlock.ALCSupervisor();
});
@ -29,33 +31,33 @@ define([
global.stubChannel(EditorApplication);
EditorApplication.findModels = sinon.stub().returns([new Backbone.SuperModel()]);
var mock = sinon.mock({ getBulkTransformedPosts: function() {} })
mock = sinon.mock({ getBulkTransformedPosts: function() {} })
.expects('getBulkTransformedPosts').once().returns(jQuery.Deferred());
var module = AutomatedLatestContentInjector({
module = AutomatedLatestContentInjector({
'newsletter_editor/components/communication': {
getBulkTransformedPosts: mock
}
});
var model = new module.ALCSupervisor();
model = new module.ALCSupervisor();
model.refresh();
mock.verify();
});
it('refreshes posts for given blocks', function() {
var block1 = new Backbone.SuperModel(),
block2 = new Backbone.SuperModel(),
postsSet1 = [
{ type: 'customTypeOne' }
],
postsSet2 = [
{ type: 'customTypeTwo' },
{ type: 'customTypeTwo' }
],
mock1 = sinon.mock(block1),
mock2 = sinon.mock(block2);
var block1 = new Backbone.SuperModel();
var block2 = new Backbone.SuperModel();
var postsSet1 = [
{ type: 'customTypeOne' }
];
var postsSet2 = [
{ type: 'customTypeTwo' },
{ type: 'customTypeTwo' }
];
var mock1 = sinon.mock(block1);
var mock2 = sinon.mock(block2);
mock1.expects('trigger').once().withArgs('refreshPosts', postsSet1);
mock2.expects('trigger').once().withArgs('refreshPosts', postsSet2);
@ -69,7 +71,8 @@ define([
describe('Automated latest content', function () {
describe('model', function () {
var model, module;
var model;
var module;
before(function() {
module = AutomatedLatestContentBlock;
@ -172,6 +175,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
automatedLatestContent: {
@ -219,7 +223,7 @@ define([
}
}
});
var model = new (module.AutomatedLatestContentBlockModel)();
model = new (module.AutomatedLatestContentBlockModel)();
expect(model.get('amount')).to.equal('17');
expect(model.get('contentType')).to.equal('mailpoet_page');
@ -251,8 +255,9 @@ define([
});
it('accepts displayable posts', function() {
var model;
EditorApplication.getBlockTypeModel = sinon.stub().returns(ContainerBlock.ContainerBlockModel);
var model = new (module.AutomatedLatestContentBlockModel)();
model = new (module.AutomatedLatestContentBlockModel)();
model.updatePosts([{
type: 'someCustomType'
@ -264,7 +269,9 @@ define([
});
describe('block view', function () {
var model, view, module;
var model;
var view;
var module;
before(function() {
module = AutomatedLatestContentBlock;
@ -290,11 +297,8 @@ define([
});
describe('replaceAllButtonStyles', function () {
var model, view, module, onStub;
before(function() {
module = AutomatedLatestContentBlock;
});
var onStub;
var view;
beforeEach(function () {
onStub = sinon.stub();
@ -302,8 +306,7 @@ define([
global.stubConfig(EditorApplication);
EditorApplication.getBlockTypeModel = sinon.stub().returns(Backbone.Model);
EditorApplication.getBlockTypeView = sinon.stub().returns(Backbone.View);
model = {set: sinon.stub()};
view = new (module.AutomatedLatestContentBlockView)({model: model});
view = new (AutomatedLatestContentBlock.AutomatedLatestContentBlockView)({model: {set: sinon.stub()}});
});
it('listens to the event', function () {
@ -321,13 +324,15 @@ define([
}
};
callback(data);
expect(model.set).to.have.been.callCount(1);
expect(model.set).to.have.been.calledWithMatch(sinon.match.has('readMoreButton', data));
expect(view.model.set).to.have.been.callCount(1);
expect(view.model.set).to.have.been.calledWithMatch(sinon.match.has('readMoreButton', data));
});
});
describe('block settings view', function () {
var model, view, module;
var model;
var view;
var module;
before(function() {
module = AutomatedLatestContentInjector({
@ -496,7 +501,8 @@ define([
});
describe('when "title only" display type is selected', function() {
var model, view;
var model;
var view;
beforeEach(function() {
model = new (module.AutomatedLatestContentBlockModel)();
view = new (module.AutomatedLatestContentBlockSettingsView)({model: model});
@ -509,7 +515,8 @@ define([
});
describe('when "title as list" is selected', function() {
var model, view;
var model;
var view;
beforeEach(function() {
model = new (module.AutomatedLatestContentBlockModel)();
view = new (module.AutomatedLatestContentBlockSettingsView)({model: model});

View File

@ -108,6 +108,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
button: {
@ -131,7 +132,7 @@ define([
}
}
});
var model = new (ButtonBlock.ButtonBlockModel)();
model = new (ButtonBlock.ButtonBlockModel)();
expect(model.get('text')).to.equal('Some new text');
expect(model.get('url')).to.equal('http://somenewurl.com');
@ -173,7 +174,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
before(function () {
global.stubChannel(EditorApplication);
@ -264,8 +266,9 @@ define([
});
describe('replaceAllButtonStyles', function () {
var model, view, onStub;
var model;
var view;
var onStub;
beforeEach(function () {
onStub = sinon.stub();
@ -317,7 +320,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
before(function() {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);

View File

@ -13,8 +13,9 @@ define([
describe('model', function () {
describe('by default', function () {
var model;
global.stubConfig(EditorApplication);
var model = new ModelClass();
model = new ModelClass();
it('has container type', function () {
expect(model.get('type')).to.equal('container');
@ -33,6 +34,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
container: {
@ -44,7 +46,7 @@ define([
}
}
});
var model = new (ContainerBlock.ContainerBlockModel)();
model = new (ContainerBlock.ContainerBlockModel)();
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
});
@ -52,10 +54,10 @@ define([
describe('when creating with children', function () {
var testModel = {
type: 'sampleType',
someField: 'Some Content'
},
model;
type: 'sampleType',
someField: 'Some Content'
};
var model;
it('will recursively create children', function () {
EditorApplication.getBlockTypeModel = sinon.stub().returns(Backbone.Model);
@ -107,10 +109,12 @@ define([
});
describe('block view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication);
var model = new (ContainerBlock.ContainerBlockModel)(),
view = new (ContainerBlock.ContainerBlockView)({model: model});
model = new (ContainerBlock.ContainerBlockModel)();
view = new (ContainerBlock.ContainerBlockView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
@ -119,8 +123,8 @@ define([
describe('once rendered', function () {
describe('on root level', function () {
var model = new (ContainerBlock.ContainerBlockModel)(),
view;
var model = new (ContainerBlock.ContainerBlockModel)();
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -151,8 +155,8 @@ define([
});
describe.skip('on non-root levels', function () {
var model = new (ContainerBlock.ContainerBlockModel)(),
view;
var model = new (ContainerBlock.ContainerBlockModel)();
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -187,17 +191,20 @@ define([
});
describe('settings view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication);
var model = new (ContainerBlock.ContainerBlockModel)(),
view = new (ContainerBlock.ContainerBlockSettingsView)({model: model});
model = new (ContainerBlock.ContainerBlockModel)();
view = new (ContainerBlock.ContainerBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
});
describe('once rendered', function () {
var model, view;
var model;
var view;
beforeEach(function() {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication);

View File

@ -70,6 +70,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
divider: {
@ -85,7 +86,7 @@ define([
}
}
});
var model = new (DividerBlock.DividerBlockModel)();
model = new (DividerBlock.DividerBlockModel)();
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
expect(model.get('styles.block.padding')).to.equal('37px');
@ -137,13 +138,15 @@ define([
});
describe('settings view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
dividers: ['solid', 'inset']
});
var model = new (DividerBlock.DividerBlockModel)(),
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
model = new (DividerBlock.DividerBlockModel)();
view = new (DividerBlock.DividerBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
@ -151,7 +154,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
before(function() {
global.stubChannel(EditorApplication);

View File

@ -79,6 +79,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
footer: {
@ -101,7 +102,7 @@ define([
}
}
});
var model = new (FooterBlock.FooterBlockModel)();
model = new (FooterBlock.FooterBlockModel)();
expect(model.get('text')).to.equal('some custom config text');
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
@ -116,11 +117,12 @@ define([
});
describe('block view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication);
var model = new (FooterBlock.FooterBlockModel)(),
view;
model = new (FooterBlock.FooterBlockModel)();
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -134,13 +136,15 @@ define([
});
describe('settings view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
fonts: ['Arial', 'Tahoma'],
textSizes: ['16px', '20px']
});
var model = new (FooterBlock.FooterBlockModel)(),
view = new (FooterBlock.FooterBlockSettingsView)({model: model});
model = new (FooterBlock.FooterBlockModel)();
view = new (FooterBlock.FooterBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
@ -148,7 +152,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
beforeEach(function() {
global.stubChannel(EditorApplication);
@ -188,7 +193,7 @@ define([
expect(model.get('styles.link.textDecoration')).to.equal('underline');
});
it('updates the model when background color changes', function () {
it('updates the model when text alignment changes', function () {
view.$('.mailpoet_field_footer_alignment').last().prop('checked', true).change();
expect(model.get('styles.text.textAlign')).to.equal('right');
});

View File

@ -79,6 +79,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
header: {
@ -101,7 +102,7 @@ define([
}
}
});
var model = new (HeaderBlock.HeaderBlockModel)();
model = new (HeaderBlock.HeaderBlockModel)();
expect(model.get('text')).to.equal('some custom config text');
expect(model.get('styles.block.backgroundColor')).to.equal('#123456');
@ -115,11 +116,12 @@ define([
});
describe('block view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication);
var model = new (HeaderBlock.HeaderBlockModel)(),
view;
model = new (HeaderBlock.HeaderBlockModel)();
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -133,14 +135,16 @@ define([
});
describe('settings view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication, {
fonts: ['Arial', 'Tahoma'],
textSizes: ['16px', '20px']
});
var model = new (HeaderBlock.HeaderBlockModel)(),
view = new (HeaderBlock.HeaderBlockSettingsView)({model: model});
model = new (HeaderBlock.HeaderBlockModel)();
view = new (HeaderBlock.HeaderBlockSettingsView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
@ -148,7 +152,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
beforeEach(function() {
global.stubChannel(EditorApplication);

View File

@ -74,6 +74,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
image: {
@ -91,7 +92,7 @@ define([
}
}
});
var model = new (ImageBlock.ImageBlockModel)();
model = new (ImageBlock.ImageBlockModel)();
expect(model.get('link')).to.equal('http://example.org/customConfigPage');
expect(model.get('src')).to.equal('http://example.org/someCustomConfigImage.png');
@ -104,11 +105,12 @@ define([
});
describe('block view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
global.stubAvailableStyles(EditorApplication);
var model = new (ImageBlock.ImageBlockModel)(),
view;
model = new (ImageBlock.ImageBlockModel)();
beforeEach(function () {
view = new (ImageBlock.ImageBlockView)({model: model});
@ -158,7 +160,8 @@ define([
});
describe('block settings view', function () {
var model, view;
var model;
var view;
var newWidth = 123;
var newHeight = 456;
var newLink = 'http://example.org/someNewLink';
@ -206,11 +209,15 @@ define([
});
it('updates the model when padding changes', function () {
var newValue = 'false';
view.$('.mailpoet_field_image_full_width').prop('checked', false).change();
expect(model.get('fullWidth')).to.equal(false);
});
it('updates the model when alignment changes', function () {
view.$('.mailpoet_field_image_alignment').first().prop('checked', true).change();
expect(model.get('styles.block.textAlign')).to.equal('left');
});
it.skip('closes the sidepanel after "Done" is clicked', function() {
var mock = sinon.mock().once();
global.MailPoet.Modal.cancel = mock;

View File

@ -132,6 +132,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
posts: {
@ -179,7 +180,7 @@ define([
}
}
});
var model = new (PostsBlock.PostsBlockModel)();
model = new (PostsBlock.PostsBlockModel)();
expect(model.get('amount')).to.equal('17');
expect(model.get('contentType')).to.equal('mailpoet_page');
@ -261,7 +262,8 @@ define([
});
describe('block view', function () {
var model, view;
var model;
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -285,7 +287,8 @@ define([
});
describe('block settings view', function () {
var model, view;
var model;
var view;
before(function () {
CommunicationComponent.getPostTypes = function() {
@ -421,7 +424,8 @@ define([
});
describe('when "title only" display type is selected', function() {
var model, view;
var model;
var view;
beforeEach(function() {
model = new (PostsBlock.PostsBlockModel)();
model.request = sinon.stub().returns({$el: {}});

View File

@ -29,6 +29,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
social: {
@ -36,7 +37,7 @@ define([
}
}
});
var model = new (SocialBlock.SocialBlockModel)();
model = new (SocialBlock.SocialBlockModel)();
expect(model.get('iconSet')).to.equal('customConfigIconSet');
});
@ -130,7 +131,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
before(function () {
global.stubChannel(EditorApplication);
@ -215,7 +217,8 @@ define([
});
describe('once rendered', function () {
var model, view;
var model;
var view;
beforeEach(function () {
global.stubChannel(EditorApplication);
global.stubAvailableStyles(EditorApplication, {

View File

@ -55,6 +55,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
spacer: {
@ -67,7 +68,7 @@ define([
}
}
});
var model = new (SpacerBlock.SpacerBlockModel)();
model = new (SpacerBlock.SpacerBlockModel)();
expect(model.get('styles.block.backgroundColor')).to.equal('#567890');
expect(model.get('styles.block.height')).to.equal('19px');
@ -119,11 +120,12 @@ define([
});
describe('settings view', function () {
var model;
var view;
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);
var model = new (SpacerBlock.SpacerBlockModel)(),
view;
model = new (SpacerBlock.SpacerBlockModel)();
beforeEach(function () {
global.stubChannel(EditorApplication);
@ -135,7 +137,8 @@ define([
});
describe('once rendered', function () {
var view, model;
var view;
var model;
beforeEach(function() {
global.stubChannel(EditorApplication);
global.stubConfig(EditorApplication);

View File

@ -23,6 +23,7 @@ define([
});
it('uses defaults from config when they are set', function () {
var model;
global.stubConfig(EditorApplication, {
blockDefaults: {
text: {
@ -30,16 +31,18 @@ define([
}
}
});
var model = new (TextBlock.TextBlockModel)();
model = new (TextBlock.TextBlockModel)();
expect(model.get('text')).to.equal('some custom config text');
});
});
describe('block view', function () {
var model;
var view;
global.stubConfig(EditorApplication);
var model = new (TextBlock.TextBlockModel)(),
view = new (TextBlock.TextBlockView)({model: model});
model = new (TextBlock.TextBlockModel)();
view = new (TextBlock.TextBlockView)({model: model});
it('renders', function () {
expect(view.render).to.not.throw();
@ -47,8 +50,8 @@ define([
});
describe('once rendered', function () {
var model = new (TextBlock.TextBlockModel)(),
view;
var model = new (TextBlock.TextBlockModel)();
var view;
beforeEach(function () {
global.stubConfig(EditorApplication);

View File

@ -32,15 +32,15 @@ define([
});
it('caches results', function() {
var deferred = jQuery.Deferred(),
mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred),
module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
var deferred = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred);
var module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
});
}
});
deferred.resolve({
post: 'val1',
page: 'val2'
@ -54,16 +54,16 @@ define([
describe('getTaxonomies', function() {
it('sends post type to endpoint', function() {
var spy,
post = function(params) {
var deferred = jQuery.Deferred();
deferred.resolve({
category: 'val1',
post_tag: 'val2'
});
return deferred;
},
module;
var spy;
var post = function() {
var deferred = jQuery.Deferred();
deferred.resolve({
category: 'val1',
post_tag: 'val2'
});
return deferred;
};
var module;
spy = sinon.spy(post);
module = CommunicationInjector({
mailpoet: {
@ -99,15 +99,15 @@ define([
});
it('caches results', function() {
var deferred = jQuery.Deferred(),
mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred),
module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
var deferred = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred);
var module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
});
}
});
deferred.resolve({ category: 'val1' });
module.getTaxonomies('page');
module.getTaxonomies('page');
@ -119,7 +119,7 @@ define([
describe('getTerms', function() {
it('sends terms to endpoint', function() {
var spy;
var post = function(params) {
var post = function() {
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
@ -163,15 +163,15 @@ define([
});
it('caches results', function() {
var deferred = jQuery.Deferred(),
mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred),
module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
var deferred = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred);
var module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
});
}
});
deferred.resolve({ term1: 'term1val1', term2: 'term2val2' });
module.getTerms({ taxonomies: ['category'] });
module.getTerms({ taxonomies: ['category'] });
@ -182,13 +182,13 @@ define([
describe('getPosts', function() {
it('sends options to endpoint', function() {
var spy,
post = function(params) {
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
},
module;
var spy;
var post = function() {
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
};
var module;
spy = sinon.spy(post);
module = CommunicationInjector({
mailpoet: {
@ -231,15 +231,15 @@ define([
});
it('caches results', function() {
var deferred = jQuery.Deferred(),
mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred),
module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
var deferred = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred);
var module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
});
}
});
deferred.resolve({
type: 'posts',
search: 'some search term'
@ -253,13 +253,13 @@ define([
describe('getTransformedPosts', function() {
it('sends options to endpoint', function() {
var spy,
post = function(params) {
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
},
module;
var spy;
var post = function() {
var deferred = jQuery.Deferred();
deferred.resolve({});
return deferred;
};
var module;
spy = sinon.spy(post);
module = CommunicationInjector({
mailpoet: {
@ -302,15 +302,15 @@ define([
});
it('caches results', function() {
var deferred = jQuery.Deferred(),
mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred),
module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
var deferred = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(deferred);
var module = CommunicationInjector({
mailpoet: {
Ajax: {
post: mock
}
});
}
});
deferred.resolve({
type: 'posts',
posts: [1, 3]

View File

@ -7,10 +7,11 @@ define([
describe('Config', function () {
it('loads and stores configuration', function() {
var model;
ConfigComponent.setConfig({
testConfig: 'testValue'
});
var model = ConfigComponent.getConfig();
model = ConfigComponent.getConfig();
expect(model.get('testConfig')).to.equal('testValue');
});
});

View File

@ -65,8 +65,8 @@ define([
describe('block types', function() {
it('registers a block type view and model', function() {
var blockModel = new Backbone.SuperModel(),
blockView = new Backbone.View();
var blockModel = new Backbone.SuperModel();
var blockView = new Backbone.View();
ContentComponent.registerBlockType('testType', {
blockModel: blockModel,
blockView: blockView
@ -78,13 +78,16 @@ define([
describe('transformation to json', function() {
it('includes content, globalStyles and initial newsletter fields', function() {
var json;
var dataField = {
containerModelField: 'containerModelValue'
}, stylesField = {
globalStylesField: 'globalStylesValue'
}, newsletterFields = {
subject: 'test newsletter subject'
};
containerModelField: 'containerModelValue'
};
var stylesField = {
globalStylesField: 'globalStylesValue'
};
var newsletterFields = {
subject: 'test newsletter subject'
};
EditorApplication._contentContainer = {
toJSON: function() {
return dataField;
@ -104,7 +107,7 @@ define([
}
};
};
var json = ContentComponent.toJSON();
json = ContentComponent.toJSON();
expect(json).to.deep.equal(_.extend({
body: {
content: dataField,

View File

@ -23,7 +23,8 @@ define([
});
describe('once rendered', function() {
var view, model;
var view;
var model;
beforeEach(function() {
model = new Backbone.SuperModel({
subject: 'a test subject',

View File

@ -38,8 +38,9 @@ define([
});
it('triggers afterEditorSave event', function() {
var spy = sinon.spy(),
promise = jQuery.Deferred();
var module;
var spy = sinon.spy();
var promise = jQuery.Deferred();
global.stubChannel(EditorApplication, {
trigger: spy
});
@ -48,7 +49,7 @@ define([
type: 'container'
}
});
var module = SaveInjector({
module = SaveInjector({
'newsletter_editor/components/communication': {
saveNewsletter: sinon.stub().returns(promise)
}
@ -74,19 +75,20 @@ define([
});
it('encodes newsletter body in JSON format', function() {
var body = {type: 'testType'},
mock = sinon.mock()
.once()
.withArgs({
body: JSON.stringify(body)
})
.returns(jQuery.Deferred());
var module;
var body = {type: 'testType'};
var mock = sinon.mock()
.once()
.withArgs({
body: JSON.stringify(body)
})
.returns(jQuery.Deferred());
global.stubChannel(EditorApplication);
EditorApplication.toJSON = sinon.stub().returns({
body: body
});
var module = SaveInjector({
module = SaveInjector({
'newsletter_editor/components/communication': {
saveNewsletter: mock
}
@ -134,15 +136,17 @@ define([
});
it('triggers template saving when clicked on "save as template" button', function() {
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(jQuery.Deferred()),
html2canvasMock = jQuery.Deferred();
var mock = sinon.mock({ post: function() {} }).expects('post').once().returns(jQuery.Deferred());
var html2canvasMock = jQuery.Deferred();
var module;
var view;
html2canvasMock.resolve({
toDataURL: function() { return 'somedataurl'; }
});
EditorApplication.getBody = sinon.stub();
var module = SaveInjector({
module = SaveInjector({
mailpoet: {
Ajax: {
post: mock
@ -163,7 +167,7 @@ define([
};
}
});
var view = new (module.SaveView)();
view = new (module.SaveView)();
view.render();
view.$('.mailpoet_save_as_template_name').val('A sample template');
@ -174,18 +178,19 @@ define([
});
it('saves newsletter when clicked on "next" button', function() {
var spy = sinon.spy(),
module = SaveInjector({
'newsletter_editor/components/communication': {
saveNewsletter: function() {
return jQuery.Deferred();
}
var spy = sinon.spy();
var view;
var module = SaveInjector({
'newsletter_editor/components/communication': {
saveNewsletter: function() {
return jQuery.Deferred();
}
});
}
});
global.stubChannel(EditorApplication, {
trigger: spy
});
var view = new (module.SaveView)();
view = new (module.SaveView)();
view.render();
view.$('.mailpoet_save_next').click();

View File

@ -47,7 +47,9 @@ define([
});
describe('once rendered', function() {
var model, availableStyles, view;
var model;
var availableStyles;
var view;
before(function() {
model = new Backbone.SuperModel({
text: {

View File

@ -9,10 +9,11 @@ define([
describe('Styles', function () {
it('loads and stores globally available styles', function() {
var model;
StylesComponent.setGlobalStyles({
testStyle: 'testValue'
});
var model = StylesComponent.getGlobalStyles();
model = StylesComponent.getGlobalStyles();
expect(model.get('testStyle')).to.equal('testValue');
});
@ -35,7 +36,8 @@ define([
});
describe('view', function() {
var model, view;
var model;
var view;
beforeEach(function() {
model = new (StylesComponent.StylesModel)();
view = new (StylesComponent.StylesView)({ model: model });

View File

@ -133,6 +133,29 @@ class RendererTest extends \MailPoetTest {
expect($DOM('tr > td > img', 0)->attr('style'))->notEmpty();
}
function testItRendersAlignedImage() {
$newsletter = $this->newsletter['body'];
$template = $newsletter['content']['blocks'][0]['blocks'][0]['blocks'][1];
// default alignment (center)
unset($template['styles']['block']['textAlign']);
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
expect($DOM('tr > td', 0)->attr('align'))->equals('center');
$template['styles']['block']['textAlign'] = 'center';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
expect($DOM('tr > td', 0)->attr('align'))->equals('center');
$template['styles']['block']['textAlign'] = 'something odd';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
expect($DOM('tr > td', 0)->attr('align'))->equals('center');
// left alignment
$template['styles']['block']['textAlign'] = 'left';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
expect($DOM('tr > td', 0)->attr('align'))->equals('left');
// right alignment
$template['styles']['block']['textAlign'] = 'right';
$DOM = $this->DOM_parser->parseStr(Image::render($template, $columnCount = 1));
expect($DOM('tr > td', 0)->attr('align'))->equals('right');
}
function testItDoesNotRenderImageWithoutSrc() {
$image = array(
'src' => '',

View File

@ -1,5 +1,5 @@
<div class="mailpoet_tools"></div>
<div class="mailpoet_content" style="text-align: {{ model.styles.block.textAlign }}; width: {{model.width}}">
<div class="mailpoet_content" style="{{#ifCond model.styles.block.textAlign '==' 'left'}}margin: 0 auto 0 0; {{/ifCond}}{{#ifCond model.styles.block.textAlign '==' 'center'}}margin: auto; {{/ifCond}}{{#ifCond model.styles.block.textAlign '==' 'right'}}margin: 0 0 0 auto; {{/ifCond}}width: {{model.width}}">
<div class="mailpoet_image">
<a href="{{ model.link }}" onClick="return false;">
<img src="{{#ifCond model.src '!=' ''}}{{ model.src }}{{ else }}{{ imageMissingSrc }}{{/ifCond}}" alt="{{ model.alt }}" onerror="if(this.src != '{{ imageMissingSrc }}') {this.src = '{{ imageMissingSrc }}'; this.style.width='auto';}" width="{{model.width}}" />

View File

@ -27,23 +27,23 @@
<label>
<div class="mailpoet_form_field_title"><%= __('Width') %></div>
<div class="mailpoet_form_field_input_option">
<input
<input
class="mailpoet_input mailpoet_input_small mailpoet_field_image_width_input"
name="image-width-input"
type="number"
value="{{getNumber model.width}}"
name="image-width-input"
type="number"
value="{{getNumber model.width}}"
min="36"
max="660"
step="2"
step="2"
/> px
<input
class="mailpoet_range mailpoet_range_small mailpoet_field_image_width"
name="image-width"
type="range"
value="{{getNumber model.width}}"
<input
class="mailpoet_range mailpoet_range_small mailpoet_field_image_width"
name="image-width"
type="range"
value="{{getNumber model.width}}"
min="36"
max="660"
step="2"
step="2"
/>
</div>
</label>
@ -57,6 +57,27 @@
<span id="tooltip-designer-full-width" class="tooltip-help-designer-full-width"></span>
</div>
</div>
<div class="mailpoet_form_field">
<div class="mailpoet_form_field_title"><%= __('Alignment') %></div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="alignment" class="mailpoet_field_image_alignment" value="left" {{#ifCond model.styles.block.textAlign '===' 'left'}}CHECKED{{/ifCond}}/>
<%= __('Left') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="alignment" class="mailpoet_field_image_alignment" value="center" {{#ifCond model.styles.block.textAlign '===' 'center'}}CHECKED{{/ifCond}}/>
<%= __('Center') %>
</label>
</div>
<div class="mailpoet_form_field_radio_option">
<label>
<input type="radio" name="alignment" class="mailpoet_field_image_alignment" value="right" {{#ifCond model.styles.block.textAlign '===' 'right'}}CHECKED{{/ifCond}}/>
<%= __('Right') %>
</label>
</div>
</div>
<hr />
<div class="mailpoet_form_field">
<input type="button" name="select-another-image" class="button button-secondary mailpoet_button_full mailpoet_field_image_select_another_image" value="<%= __('Select another image') | escape('html_attr') %>" />