File: D:/HostingSpaces/SBogers10/demo.komma.pro/wwwroot/js/kms/core.js
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
/**
* Event semi polyfill. IE does not support new event. But does support document.createEvent.
* Same thing for dispatching events. But then with createEvent and fireEvent.
*/
function createNewEvent(eventName) {
var event;
if (typeof Event === 'function') {
//On ie it is typeOf object.
event = new Event(eventName); //Non-ie
} else {
event = document.createEvent('Event'); //ie
event.initEvent(eventName, true, true);
}
return event;
}
function dispatchEventForElement(element, event) {
if (document.createEvent) {
element.dispatchEvent(event);
} else {
element.fireEvent("on" + event.eventType, event);
}
}
Number.isInteger = Number.isInteger || function (value) {
return typeof value === "number" && isFinite(value) && Math.floor(value) === value;
};
!function () {
"use strict";
if ("undefined" != typeof window) {
var t = window.navigator.userAgent.match(/Edge\/(\d{2})\./),
n = !!t && 16 <= parseInt(t[1], 10);
if (!("objectFit" in document.documentElement.style != !1) || n) {
var o = function o(t, e, i) {
var n, o, l, a, d;
if ((i = i.split(" ")).length < 2 && (i[1] = i[0]), "x" === t) n = i[0], o = i[1], l = "left", a = "right", d = e.clientWidth;else {
if ("y" !== t) return;
n = i[1], o = i[0], l = "top", a = "bottom", d = e.clientHeight;
}
if (n !== l && o !== l) {
if (n !== a && o !== a) return "center" === n || "50%" === n ? (e.style[l] = "50%", void (e.style["margin-" + l] = d / -2 + "px")) : void (0 <= n.indexOf("%") ? (n = parseInt(n)) < 50 ? (e.style[l] = n + "%", e.style["margin-" + l] = d * (n / -100) + "px") : (n = 100 - n, e.style[a] = n + "%", e.style["margin-" + a] = d * (n / -100) + "px") : e.style[l] = n);
e.style[a] = "0";
} else e.style[l] = "0";
},
l = function l(t) {
var e = t.dataset ? t.dataset.objectFit : t.getAttribute("data-object-fit"),
i = t.dataset ? t.dataset.objectPosition : t.getAttribute("data-object-position");
e = e || "cover", i = i || "50% 50%";
var n = t.parentNode;
return function (t) {
var e = window.getComputedStyle(t, null),
i = e.getPropertyValue("position"),
n = e.getPropertyValue("overflow"),
o = e.getPropertyValue("display");
i && "static" !== i || (t.style.position = "relative"), "hidden" !== n && (t.style.overflow = "hidden"), o && "inline" !== o || (t.style.display = "block"), 0 === t.clientHeight && (t.style.height = "100%"), -1 === t.className.indexOf("object-fit-polyfill") && (t.className = t.className + " object-fit-polyfill");
}(n), function (t) {
var e = window.getComputedStyle(t, null),
i = {
"max-width": "none",
"max-height": "none",
"min-width": "0px",
"min-height": "0px",
top: "auto",
right: "auto",
bottom: "auto",
left: "auto",
"margin-top": "0px",
"margin-right": "0px",
"margin-bottom": "0px",
"margin-left": "0px"
};
for (var n in i) {
e.getPropertyValue(n) !== i[n] && (t.style[n] = i[n]);
}
}(t), t.style.position = "absolute", t.style.width = "auto", t.style.height = "auto", "scale-down" === e && (e = t.clientWidth < n.clientWidth && t.clientHeight < n.clientHeight ? "none" : "contain"), "none" === e ? (o("x", t, i), void o("y", t, i)) : "fill" === e ? (t.style.width = "100%", t.style.height = "100%", o("x", t, i), void o("y", t, i)) : (t.style.height = "100%", void ("cover" === e && t.clientWidth > n.clientWidth || "contain" === e && t.clientWidth < n.clientWidth ? (t.style.top = "0", t.style.marginTop = "0", o("x", t, i)) : (t.style.width = "100%", t.style.height = "auto", t.style.left = "0", t.style.marginLeft = "0", o("y", t, i))));
},
e = function e(t) {
if (void 0 === t || t instanceof Event) t = document.querySelectorAll("[data-object-fit]");else if (t && t.nodeName) t = [t];else {
if ("object" != _typeof(t) || !t.length || !t[0].nodeName) return !1;
t = t;
}
for (var e = 0; e < t.length; e++) {
if (t[e].nodeName) {
var i = t[e].nodeName.toLowerCase();
"img" !== i || n ? "video" === i ? 0 < t[e].readyState ? l(t[e]) : t[e].addEventListener("loadedmetadata", function () {
l(this);
}) : l(t[e]) : t[e].complete ? l(t[e]) : t[e].addEventListener("load", function () {
l(this);
});
}
}
return !0;
};
document.addEventListener("DOMContentLoaded", e), window.addEventListener("resize", e), window.objectFitPolyfill = e;
} else window.objectFitPolyfill = function () {
return !1;
};
}
}();
(function (global, factory) {
(typeof exports === "undefined" ? "undefined" : _typeof(exports)) === 'object' && typeof module !== 'undefined' ? factory() : typeof define === 'function' && define.amd ? define(factory) : factory();
})(this, function () {
'use strict';
/**
* @this {Promise}
*/
function finallyConstructor(callback) {
var constructor = this.constructor;
return this.then(function (value) {
return constructor.resolve(callback()).then(function () {
return value;
});
}, function (reason) {
return constructor.resolve(callback()).then(function () {
return constructor.reject(reason);
});
});
} // Store setTimeout reference so promise-polyfill will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var setTimeoutFunc = setTimeout;
function noop() {} // Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function () {
fn.apply(thisArg, arguments);
};
}
/**
* @constructor
* @param {Function} fn
*/
function Promise(fn) {
if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
/** @type {!number} */
this._state = 0;
/** @type {!boolean} */
this._handled = false;
/** @type {Promise|undefined} */
this._value = undefined;
/** @type {!Array<!Function>} */
this._deferreds = [];
doResolve(fn, this);
}
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function () {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
}
function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
if (newValue && (_typeof(newValue) === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
}
function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function () {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
/**
* @constructor
*/
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false;
try {
fn(function (value) {
if (done) return;
done = true;
resolve(self, value);
}, function (reason) {
if (done) return;
done = true;
reject(self, reason);
});
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
}
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
// @ts-ignore
var prom = new this.constructor(noop);
handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
};
Promise.prototype['finally'] = finallyConstructor;
Promise.all = function (arr) {
return new Promise(function (resolve, reject) {
if (!arr || typeof arr.length === 'undefined') throw new TypeError('Promise.all accepts an array');
var args = Array.prototype.slice.call(arr);
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
try {
if (val && (_typeof(val) === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(val, function (val) {
res(i, val);
}, reject);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.resolve = function (value) {
if (value && _typeof(value) === 'object' && value.constructor === Promise) {
return value;
}
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
for (var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject);
}
});
}; // Use polyfill for setImmediate for performance gains
Promise._immediateFn = typeof setImmediate === 'function' && function (fn) {
setImmediate(fn);
} || function (fn) {
setTimeoutFunc(fn, 0);
};
Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
};
/** @suppress {undefinedVars} */
var globalNS = function () {
// the only reliable means to get the global object is
// `Function('return this')()`
// However, this causes CSP violations in Chrome apps.
if (typeof self !== 'undefined') {
return self;
}
if (typeof window !== 'undefined') {
return window;
}
if (typeof global !== 'undefined') {
return global;
}
throw new Error('unable to locate global object');
}();
if (!('Promise' in globalNS)) {
globalNS['Promise'] = Promise;
} else if (!globalNS.Promise.prototype['finally']) {
globalNS.Promise.prototype['finally'] = finallyConstructor;
}
});
/* ==========================================================================
Helper functions
========================================================================== */
/*
* Simple isset method for this does not exist in javascript
*/
var isset = function isset(obj) {
return typeof obj !== 'undefined' && obj !== null;
};
/*
* Easing Functions - inspired from http://gizma.com/easing/
* only considering the t value for the range [0, 1] => [0, 1]
*/
var EasingFunctions = {
// no easing, no acceleration
linear: function linear(t) {
return t;
},
// accelerating from zero velocity
easeInQuad: function easeInQuad(t) {
return t * t;
},
// decelerating to zero velocity
easeOutQuad: function easeOutQuad(t) {
return t * (2 - t);
},
// acceleration until halfway, then deceleration
easeInOutQuad: function easeInOutQuad(t) {
return t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
},
// accelerating from zero velocity
easeInCubic: function easeInCubic(t) {
return t * t * t;
},
// decelerating to zero velocity
easeOutCubic: function easeOutCubic(t) {
return --t * t * t + 1;
},
// acceleration until halfway, then deceleration
easeInOutCubic: function easeInOutCubic(t) {
return t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
},
// accelerating from zero velocity
easeInQuart: function easeInQuart(t) {
return t * t * t * t;
},
// decelerating to zero velocity
easeOutQuart: function easeOutQuart(t) {
return 1 - --t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuart: function easeInOutQuart(t) {
return t < .5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
},
// accelerating from zero velocity
easeInQuint: function easeInQuint(t) {
return t * t * t * t * t;
},
// decelerating to zero velocity
easeOutQuint: function easeOutQuint(t) {
return 1 + --t * t * t * t * t;
},
// acceleration until halfway, then deceleration
easeInOutQuint: function easeInOutQuint(t) {
return t < .5 ? 16 * t * t * t * t * t : 1 + 16 * --t * t * t * t * t;
}
};
/*
* Vanilla version of the $.getScript
*/
var getScript = function getScript(source, callback) {
var script = document.createElement('script');
script.async = 1;
var scripts = document.getElementsByTagName('script');
var prior = scripts[scripts.length - 1];
script.onload = script.onreadystatechange = function (_, isAbort) {
if (isAbort || !script.readyState || /loaded|complete/.test(script.readyState)) {
script.onload = script.onreadystatechange = null;
script = undefined;
if (!isAbort) {
if (callback) callback();
}
}
};
script.src = source;
prior.parentNode.insertBefore(script, prior);
};
/**
* Returns a function, that, as long as it continues to be invoked, will not
* be triggered. The function will be called after it stops being called for
* N milliseconds. If `immediate` is passed, trigger the function on the
* leading edge, instead of the trailing.
* @param func
* @param wait
* @param immediate
* @returns {Function}
*/
var debounce = function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this,
args = arguments;
var later = function later() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
/*
* Cookie helper class
*/
var Cookie = {
set: function set(name, value, days) {
var domain, domainParts, date, expires, host;
if (days) {
date = new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
expires = "; expires=" + date.toGMTString();
} else {
expires = "";
}
host = location.host;
if (host.split('.').length === 1) {
// no "." in a domain - it's localhost or something similar
document.cookie = name + "=" + value + expires + "; path=/";
} else {
// Remember the cookie on all sub domains.
//
// Start with trying to set cookie to the top domain.
// (example: if user is on foo.com, try to set
// cookie to domain ".com")
//
// If the cookie will not be set, it means ".com"
// is a top level domain and we need to
// set the cookie to ".foo.com"
domainParts = host.split('.');
domainParts.shift();
domain = '.' + domainParts.join('.'); // For development purpose, remove this when moving to production
// domain = '.komma.pro';
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain; // check if cookie was successfuly set to the given domain
// (otherwise it was a Top-Level Domain)
if (Cookie.get(name) == null || Cookie.get(name) != value) {
// append "." to current domain
domain = '.' + host;
document.cookie = name + "=" + value + expires + "; path=/; domain=" + domain;
}
}
},
get: function get(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) return c.substring(nameEQ.length, c.length);
}
return null;
},
erase: function erase(name) {
Cookie.set(name, '', -1);
}
};
/*
* Javascript version of the number_format method of PHP
*/
var number_format = function number_format(number, decimals, dec_point, thousands_sep) {
// Strip all characters but numerical ones.
number = (number + '').replace(/[^0-9+\-Ee.]/g, '');
var n = !isFinite(+number) ? 0 : +number,
prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
sep = typeof thousands_sep === 'undefined' ? ',' : thousands_sep,
dec = typeof dec_point === 'undefined' ? '.' : dec_point,
s = '',
toFixedFix = function toFixedFix(n, prec) {
var k = Math.pow(10, prec);
return '' + Math.round(n * k) / k;
}; // Fix for IE parseFloat(0.55).toFixed(0) = 0;
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)).split('.');
if (s[0].length > 3) {
s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep);
}
if ((s[1] || '').length < prec) {
s[1] = s[1] || '';
s[1] += new Array(prec - s[1].length + 1).join('0');
}
return s.join(dec);
};
/**
* Element.closest() polyfill
* https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill
*/
if (!Element.prototype.closest) {
if (!Element.prototype.matches) {
Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector;
}
Element.prototype.closest = function (s) {
var el = this;
var ancestor = this;
if (!document.documentElement.contains(el)) return null;
do {
if (ancestor.matches(s)) return ancestor;
ancestor = ancestor.parentElement;
} while (ancestor !== null);
return null;
};
}
var Ajax = {
//TODO: Can we create a prepare request function ??
get: function get(url, callback) {
var xhr = new XMLHttpRequest();
var token = document.querySelector('meta[name="csrf-token"]').content;
xhr.open('get', url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRF-TOKEN', token);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
callback(xhr);
}
};
xhr.send();
},
post: function post(url, data, callback) {
var xhr = new XMLHttpRequest();
var token = document.querySelector('meta[name="csrf-token"]').content;
xhr.open('post', url, true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRF-TOKEN', token);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
callback(xhr);
}
};
xhr.send(JSON.stringify(data));
}
};
/**
* Capitalize the first letter of the string
* @param string
* @returns {string}
*/
var capitalizeFirstLetter = function capitalizeFirstLetter(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
};
/**
* Convert snake case into camelcase
*
* @param string
* @returns {*}
*/
function snakeToCamel(string) {
return string.replace(/(\-\w)/g, function (m) {
return m[1].toUpperCase();
});
}
/**
* Get the index of an element inside its parent
*/
function indexInParent(node) {
var children = node.parentNode.childNodes;
var num = 0;
for (var i = 0; i < children.length; i++) {
if (children[i] == node) return num;
if (children[i].nodeType == 1) num++;
}
return -1;
}
/**
* For security reasons iOS Safari only allows document.execCommand('copy') for text within a contentEditable container.
* The workaround is to detect iOS Safari and quickly toggle contentEditable before executing document.execCommand('copy').
* Wheb input or textarea is READONLY you can prevent the unwanted popup with copy/paste functions and the input keyboard
* Code from: https://stackoverflow.com/a/46981847
*/
function copyToClipboard(el) {
// resolve the element
el = typeof el === 'string' ? document.querySelector(el) : el; // handle iOS as a special case
if (navigator.userAgent.match(/ipad|ipod|iphone/i)) {
// save current contentEditable/readOnly status
var editable = el.contentEditable;
var readOnly = el.readOnly; // convert to editable with readonly to stop iOS keyboard opening
el.contentEditable = true;
el.readOnly = true; // create a selectable range
var range = document.createRange();
range.selectNodeContents(el); // select the range
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999); // restore contentEditable/readOnly to original state
el.contentEditable = editable;
el.readOnly = readOnly;
} else {
el.select();
} // execute copy command
document.execCommand('copy');
}
'use strict';
document.addEventListener('DOMContentLoaded', function () {
//Add csrf token for all axios requests.
window.axios.defaults.headers.common = {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
}; //Make the tabs work
new TabsController('tab-content', 'entity-tabs', 'active', true, 'input[name="tabslug"]'); //Get the form and its translations
var form = document.getElementById('entity-form');
var globalTranslations = form ? form.dataset.globalTranslations : {}; //Add a style manager for the visible save button
//enable save button when present
var visibleSaveButton = document.getElementById('save-button');
var saveButtonStyleController = new styleClassController(visibleSaveButton);
if (visibleSaveButton) {
var realSaveButton = document.querySelector('input[value="submit-cloaked"]');
visibleSaveButton.addEventListener('click', function (visibleSaveButtonClickEvent) {
visibleSaveButtonClickEvent.preventDefault();
if (form.checkValidity()) saveButtonStyleController.requestAddClass('disabled'); //only disable the form when it is valid.
realSaveButton.click();
});
} //Make the trash button only work when it is confirmed by a model
var trashButton = document.querySelector('.js-open-confirmation');
if (trashButton) new ConfirmationController(trashButton); //Create an attributeInitializer that will look inside of the selected element and when something changes in it, triggers callbacks if needed
var attributeInitializer = new Initializer('#entity-form'); //For the entity form and its attributes.
var entitiesListInitializer = new Initializer('#entities'); //For the sidebar
//Make sure the component areas can copy data to each other
if (form) {
var componentAreaCopier = new ComponentAreaCopier(); //Make sure all component areas will be initialized when they show up
attributeInitializer.bindSelectorToCallback('.js-components-area', function (attributesInitializer, componentAreaAttributeElement) {
var componentAreaManager = new ComponentAreaManager(componentAreaAttributeElement, new ComponentManagerApiController());
componentAreaCopier.registerComponentAreaManager(componentAreaManager);
componentAreaManager.on('componentAdded', function (componentHtmlElement, loaded) {
//Loaded is true when the complete controller finished the initial component load from the savestate. This initial load also triggers componentAdded 'events'.
if (loaded && preventNavigationController) {
preventNavigationController.refresh(); //Refresh, since there will be more inputs. The ones of the sortable.
}
});
});
} //Make sure all documents attributes are initialized when they show up.
attributeInitializer.bindSelectorToCallback('.entity-attribute-documents', function (attributesInitializer, documentsGroupAttributeElement) {
var uploadRoute = form.dataset.uploadRoute;
var maxPostSize = form.dataset.maxPostSize;
var maxUploadSize = form.dataset.maxUploadSize;
var html5Uploader = new HTML5Uploader(uploadRoute, maxPostSize, maxUploadSize, globalTranslations);
var documentManager = new DocumentManager(documentsGroupAttributeElement, html5Uploader, globalTranslations);
new FileDragAndDropHandler(documentsGroupAttributeElement.querySelector('.drag-and-drop-area')).hookTo(documentManager); //Enable or disable the save button depending on the uploads
html5Uploader.on('uploadStart', function () {
saveButtonStyleController.requestAddClass('disabled');
});
html5Uploader.on('uploadComplete', function () {
saveButtonStyleController.requestRemoveClass('disabled');
});
html5Uploader.on('uploadCanceled', function () {
saveButtonStyleController.requestRemoveClass('disabled');
});
html5Uploader.on('uploadFailed', function () {
saveButtonStyleController.requestRemoveClass('disabled');
});
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-multiselect-combo-box', function (attributesInitializer, multiselectComboBoxAttributeElement) {
if (multiselectComboBoxAttributeElement.dataset.readonly === 'false') {
var multiSelect = new MultiSelect(multiselectComboBoxAttributeElement);
}
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-on-off', function (attributesInitializer, onOffAttributeElement) {
var onOff = new OnOff(onOffAttributeElement);
});
attributeInitializer.bindSelectorToCallback('.js-video', function (attributesInitializer, videoAttributeElement) {
var video = new Video(videoAttributeElement);
}); //Make sure all confirmable things are initialized when they show up
attributeInitializer.bindSelectorToCallback('.js-confirm', function (attributesInitializer, confirmableHtmlElement) {
new ConfirmationController(confirmableHtmlElement);
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-select', function (attributesInitializer, selectAttributeElement) {
var select = new Select(selectAttributeElement);
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-currency', function (attributesInitializer, currencyAttributeElement) {
var currency = new Currency(currencyAttributeElement);
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-quantity-discount', function (attributesInitializer, quantityDiscountElement) {
var quantityDiscountController = new QuantityDiscount(quantityDiscountElement);
});
attributeInitializer.bindSelectorToCallback('.entity-attribute-send-password-mail-button', function (entitiesListInitializer, passwordMailButtonWrapper) {
var passwordMailButton = new SendPasswordMailButton(passwordMailButtonWrapper);
});
attributeInitializer.bindSelectorToCallback('[data-to-copy]', function (attributesInitializer, element) {
console.log('found copy text element');
new CopyText(element);
});
entitiesListInitializer.bindSelectorToCallback('.js-sortable', function (entitiesListInitializer, sortableListElement) {
//Create a new instance of the sortable controller
var sortableController = new SortableController(sortableListElement); //Load the HTML elements in the ul
var loadedPromise = sortableController.load();
loadedPromise.then(function (rootUl) {
if (preventNavigationController) {
console.log('Refreshing the prevent navigation controller since there may be more anchors.');
preventNavigationController.refresh(); //Refresh, since there will be more anchors. The ones of the sortable.
}
})["catch"](function (errorMessage) {
console.error(errorMessage);
});
}); //Make sure all tiny mce editors are initialized //TODO. needs fix
attributeInitializer.bindSelectorToCallback('textarea.tiny-mce', function (attributesInitializer, inputElement) {
if (!inputElement.id) inputElement.id = new Date().getTime() + Math.random().toString(36).substring(7); //Gives the element a random id if it does not have any. Makes it selectable by tinyMce's remove method.
tinymce.remove('#' + inputElement.id); //Removes the previous tiny mce editor instance if any.
tinymce.init({
target: inputElement,
menubar: false,
statusbar: false,
plugins: 'code paste link lists directionality',
toolbar: 'styleselect | bold italic strikethrough | numlist bullist | link unlink | outdent indent | code ',
height: '200',
paste_as_text: true,
style_formats: [{
title: 'Paragraaf',
block: 'p'
}, {
title: 'Subtitel',
block: 'h3'
}, {
title: 'Titel',
block: 'h2'
}],
convert_urls: false,
setup: function setup(editor) {
editor.on('change', function (changeEvent) {
// ensure the current element is passed, not just the last one
(function (input) {
return updateOriginalInput(editor, input);
})(inputElement);
});
}
});
function updateOriginalInput(tinyMceEditor, inputElement) {
tinyMceEditor.save();
var changeEvent = createNewEvent('change');
dispatchEventForElement(inputElement, changeEvent);
}
}, false); //Start looking for changes in #entity-form. And if one occurs, check if it did include one of the bound selector.
attributeInitializer.startObserving(); //Start looking for changes in the entities list (since they may be loaded async). And if one occurs, check if it did include one of the bound selector.
entitiesListInitializer.startObserving();
var entityAttributesContainer = document.querySelector('.entity-attributes');
var preventNavigationController = null;
if (entityAttributesContainer) preventNavigationController = new PreventNavigationController(entityAttributesContainer);
});
(function ($) {
// Hierarchical list
$('.entities-list-item .dropdown-icon').click(function (e) {
e.preventDefault();
if ($(this).parent().parent().hasClass('open')) {
//$('.animate-to-triangle', this)[0].beginElement();
$(this).parent().parent().removeClass('open');
} else {
//$('.animate-to-minus', this)[0].beginElement();
$(this).parent().parent().addClass('open');
}
});
var toggled = true; // Scroll to active item in list
$(window).load(function () {
if ($('#entity-form .lock').hasClass('open')) {
toggled = false;
$('#entity-form').find('input, textarea').attr('disabled', toggled);
}
var $container = $('#entities .entities-list:visible');
var $activeListItem = $('.active', $container).first();
if ($activeListItem.length == 0) return;
var top = $activeListItem.position().top;
$container.scrollTop(top);
$('.site-brand-name input').attr('placeholder', $('#global_name').val());
});
$('#global_name').change(function () {
$('.site-brand-name input').attr('placeholder', $(this).val());
}); //error accordian
$('.error-accordion .collapsible-ul').hide();
$('.error-accordion h3').click(function () {
$(this).parent().find('.collapsible-ul').toggle();
}); //$('.order-status.selectize').selectize();
// Product category selector
//$('#productCategorySelector').selectize();
//$('#selectYearAndMonthForm select').selectize();
$('#selectYearAndMonthForm select').change(function () {
var location = '/kms/orders/voltooid?month=' + $('#selectYearAndMonthForm select#orderMonthSelector').val() + '&year=' + $('#selectYearAndMonthForm select#orderYearSelector').val();
window.location = location;
});
$('#productCategorySelector').change(function () {
window.location = '/kms/products?category=' + encodeURIComponent(this.value);
}); // Flash messages
var hideFlashMessage = function hideFlashMessage() {
$('#flash-message').fadeOut();
};
$('#flash-message').click(function () {
hideFlashMessage();
});
setTimeout(function () {
hideFlashMessage();
}, 5000); //var toggled = true;
$('#entity-form .lock').click(function () {
$(this).toggleClass('open');
toggled = !toggled;
$(this).parents('#entity-form').find('input, textarea').attr('disabled', toggled);
});
/* grab important elements */
var sortInput = jQuery('#sort_order');
var submit = jQuery('#autoSubmit');
var messageBox = jQuery('#message-box');
var list = jQuery('.fieldGroupItems');
/* create requesting function to avoid duplicate code */
/* worker function */
var fnSubmit = function fnSubmit(save) {
var sortOrder = [];
list.children('li').each(function () {
sortOrder.push(jQuery(this).data('id'));
});
sortInput.val(sortOrder.join(','));
console.log(sortInput.val());
if (save) {
request();
}
};
/* store values */
list.children('li').each(function () {
var li = jQuery(this);
li.data('id', li.attr('title')).attr('title', '');
});
/* sortables */
list.sortable({
opacity: 0.7,
update: function update() {}
});
/* ajax form submission */
jQuery('#dd-form').bind('submit', function (e) {
if (e) e.preventDefault();
fnSubmit(true);
});
})(jQuery);
document.addEventListener('DOMContentLoaded', function () {
var activeListItem = document.querySelector('#sidebar .navigation li.active'); //Make parent menu items (li with class has-sub-items) also active
function makeParentListItemsActiveIfHasClassHasSubItems(liElement) {
var parent = liElement.parentElement ? liElement.parentElement : liElement.parentNode; // Stupid IE11
if (parent.classList.contains('has-sub-items') && parent.tagName === "LI") {
parent.classList.add('active');
makeParentListItemsActiveIfHasClassHasSubItems(parent);
}
}
if (activeListItem) {
makeParentListItemsActiveIfHasClassHasSubItems(activeListItem);
} //Make site list items openable by toggling a class tot them when clicked
var siteListItems = document.querySelectorAll('#sidebar .navigation .has-sub-items');
var siteListItemsLength = siteListItems.length;
for (var i = 0; i < siteListItemsLength; i++) {
var siteListItem = siteListItems[i];
siteListItem.addEventListener('click', function (ev) {
this.classList.toggle('active');
makeParentListItemsActiveIfHasClassHasSubItems(this);
ev.stopImmediatePropagation();
});
}
});
/**
* Fills an ul element with data retrieved from an api and makes the items searchable.
/* The data to and from the api has this structure for example.
*
* [
* {
* id:1
* title:false
* thumbnail:false
* status: "active"
* routes: []
* children: [
* {
* id: 12
* title: "Thuis"
* thumbnail: false
* routes: [{
* 40: "en/Homenew",
* 104: "nl/Thuisnew"
* }]
* }
* ]
* }
* ]
*/
var SearchController =
/*#__PURE__*/
function () {
function SearchController(sectionId, selector, inputSelector, mainUlId, resultCounterId) {
_classCallCheck(this, SearchController);
this.apiUrl = '';
this.data = '';
this.dataToLoad = '';
this.editEntitiesUrl = '';
this.initialized = false;
this.sectionId = sectionId;
this.selector = selector;
this.mainUlId = mainUlId;
this.resultCounterId = resultCounterId;
this.disabled = true;
this.inputSelector = inputSelector;
this.listItemClass = 'entities-list-item'; //The class that is added to li items that must be visible because they match (a part) of the search value).
//This is also the class applied to the rootUl if any results are found.
this.visibleClass = 'active';
this.invisibleClass = 'hide';
this.section = document.getElementById(this.sectionId);
var rootUl = document.querySelector(this.selector);
if (!rootUl) {
console.error('SearchController: Could not find the root ul by using the selector "' + this.selector + '"');
}
this.siteSlug = rootUl.dataset.siteSlug;
this.slug = rootUl.dataset.slug;
this.activeId = rootUl.dataset.activeId;
axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
}
/**
* Initializes the controller so that it knows where it can get its data from,
* where it needs to direct users when they click on an item and from which ul it
* should create a sortable ul
*
* @param dataSource A string|Object
* @param editEntitiesUrl A string
*
* In case of the data source being an object it must look like this:
* {
* data: [{
* id: null,
* routes: []
* status: "",
* title: "",
* children: [
* {
* id: "2",
* routes: []
* status: "",
* title: "My username"
* children: []
* }
* ]
* }]
* }
*/
_createClass(SearchController, [{
key: "init",
value: function init(dataSource, editEntitiesUrl) {
if (typeof dataSource === "string") {
this.apiUrl = dataSource;
} else {
this.dataToLoad = dataSource;
}
this.editEntitiesUrl = editEntitiesUrl;
this.initialized = true;
}
/**
* Returns a promise that resolves with the root ul that then will contain the items retrieved from the api
*/
}, {
key: "load",
value: function load() {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');
return;
}
var self = this;
return new Promise(function (resolve, reject) {
if (self.initialized === false) {
reject('Please initialize the controller with the init method first.');
}
if (self.apiUrl !== '' && self.dataToLoad === '') {
axios.get(self.apiUrl).then(function (response) {
if (!response.data || response.data.length == 0) {
reject('The searchable did not get any data from the api');
return;
} // console.log(response);
/** @var Array[] menuItemArrays*/
var childrenLength = response.data.children.length;
for (var i = 0; i < childrenLength; i++) {
/** @var array htmlElements **/
var htmlElements = self.createHtmlElement(response.data.children[i]);
var htmlElementsLength = htmlElements.length;
for (var j = 0; j < htmlElementsLength; j++) {
document.querySelector(self.selector).appendChild(htmlElements[j]);
}
} // console.log(document.querySelector(self.selector));
self.initializeSearch();
resolve(document.querySelector(self.selector));
})["catch"](function (error) {
reject(error);
});
} else if (self.apiUrl === '' && self.dataToLoad !== '') {
// console.log('data children');
// console.log(self.dataToLoad.data.children);
/** @var Array[] menuItemArrays*/
var childrenLength = self.dataToLoad.data.children.length;
for (var i = 0; i < childrenLength; i++) {
/** @var array htmlElements **/
var htmlElements = self.createHtmlElement(self.dataToLoad.data.children[i]);
var htmlElementsLength = htmlElements.length;
for (var j = 0; j < htmlElementsLength; j++) {
document.querySelector(self.selector).appendChild(htmlElements[j]);
}
}
self.initializeSearch();
resolve(document.querySelector(self.selector));
}
});
}
/**
* Initialize search functionality on the ul this searchable does its job for
*/
/**
* Initialize search functionality on the ul this searchable does its job for
*/
}, {
key: "initializeSearch",
value: function initializeSearch() {
var _this = this;
var section = this.section;
var input = document.querySelector(this.inputSelector);
var searchUl = document.querySelector(this.selector);
var resultCounter = document.getElementById(this.resultCounterId); //handles searching
input.addEventListener('keyup', function (event) {
var mainUl = document.getElementById(_this.mainUlId);
var resultsCount = 0;
var filterValue = input.value.toLowerCase();
var noSearchValue = filterValue == '' ? true : false;
var listItems = searchUl.querySelectorAll('li');
var listItemsCount = listItems.length; // console.log(listItemsCount);
for (var i = 0; i < listItemsCount; i++) {
var itemValue = listItems[i].dataset.title.toLowerCase();
var itemModel = JSON.parse(listItems[i].dataset.model);
var foundInTitle = itemValue.indexOf(filterValue) > -1 && noSearchValue === false;
var foundInModel = false;
if (!foundInTitle && filterValue.length >= 3) {
//Only search when the length of the filter value is 3 or more to filter out crap
for (var property in itemModel) {
if (!itemModel.hasOwnProperty(property)) continue;
if (String(itemModel[property]).toLowerCase().indexOf(filterValue.toLowerCase()) !== -1) {
foundInModel = true;
break;
}
}
}
if (foundInTitle || foundInModel) {
//item found
resultsCount++;
listItems[i].classList.add(_this.visibleClass);
listItems[i].setAttribute('dusk', 'found_search_item');
} else {
//item not found
listItems[i].classList.remove(_this.visibleClass);
listItems[i].removeAttribute('dusk');
}
}
if (resultsCount > 0) {// searchUl.parentNode.classList.add(this.visibleClass);
// mainUl.classList.add(this.invisibleClass);
} else {// searchUl.parentNode.classList.remove(this.visibleClass);
// mainUl.classList.remove(this.invisibleClass);
}
resultCounter.innerHTML = resultsCount + "";
if (!noSearchValue) {
section.classList.add(_this.visibleClass);
mainUl.classList.add(_this.invisibleClass);
} else {
section.classList.remove(_this.visibleClass);
mainUl.classList.remove(_this.invisibleClass);
}
});
searchUl.classList.remove(this.visibleClass);
}
/**
* Creates a menu item (HTMLElement) and sub menu items if necessary
*
* @param data
* @param items array Used internally. Humans must not touch this
* @param currentTreeLevel string Used internally. Humans must not touch this
* @returns {Array}
*/
}, {
key: "createHtmlElement",
value: function createHtmlElement(data) {
var items = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var currentTreeLevel = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '';
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');
return;
}
var self = this;
var activeId = self.activeId;
var id = data.id;
var title = data.title;
var model = data.model;
var children = data.children;
var status = data.status;
var thumbnail = data.thumbnail;
var breadcrumb = currentTreeLevel;
var treeBreadcrumb = currentTreeLevel !== "" ? currentTreeLevel + " | " + title : title; // console.log(breadcrumb);
// console.log(data);
//Render all child html items first
var childItems = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var object = _step.value;
childItems.push(this.createHtmlElement(object, items, treeBreadcrumb));
} //Displays a red or green line in front of the item depending on if the status (class) is active or not
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator["return"] != null) {
_iterator["return"]();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var colorStatusHtml = "\n <span class=\"color-status\" data-status-type=\"".concat(status, "\">\n \n </span>\n "); //Displays the items icon thumbnail OR the first character of its title
var iconHtml = "\n <div class=\"icon\" ".concat(thumbnail ? "style=\"background-image: url('".concat(thumbnail, "');\"") : '', " >\n ").concat(thumbnail ? '' : "<span>".concat(title ? title.substring(0, 1) : "", "</span>"), "\n </div>\n "); //The main item html that has all other items
var listIem = "\n <li data-title=\"".concat(title, "\" data-model='").concat(JSON.stringify(model) || JSON.stringify({}), "' class=\"").concat(this.listItemClass, " ").concat(id == activeId ? 'active' : '', "\"> \n <a href=\"").concat(this.editEntitiesUrl, "/").concat(id, "\"> \n ").concat(colorStatusHtml, "\n ").concat(iconHtml, "\n <p data-breadcrumb=\"").concat(breadcrumb, "\">").concat(title, "</p>\n </a>\n </li> \n "); //Render it to a real html element
var domParser = new DOMParser();
var document = domParser.parseFromString(listIem, "text/html");
items.push(document.body.firstChild); // console.log('rendered item: ');
// console.log(items);
//And then add the children inside
var childrenLength = childItems.length;
if (childrenLength > 0) {
for (var i = 0; i < childrenLength; i++) {
var child = childItems[i];
items.push(child[0]);
}
} // console.log('items result:');
// console.log(items);
//and return it
return items;
}
}]);
return SearchController;
}();
/**
* Fills an ul element with data retrieved from an api and makes the items draggable so that you can sort them.
* Also updates the api with the new positions of the items. The data to and from the api has this structure for example.
*
* [
* {
* id:1
* title:false
* thumbnail:false
* status: "active"
* routes: []
* children: [
* {
* id: 12
* title: "Thuis"
* thumbnail: false
* routes: [{
* 40: "en/Homenew",
* 104: "nl/Thuisnew"
* }]
* }
* ]
* }
* ]
*
*/
var SortableController =
/*#__PURE__*/
function () {
function SortableController(wrapper) {
_classCallCheck(this, SortableController);
if (!wrapper) {
console.error('SortableController: Expected a wrapper but did not get any. Sortable controller stopped working');
return;
}
this._wrapper = wrapper;
this._list = this._wrapper.querySelector('ul');
if (!this._list) {
console.error('SortableController: Expected the wrapper to have an ul. But did not have any. Sortable controller stopped working');
return;
}
if (!("slug" in this._list.dataset)) {
console.error('Make sure that the wrapper contains the data-slug attribute that contains the slug. Sortable controller stopped working.');
return;
}
this.slug = this._list.dataset.slug;
this._apiUrl = '/kms/api/' + this.slug;
this._editEntitiesUrl = '/kms/' + this.slug;
this._disabled = true;
this._listItemClass = 'entities-list-item';
this._enableSortButton = this._wrapper.querySelector('.entities-order .sortable-button.enable-sortable');
if (!this._enableSortButton) {
console.error('SortableController: The sort button could not be found inside the wrapper.', this._wrapper, 'Selector: ".entities-order .sortable-button .enable-sortable". Sortable controller stopped working');
return;
}
this._saveSortOrderButton = this._wrapper.querySelector('.entities-order .sortable-button.save-order');
if (!this._saveSortOrderButton) {
console.error('SortableController: The sort button could not be found inside the wrapper.', this._wrapper, 'Selector: ".entities-order .sortable-button .save-order". Sortable controller stopped working');
return;
}
this._activeId = this._list.dataset.activeId;
axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
this._updateVisibility(this._list);
this.enableSortable = this.enableSortable.bind(this); //needed to set the correct "this" in the method
this.disableSortable = this.disableSortable.bind(this); //needed to set the correct "this" in the method
this._setSortButtonsEnabled(true);
}
/**
* Enables or disables the sort buttons. The ones that you can
* click to enable sorting, and saving it afterwards
*
* @param enabled
* @private
*/
_createClass(SortableController, [{
key: "_setSortButtonsEnabled",
value: function _setSortButtonsEnabled(enabled) {
this._enableSortButton.removeEventListener('click', this.enableSortable);
this._saveSortOrderButton.removeEventListener('click', this.disableSortable);
if (enabled) {
this._enableSortButton.addEventListener('click', this.enableSortable);
this._saveSortOrderButton.addEventListener('click', this.disableSortable);
}
}
/**
* Does look at the list, and loops over all list items that have sub lists.
* When they have, it looks at the display property style state of those list items, and updates the ul children to
* match that display property state
*
* {HTMLUListElement}
* @private
*/
}, {
key: "_updateVisibility",
value: function _updateVisibility(list) {
if (list.tagName !== 'UL') {
console.error('sortableController: Could not update the unordered list diplay style state since the list is no <ul> tag');
}
var childrenThatHaveSubUl = list.querySelectorAll("li ul");
var childrenThatHaveSubUlLength = childrenThatHaveSubUl.length;
for (var i = 0; i < childrenThatHaveSubUlLength; i++) {
var child = childrenThatHaveSubUl[i];
child.style.display = child.style.display !== 'none' ? 'none' : ''; //Check if the children of this child must also be updated
var childChildren = child.children;
var childChildrenCount = childChildren.length;
for (var x = 0; x < childChildrenCount; x++) {
var childChild = child.children[x];
if (childChild.tagName === 'UL') {
this._updateVisibility(childChild);
}
}
}
}
/**
* Makes sure that all items that have class .sortable are now also are sortable
*
* @private
*/
}, {
key: "_updateSortableJavascript",
value: function _updateSortableJavascript() {
// console.log("Updating all sortable elements with these selectors: '" + this.selector+" .sortable' AND '"+this.selector+"'");
$('.sortable').sortable({
connectWith: ".sortable",
placeholder: "sortable-placeholder",
disabled: this._disabled
});
}
/**
* Makes the rootUl sortable
*/
}, {
key: "enableSortable",
value: function enableSortable() {
this._enableSortButton.classList.remove('show');
this._saveSortOrderButton.classList.add('show');
this._disabled = false;
$(this._wrapper).sortable({
disabled: this._disabled
}).addClass('sorting');
this._updateSortableJavascript();
}
/**
* Disables the rootUl so that it cannot be sorted
*/
}, {
key: "disableSortable",
value: function disableSortable() {
this._enableSortButton.classList.add('show');
this._saveSortOrderButton.classList.remove('show');
this._disabled = true;
$(this._wrapper).sortable({
disabled: this._disabled
}).removeClass('sorting');
this._save();
}
/**
* Saves the item data to the api
*/
}, {
key: "_save",
value: function _save() {
var itemsJson = this._itemsToJson(this._list);
var rootItem = {
id: 1,
routes: {},
status: null,
thumbnail: false,
title: null,
children: itemsJson
};
itemsJson = JSON.stringify(rootItem);
var apiJson = {
"tree": itemsJson
};
axios.post(this._apiUrl, apiJson).then(function (response) {// console.log('Successfully stored the sort order to the api: ');
// console.log(response);
})["catch"](function (error) {
// console.error('Could not save sortable sort order to api because an error occured: ');
console.error(error);
});
}
/**
* Converts the rootUl back to json for saving it
*
* @param htmlElement HTMLElement Root ul
* @return null|[] if something went wrong null, or an array containing json items if successful.
* @private
*/
}, {
key: "_itemsToJson",
value: function _itemsToJson(htmlElement) {
var jsonArray = [];
var error = false; //find all child li items
var listItems = htmlElement.querySelectorAll('li.' + this._listItemClass);
var listItemsLength = listItems.length;
for (var i = 0; i < listItemsLength; i++) {
var listItem = listItems[i]; //Skip children of children because they will be processed in the recursive _itemsToJson call down here.
if (listItem.parentElement !== htmlElement) continue;
var elementJson = listItem.dataset.json;
if (!elementJson) {
console.error('One or more li HTMLElements with class "' + this._listItemClass + '" did not have data-json attribute set while it should.');
error = true;
}
var currentItemJson = JSON.parse(listItem.dataset.json);
var childUl = listItem.querySelector('ul');
if (childUl) {
currentItemJson.children = this._itemsToJson(childUl);
if (!currentItemJson.children) error = true;
} else {
currentItemJson.children = [];
}
jsonArray.push(currentItemJson);
}
if (error) return null;
return jsonArray;
}
/**
* Returns a promise that resolves with the root ul that then will contain the items retrieved from the api
* @private
*/
}, {
key: "load",
value: function load() {
self = this;
return new Promise(function (resolve, reject) {
axios.get(self._apiUrl).then(function (response) {
if (!response.data || response.data.length == 0) {
reject('The sortable did not get any data from the api');
return;
}
var childrenLength = response.data.children.length;
for (var i = 0; i < childrenLength; i++) {
var itemObject = response.data.children[i];
self._list.appendChild(self._createHtmlElement(itemObject));
}
self._updateSortableJavascript();
resolve(self._list);
})["catch"](function (error) {
reject(error);
});
});
}
/**
* Removes all children from the ul
*/
}, {
key: "clearList",
value: function clearList() {
while (this._list.firstChild) {
this._list.removeChild(this._list.firstChild);
}
}
/**
* Creates a menu item (HTMLElement) and sub menu items if necessary
*
* @param data
* @returns {HTMLElement}
* @private
*/
}, {
key: "_createHtmlElement",
value: function _createHtmlElement(data) {
self = this;
var activeId = self._activeId;
var id = data.id;
var title = data.title || '-';
var thumbnail = data.thumbnail;
var children = data.children;
var status = data.status;
var routes = data.routes; //Generate json data representing that element
var routesForElement = {};
for (var routeProperty in routes) {
if (!routes.hasOwnProperty(routeProperty)) continue;
routesForElement[routeProperty] = routes[routeProperty];
}
if (routesForElement == {}) routesForElement = [];
var elementJson = {
id: data.id,
title: data.title,
thumbnail: data.thumbnail,
routes: routesForElement,
status: data.status
};
elementJson = JSON.stringify(elementJson); //Render all child html items first
var childItems = [];
var childrenLength = children.length;
for (var index = 0; index < childrenLength; index++) {
var obj = children[index];
childItems.push(this._createHtmlElement(obj));
} //Displays a red or green line in front of the item depending on if the status (class) is active or not
var colorStatusHtml = "\n <span class=\"color-status\" data-status-type=\"".concat(status, "\">\n \n </span>\n "); //Displays the items icon thumbnail OR the first character of its title
var iconHtml = "\n <div class=\"icon\">\n ".concat(thumbnail ? thumbnail : "<span>".concat(title ? title.substring(0, 1) : "", "</span>"), "\n </div>\n "); //The main item html that has all other items
var listIem = "\n <li data-json='".concat(elementJson, "' class=\"").concat(this._listItemClass, " ").concat(id == activeId ? 'active' : '', "\"> \n <a href=\"").concat(this._editEntitiesUrl, "/").concat(id, "\">\n ").concat(colorStatusHtml, "\n ").concat(iconHtml, "\n <p>").concat(title, "</p>\n </a>\n </li> \n "); //Render it to a real html element
var domParser = new DOMParser();
var document = domParser.parseFromString(listIem, "text/html");
var node = document.body.firstChild;
var childItemsLength = childItems.length; //And add the children inside
if (childItemsLength > 0) {
var subList = document.createElement('ul');
subList.className = 'sortable'; // console.log('rendered childItems');
// console.log(childItems);
for (var _index = 0; _index < childItemsLength; _index++) {
var element = childItems[_index];
subList.appendChild(element);
}
node.appendChild(subList);
} //and return it
return node;
}
}]);
return SortableController;
}();
/**
* The confirmation controller receives a navigatable element in its constructor.
* This is an element, that when clicked on, navigates the browser to some other page. Usually an anchor tag <a>.
*
* When the user then clicks on that element, he must confirm the click, because a modal will pop up, asking for confirmation.
* When the user presses the confirm button, the button will be really clicked.
* Or otherwise, when a confirmCallback is specified with the setConfirmCallback method, that callback will be triggered instead.
*
* You can disable confirmation by passing a callback to the setOnlyConfirmIfTrueCallback method. When this callback returns false,
* not confirmation modal is shown when the users presses the navigatable element.
*
* You can set the translations for the model using the methods setHeaderText, setMessage, setConfirmText and setCancelText.
* You can also set the translations automatically up construction when you set the following data attributes on the navigatable element:
* data-confirm-header, data-confirm-message, data-confirm-confirm-text, data-confirm-cancel-text.
*/
var ConfirmationController =
/*#__PURE__*/
function () {
/**
* ConfirmationController constructor
*
* @param navigatableElement {HTMLElement}
*/
function ConfirmationController(navigatableElement) {
_classCallCheck(this, ConfirmationController);
if (this._validateElement(navigatableElement) === false) {
console.error('The navigatableElement isn\'t valid. Did not enable the confirmation functionality for that navigatableElement. You passed a "' + navigatableElement.tagName + '"');
return;
} //Set translations form dataset or an empty string if not present
this._headerText = navigatableElement.dataset.confirmHeader ? navigatableElement.dataset.confirmHeader : '';
this._message = navigatableElement.dataset.confirmMessage ? navigatableElement.dataset.confirmMessage : '';
this._confirmText = navigatableElement.dataset.confirmConfirmText ? navigatableElement.dataset.confirmConfirmText : '';
this._cancelText = navigatableElement.dataset.confirmCancelText ? navigatableElement.dataset.confirmCancelText : '';
this._navigatableElement = navigatableElement;
this._onlyConfirmIfTrueCallback = null; //An optional callback that must return true or false to determine if the confirmation controller must show the prompt or not for the navigatableElement
this._confirmCallback = null; //An optional callback that is triggered when the user confirmed the action
this._clickEventHandler = this._clickEventHandler.bind(this);
this._confirmClicked = this._confirmClicked.bind(this);
this._cancelClicked = this._cancelClicked.bind(this);
this.enableListeners();
}
/**
* Enables listeners so that clicking on the navigatableElement can work.
*/
_createClass(ConfirmationController, [{
key: "enableListeners",
value: function enableListeners() {
this.disableListeners(); // this._navigatableElement.addEventListener('click', this._clickEventHandler.bind(this));
this._navigatableElement.addEventListener('click', this._clickEventHandler);
}
/**
* Disables listeners so that clicking on the navigatableElement will never trigger the confirmation modal anymore via this controller
*/
}, {
key: "disableListeners",
value: function disableListeners() {
// this._navigatableElement.removeEventListener('click', this._clickEventHandler.bind(this));
this._navigatableElement.removeEventListener('click', this._clickEventHandler);
}
/**
* The click event handler that handles clicks on the navigatable that was passed in via the constructor.
*
* @var {MouseEvent} clickEvent
*/
}, {
key: "_clickEventHandler",
value: function _clickEventHandler(clickEvent) {
var confirm = false;
if (this._onlyConfirmIfTrueCallback && this._onlyConfirmIfTrueCallback.call() === true || !this._onlyConfirmIfTrueCallback) confirm = true;
if (confirm) {
clickEvent.preventDefault();
this._showConfirmationPrompt(true, this._navigatableElement);
}
}
/**
* Sets a callback that wil be used to check if we actually need to confirm the action on the navigatableElement or just allow it.
* When the callback returns true or if you don't specify the callback, confirmation wil be done. When false. it won't be done.
*
* @param onlyConfirmIfTrueCallback
*/
}, {
key: "setOnlyConfirmIfTrueCallback",
value: function setOnlyConfirmIfTrueCallback(onlyConfirmIfTrueCallback) {
this._onlyConfirmIfTrueCallback = onlyConfirmIfTrueCallback;
}
/**
* Add or remove listeners to / from the modal buttons elements.
* Usually the yes no button elements.
*
* @private
*/
}, {
key: "_addListenersToModalButtons",
value: function _addListenersToModalButtons() {
var add = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
var originalNavigatableElement = arguments.length > 1 ? arguments[1] : undefined;
if (!this.promptElement) console.error('First create the modal with the createPrompt method');
var confirmButton = this.promptElement.querySelector('button.confirm');
var cancelButton = this.promptElement.querySelector('button.cancel');
var shader = this.promptElement.querySelector('div.shader');
confirmButton.removeEventListener('click', this._confirmClicked);
cancelButton.removeEventListener('click', this._cancelClicked);
shader.removeEventListener('click', this._cancelClicked);
if (add) {
confirmButton.addEventListener('click', this._confirmClicked);
cancelButton.addEventListener('click', this._cancelClicked);
shader.addEventListener('click', this._cancelClicked);
}
}
/**
* Sets an optional callback that is triggered when the user confirmed the action.
* If you don't specify it, the nearest form that is found by traversing up into the dom is submitted
*
* @param callback
*/
}, {
key: "setConfirmCallback",
value: function setConfirmCallback(callback) {
this._confirmCallback = callback;
return this;
}
/**
* Triggered when the user clicks the confirm button
*
* @param mouseEvent
* @private
*/
}, {
key: "_confirmClicked",
value: function _confirmClicked(mouseEvent) {
mouseEvent.preventDefault();
if (this._confirmCallback) {
this._confirmCallback.call();
} else {
//Find the nearest form by traversing up into the dom
this._addNavigatableElementValueToForm();
this._submitNearestForm(false);
}
this._showConfirmationPrompt(false);
}
/**
* If the navigatable element contains a value,
* we add a hidden input to the form with the same name and value
* as the navigatable. Making sure that its value is passed as expected.
*
* @private
*/
}, {
key: "_addNavigatableElementValueToForm",
value: function _addNavigatableElementValueToForm() {
if (this._navigatableElement.hasAttribute('value') && this._navigatableElement.hasAttribute('name')) {
var form = this._findForm(this._navigatableElement);
if (!form) return;
var input = document.createElement('INPUT');
input.setAttribute('type', 'hidden');
input.setAttribute('name', this._navigatableElement.getAttribute('name'));
input.setAttribute('value', this._navigatableElement.getAttribute('value'));
form.appendChild(input);
}
}
/**
* Submit the nearest form the navigatableElement is in,
* by traversing up into the dome as long as no form is found
*
* @private
*/
}, {
key: "_submitNearestForm",
value: function _submitNearestForm() {
var form = this._findForm(this._navigatableElement);
if (form) {
form.submit();
return true;
} else {
console.error('Could not submit the nearest form this navigatableElement should be in, because it isn\'t in a form.');
return false;
}
}
/**
* Triggered when the user clicks the cancel button
*
* @param mouseEvent
* @private
*/
}, {
key: "_cancelClicked",
value: function _cancelClicked(mouseEvent) {
mouseEvent.preventDefault();
this._showConfirmationPrompt(false);
}
/**
* Retrieves an HTML element and starts traversing up into the dom to find the first form the element is in.
* Returns the form or false if it cannot be found
* @param element
* @private
*/
}, {
key: "_findForm",
value: function _findForm(element) {
if (!element.parentNode) return false;
if (element.parentNode.tagName === 'FORM') return element.parentNode;
return this._findForm(element.parentNode);
}
/**
* Create a div that represents a prompt
*
* @returns {HTMLDivElement}
* @private
*/
}, {
key: "_createPrompt",
value: function _createPrompt() {
var html = '<div class="modal">' + '<div class="header"><h4>' + this._headerText + '</h4></div>' + '<div class="body">' + '<p class="message">' + this._message + '</p>' + '<div class="navigatableElements buttons">' + '<button class="confirm" dusk="confirmation_confirm">' + this._confirmText + '</button>' + '<button class="cancel" dusk="confirmation_cancel">' + this._cancelText + '</button>' + '</div>' + '</div>' + '</div>' + '</div>' + '<div class="shader"></div>';
var promptElement = document.createElement("div");
promptElement.setAttribute('id', 'confirmBox');
promptElement.setAttribute('dusk', 'confirmBox');
promptElement.innerHTML = html;
return promptElement;
}
/**
* Show or hides the confirmation prompt by adding a hidden class to it.
*
* @private
*/
}, {
key: "_showConfirmationPrompt",
value: function _showConfirmationPrompt(show, originalNavigatableElement) {
console.log(show ? 'show confirmation prompt' : 'hide confirmation prompt');
if (!this.promptElement) {
this.promptElement = this._createPrompt();
this._addListenersToModalButtons(true, originalNavigatableElement);
}
if (show) {
this.promptElement.classList.add('show');
document.body.appendChild(this.promptElement);
} else {
this.promptElement.classList.remove('show');
if (this.promptElement.parentElement) {
this.promptElement.parentElement.removeChild(this.promptElement);
}
this.promptElement = null;
}
}
/**
* Returns true when the navigatableElement is a navigatableElement tag or an input tag with type navigatableElement.
* false if not
*
* @param navigatableElement {HTMLElement}
* @returns {boolean}
*
* @private
*/
}, {
key: "_validateElement",
value: function _validateElement(navigatableElement) {
if (!navigatableElement) return false;
if (navigatableElement.tagName !== 'BUTTON' && navigatableElement.tagName !== 'INPUT' && navigatableElement.tagName !== 'A') return false;
if (navigatableElement.tagName === "INPUT") {
if (navigatableElement.getAttribute('type').toLowerCase() !== 'navigatableElement' && navigatableElement.getAttribute('type').toLowerCase() !== 'submit') return false;
}
return true;
}
/**
* @param text
* @returns {ConfirmationController}
*/
}, {
key: "setHeaderText",
value: function setHeaderText(text) {
this._headerText = text;
return this;
}
/**
* @param text
* @returns {ConfirmationController}
*/
}, {
key: "setMessage",
value: function setMessage(text) {
this._message = text;
return this;
}
/**
* @param text
* @returns {ConfirmationController}
*/
}, {
key: "setConfirmText",
value: function setConfirmText(text) {
this._confirmText = text;
return this;
}
/**
* @param text
* @returns {ConfirmationController}
*/
}, {
key: "setCancelText",
value: function setCancelText(text) {
this._cancelText = text;
return this;
}
}]);
return ConfirmationController;
}();
/**
* Observes the contents of the element you reference to via the constructor selector variable
* for added nodes and triggers callbacks for added nodes when the match the given selector that
* you've specified with the bindSelectorToCallback method
*/
var Initializer =
/*#__PURE__*/
function () {
/**
* @param wrapperSelector A selector of an element that contains all elements that are bound with the bindSelectorToCallback method. As close as possible to the things they match
*/
function Initializer(wrapperSelector) {
_classCallCheck(this, Initializer);
this._bindings = {};
this._onceBindings = {}; //Selectors that may only be initialized once if the binding in this object is true
this._booted = false;
this._observer = null;
this._isObserving = false;
this._showDebugInformation = false;
this._observerConfiguration = {
// attributeFilter: [], //An array of specific attribute names to be monitored. If this property isn't included, changes to all attributes cause mutation notifications. No default value.
// attributeOldValue: false, //Set to true to record the previous value of any attribute that changes when monitoring the node or nodes for attribute changes.
// attributes: true, //Set to true to watch for changes to the value of attributes on the node or nodes being monitored. The default value is false.
// characterDataOldValue: true, //Set to true to record the previous value of a node's text whenever the text changes on nodes being monitored.
// characterData: true, //Set to true to monitor the specified target node or subtree for changes to the character data contained within the node or nodes. No default value.
subtree: true,
//Set to true to extend monitoring to the entire subtree of nodes rooted at target. All of the other MutationObserverInit properties are then extended to all of the nodes in the subtree instead of applying solely to the target node. The default value is false.,
childList: true //Set to true to monitor the target node (and, if subtree is true, its descendants) for the addition or removal of new child nodes. The default is false.
};
this._wrapper = document.querySelector(wrapperSelector);
if (!this._wrapper) {
console.info('The selector "' + wrapperSelector + '" does not match any element. Could not monitor html elements and initialize them when needed.');
return;
}
this._boot();
}
/**
* Configure the Initializer to observe the element that wrapperSelector references
* for new HTMLElements, referenced by the bound selectors. So that when they are found, the callback is called.
* You need to call startObserving to start the observation.
*/
_createClass(Initializer, [{
key: "_boot",
value: function _boot() {
if (this._booted) return;
this._booted = true;
this._observer = new MutationObserver(this._mutationsObserved.bind(this));
}
/**
* Start observing for changes
*
* @return {Initializer}
*/
}, {
key: "startObserving",
value: function startObserving() {
if (!this._booted) return this;
if (this._showDebugInformation) console.log('Starting to observe');
this._observer.takeRecords(); //Take the mutation records out of the observer so that it is cleared
this._observer.observe(this._wrapper, this._observerConfiguration);
this._isObserving = true;
return this;
}
/**
* Stop observing for changes
*
* @return {Initializer}
*/
}, {
key: "stopObserving",
value: function stopObserving() {
if (!this._booted) return this;
if (this._showDebugInformation) console.log('Stopped to observe');
this._observer.disconnect();
this._isObserving = false;
return this;
}
/**
* Triggered by the MutationObserver when a mutation had taken place
*
* @param {MutationRecord[]} mutationsList
*/
}, {
key: "_mutationsObserved",
value: function _mutationsObserved(mutationsList) {
if (!this._booted) return this;
var mutationsCount = mutationsList.length;
if (this._showDebugInformation) console.info('Detected ' + mutationsCount + ' changes');
for (var mutationNumber = 0; mutationNumber < mutationsCount; mutationNumber++) {
var mutationRecord = mutationsList[mutationNumber];
var addedNodes = mutationRecord.addedNodes;
var addedNodesCount = addedNodes.length;
for (var addedNodeNumber = 0; addedNodeNumber < addedNodesCount; addedNodeNumber++) {
var addedNode = addedNodes[addedNodeNumber];
this._triggerBoundCallbacksIfElementMatchesSelector(addedNode);
}
}
}
/**
* @param {HTMLElement} htmlElement
*/
}, {
key: "_triggerBoundCallbacksIfElementMatchesSelector",
value: function _triggerBoundCallbacksIfElementMatchesSelector(htmlElement) {
if (!htmlElement || !(htmlElement instanceof HTMLElement)) return;
for (var selector in this._bindings) {
// if(this._showDebugInformation) console.log('Checking if selector "'+selector+'" matches HTMLElement', htmlElement);
var addedElementMatchesSelector = htmlElement.matches(selector);
var elementsThatMatch = htmlElement.querySelectorAll(selector);
var matchingElementsCount = elementsThatMatch.length;
if (addedElementMatchesSelector || matchingElementsCount > 0) {
if (addedElementMatchesSelector) {
if (!htmlElement.hasAttribute('initialized') || this._onceBindings[selector] === false) {
if (this._showDebugInformation) console.log('Calling callback for selector "' + selector + '". HTMLElement', htmlElement);
this._bindings[selector](this, htmlElement);
htmlElement.setAttribute('initialized', '');
}
}
if (matchingElementsCount > 0) {
for (var currentMatchingElementIndex = 0; currentMatchingElementIndex < matchingElementsCount; currentMatchingElementIndex++) {
var currentMatchingElement = elementsThatMatch[currentMatchingElementIndex];
if (!currentMatchingElement.hasAttribute('initialized') || this._onceBindings[selector] === false) {
if (this._showDebugInformation) console.log('Calling callback for selector "' + selector + '". HTMLElement', currentMatchingElement);
this._bindings[selector](this, currentMatchingElement);
currentMatchingElement.setAttribute('initialized', '');
}
}
}
}
}
}
/**
* @param {string} selector
* @param {function} callback
* @param once
* @return {Initializer}
*/
}, {
key: "bindSelectorToCallback",
value: function bindSelectorToCallback(selector, callback) {
var once = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
if (!this._booted) return this; //Register the selector and callback
if (typeof selector !== 'string') {
console.error('Initializer:bind The selector must be a string');
return this;
}
if (typeof callback !== 'function') {
console.error('Initializer:bind The callback must be a function');
return this;
}
if (typeof once !== 'boolean') {
console.error('Initializer:bind The once variable must be a boolean');
return this;
}
this._bindings[selector] = callback;
this._onceBindings[selector] = once;
if (this._showDebugInformation) console.log('bound selector "' + selector + '" to a callback'); //Trigger existing matches. Temporary stops the observer to prevent double callback calling.
var wasObserving = this._isObserving;
if (this._isObserving) this.stopObserving();
var nodeList = this._wrapper.querySelectorAll(selector);
if (!nodeList) return this;
var length = nodeList.length;
for (var currentElementNumber = 0; currentElementNumber < length; currentElementNumber++) {
callback(this, nodeList[currentElementNumber]);
}
if (wasObserving) this.startObserving();
return this;
}
}]);
return Initializer;
}();
/**
* Tracks all inputs, selects and textareas in the given wrapper element for changes.
*
* If the user clicked an anchor the user first must confirm that he's going to lose changes before the anchor is followed.
*/
var PreventNavigationController =
/*#__PURE__*/
function () {
/**
* inputs in the wrapper element will be tracked for changes
*
* @param WrapperElement {Element}
*/
function PreventNavigationController(WrapperElement) {
_classCallCheck(this, PreventNavigationController);
//Create properties
this._inputs = []; //Inputs, textareas, selects
this._wrapperElement = null;
this._translation = {
headerText: '',
message: '',
confirmText: '',
cancelText: ''
};
this._anchorsAndConfirmationControllers = []; //Contains objects containing properties element and controller to keep track of all anchors and their confirmation controllers.
this._changed = false; //Automatically set to true when one of the inputs was changed
//validate stuff
if (!WrapperElement) {
console.error('PreventNavigationController: No wrapper element given. Not preventing navigation.');
return;
}
if (!WrapperElement.dataset.translation) {
console.error('PreventNavigationController: No translation present. Not preventing navigation when not all changes have been saved.');
return;
}
this._wrapperElement = WrapperElement; //Make sure event handlers have the correct this
this._inputChanged = this._inputChanged.bind(this); //Delegate control to specialist parts of the class
this._loadTranslation();
this.refresh();
}
/**
* Returns true if the given input element is an HTMLElement of tag Input.
*
* @param InputElement {HTMLElement}
* @returns {boolean}
* @private
*/
_createClass(PreventNavigationController, [{
key: "_isTrackableElement",
value: function _isTrackableElement(InputElement) {
return InputElement.tagName === "INPUT" || InputElement.tagName === "SELECT" || InputElement.tagName === "TEXTAREA";
}
/**
* Add / remove listeners on inputs
*
* @param inputs {array}
* @param add {boolean} true when you want to add the listeners. false if not.
* @private
*/
}, {
key: "_setListenersOnInputs",
value: function _setListenersOnInputs(inputs) {
var add = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var nInputs = inputs.length;
for (var currentInputNumber = 0; currentInputNumber < nInputs; currentInputNumber++) {
if (add) {
inputs[currentInputNumber].addEventListener('change', this._inputChanged);
} else {
inputs[currentInputNumber].removeEventListener('change', this._inputChanged);
}
}
}
/**
* Triggered in response to a changed input
*
* @param event
* @private
*/
}, {
key: "_inputChanged",
value: function _inputChanged(event) {
// console.log('Form changed. Source: ', event.target);
this._changed = true;
}
/**
* Loads the translations. They are defined on the wrapper element
* @private
*/
}, {
key: "_loadTranslation",
value: function _loadTranslation() {
this._translation = JSON.parse(this._wrapperElement.dataset.translation);
}
/**
* Scans the documents for inputs to monitor changes on. These inputs will be stored as a reference.
* We then intercept clicks on all elements that cause navigations (<a> tags).
* And finally we set listeners on the found inputs to detect changes. This ultimately will cause
* a model to pop up when navigating. If the page is changed somehow, you may also call this method to
* make the controller aware of changes.
*/
}, {
key: "refresh",
value: function refresh() {
this._inputs = this._findAllInputsIn(this._wrapperElement);
this._interceptClicksOnAnchors();
this._setListenersOnInputs(this._inputs);
}
/**
* Scans the document for anchor tags. Intercepts click events on them when one of the inputs
* has changed. When a click is intercepted, a modal is shown asking for confirmation.
* When the action gets confirmed, a new click on the anchor tag is done, without interception.
*
* @private
*/
}, {
key: "_interceptClicksOnAnchors",
value: function _interceptClicksOnAnchors() {
var _this2 = this;
this._clearAnchorsAndControllers();
var anchors = document.getElementsByTagName('A');
var nAnchors = anchors.length;
var _loop = function _loop(linkNumber) {
var currentAnchor = anchors[linkNumber];
var anchorHref = currentAnchor.getAttribute('href');
if (anchorHref && anchorHref.substr(0, 1) !== "#") {
//The anchor has an href value and that is not a hash.
var controller = new ConfirmationController(currentAnchor);
controller.setHeaderText(_this2._translation.headerText).setMessage(_this2._translation.message).setConfirmText(_this2._translation.confirmText).setCancelText(_this2._translation.cancelText).setConfirmCallback(function () {
window.location = anchorHref;
}).setOnlyConfirmIfTrueCallback(function (controller) {
return function () {
return controller._hasChanged();
};
}(_this2));
_this2._anchorsAndConfirmationControllers.push({
'anchor': currentAnchor,
'controller': controller
});
}
};
for (var linkNumber = 0; linkNumber < nAnchors; linkNumber++) {
_loop(linkNumber);
}
}
/**
* Clears all anchors and controllers.
* Also disables event listeners in those controllers.
*
* @private
*/
}, {
key: "_clearAnchorsAndControllers",
value: function _clearAnchorsAndControllers() {
var anchorControllerObject;
while (anchorControllerObject = this._anchorsAndConfirmationControllers.pop()) {
/** @var {ConfirmationController} controller **/
// console.log('Clearing old confirmation controller: ', anchorControllerObject);
var controller = anchorControllerObject.controller;
controller.disableListeners();
controller = null;
}
}
/**
* Returns true if one of the input has changed
* @returns {boolean}
* @private
*/
}, {
key: "_hasChanged",
value: function _hasChanged() {
// console.log('Something changed! Preventing navigation');
return this._changed;
}
/**
* Finds all html elements witch match elements specified in the isTrackableElement method
*
* @param WrapperElement {Element}
* @param inputs
* @returns {Array}
* @private
*/
}, {
key: "_findAllInputsIn",
value: function _findAllInputsIn(WrapperElement) {
var inputs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var nChildren = WrapperElement.children.length;
if (nChildren === 0) return inputs;
for (var index = 0; index < nChildren; index++) {
var currentChild = WrapperElement.children[index];
if (this._isTrackableElement(currentChild)) {
inputs.push(currentChild);
}
if (currentChild.children.length > 0) {
var childInputs = this._findAllInputsIn(currentChild, inputs);
inputs.concat(childInputs);
}
}
return inputs;
}
}]);
return PreventNavigationController;
}();
var TabsController =
/*#__PURE__*/
function () {
/**
* The tab controller can obviously control tabs.
* You just have to have a couple of divs that hold the content. These divs must all have a class name that
* corresponds to the value of constructor parameter tabContentIdAndClassAndPrefix. And they all must have an id
* that also starts with the value of tabContentIdAndClassAndPrefix followed by the tab slug.
*
* You can specify if you have an hidden input field that needs to be updated with the curent tab slug. You do that
* with the tabSlugInputSelector.
*
* Content tab divs and buttons will also receive an active state class to make them visible / stand out when present
* or hidden / to background. You can specify this class name with the activeClass parameter.
*
* The tabButtonGroupSelector selects an element that holds the tab buttons to switch tabs. This element must have an
* ul element that holds li elements containing a elements.
*
* The reactToUrlHashChange parameter controls if the controller should react if the hash part of the url did change.
*
* @param tabContentIdAndClassAndPrefix string
* @param tabButtonGroupSelector string
* @param activeClass string
* @param reactToUrlHashChange
* @param tabSlugInputSelector string
*/
function TabsController() {
var tabContentIdAndClassAndPrefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'tab';
var tabButtonGroupSelector = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '.tab-buttons';
var activeClass = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'active';
var reactToUrlHashChange = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
var tabSlugInputSelector = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : undefined;
_classCallCheck(this, TabsController);
var input = document.querySelector(this.tabSlugInputId);
this.tabButtonGroupSelector = tabButtonGroupSelector;
this.tabContentDivsClassAndIdPrefix = tabContentIdAndClassAndPrefix;
this.tabSlugInputId = tabSlugInputSelector;
this.activeClass = activeClass;
this.reactToUrlHashChange = reactToUrlHashChange;
if (reactToUrlHashChange) this.addListenerForHashChange(reactToUrlHashChange);
}
/**
* Opens the tab by using the tab slug
*
* @param tabSlug
*/
_createClass(TabsController, [{
key: "openTab",
value: function openTab(tabSlug, clearStorage) {
//Remove trailing / on the left hand side
tabSlug = this.removeLeftHandSlashInSlug(tabSlug);
if (!tabSlug) {
return;
}
if (clearStorage) {
sessionStorage.clear();
}
this.updateTabSlugInput(tabSlug);
this.showTabContentForTabWithSlug(tabSlug);
this.makeTabButtonActiveForSlug(tabSlug);
}
/**
* Removes a / at the beginning of a tab slug
*
* @param tabSlug
*/
}, {
key: "removeLeftHandSlashInSlug",
value: function removeLeftHandSlashInSlug(tabSlug) {
return tabSlug.replace(/^\/(.*)/, '$1');
}
/**
* Updates a usually hidden input field holding the tab slug with a new slug
*
* @param newSlug string
*/
}, {
key: "updateTabSlugInput",
value: function updateTabSlugInput(newSlug) {
if (this.tabSlugInputId === undefined) return;
var element = document.querySelector(this.tabSlugInputId);
if (element) element.value = newSlug;
}
}, {
key: "makeTabButtonActiveForSlug",
value: function makeTabButtonActiveForSlug(slug) {
var entityTabs = document.querySelectorAll('.entity-tabs > ul > li');
var nTabs = entityTabs.length;
for (var index = 0; index < nTabs; index++) {
var currentTab = entityTabs[index];
currentTab.classList.remove('active');
}
var activeTab = document.querySelector('.entity-tabs > ul > li a[href="#' + slug + '"]');
activeTab.parentElement.classList.add('active');
}
/**
* Shows the tab content div with the specified slug and hides the other content div tabs.
* It does this by adding and removing classes (the active class you specified in the constructor)
*
* @param slug string
*/
}, {
key: "showTabContentForTabWithSlug",
value: function showTabContentForTabWithSlug(slug) {
//Remove the active class from all tab content classes
var items = document.querySelectorAll('.' + this.tabContentDivsClassAndIdPrefix);
var length = items.length;
for (var index = 0; index < length; index++) {
var element = items[index];
element.classList.remove(this.activeClass);
} //Add the active class to the tab that has the correct slug appended to the IdPrefix
var activeTab = document.querySelector("#" + this.tabContentDivsClassAndIdPrefix + "-" + slug);
if (!activeTab) {
console.error("TabsController: Could not make content tab active. It should have an ID with: #" + this.tabContentDivsClassAndIdPrefix + "-" + slug);
return;
}
activeTab.classList.add(this.activeClass);
if (!isset(sessionStorage.getItem("componentScrollPosition"))) {
activeTab.parentElement.scrollTop = 0;
}
activeTab.parentElement.addEventListener('scroll', function (ev) {
sessionStorage.componentScrollPosition = ev.currentTarget.scrollTop;
});
}
/**
* Adds or disables listening for hashchange events to update tabs
*
* @param boolean
*/
}, {
key: "addListenerForHashChange",
value: function addListenerForHashChange(_boolean) {
if (_boolean) {
window.addEventListener('hashchange', this.hashChanged.bind(this));
window.addEventListener('load', this.hashChanged.bind(this));
} else {
window.removeEventListener('hashchange', this.hashChanged);
window.removeEventListener('load', this.hashChanged);
}
}
/**
* Called automatically by an internal listener (controlled by the addListenerForHashChange method)
*/
}, {
key: "hashChanged",
value: function hashChanged(event) {
var tabSlug = window.location.hash.substring(1);
if (tabSlug) {
this.openTab(tabSlug, true);
return;
}
var tabSlugElement = document.querySelector(this.tabSlugInputId);
if (!tabSlugElement) return;
this.openTab(tabSlugElement.value, false);
}
}]);
return TabsController;
}();