2020-02-25 21:54:43 +01:00

2369 lines
82 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var Formwork = {
baseUri: '',
config: {},
init: function () {
this.baseUri = $('meta[name=base-uri]').getAttribute('content');
Formwork.Modals.init();
Formwork.Forms.init();
Formwork.Dropdowns.init();
Formwork.Tooltips.init();
Formwork.Dashboard.init();
Formwork.Pages.init();
Formwork.Updates.init();
$('.toggle-navigation').addEventListener('click', function () {
$('.sidebar').classList.toggle('show');
});
$$('[data-chart-data]').forEach(function (element) {
var data = JSON.parse(element.getAttribute('data-chart-data'));
Formwork.Chart(element, data);
});
$$('meta[name=notification]').forEach(function (element) {
var notification = new Formwork.Notification(element.getAttribute('content'), element.getAttribute('data-type'), element.getAttribute('data-interval'));
notification.show();
element.parentNode.removeChild(element);
});
if ($('[data-command=save]')) {
document.addEventListener('keydown', function (event) {
if (!event.altKey && (event.ctrlKey || event.metaKey)) {
if (event.which === 83) { // ctrl/cmd + S
$('[data-command=save]').click();
event.preventDefault();
}
}
});
}
},
initGlobals: function (global) {
global.$ = function (selector, parent) {
if (typeof parent === 'undefined') {
parent = document;
}
return parent.querySelector(selector);
};
global.$$ = function (selector, parent) {
if (typeof parent === 'undefined') {
parent = document;
}
return parent.querySelectorAll(selector);
};
// NodeList.prototype.forEach polyfill
if (!('forEach' in global.NodeList.prototype)) {
global.NodeList.prototype.forEach = global.Array.prototype.forEach;
}
// Element.prototype.matches polyfill
if (!('matches' in global.Element.prototype)) {
global.Element.prototype.matches = global.Element.prototype.msMatchesSelector || global.Element.prototype.webkitMatchesSelector;
}
// Element.prototype.closest polyfill
if (!('closest' in global.Element.prototype)) {
global.Element.prototype.closest = function (selectors) {
var element = this;
do {
if (element.matches(selectors)) {
return element;
}
element = element.parentElement || element.parentNode;
} while (element !== null && element.nodeType === 1);
return null;
};
}
}
};
document.addEventListener('DOMContentLoaded', function () {
Formwork.init();
});
Formwork.initGlobals(window);
Formwork.ArrayInput = function (input) {
var isAssociative = input.classList.contains('array-input-associative');
var inputName = input.getAttribute('data-name');
$('.array-input-add', input).addEventListener('click', addRow);
$('.array-input-remove', input).addEventListener('click', removeRow);
if (isAssociative) {
$('.array-input-key', input).addEventListener('keyup', function () {
$('.array-input-value', this.parentNode).setAttribute('name', inputName + '[' + this.value + ']');
});
$('.array-input-value', input).addEventListener('keyup', function () {
this.setAttribute('name', inputName + '[' + $('.array-input-key', this.parentNode).value + ']');
});
}
/* global Sortable:false */
Sortable.create(input, {
handle: '.sort-handle',
forceFallback: true
});
function addRow() {
var row = this.closest('.array-input-row');
var clone = row.cloneNode(true);
$('.array-input-key', clone).value = '';
$('.array-input-value', clone).value = '';
$('.array-input-add', clone).addEventListener('click', addRow);
$('.array-input-remove', clone).addEventListener('click', removeRow);
if (row.nextSibling) {
row.parentNode.insertBefore(clone, row.nextSibling);
} else {
row.parentNode.appendChild(clone);
}
}
function removeRow() {
var row = this.closest('.array-input-row');
if ($$('.array-input-row', row.parentNode).length > 0) {
row.parentNode.removeChild(row);
} else {
$('.array-input-key', row).value = '';
$('.array-input-value', row).value = '';
Formwork.Utils.triggerEvent($('.array-input-key', row), 'keyup');
}
}
};
Formwork.Chart = function (element, data) {
var options = {
showArea: true,
fullWidth: true,
scaleMinSpace: 20,
divisor: 5,
chartPadding: 20,
lineSmooth: false,
low: 0,
axisX: {
showGrid: false,
labelOffset: {
x: 0, y: 10
}
},
axisY: {
onlyInteger: true,
offset: 15,
labelOffset: {
x: 0, y: 5
}
}
};
/* global Chartist:false */
var chart = new Chartist.Line(element, data, options);
chart.container.addEventListener('mouseover', function (event) {
var tooltipOffset, tooltip, strokeWidth;
if (event.target.getAttribute('class') === 'ct-point') {
tooltipOffset = {
x: 0, y: -8
};
if (navigator.userAgent.indexOf('Firefox') !== -1) {
strokeWidth = parseFloat(getComputedStyle(event.target)['stroke-width']);
tooltipOffset.x += strokeWidth / 2;
tooltipOffset.y += strokeWidth / 2;
}
tooltip = new Formwork.Tooltip(event.target.getAttribute('ct:value'), {
referenceElement: event.target,
offset: tooltipOffset
});
tooltip.show();
}
});
};
Formwork.Dashboard = {
init: function () {
var clearCacheCommand = $('[data-command=clear-cache]');
var makeBackupCommand = $('[data-command-make-backup]');
if (clearCacheCommand) {
clearCacheCommand.addEventListener('click', function () {
Formwork.Request({
method: 'POST',
url: Formwork.baseUri + 'cache/clear/',
data: {'csrf-token': $('meta[name=csrf-token]').getAttribute('content')}
}, function (response) {
var notification = new Formwork.Notification(response.message, response.status, 5000);
notification.show();
});
});
}
if (makeBackupCommand) {
$(makeBackupCommand).addEventListener('click', function () {
var button = this;
button.setAttribute('disabled', '');
Formwork.Request({
method: 'POST',
url: Formwork.baseUri + 'backup/make/',
data: {'csrf-token': $('meta[name=csrf-token]').getAttribute('content')}
}, function (response) {
var notification = new Formwork.Notification(response.message, response.status, 5000);
notification.show();
setTimeout(function () {
if (response.status === 'success') {
Formwork.Utils.download(response.data.uri, $('meta[name=csrf-token]').getAttribute('content'));
}
button.removeAttribute('disabled');
}, 1000);
});
});
}
}
};
Formwork.DatePicker = function (input, options) {
var defaults = {
dayLabels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'],
monthLabels: ['January', 'February', 'March', 'April', 'May', 'June', 'July' ,'August', 'September', 'October', 'November', 'December'],
weekStarts: 0,
todayLabel: 'Today',
format: 'YYYY-MM-DD'
};
var today = new Date();
var dateKeeper, dateHelpers, calendar;
options = Formwork.Utils.extendObject({}, defaults, options);
dateKeeper = {
year: today.getFullYear(),
month: today.getMonth(),
day: today.getDate(),
setDate: function (date) {
this.year = date.getFullYear();
this.month = date.getMonth();
this.day = date.getDate();
},
lastDay: function () {
this.day = dateHelpers.daysInMonth(this.month, this.year);
},
prevYear: function () {
this.year--;
},
nextYear: function () {
this.year++;
},
prevMonth: function () {
this.month = dateHelpers.mod(this.month - 1, 12);
if (this.month === 11) {
this.prevYear();
}
if (this.day > dateHelpers.daysInMonth(this.month, this.year)) {
this.lastDay();
}
},
nextMonth: function () {
this.month = dateHelpers.mod(this.month + 1, 12);
if (this.month === 0) {
this.nextYear();
}
if (this.day > dateHelpers.daysInMonth(this.month, this.year)) {
this.lastDay();
}
},
prevWeek: function () {
this.day -= 7;
if (this.day < 1) {
this.prevMonth();
this.day += dateHelpers.daysInMonth(this.month, this.year);
}
},
nextWeek: function () {
this.day += 7;
if (this.day > dateHelpers.daysInMonth(this.month, this.year)) {
this.day -= dateHelpers.daysInMonth(this.month, this.year);
this.nextMonth();
}
},
prevDay: function () {
this.day--;
if (this.day < 1) {
this.prevMonth();
this.lastDay();
}
},
nextDay: function () {
this.day++;
if (this.day > dateHelpers.daysInMonth(this.month, this.year)) {
this.nextMonth();
this.day = 1;
}
}
};
dateHelpers = {
_daysInMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
mod: function (n, m) {
return ((n % m) + m) % m;
},
pad: function (num) {
return num.toString().length === 1 ? '0' + num : num;
},
isValidDate: function (date) {
return date && !isNaN(Date.parse(date));
},
isLeapYear: function (year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
},
daysInMonth: function (month, year) {
return month === 1 && this.isLeapYear(year) ? 29 : this._daysInMonth[month];
},
formatDateTime: function (date) {
var format = options.format;
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var hours = date.getHours();
var minutes = date.getMinutes();
var seconds = date.getSeconds();
var am = hours < 12;
if (format.indexOf('a') > -1) {
hours = dateHelpers.mod(hours, 12) > 0 ? dateHelpers.mod(hours, 12) : 12;
}
return format.replace('YYYY', year)
.replace('YY', year.toString().substr(-2))
.replace('MM', dateHelpers.pad(month))
.replace('DD', dateHelpers.pad(day))
.replace('hh', dateHelpers.pad(hours))
.replace('mm', dateHelpers.pad(minutes))
.replace('ss', dateHelpers.pad(seconds))
.replace('a', am ? 'AM' : 'PM');
}
};
calendar = $('.calendar') ? $('.calendar') : generateCalendar();
initInput();
function initInput() {
var value = input.value;
input.readOnly = true;
input.size = options.format.length;
if (dateHelpers.isValidDate(value)) {
value = new Date(value);
input.setAttribute('data-date', value);
input.value = dateHelpers.formatDateTime(value);
}
input.addEventListener('change', function () {
if (this.value === '') {
this.setAttribute('data-date', '');
} else {
this.value = dateHelpers.formatDateTime(this.getAttribute('data-date'));
}
});
input.addEventListener('keydown', function (event) {
var date = this.getAttribute('data-date');
dateKeeper.setDate(dateHelpers.isValidDate(date) ? new Date(date) : new Date());
switch (event.which) {
case 13: // enter
$('.calendar-day.selected', calendar).click();
calendar.style.display = 'none';
break;
case 8: // backspace
this.value = '';
this.blur();
calendar.style.display = 'none';
break;
case 27: // escape
this.blur();
calendar.style.display = 'none';
break;
case 37: // left arrow
if (event.ctrlKey || event.metaKey) {
if (event.shiftKey) {
dateKeeper.prevYear();
} else {
dateKeeper.prevMonth();
}
} else {
dateKeeper.prevDay();
}
updateInput(this);
break;
case 38: // up arrow
dateKeeper.prevWeek();
updateInput(this);
break;
case 39: // right arrow
if (event.ctrlKey || event.metaKey) {
if (event.shiftKey) {
dateKeeper.nextYear();
} else {
dateKeeper.nextMonth();
}
} else {
dateKeeper.nextDay();
}
updateInput(this);
break;
case 40: // down arrow
dateKeeper.nextWeek();
updateInput(this);
break;
case 48: // 0
if (event.ctrlKey || event.metaKey) {
dateKeeper.setDate(new Date());
}
updateInput(this);
break;
default:
return;
}
event.stopPropagation();
event.preventDefault();
});
input.addEventListener('focus', function () {
var date = dateHelpers.isValidDate(this.getAttribute('data-date')) ? new Date(this.getAttribute('data-date')) : new Date();
dateKeeper.setDate(date);
generateCalendarTable(dateKeeper.year, dateKeeper.month, dateKeeper.day);
calendar.style.display = 'block';
setCalendarPosition();
});
input.addEventListener('blur', function () {
calendar.style.display = 'none';
});
}
function updateInput(input) {
var date = new Date(dateKeeper.year, dateKeeper.month, dateKeeper.day);
generateCalendarTable(dateKeeper.year, dateKeeper.month, dateKeeper.day);
input.value = dateHelpers.formatDateTime(date);
input.setAttribute('data-date', date);
}
function getCurrentInput() {
return document.activeElement.classList.contains('date-input') ? document.activeElement : null;
}
function generateCalendar() {
calendar = document.createElement('div');
calendar.className = 'calendar';
calendar.innerHTML = '<div class="calendar-buttons"><button type="button" class="prevMonth"><i class="i-chevron-left"></i></button><button class="currentMonth">' + options.todayLabel + '</button><button type="button" class="nextMonth"><i class="i-chevron-right"></i></button></div><div class="calendar-separator"></div><table class="calendar-table"></table>';
document.body.appendChild(calendar);
$('.currentMonth', calendar).addEventListener('mousedown', function (event) {
var input = getCurrentInput();
var today = new Date();
dateKeeper.setDate(today);
updateInput(input);
input.blur();
event.preventDefault();
});
Formwork.Utils.longClick($('.prevMonth', calendar), function (event) {
dateKeeper.prevMonth();
generateCalendarTable(dateKeeper.year, dateKeeper.month);
event.preventDefault();
}, 750, 500);
Formwork.Utils.longClick($('.nextMonth', calendar), function (event) {
dateKeeper.nextMonth();
generateCalendarTable(dateKeeper.year, dateKeeper.month);
event.preventDefault();
}, 750, 500);
window.addEventListener('mousedown', function (event) {
if (calendar.style.display !== 'none') {
if (event.target.closest('.calendar')) {
event.preventDefault();
}
}
});
window.addEventListener('resize', Formwork.Utils.throttle(setCalendarPosition, 100));
return calendar;
}
function generateCalendarTable(year, month, day) {
var i, j;
var num = 1;
var firstDay = new Date(year, month, 1).getDay();
var monthLength = dateHelpers.daysInMonth(month, year);
var monthName = options.monthLabels[month];
var start = dateHelpers.mod(firstDay - options.weekStarts, 7);
var html = '';
html += '<tr><th class="calendar-header" colspan="7">';
html += monthName + '&nbsp;' + year;
html += '</th></tr>';
html += '<tr>';
for (i = 0; i < 7; i++ ){
html += '<td class="calendar-header-day">';
html += options.dayLabels[dateHelpers.mod(i + options.weekStarts, 7)];
html += '</td>';
}
html += '</tr><tr>';
for (i = 0; i < 6; i++) {
for (j = 0; j < 7; j++) {
if (num <= monthLength && (i > 0 || j >= start)) {
if (num === day) {
html += '<td class="calendar-day selected">';
} else {
html += '<td class="calendar-day">';
}
html += num++;
} else if (num === 1) {
html += '<td class="calendar-prev-month-day">';
html += dateHelpers.daysInMonth(dateHelpers.mod(month - 1, 12), year) - start + j + 1;
} else {
html += '<td class="calendar-next-month-day">';
html += num++ - monthLength;
}
html += '</td>';
}
html += '</tr><tr>';
}
html += '</tr>';
$('.calendar-table', calendar).innerHTML = html;
$$('.calendar-day', calendar).forEach(function (element) {
element.addEventListener('mousedown', function (event) {
event.stopPropagation();
event.preventDefault();
});
element.addEventListener('click', function () {
var input = getCurrentInput();
var date = new Date(dateKeeper.year, dateKeeper.month, parseInt(this.textContent));
input.setAttribute('data-date', date);
input.value = dateHelpers.formatDateTime(date);
input.blur();
});
});
}
function setCalendarPosition() {
var inputRect, inputTop, inputLeft,
calendarRect, calendarTop, calendarLeft, calendarWidth, calendarHeight,
windowWidth, windowHeight;
input = getCurrentInput();
if (!input || calendar.style.display !== 'block') {
return;
}
inputRect = input.getBoundingClientRect();
inputTop = inputRect.top + window.pageYOffset;
inputLeft = inputRect.left + window.pageXOffset;
calendar.style.top = (inputTop + input.offsetHeight) + 'px';
calendar.style.left = (inputLeft + input.offsetLeft) + 'px';
calendarRect = calendar.getBoundingClientRect();
calendarTop = calendarRect.top + window.pageYOffset;
calendarLeft = calendarRect.left + window.pageXOffset;
calendarWidth = Formwork.Utils.outerWidth(calendar);
calendarHeight = Formwork.Utils.outerHeight(calendar);
windowWidth = document.documentElement.clientWidth;
windowHeight = document.documentElement.clientHeight;
if (calendarLeft + calendarWidth > windowWidth) {
calendar.style.left = (windowWidth - calendarWidth) + 'px';
}
if (calendarTop < window.pageYOffset || window.pageYOffset < calendarTop + calendarHeight - windowHeight) {
window.scrollTo(window.pageXOffset, calendarTop + calendarHeight - windowHeight);
}
}
};
Formwork.Dropdowns = {
init: function () {
if ($('.dropdown')) {
document.addEventListener('click', function (event) {
var button = event.target.closest('.dropdown-button');
var dropdown, isVisible;
if (button) {
dropdown = document.getElementById(button.getAttribute('data-dropdown'));
isVisible = getComputedStyle(dropdown).display !== 'none';
event.preventDefault();
}
$$('.dropdown-menu').forEach(function (element) {
element.style.display = '';
});
if (dropdown && !isVisible) {
dropdown.style.display = 'block';
}
});
}
}
};
Formwork.Editor = function (textarea) {
/* global CodeMirror:false */
var editor = CodeMirror.fromTextArea(textarea, {
mode: 'markdown',
theme: 'formwork',
indentUnit: 4,
lineWrapping: true,
addModeClass: true,
extraKeys: {'Enter': 'newlineAndIndentContinueMarkdownList'}
});
var toolbar = $('.editor-toolbar[data-for=' + textarea.id + ']');
$('[data-command=bold]', toolbar).addEventListener('click', function () {
insertAtCursor('**');
});
$('[data-command=italic]', toolbar).addEventListener('click', function () {
insertAtCursor('_');
});
$('[data-command=ul]', toolbar).addEventListener('click', function () {
insertAtCursor(prependSequence() + '- ', '');
});
$('[data-command=ol]', toolbar).addEventListener('click', function () {
var num = /^\d+\./.exec(lastLine(editor.getValue()));
if (num) {
insertAtCursor('\n' + (parseInt(num) + 1) + '. ', '');
} else {
insertAtCursor(prependSequence() + '1. ', '');
}
});
$('[data-command=quote]', toolbar).addEventListener('click', function () {
insertAtCursor(prependSequence() + '> ', '');
});
$('[data-command=link]', toolbar).addEventListener('click', function () {
var selection = editor.getSelection();
if (/^(https?:\/\/|mailto:)/i.test(selection)) {
insertAtCursor('[', '](' + selection + ')', true);
} else if (selection !== '') {
insertAtCursor('[' + selection + '](http://', ')', true);
} else {
insertAtCursor('[', '](http://)');
}
});
$('[data-command=image]', toolbar).addEventListener('click', function () {
Formwork.Modals.show('imagesModal', null, function (modal) {
var selected = $('.image-picker-thumbnail.selected', modal);
if (selected) {
selected.classList.remove('selected');
}
function confirmImage() {
var filename = $('.image-picker-thumbnail.selected', $('#imagesModal')).getAttribute('data-filename');
if (filename !== undefined) {
insertAtCursor(prependSequence() + '![', '](' + filename + ')');
} else {
insertAtCursor(prependSequence() + '![](', ')');
}
this.removeEventListener('click', confirmImage);
}
$('.image-picker-confirm', modal).addEventListener('click', confirmImage);
});
});
$('[data-command=summary]', toolbar).addEventListener('click', function () {
var prevChar, prepend;
if (!hasSummarySequence()) {
prevChar = prevCursorChar();
prepend = (prevChar === undefined || prevChar === '\n') ? '' : '\n';
insertAtCursor(prepend + '\n===\n\n', '');
this.setAttribute('disabled', '');
}
});
$('[data-command=undo]', toolbar).addEventListener('click', function () {
editor.undo();
editor.focus();
});
$('[data-command=redo]', toolbar).addEventListener('click', function () {
editor.redo();
editor.focus();
});
disableSummaryCommand();
editor.on('changes', Formwork.Utils.debounce(function () {
textarea.value = editor.getValue();
disableSummaryCommand();
if (editor.historySize().undo < 1) {
$('[data-command=undo]').setAttribute('disabled', '');
} else {
$('[data-command=undo]').removeAttribute('disabled');
}
if (editor.historySize().redo < 1) {
$('[data-command=redo]').setAttribute('disabled', '');
} else {
$('[data-command=redo]').removeAttribute('disabled');
}
}, 500));
document.addEventListener('keydown', function (event) {
if (!event.altKey && (event.ctrlKey || event.metaKey)) {
switch (event.which) {
case 66: // ctrl/cmd + B
$('[data-command=bold]', toolbar).click();
event.preventDefault();
break;
case 73: // ctrl/cmd + I
$('[data-command=italic]', toolbar).click();
event.preventDefault();
break;
case 75: // ctrl/cmd + K
$('[data-command=link]', toolbar).click();
event.preventDefault();
break;
}
}
});
function hasSummarySequence() {
return /\n+===\n+/.test(editor.getValue());
}
function disableSummaryCommand() {
if (hasSummarySequence()) {
$('[data-command=summary]', toolbar).setAttribute('disabled', '');
} else {
$('[data-command=summary]', toolbar).removeAttribute('disabled');
}
}
function lastLine(text) {
var index = text.lastIndexOf('\n');
if (index === -1) {
return text;
}
return text.substring(index + 1);
}
function prevCursorChar() {
var line = editor.getLine(editor.getCursor().line);
return line.length === 0 ? undefined : line.slice(-1);
}
function prependSequence() {
switch (prevCursorChar()) {
case undefined:
return '';
case '\n':
return '\n';
default:
return '\n\n';
}
}
function insertAtCursor(leftValue, rightValue, dropSelection) {
var selection, cursor, lineBreaks;
if (rightValue === undefined) {
rightValue = leftValue;
}
selection = dropSelection === true ? '' : editor.getSelection();
cursor = editor.getCursor();
lineBreaks = leftValue.split('\n').length - 1;
editor.replaceSelection(leftValue + selection + rightValue);
editor.setCursor(cursor.line + lineBreaks, cursor.ch + leftValue.length - lineBreaks);
editor.focus();
}
};
Formwork.FileInput = function (input) {
var label = $('label[for="' + input.id + '"]');
input.setAttribute('data-label', $('label[for="' + input.id + '"] span').innerHTML);
input.addEventListener('change', updateLabel);
input.addEventListener('input', updateLabel);
label.addEventListener('drag', preventDefault);
label.addEventListener('dragstart', preventDefault);
label.addEventListener('dragend', preventDefault);
label.addEventListener('dragover', handleDragenter);
label.addEventListener('dragenter', handleDragenter);
label.addEventListener('dragleave', handleDragleave);
label.addEventListener('drop', function (event) {
var target = document.getElementById(this.getAttribute('for'));
target.files = event.dataTransfer.files;
// Firefox won't trigger a change event, so we explicitly do that
Formwork.Utils.triggerEvent(target, 'change');
event.preventDefault();
});
function updateLabel() {
var span = $('label[for="' + this.id + '"] span');
if (this.files.length > 0) {
span.innerHTML = this.files[0].name;
} else {
span.innerHTML = this.getAttribute('data-label');
}
}
function preventDefault(event) {
event.preventDefault();
}
function handleDragenter(event) {
this.classList.add('drag');
event.preventDefault();
}
function handleDragleave(event) {
this.classList.remove('drag');
event.preventDefault();
}
};
Formwork.Form = function (form) {
var originalData = Formwork.Utils.serializeForm(form);
form.addEventListener('submit', function () {
window.removeEventListener('beforeunload', handleBeforeunload);
});
window.addEventListener('beforeunload', handleBeforeunload);
$$('input[type=file][data-auto-upload]', form).forEach(function (element) {
element.addEventListener('change', function () {
if (!hasChanged(false)) {
form.submit();
}
});
});
$('#changesModal [data-command=continue]').addEventListener('click', function () {
window.removeEventListener('beforeunload', handleBeforeunload);
window.location.href = this.getAttribute('data-href');
});
$$('a[href]:not([href^="#"]):not([target="_blank"])').forEach(function (element) {
element.addEventListener('click', function (event) {
if (hasChanged()) {
event.preventDefault();
Formwork.Modals.show('changesModal', null, function (modal) {
$('[data-command=continue]', modal).setAttribute('data-href', element.href);
});
}
});
});
function handleBeforeunload(event) {
if (hasChanged()) {
event.preventDefault();
event.returnValue = '';
}
}
function hasChanged(checkFileInputs) {
var fileInputs, i;
fileInputs = $$('input[file]', form);
if (typeof checkFileInputs === 'undefined') {
checkFileInputs = true;
}
if (checkFileInputs === true && fileInputs.length > 0) {
for (i = 0; i < fileInputs.length; i++) {
if (fileInputs[i].files.length > 0) {
return true;
}
}
}
return Formwork.Utils.serializeForm(form) !== originalData;
}
};
Formwork.Forms = {
init: function () {
$$('[data-form]').forEach(function (element) {
Formwork.Form(element);
});
$$('input[data-enable]').forEach(function (element) {
element.addEventListener('change', function () {
var i, input;
var inputs = this.getAttribute('data-enable').split(',');
for (i = 0; i < inputs.length; i++) {
input = $('input[name="' + inputs[i] + '"]');
if (!this.checked) {
input.setAttribute('disabled', '');
} else {
input.removeAttribute('disabled');
}
}
});
});
$$('.input-reset').forEach(function (element) {
element.addEventListener('click', function () {
var target = document.getElementById(this.getAttribute('data-reset'));
target.value = '';
Formwork.Utils.triggerEvent(target, 'change');
});
});
$$('.date-input').forEach(function (element) {
Formwork.DatePicker(element, Formwork.config.DatePicker);
});
$$('.image-input').forEach(function (element) {
element.addEventListener('click', function () {
Formwork.Modals.show('imagesModal', null, function (modal) {
var selected = $('.image-picker-thumbnail.selected', modal);
if (selected) {
selected.classList.remove('selected');
}
if (this.value) {
$('.image-picker-thumbnail[data-filename="' + this.value + '"]', modal).classList.add('selected');
}
$('.image-picker-confirm', modal).setAttribute('data-target', element.id);
});
});
});
$$('.image-picker').forEach(function (element) {
Formwork.ImagePicker(element);
});
$$('.editor-textarea').forEach(function (element) {
Formwork.Editor(element);
});
$$('input[type=file]').forEach(function (element) {
Formwork.FileInput(element);
});
$$('input[data-field=tags]').forEach(function (element) {
Formwork.TagInput(element);
});
$$('input[type=range]').forEach(function (element) {
Formwork.RangeInput(element);
});
$$('.array-input').forEach(function (element) {
Formwork.ArrayInput(element);
});
}
};
Formwork.ImagePicker = function (element) {
var options = $$('option', element);
var confirmCommand = $('.image-picker-confirm', element.parentNode);
var uploadCommand = $('[data-command=upload]', element.parentNode);
var container, thumbnail, i;
element.style.display = 'none';
if (options.length > 0) {
container = document.createElement('div');
container.className = 'image-picker-thumbnails';
for (i = 0; i < options.length; i++) {
thumbnail = document.createElement('div');
thumbnail.className = 'image-picker-thumbnail';
thumbnail.style.backgroundImage = 'url(' + options[i].value + ')';
thumbnail.setAttribute('data-uri', options[i].value);
thumbnail.setAttribute('data-filename', options[i].text);
thumbnail.addEventListener('click', handleThumbnailClick);
thumbnail.addEventListener('dblclick', handleThumbnailDblclick);
container.appendChild(thumbnail);
}
element.parentNode.insertBefore(container, element);
$('.image-picker-empty-state').style.display = 'none';
}
confirmCommand.addEventListener('click', function () {
var selectedThumbnail = $('.image-picker-thumbnail.selected');
var target = document.getElementById(this.getAttribute('data-target'));
if (selectedThumbnail && target) {
target.value = selectedThumbnail.getAttribute('data-filename');
}
});
uploadCommand.addEventListener('click', function () {
document.getElementById(this.getAttribute('data-upload-target')).click();
});
function handleThumbnailClick() {
var target = document.getElementById($('.image-picker-confirm').getAttribute('data-target'));
if (target) {
target.value = this.getAttribute('data-filename');
}
$$('.image-picker-thumbnail').forEach(function (element) {
element.classList.remove('selected');
});
this.classList.add('selected');
}
function handleThumbnailDblclick() {
this.click();
$('.image-picker-confirm').click();
}
};
Formwork.Modals = {
init: function () {
$$('[data-modal]').forEach(function (element) {
element.addEventListener('click', function () {
var modal = this.getAttribute('data-modal');
var action = this.getAttribute('data-modal-action');
if (action) {
Formwork.Modals.show(modal, action);
} else {
Formwork.Modals.show(modal);
}
});
});
$$('.modal [data-dismiss]').forEach(function (element) {
element.addEventListener('click', function () {
var valid;
if (this.hasAttribute('data-validate')) {
valid = Formwork.Modals.validate(this.getAttribute('data-dismiss'));
if (!valid) {
return;
}
}
Formwork.Modals.hide(this.getAttribute('data-dismiss'));
});
});
$$('.modal').forEach(function (element) {
element.addEventListener('click', function (event) {
if (event.target === this) {
Formwork.Modals.hide();
}
});
});
document.addEventListener('keyup', function (event) {
// ESC key
if (event.which === 27) {
Formwork.Modals.hide();
}
});
},
show: function (id, action, callback) {
var modal = document.getElementById(id);
if (!modal) {
return;
}
modal.classList.add('show');
if (action) {
$('form', modal).setAttribute('action', action);
}
if ($('[autofocus]', modal)) {
Formwork.Utils.triggerEvent($('[autofocus]', modal), 'focus'); // Firefox bug
}
if (typeof callback === 'function') {
callback(modal);
}
$$('.tooltip').forEach(function (element) {
element.parentNode.removeChild(element);
});
this.createBackdrop();
},
hide: function (id) {
if (typeof id !== 'undefined') {
document.getElementById(id).classList.remove('show');
} else {
$$('.modal').forEach(function (element) {
element.classList.remove('show');
});
}
this.removeBackdrop();
},
createBackdrop: function () {
var backdrop;
if (!$('.modal-backdrop')) {
backdrop = document.createElement('div');
backdrop.className = 'modal-backdrop';
document.body.appendChild(backdrop);
}
},
removeBackdrop: function () {
var backdrop = $('.modal-backdrop');
if (backdrop) {
backdrop.parentNode.removeChild(backdrop);
}
},
validate: function (id) {
var valid = false;
var modal = document.getElementById(id);
$$('[required]', id).forEach(function (element) {
if (element.value === '') {
element.classList('input-invalid');
Formwork.Utils.triggerEvent(element, 'focus');
$('.modal-error', modal).style.display = 'block';
valid = false;
return false;
}
valid = true;
});
return valid;
}
};
Formwork.Notification = function (text, type, interval, options) {
var defaults = {
top: 20,
right: 20,
fadeOutDelay: 300,
mouseleaveDelay: 1000
};
var notification, timer;
options = Formwork.Utils.extendObject({}, defaults, options);
function show() {
var position;
notification = document.createElement('div');
notification.className = 'notification';
notification.classList.add('notification-' + type);
notification.innerHTML = text;
position = getNotificationPosition();
notification.style.top = position.top + 'px';
notification.style.right = position.right + 'px';
document.body.appendChild(notification);
timer = setTimeout(remove, interval);
notification.addEventListener('click', remove);
notification.addEventListener('mouseenter', function () {
clearTimeout(timer);
});
notification.addEventListener('mouseleave', function () {
timer = setTimeout(remove, options.mouseleaveDelay);
});
}
function remove() {
var found = false;
var offset = Formwork.Utils.outerHeight(notification);
$$('.notification').forEach(function (element) {
if (element === notification) {
found = true;
element.classList.add('fadeout');
} else if (found) {
element.style.top = (parseInt(element.style.top) - offset) + 'px';
}
});
setTimeout(function () {
if (notification && notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, options.fadeOutDelay);
}
function getNotificationPosition() {
var top = options.top;
var right = options.right;
var lastNotification = getLastNotification();
var rect;
if (lastNotification !== undefined) {
rect = lastNotification.getBoundingClientRect();
top = rect.top + Formwork.Utils.outerHeight(lastNotification);
}
return {
top: top,
right: right
};
}
function getLastNotification() {
var visible;
if ($$('.notification').length > 0) {
visible = $$('.notification:not(.fadeout)');
return visible[visible.length - 1];
}
}
return {
show: show,
remove: remove
};
};
Formwork.Pages = {
init: function () {
var commandExpandAllPages = $('[data-command=expand-all-pages]');
var commandCollapseAllPages = $('[data-command=collapse-all-pages]');
var commandReorderPages = $('[data-command=reorder-pages]');
var searchInput = $('.page-search');
var newPageModal = document.getElementById('newPageModal');
var slugModal = document.getElementById('slugModal');
$$('.pages-list').forEach(function (element) {
if (element.getAttribute('data-sortable-children') === 'true') {
initSortable(element);
}
});
$$('.page-details').forEach(function (element) {
var toggle = $('.page-children-toggle', element);
if (toggle) {
element.addEventListener('click', function () {
toggle.click();
});
}
});
$$('.page-details a').forEach(function (element) {
element.addEventListener('click', function (event) {
event.stopPropagation();
});
});
$$('.page-children-toggle').forEach(function (element) {
element.addEventListener('click', function (event) {
togglePagesList(this);
event.stopPropagation();
});
});
if (commandExpandAllPages) {
commandExpandAllPages.addEventListener('click', function () {
expandAllPages();
this.blur();
});
}
if (commandCollapseAllPages) {
commandCollapseAllPages.addEventListener('click', function () {
collapseAllPages();
this.blur();
});
}
if (commandReorderPages) {
commandReorderPages.addEventListener('click', function () {
this.classList.toggle('active');
$$('.pages-list .sort-handle').forEach(function (element) {
Formwork.Utils.toggleElement(element);
});
this.blur();
});
}
if (searchInput) {
searchInput.addEventListener('focus', function () {
$$('.pages-children').forEach(function (element) {
element.setAttribute('data-display', getComputedStyle(element).display);
});
});
searchInput.addEventListener('keyup', Formwork.Utils.debounce(handleSearch, 100));
document.addEventListener('keydown', function (event) {
if (event.ctrlKey || event.metaKey) {
// ctrl/cmd + F
if (event.which === 70 && document.activeElement !== searchInput) {
searchInput.focus();
event.preventDefault();
}
}
});
}
if (newPageModal) {
$('#page-title', newPageModal).addEventListener('keyup', function () {
$('#page-slug', newPageModal).value = Formwork.Utils.slug(this.value);
});
$('#page-slug', newPageModal).addEventListener('keyup', handleSlugChange);
$('#page-slug', newPageModal).addEventListener('blur', handleSlugChange);
$('#page-parent', newPageModal).addEventListener('change', function () {
var option = this.options[this.selectedIndex];
var pageTemplate = $('#page-template', newPageModal);
var allowedTemplates = option.getAttribute('data-allowed-templates');
var i = 0;
if (allowedTemplates !== null) {
allowedTemplates = allowedTemplates.split(', ');
pageTemplate.setAttribute('data-previous-value', pageTemplate.value);
pageTemplate.value = allowedTemplates[0];
for (i = 0; i < pageTemplate.options.length; i++) {
if (allowedTemplates.indexOf(pageTemplate.options[i].value) === -1) {
pageTemplate.options[i].setAttribute('disabled', '');
}
}
} else {
pageTemplate.value = pageTemplate.getAttribute('data-previous-value');
pageTemplate.removeAttribute('data-previous-value');
for (i = 0; i < pageTemplate.options.length; i++) {
pageTemplate.options[i].disabled = false;
}
}
});
}
if (slugModal) {
$('[data-command=change-slug]').addEventListener('click', function () {
Formwork.Modals.show('slugModal', null, function (modal) {
var slug = document.getElementById('slug').value;
var slugInput = $('#page-slug', modal);
slugInput.value = slug;
slugInput.setAttribute('placeholder', slug);
slugInput.focus();
});
});
$('#page-slug', slugModal).addEventListener('keydown', function (event) {
// enter
if (event.which === 13) {
$('[data-command=continue]', slugModal).click();
}
});
$('#page-slug', slugModal).addEventListener('keyup', handleSlugChange);
$('#page-slug', slugModal).addEventListener('blur', handleSlugChange);
$('[data-command=generate-slug]', slugModal).addEventListener('click', function () {
var slug = Formwork.Utils.slug(document.getElementById('title').value);
$('#page-slug', slugModal).value = slug;
$('#page-slug', slugModal).focus();
});
$('[data-command=continue]', slugModal).addEventListener('click', function () {
var slug = $('#page-slug', slugModal).value.replace(/^-+|-+$/, '');
var route;
if (slug.length > 0) {
route = $('.page-route span').innerHTML;
$$('#page-slug, #slug').forEach(function (element) {
element.value = slug;
});
$('#page-slug', slugModal).value = slug;
document.getElementById('slug').value = slug;
$('.page-route span').innerHTML = route.replace(/\/[a-z0-9-]+\/$/, '/' + slug + '/');
}
Formwork.Modals.hide('slugModal');
});
}
function expandAllPages() {
$$('.pages-children').forEach(function (element) {
element.style.display = 'block';
});
$$('.pages-list .page-children-toggle').forEach(function (element) {
element.classList.remove('toggle-collapsed');
element.classList.add('toggle-expanded');
});
}
function collapseAllPages() {
$$('.pages-children').forEach(function (element) {
element.style.display = 'none';
});
$$('.pages-list .page-children-toggle').forEach(function (element) {
element.classList.remove('toggle-expanded');
element.classList.add('toggle-collapsed');
});
}
function togglePagesList(list) {
$$('.pages-list', list.closest('li')).forEach(function (element) {
Formwork.Utils.toggleElement(element);
});
list.classList.toggle('toggle-expanded');
list.classList.toggle('toggle-collapsed');
}
function initSortable(element) {
var originalOrder = [];
/* global Sortable:false */
var sortable = Sortable.create(element, {
handle: '.sort-handle',
filter: '[data-sortable=false]',
forceFallback: true,
onClone: function (event) {
event.item.closest('.pages-list').classList.add('dragging');
$$('.pages-children', event.item).forEach(function (element) {
element.style.display = 'none';
});
$$('.page-children-toggle').forEach(function (element) {
element.classList.remove('toggle-expanded');
element.classList.add('toggle-collapsed');
element.style.opacity = '0.5';
});
},
onMove: function (event) {
if (event.related.getAttribute('data-sortable') === 'false') {
return false;
}
$$('.pages-children', event.related).forEach(function (element) {
element.style.display = 'none';
});
},
onEnd: function (event) {
var data, notification;
event.item.closest('.pages-list').classList.remove('dragging');
$$('.page-children-toggle').forEach(function (element) {
element.style.opacity = '';
});
if (event.newIndex === event.oldIndex) {
return;
}
sortable.option('disabled', true);
data = {
'csrf-token': $('meta[name=csrf-token]').getAttribute('content'),
parent: element.getAttribute('data-parent'),
from: event.oldIndex,
to: event.newIndex
};
Formwork.Request({
method: 'POST',
url: Formwork.baseUri + 'pages/reorder/',
data: data
}, function (response) {
if (response.status) {
notification = new Formwork.Notification(response.message, response.status, 5000);
notification.show();
}
if (!response.status || response.status === 'error') {
sortable.sort(originalOrder);
}
sortable.option('disabled', false);
originalOrder = sortable.toArray();
});
}
});
originalOrder = sortable.toArray();
}
function handleSearch() {
var regexp;
if (this.value.length === 0) {
$$('.pages-children').forEach(function (element) {
element.style.display = element.getAttribute('data-display');
});
$$('.page-details').forEach(function (element) {
element.style.paddingLeft = '';
});
$$('.pages-item, .page-children-toggle').forEach(function (element) {
element.style.display = '';
});
} else {
regexp = new RegExp(Formwork.Utils.escapeRegExp(this.value), 'i');
$$('.pages-children').forEach(function (element) {
element.style.display = 'block';
});
$$('.page-children-toggle').forEach(function (element) {
element.style.display = 'none';
});
$$('.page-details').forEach(function (element) {
element.style.paddingLeft = '0';
});
$$('.page-title a').forEach(function (element) {
var pagesItem = element.closest('.pages-item');
var text = element.textContent;
var matched = !!text.match(regexp);
pagesItem.style.display = matched ? 'block' : 'none';
});
}
}
function handleSlugChange() {
this.value = Formwork.Utils.validateSlug(this.value);
}
}
};
Formwork.RangeInput = function (input) {
input.addEventListener('change', updateValueLabel);
input.addEventListener('input', updateValueLabel);
function updateValueLabel() {
$('.range-input-value', this.parentNode).innerHTML = this.value;
}
};
Formwork.Request = function (options, callback) {
var request = new XMLHttpRequest();
var handler, response, code;
request.open(options.method, options.url, true);
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
request.send(Formwork.Utils.serializeObject(options.data));
if (typeof callback === 'function') {
handler = function () {
response = JSON.parse(this.response);
code = response.code || this.status;
if (parseInt(code) === 400) {
location.reload();
} else {
callback(response, request);
}
};
request.onload = handler;
request.onerror = handler;
}
return request;
};
Formwork.TagInput = function (input) {
var options = {addKeyCodes: [32]};
var tags = [];
var field, innerInput, hiddenInput, placeholder, dropdown;
createField();
createDropdown();
registerInputEvents();
function createField() {
var isRequired = input.hasAttribute('required');
var isDisabled = input.hasAttribute('disabled');
field = document.createElement('div');
field.className = 'tag-input';
innerInput = document.createElement('input');
innerInput.className = 'tag-inner-input';
innerInput.id = input.id;
innerInput.type = 'text';
innerInput.placeholder = input.placeholder;
innerInput.setAttribute('size', '');
hiddenInput = document.createElement('input');
hiddenInput.className = 'tag-hidden-input';
hiddenInput.name = input.name;
hiddenInput.id = input.id;
hiddenInput.type = 'text';
hiddenInput.value = input.value;
hiddenInput.readOnly = true;
hiddenInput.hidden = true;
if (isRequired) {
hiddenInput.required = true;
}
if (isDisabled) {
field.disabled = true;
innerInput.disabled = true;
hiddenInput.disabled = true;
}
input.parentNode.replaceChild(field, input);
field.appendChild(innerInput);
field.appendChild(hiddenInput);
if (hiddenInput.value) {
tags = hiddenInput.value.split(', ');
tags.forEach(function (value, index) {
value = value.trim();
tags[index] = value;
insertTag(value);
});
}
if (innerInput.placeholder) {
placeholder = innerInput.placeholder;
updatePlaceholder();
} else {
placeholder = '';
}
field.addEventListener('mousedown', function (event) {
innerInput.focus();
event.preventDefault();
});
}
function createDropdown() {
var list, key, item;
if (input.hasAttribute('data-options')) {
list = JSON.parse(input.getAttribute('data-options'));
dropdown = document.createElement('div');
dropdown.className = 'dropdown-list';
for (key in list) {
item = document.createElement('div');
item.className = 'dropdown-item';
item.innerHTML = list[key];
item.setAttribute('data-value', key);
item.addEventListener('click', function () {
addTag(this.getAttribute('data-value'));
});
dropdown.appendChild(item);
}
field.appendChild(dropdown);
innerInput.addEventListener('focus', function () {
if (getComputedStyle(dropdown).display === 'none') {
updateDropdown();
dropdown.scrollTop = 0;
dropdown.style.display = 'block';
}
});
innerInput.addEventListener('blur', function () {
if (getComputedStyle(dropdown).display !== 'none') {
updateDropdown();
dropdown.style.display = 'none';
}
});
innerInput.addEventListener('keydown', function (event) {
switch (event.which) {
case 8: // backspace
updateDropdown();
break;
case 13: // enter
if (getComputedStyle(dropdown).display !== 'none') {
addTagFromSelectedDropdownItem();
event.preventDefault();
}
break;
case 38: // up arrow
if (getComputedStyle(dropdown).display !== 'none') {
selectPrevDropdownItem();
event.preventDefault();
}
break;
case 40: // down arrow
if (getComputedStyle(dropdown).display !== 'none') {
selectNextDropdownItem();
event.preventDefault();
}
break;
default:
if (options.addKeyCodes.indexOf(event.which) > -1) {
addTagFromSelectedDropdownItem();
event.preventDefault();
}
}
});
innerInput.addEventListener('keyup', Formwork.Utils.debounce(function (event) {
var value = innerInput.value.trim();
switch (event.which) {
case 27: // escape
dropdown.style.display = 'none';
break;
case 38: // up arrow
case 40: // down arrow
return true;
default:
dropdown.style.display = 'block';
filterDropdown(value);
if (value.length > 0) {
selectFirstDropdownItem();
}
}
}, 100));
}
}
function registerInputEvents() {
innerInput.addEventListener('focus', function () {
field.classList.add('focused');
});
innerInput.addEventListener('blur', function () {
var value = innerInput.value.trim();
if (value !== '') {
addTag(value);
}
field.classList.remove('focused');
});
innerInput.addEventListener('keydown', function () {
var value = innerInput.value.trim();
switch (event.which) {
case 8: // backspace
if (value === '') {
removeTag(tags[tags.length - 1]);
if (innerInput.previousSibling){
innerInput.parentNode.removeChild(innerInput.previousSibling);
}
event.preventDefault();
} else {
innerInput.size = Math.max(innerInput.value.length, innerInput.placeholder.length, 1);
}
break;
case 13: // enter
case 188: // comma
if (value !== '') {
addTag(value);
}
event.preventDefault();
break;
case 27: // escape
clearInput();
innerInput.blur();
event.preventDefault();
break;
default:
if (value !== '' && options.addKeyCodes.indexOf(event.which) > -1) {
addTag(value);
event.preventDefault();
break;
}
if (value.length > 0) {
innerInput.size = innerInput.value.length + 2;
}
break;
}
});
}
function updateTags() {
hiddenInput.value = tags.join(', ');
updatePlaceholder();
}
function updatePlaceholder() {
if (placeholder.length > 0) {
if (tags.length === 0) {
innerInput.placeholder = placeholder;
innerInput.size = placeholder.length;
} else {
innerInput.placeholder = '';
innerInput.size = 1;
}
}
}
function validateTag(value) {
if (tags.indexOf(value) === -1) {
if (dropdown !== null) {
return $('[data-value="' + value + '"]', dropdown) !== null;
}
return true;
}
return false;
}
function insertTag(value) {
var tag = document.createElement('span');
var tagRemove = document.createElement('i');
tag.className = 'tag';
tag.innerHTML = value;
tag.style.marginRight = '.25rem';
innerInput.parentNode.insertBefore(tag, innerInput);
tagRemove.className = 'tag-remove';
tagRemove.addEventListener('mousedown', function (event) {
removeTag(value);
tag.parentNode.removeChild(tag);
event.preventDefault();
});
tag.appendChild(tagRemove);
}
function addTag(value) {
if (validateTag(value)) {
tags.push(value);
insertTag(value);
updateTags();
} else {
updatePlaceholder();
}
innerInput.value = '';
if (dropdown !== null) {
updateDropdown();
}
}
function removeTag(value) {
var index = tags.indexOf(value);
if (index > -1) {
tags.splice(index, 1);
updateTags();
}
if (dropdown !== null) {
updateDropdown();
}
}
function clearInput() {
innerInput.value = '';
updatePlaceholder();
}
function updateDropdown() {
var visibleItems = 0;
$$('.dropdown-item', dropdown).forEach(function (element) {
if (getComputedStyle(element).display !== 'none') {
visibleItems++;
}
if (tags.indexOf(element.getAttribute('data-value')) === -1) {
element.style.display = 'block';
} else {
element.style.display = 'none';
}
element.classList.remove('selected');
});
if (visibleItems > 0) {
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
}
function filterDropdown(value) {
var visibleItems = 0;
dropdown.style.display = 'block';
$$('.dropdown-item', dropdown).forEach(function (element) {
var text = element.textContent;
var regexp = new RegExp(Formwork.Utils.escapeRegExp(value), 'i');
var matched = !!text.match(regexp);
if (matched && element.style.display !== 'none') {
element.style.display = 'block';
visibleItems++;
} else {
element.style.display = 'none';
}
});
if (visibleItems > 0) {
dropdown.style.display = 'block';
} else {
dropdown.style.display = 'none';
}
}
function scrollToDropdownItem(item) {
var dropdownScrollTop = dropdown.scrollTop;
var dropdownHeight = dropdown.clientHeight;
var dropdownScrollBottom = dropdownScrollTop + dropdownHeight;
var dropdownStyle = getComputedStyle(dropdown);
var dropdownPaddingTop = parseInt(dropdownStyle.paddingTop);
var dropdownPaddingBottom = parseInt(dropdownStyle.paddingBottom);
var itemTop = item.offsetTop;
var itemHeight = item.clientHeight;
var itemBottom = itemTop + itemHeight;
if (itemTop < dropdownScrollTop) {
dropdown.scrollTop = itemTop - dropdownPaddingTop;
} else if (itemBottom > dropdownScrollBottom) {
dropdown.scrollTop = itemBottom - dropdownHeight + dropdownPaddingBottom;
}
}
function addTagFromSelectedDropdownItem() {
var selectedItem = $('.dropdown-item.selected', dropdown);
if (getComputedStyle(selectedItem).display !== 'none') {
innerInput.value = selectedItem.getAttribute('data-value');
}
}
function selectDropdownItem(item) {
var selectedItem = $('.dropdown-item.selected', dropdown);
if (selectedItem) {
selectedItem.classList.remove('selected');
}
if (item) {
item.classList.add('selected');
scrollToDropdownItem(item);
}
}
function selectFirstDropdownItem() {
var items = $$('.dropdown-item', dropdown);
var i;
for (i = 0; i < items.length; i++) {
if (getComputedStyle(items[i]).display !== 'none') {
selectDropdownItem(items[i]);
return;
}
}
}
function selectLastDropdownItem() {
var items = $$('.dropdown-item', dropdown);
var i;
for (i = items.length - 1; i >= 0; i--) {
if (getComputedStyle(items[i]).display !== 'none') {
selectDropdownItem(items[i]);
return;
}
}
}
function selectPrevDropdownItem() {
var selectedItem = $('.dropdown-item.selected', dropdown);
var previousItem;
if (selectedItem) {
previousItem = selectedItem.previousSibling;
while (previousItem && previousItem.style.display === 'none') {
previousItem = previousItem.previousSibling;
}
if (previousItem) {
return selectDropdownItem(previousItem);
}
selectDropdownItem(selectedItem.previousSibling);
}
selectLastDropdownItem();
}
function selectNextDropdownItem() {
var selectedItem = $('.dropdown-item.selected', dropdown);
var nextItem;
if (selectedItem) {
nextItem = selectedItem.nextSibling;
while (nextItem && nextItem.style.display === 'none') {
nextItem = nextItem.nextSibling;
}
if (nextItem) {
return selectDropdownItem(nextItem);
}
}
selectFirstDropdownItem();
}
};
Formwork.Tooltip = function (text, options) {
var defaults = {
container: document.body,
referenceElement: document.body,
position: 'top',
offset: {
x: 0, y: 0
},
delay: 500
};
var referenceElement = options.referenceElement;
var tooltip, timer;
options = Formwork.Utils.extendObject({}, defaults, options);
// IE 10-11 support classList only on HTMLElement
if (referenceElement instanceof HTMLElement) {
// Remove tooltip when clicking on buttons
if (referenceElement.tagName.toLowerCase() === 'button' || referenceElement.classList.contains('button')) {
referenceElement.addEventListener('click', remove);
}
}
referenceElement.addEventListener('mouseout', remove);
function show() {
timer = setTimeout(function () {
var position;
tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.setAttribute('role', 'tooltip');
tooltip.style.display = 'block';
tooltip.innerHTML = text;
options.container.appendChild(tooltip);
position = getTooltipPosition(tooltip);
tooltip.style.top = position.top + 'px';
tooltip.style.left = position.left + 'px';
}, options.delay);
}
function remove() {
clearTimeout(timer);
if (tooltip !== undefined && options.container.contains(tooltip)) {
options.container.removeChild(tooltip);
}
}
function getTooltipPosition(tooltip) {
var rect = referenceElement.getBoundingClientRect();
var top = rect.top + window.pageYOffset;
var left = rect.left + window.pageXOffset;
var hw = (rect.width - tooltip.offsetWidth) / 2;
var hh = (rect.height - tooltip.offsetHeight) / 2;
switch (options.position) {
case 'top':
return {
top: Math.round(top - tooltip.offsetHeight + options.offset.y),
left: Math.round(left + hw + options.offset.x)
};
case 'right':
return {
top: Math.round(top + hh + options.offset.y),
left: Math.round(left + referenceElement.offsetWidth + options.offset.x)
};
case 'bottom':
return {
top: Math.round(top + referenceElement.offsetHeight + options.offset.y),
left: Math.round(left + hw + options.offset.x)
};
case 'left':
return {
top: Math.round(top + hh + options.offset.y),
left: Math.round(left - tooltip.offsetWidth + options.offset.x)
};
}
}
return {
show: show,
remove: remove
};
};
Formwork.Tooltips = {
init: function () {
$$('[title]').forEach(function (element) {
element.setAttribute('data-tooltip', element.getAttribute('title'));
element.removeAttribute('title');
});
$$('[data-tooltip]').forEach(function (element) {
element.addEventListener('mouseover', function () {
var tooltip = new Formwork.Tooltip(this.getAttribute('data-tooltip'), {
referenceElement: this,
position: 'bottom',
offset: {
x: 0, y: 4
}
});
tooltip.show();
});
});
$$('[data-overflow-tooltip="true"]').forEach(function (element) {
element.addEventListener('mouseover', function () {
var tooltip;
if (this.offsetWidth < this.scrollWidth) {
tooltip = new Formwork.Tooltip(this.textContent.trim(), {
referenceElement: this,
position: 'bottom',
offset: {
x: 0, y: 4
}
});
tooltip.show();
}
});
});
}
};
Formwork.Updates = {
init: function () {
var updaterComponent = document.getElementById('updater-component');
var updateStatus, spinner,
currentVersion, currentVersionName,
newVersion, newVersionName;
if (updaterComponent) {
updateStatus = $('.update-status');
spinner = $('.spinner');
currentVersion = $('.current-version');
currentVersionName = $('.current-version-name');
newVersion = $('.new-version');
newVersionName = $('.new-version-name');
setTimeout(function () {
var data = {'csrf-token': $('meta[name=csrf-token]').getAttribute('content')};
Formwork.Request({
method: 'POST',
url: Formwork.baseUri + 'updates/check/',
data: data
}, function (response) {
updateStatus.innerHTML = response.message;
if (response.status === 'success') {
if (response.data.uptodate === false) {
showNewVersion(response.data.release.name);
} else {
showCurrentVersion();
}
} else {
spinner.classList.add('spinner-error');
}
});
}, 1000);
$('[data-command=install-updates]').addEventListener('click', function () {
newVersion.style.display = 'none';
spinner.classList.remove('spinner-info');
updateStatus.innerHTML = updateStatus.getAttribute('data-installing-text');
Formwork.Request({
method: 'POST',
url: Formwork.baseUri + 'updates/update/',
data: {'csrf-token': $('meta[name=csrf-token]').getAttribute('content')}
}, function (response) {
var notification = new Formwork.Notification(response.message, response.status, 5000);
notification.show();
updateStatus.innerHTML = response.data.status;
if (response.status === 'success') {
showInstalledVersion();
} else {
spinner.classList.add('spinner-error');
}
});
});
}
function showNewVersion(name) {
spinner.classList.add('spinner-info');
newVersionName.innerHTML = name;
newVersion.style.display = 'block';
}
function showCurrentVersion() {
spinner.classList.add('spinner-success');
currentVersion.style.display = 'block';
}
function showInstalledVersion() {
spinner.classList.add('spinner-success');
currentVersionName.innerHTML = newVersionName.innerHTML;
currentVersion.style.display = 'block';
}
}
};
Formwork.Utils = {
debounce: function (callback, delay, leading) {
var context, args, result;
var timer = null;
function wrapper() {
context = this;
args = arguments;
if (timer) {
clearTimeout(timer);
}
if (leading && !timer) {
result = callback.apply(context, args);
}
timer = setTimeout(function () {
if (!leading) {
result = callback.apply(context, args);
}
timer = null;
}, delay);
return result;
}
return wrapper;
},
download: function (uri, csrfToken) {
var form = document.createElement('form');
var input = document.createElement('input');
form.action = uri;
form.method = 'post';
input.type = 'hidden';
input.name = 'csrf-token';
input.value = csrfToken;
form.appendChild(input);
document.body.appendChild(form);
form.submit();
document.body.removeChild(form);
},
escapeRegExp: function (string) {
return string.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
},
slug: function (string) {
var char;
var translate = {
'\t': '', '\r': '', '!': '', '"': '', '#': '', '$': '', '%': '', '\'': '-', '(': '', ')': '', '*': '', '+': '', ',': '', '.': '', ':': '', ';': '', '<': '', '=': '', '>': '', '?': '', '@': '', '[': '', ']': '', '^': '', '`': '', '{': '', '|': '', '}': '', '¡': '', '£': '', '¤': '', '¥': '', '¦': '', '§': '', '«': '', '°': '', '»': '', '': '', '': '', '“': '', '”': '', '\n': '-', ' ': '-', '-': '-', '': '-', '—': '-', '/': '-', '\\': '-', '_': '-', '~': '-', 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'Ae', 'Ç': 'C', 'Ð': 'D', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'Ø': 'O', 'Œ': 'Oe', 'Š': 'S', 'Þ': 'Th', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ý': 'Y', 'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'ae', 'å': 'a', 'æ': 'ae', '¢': 'c', 'ç': 'c', 'ð': 'd', 'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'oe', 'ø': 'o', 'œ': 'oe', 'š': 's', 'ß': 'ss', 'þ': 'th', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'ue', 'ý': 'y', 'ÿ': 'y', 'Ÿ': 'y'
};
string = string.toLowerCase();
for (char in translate) {
if (translate.hasOwnProperty(char)) {
string = string.split(char).join(translate[char]);
}
}
return string.replace(/[^a-z0-9-]/g, '').replace(/^-+|-+$/g, '').replace(/-+/g, '-');
},
validateSlug: function (slug) {
return slug.toLowerCase().replace(' ', '-').replace(/[^a-z0-9-]/g, '');
},
throttle: function (callback, delay) {
var context, args, result;
var previous = 0;
var timer = null;
function wrapper() {
var now = Date.now();
var remaining;
if (previous === 0) {
previous = now;
}
remaining = (previous + delay) - now;
context = this;
args = arguments;
if (remaining <= 0 || remaining > delay) {
if (timer) {
clearTimeout(timer);
timer = null;
}
previous = now;
result = callback.apply(context, args);
} else if (!timer){
timer = setTimeout(function () {
previous = Date.now();
result = callback.apply(context, args);
timer = null;
}, remaining);
}
return result;
}
return wrapper;
},
outerWidth: function (element) {
var width = element.offsetWidth;
var style = getComputedStyle(element);
width += parseInt(style.marginLeft) + parseInt(style.marginRight);
return width;
},
outerHeight: function (element) {
var height = element.offsetHeight;
var style = getComputedStyle(element);
height += parseInt(style.marginTop) + parseInt(style.marginBottom);
return height;
},
toggleElement: function (element) {
var visibility = element.style.display || getComputedStyle(element).display;
if (visibility === 'none') {
element.style.display = element.tagName.toLowerCase() === 'span' ? 'inline' : 'block';
} else {
element.style.display = 'none';
}
},
extendObject: function (target) {
var i, source, prop;
target = target || {};
for (i = 1; i < arguments.length; i++) {
source = arguments[i];
for (prop in source) {
target[prop] = source[prop];
}
}
return target;
},
serializeObject: function (obj) {
var query = '';
var key;
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (query.length > 0) {
query += '&';
}
query += key + '=' + encodeURIComponent(obj[key]);
}
}
return query;
},
serializeForm: function (form) {
var field, i, j;
var s = [];
var len = form.elements.length;
for (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 (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('&');
},
triggerEvent: function (target, type) {
var event;
try {
event = new Event(type);
} catch (error) {
// The browser doesn't support Event constructor
event = document.createEvent('HTMLEvents');
event.initEvent(type, true, true);
}
target.dispatchEvent(event);
},
longClick: function (element, callback, timeout, interval) {
var timer;
function clear() {
clearTimeout(timer);
}
element.addEventListener('mousedown', function (event) {
var context = this;
if (event.which !== 1) {
clear();
} else {
callback.call(context, event);
timer = setTimeout(function () {
timer = setInterval(callback.bind(context, event), interval);
}, timeout);
}
});
element.addEventListener('mouseout', clear);
window.addEventListener('mouseup', clear);
}
};