Client's public folder renamed to static
Regular campaign sender seems to have most of the code in place. (Not tested.)
This commit is contained in:
parent
89eabea0de
commit
63765f7222
354 changed files with 836 additions and 324 deletions
216
client/static/subscription/form-input-style.css
Normal file
216
client/static/subscription/form-input-style.css
Normal file
|
@ -0,0 +1,216 @@
|
|||
/* --- Colors ----------
|
||||
|
||||
Input Border: #DCE4EC
|
||||
Input Group: #FAFAFA
|
||||
Muted: #999999
|
||||
Anchor: #1F68D5
|
||||
Alerts: ...
|
||||
|
||||
*/
|
||||
|
||||
/* --- General -------- */
|
||||
|
||||
form {
|
||||
margin: .5em 0 1em;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 1.2em;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 1em;
|
||||
font-weight: 700;
|
||||
margin-bottom: .3em;
|
||||
}
|
||||
|
||||
.label-checkbox,
|
||||
.label-radio {
|
||||
font-weight: normal;
|
||||
font-size: .9em;
|
||||
}
|
||||
|
||||
.label-inline {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
margin-left: .3em;
|
||||
}
|
||||
|
||||
|
||||
/* --- Inputs ------------- */
|
||||
|
||||
input[type='email'],
|
||||
input[type='number'],
|
||||
input[type='password'],
|
||||
input[type='search'],
|
||||
input[type='tel'],
|
||||
input[type='text'],
|
||||
input[type='url'],
|
||||
textarea,
|
||||
select {
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
background-color: transparent;
|
||||
border: 2px solid #DCE4EC;
|
||||
border-radius: 4px;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
height: 2.8em;
|
||||
padding: .3em .7em;
|
||||
width: 100%;
|
||||
font-size: 1.2em;
|
||||
font-style: inherit;
|
||||
}
|
||||
|
||||
input[type='email']:focus,
|
||||
input[type='number']:focus,
|
||||
input[type='password']:focus,
|
||||
input[type='search']:focus,
|
||||
input[type='tel']:focus,
|
||||
input[type='text']:focus,
|
||||
input[type='url']:focus,
|
||||
textarea:focus,
|
||||
select:focus {
|
||||
border-color: #2D3E4F;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
input[readonly] {
|
||||
color: #999999;
|
||||
}
|
||||
input[readonly]:focus {
|
||||
border-color: #DCE4EC;
|
||||
}
|
||||
|
||||
input[type="checkbox"],
|
||||
input[type="radio"] {
|
||||
margin-bottom: 0;
|
||||
margin-right: .2em;
|
||||
}
|
||||
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
select {
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#d1d1d1" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>') center right no-repeat;
|
||||
padding-right: 2em;
|
||||
}
|
||||
|
||||
select:focus {
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" height="14" viewBox="0 0 29 14" width="29"><path fill="#2D3E4F" d="M9.37727 3.625l5.08154 6.93523L19.54036 3.625"/></svg>');
|
||||
}
|
||||
|
||||
textarea {
|
||||
min-height: 8em;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* --- Input Group --------- */
|
||||
|
||||
.input-group {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.input-group-addon {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
background: #FAFAFA;
|
||||
height: 100%;
|
||||
padding: 0 .75em;
|
||||
border: 2px solid #DCE4EC;
|
||||
box-sizing: border-box;
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.input-group-addon > * {
|
||||
line-height: 2.8em;
|
||||
}
|
||||
|
||||
|
||||
/* --- Alerts ------------- */
|
||||
|
||||
.alert {
|
||||
margin: 22px auto 0;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
font-family: inherit;
|
||||
font-size: 15px;
|
||||
line-height: 21px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.alert-dismissible .close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert-success { color: #397740; background-color: #DEF0D9; border-color: #CFEAC8; }
|
||||
.alert-info { color: #33708E; background-color: #D9EDF6; border-color: #BCDFF0; }
|
||||
.alert-warning { color: #8A6D3F; background-color: #FCF8E4; border-color: #F9F2CE; }
|
||||
.alert-danger { color: #AA4144; background-color: #F2DEDE; border-color: #EBCCCC; }
|
||||
|
||||
|
||||
/* --- GPG Key ------------- */
|
||||
|
||||
.form-group.gpg > label {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.btn-download-pubkey,
|
||||
.btn-download-pubkey:focus,
|
||||
.btn-download-pubkey:active {
|
||||
background: none;
|
||||
border: none;
|
||||
display: block;
|
||||
font: inherit;
|
||||
font-size: .8em;
|
||||
margin: .3em 0 0;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
outline-offset: 0;
|
||||
color: #1F68D5;
|
||||
cursor: pointer;
|
||||
text-transform: none;
|
||||
height: auto;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.btn-download-pubkey:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.gpg-text {
|
||||
font-family: monospace;
|
||||
font-size: .8em;
|
||||
}
|
||||
|
||||
|
||||
/* --- Other ------------- */
|
||||
|
||||
.help-block {
|
||||
display: block;
|
||||
font-size: .9em;
|
||||
line-height: 1;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
form a {
|
||||
color: #1F68D5;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
form a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
176
client/static/subscription/widget.js
Normal file
176
client/static/subscription/widget.js
Normal file
|
@ -0,0 +1,176 @@
|
|||
/* eslint-env browser */
|
||||
/* eslint prefer-arrow-callback: 0, object-shorthand: 0, new-cap: 0, no-invalid-this: 0, no-var: 0*/
|
||||
|
||||
if (typeof window.mailtrain !== 'object') {
|
||||
window.mailtrain = {};
|
||||
(function(mt) {
|
||||
'use strict';
|
||||
|
||||
if (document.documentMode <= 9 || typeof XMLHttpRequest === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var cookie = {
|
||||
create: function(name, value, days) {
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + ((days || 365) * 24 * 60 * 60 * 1000));
|
||||
var expires = '; expires=' + date.toGMTString();
|
||||
document.cookie = name + '=' + value + expires + '; path=/';
|
||||
},
|
||||
read: function(name) {
|
||||
var a = document.cookie.match('(^|;)\\s*' + name + '\\s*=\\s*([^;]+)');
|
||||
return a ? a.pop() : null;
|
||||
}
|
||||
};
|
||||
|
||||
var forEach = function(array, callback, scope) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
callback.call(scope, i, array[i]);
|
||||
}
|
||||
};
|
||||
|
||||
var loadScript = function(exists, url, callback) {
|
||||
if (eval(exists)) {
|
||||
return callback(true);
|
||||
}
|
||||
var scriptTag = document.createElement('script');
|
||||
scriptTag.setAttribute('type', 'text/javascript');
|
||||
scriptTag.setAttribute('src', url);
|
||||
if (typeof callback !== 'undefined') {
|
||||
if (scriptTag.readyState) {
|
||||
scriptTag.onreadystatechange = function() {
|
||||
(this.readyState === 'complete' || this.readyState === 'loaded') && callback();
|
||||
};
|
||||
} else {
|
||||
scriptTag.onload = callback;
|
||||
}
|
||||
}
|
||||
(document.getElementsByTagName('head')[0] || document.documentElement).appendChild(scriptTag);
|
||||
};
|
||||
|
||||
var serialize = function(form) {
|
||||
var field, s = [];
|
||||
if (typeof form === 'object' && form.nodeName === 'FORM') {
|
||||
var len = form.elements.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
field = form.elements[i];
|
||||
if (field.name && !field.disabled && field.type !== 'file' && field.type !== 'reset' && field.type !== 'submit' && field.type !== 'button') {
|
||||
if (field.type === 'select-multiple') {
|
||||
for (var j = form.elements[i].options.length - 1; j >= 0; j--) {
|
||||
if (field.options[j].selected) {
|
||||
s[s.length] = encodeURIComponent(field.name) + '=' + encodeURIComponent(field.options[j].value);
|
||||
}
|
||||
}
|
||||
} else if ((field.type !== 'checkbox' && field.type !== 'radio') || field.checked) {
|
||||
s[s.length] = encodeURIComponent(field.name) + '=' + encodeURIComponent(field.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return s.join('&').replace(/%20/g, '+');
|
||||
};
|
||||
|
||||
var getXHR = function(method, url, successHandler, errorHandler) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open(method, url, true);
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4) {
|
||||
var data;
|
||||
try {
|
||||
data = JSON.parse(xhr.responseText);
|
||||
} catch(err) {
|
||||
data = { error: err.message || err };
|
||||
}
|
||||
xhr.status === 200 && !data.error
|
||||
? successHandler && successHandler(data)
|
||||
: errorHandler && errorHandler(data);
|
||||
}
|
||||
};
|
||||
return xhr;
|
||||
};
|
||||
|
||||
var getJSON = function(url, successHandler, errorHandler) {
|
||||
var xhr = getXHR('get', url, successHandler, errorHandler);
|
||||
xhr.send();
|
||||
};
|
||||
|
||||
var sendForm = function(form, successHandler, errorHandler) {
|
||||
var url = form.getAttribute('action');
|
||||
var xhr = getXHR('post', url, successHandler, errorHandler);
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
xhr.send(serialize(form));
|
||||
};
|
||||
|
||||
var renderWidget = function(container, data) {
|
||||
container.innerHTML = data.html;
|
||||
|
||||
var form = container.querySelector('.form');
|
||||
var statusContainer = container.querySelector('.status');
|
||||
|
||||
var setStatus = function(templateName, message) {
|
||||
var html = container.querySelector('div[data-status-template="' + templateName + '"]').outerHTML;
|
||||
html = message ? html.replace('{message}', message) : html;
|
||||
statusContainer.innerHTML = html;
|
||||
};
|
||||
|
||||
if (cookie.read('has-subscribed-to-' + data.cid)) {
|
||||
setStatus('already-subscribed');
|
||||
form.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
container.querySelector('.sub-time').value = new Date().getTime();
|
||||
|
||||
if (window.moment && window.moment.tz) {
|
||||
container.querySelector('.tz-detect').value = window.moment.tz.guess() || '';
|
||||
}
|
||||
|
||||
var isSending = false;
|
||||
|
||||
form.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (isSending) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSending = true;
|
||||
setStatus('spinner');
|
||||
|
||||
sendForm(form, function(j) {
|
||||
isSending = false;
|
||||
setStatus('confirm-notice');
|
||||
form.style.display = 'none';
|
||||
container.scrollIntoView();
|
||||
cookie.create('has-subscribed-to-' + data.cid, 1);
|
||||
}, function(j) {
|
||||
isSending = false;
|
||||
setStatus('error', j.error);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
forEach(document.body.querySelectorAll('div[data-mailtrain-subscription-widget]'), function(i, container) {
|
||||
var url = container.getAttribute('data-url');
|
||||
getJSON(url, function(j) {
|
||||
renderWidget(container, j.data);
|
||||
}, function(j) {
|
||||
console.log(j);
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
loadScript('window.moment', 'https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.12.0/moment.min.js', function(existed) {
|
||||
loadScript('window.moment.tz', 'https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.3/moment-timezone-with-data.min.js', function(existed) {
|
||||
if (window.moment && window.moment.tz) {
|
||||
forEach(document.body.querySelectorAll('div[data-mailtrain-subscription-widget] .tz-detect'), function(i, el) {
|
||||
el.value = window.moment.tz.guess() || '';
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
})(window.mailtrain);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue