File: D:/HostingSpaces/Eurotools/euro-tools.nl/wwwroot/js/kms/core.js
var _createClass = 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); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* 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 = 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);
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;
}
/** @var Array[] menuItemArrays*/
response.data[0].children.forEach(function (itemObject) {
/** @var array htmlElements **/
var htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach(function (element) {
document.querySelector(self.selector).appendChild(element);
});
});
// console.log(document.querySelector(self.selector));
self.initializeSearch();
resolve(document.querySelector(self.selector));
}).catch(function (error) {
reject(error);
});
} else if (self.apiUrl === '' && self.dataToLoad !== '') {
self.dataToLoad.data.children.forEach(function (itemObject) {
var htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach(function (element) {
document.querySelector(self.selector).appendChild(element);
});
});
self.initializeSearch();
resolve(document.querySelector(self.selector));
}
});
}
/**
* 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;
for (var property in itemModel) {
if (!itemModel.hasOwnProperty(property)) continue;
if (String(itemModel[property]).indexOf(filterValue) !== -1) {
foundInModel = true;
break;
}
}
if (foundInTitle || foundInModel) {
//item found
resultsCount++;
listItems[i].classList.add(_this.visibleClass);
} else {
//item not found
listItems[i].classList.remove(_this.visibleClass);
}
}
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) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
var colorStatusHtml = '\n <span class="color-status" data-status-type="' + status + '">\n \n </span>\n ';
//Displays the items icon thumbnail OR the first character of its title
var iconHtml = '\n <div class="icon" ' + (thumbnail ? 'style="background-image: url(\'' + thumbnail + '\');"' : '') + ' >\n ' + (thumbnail ? '' : '<span>' + (title ? title.substring(0, 1) : '') + '</span>') + '\n </div>\n ';
//The main item html that has all other items
var listIem = '\n <li data-title="' + title + '" data-model=\'' + (JSON.stringify(model) || JSON.stringify({})) + '\' class="' + this.listItemClass + ' ' + (id == activeId ? 'active' : '') + '"> \n <a href="' + this.editEntitiesUrl + '/' + id + '">\n ' + colorStatusHtml + '\n ' + iconHtml + '\n <p data-breadcrumb="' + breadcrumb + '">' + 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
if (childItems.length > 0) {
// console.log('adding rendered childitems');
childItems.forEach(function (element) {
items.push(element[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 = function () {
function SortableController(selector) {
_classCallCheck(this, SortableController);
this.apiUrl = '';
this.editEntitiesUrl = '';
this.initialized = false;
this.selector = selector;
this.disabled = true;
this.listItemClass = 'entities-list-item';
var rootUl = document.querySelector(this.selector);
this.siteSlug = rootUl.dataset.siteSlug;
this.slug = rootUl.dataset.slug;
this.activeId = rootUl.dataset.activeId;
}
/**
* 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 apiUrl A string
* @param editEntitiesUrl A string
*/
_createClass(SortableController, [{
key: 'init',
value: function init(apiUrl, editEntitiesUrl) {
this.apiUrl = apiUrl;
this.editEntitiesUrl = editEntitiesUrl;
var itemsThatHaveSubUl = document.querySelectorAll(this.selector + " li ul");
itemsThatHaveSubUl.forEach(function (node, index) {
// console.log('clicked ul');
node.style.display = node.style.display !== 'none' ? 'none' : '';
});
this.initialized = true;
}
}, {
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
});
var itemsThatHaveSubUl = document.querySelectorAll(this.selector + " li ul");
itemsThatHaveSubUl.forEach(function (node, index) {
// console.log('clicked ul');
// node.style.display = node.style.display !== 'none' ? 'none' : '';
});
}
/**
* Makes the rootUl sortable
*/
}, {
key: 'enableSortable',
value: function enableSortable() {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
this.disabled = false;
$(this.selector).sortable({
disabled: this.disabled
}).addClass('sorting');
this.updateSortableJavascript();
}
/**
* Diables the rootUl so that it cannot be sorted
*/
}, {
key: 'disableSortable',
value: function disableSortable() {
// if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
this.disabled = true;
$(this.selector).sortable({
disabled: this.disabled
}).removeClass('sorting');
this.save();
}
/**
* Saves the item data to the api
*/
}, {
key: 'save',
value: function save() {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
var itemsJson = this.itemsToJson(document.querySelector(this.selector));
itemsJson = JSON.stringify(itemsJson);
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 successfull.
*/
}, {
key: 'itemsToJson',
value: function itemsToJson(htmlElement) {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
var jsonArray = [];
var error = false;
//find all child li items
var listItems = htmlElement.querySelectorAll(':scope > li.' + this.listItemClass);
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = listItems[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var listItem = _step2.value;
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);
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
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
*/
}, {
key: 'load',
value: function load() {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
self = this;
return new Promise(function (resolve, reject) {
if (self.initialized === false) {
reject('Please initialize the controller with the init method first.');
}
axios.get(self.apiUrl).then(function (response) {
if (!response.data) {
reject('The sortable did not get any data from the api');
return;
}
/** @var HTMLElement[] menuItems*/
var menuItems = [];
response.data[0].children.forEach(function (itemObject) {
menuItems.push(self.createHtmlElement(itemObject));
});
menuItems.forEach(function (htmlElementItem) {
document.querySelector(self.selector).appendChild(htmlElementItem);
});
self.updateSortableJavascript();
resolve(document.querySelector(self.selector));
}).catch(function (error) {
reject(error);
});
});
}
/**
* Removes all children from the ul
*/
}, {
key: 'clearRootUl',
value: function clearRootUl() {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
var rootUl = document.querySelector(this.selector);
while (rootUl.firstChild) {
rootUl.removeChild(rootUl.firstChild);
}
}
/**
* Creates a menu item (HTMLElement) and sub menu items if necessary
*
* @param data
* @returns {HTMLElement}
*/
}, {
key: 'createHtmlElement',
value: function createHtmlElement(data) {
if (this.initialized === false) {
console.error('Please initialize the controller with the init method first.');return;
}
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 _iteratorNormalCompletion3 = true;
var _didIteratorError3 = false;
var _iteratorError3 = undefined;
try {
for (var _iterator3 = children[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
var object = _step3.value;
childItems.push(this.createHtmlElement(object));
}
//Displays a red or green line in front of the item depending on if the status (class) is active or not
} catch (err) {
_didIteratorError3 = true;
_iteratorError3 = err;
} finally {
try {
if (!_iteratorNormalCompletion3 && _iterator3.return) {
_iterator3.return();
}
} finally {
if (_didIteratorError3) {
throw _iteratorError3;
}
}
}
var colorStatusHtml = '\n <span class="color-status" data-status-type="' + status + '">\n \n </span>\n ';
//Displays the items icon thumbnail OR the first character of its title
var iconHtml = '\n <div class="icon" ' + (thumbnail ? 'style="background-image: url(\'' + thumbnail + '\');"' : '') + ' >\n ' + (thumbnail ? '' : '<span>' + (title ? title.substring(0, 1) : '') + '</span>') + '\n </div>\n ';
//The main item html that has all other items
var listIem = '\n <li data-json=\'' + elementJson + '\' class="' + this.listItemClass + ' ' + (id == activeId ? 'active' : '') + '"> \n <a href="' + this.editEntitiesUrl + '/' + id + '">\n ' + colorStatusHtml + '\n ' + iconHtml + '\n <p>' + 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;
//And add the children inside
if (childItems.length > 0) {
var subList = document.createElement('ul');
subList.className = 'sortable';
// console.log('rendered childItems');
// console.log(childItems);
childItems.forEach(function (element) {
subList.appendChild(element);
});
node.appendChild(subList);
}
//and return it
return node;
}
}]);
return SortableController;
}();
var ConfirmationController = function () {
/**
* ConfirmationController constructor
*
* @param selector
* @param headerText
* @param message
* @param confirmText
* @param cancelText
*/
function ConfirmationController(selector, headerText, message, confirmText, cancelText) {
_classCallCheck(this, ConfirmationController);
if (ConfirmationController.selectorValid(selector) === false) {
console.error('The selector "' + selector + '" isn\'t valid. Did not enable the confirmation functionality for that selector');
return;
}
ConfirmationController.headerText = headerText;
ConfirmationController.message = message;
ConfirmationController.confirmText = confirmText;
ConfirmationController.cancelText = cancelText;
ConfirmationController.button = document.querySelector(selector);
ConfirmationController.promptElement = this.createPrompt();
document.body.appendChild(ConfirmationController.promptElement);
this.addListenersToButton(ConfirmationController.button);
this.addListenersToModalButtons();
}
/**
* Adds listeners to the button that the selector from the constructor references to.
* So that the button works
*
* @param button
*/
_createClass(ConfirmationController, [{
key: 'addListenersToButton',
value: function addListenersToButton(button) {
button.addEventListener('click', function (controller) {
return function (clickEvent) {
clickEvent.preventDefault();
controller.showConfirmationPrompt(true);
};
}(this));
}
/**
* Add listeners to the modals buttons.
* Usually the yes no buttons.
*/
}, {
key: 'addListenersToModalButtons',
value: function addListenersToModalButtons() {
if (!ConfirmationController.promptElement) console.error('First create the modal with the createPrompt method');
var confirmButton = ConfirmationController.promptElement.querySelector('button.confirm');
var cancelButton = ConfirmationController.promptElement.querySelector('button.cancel');
var shader = ConfirmationController.promptElement.querySelector('div.shader');
// console.log(confirmButton);
// console.log(cancelButton);
confirmButton.addEventListener('click', this.confirmClicked.bind(this));
cancelButton.addEventListener('click', this.cancelClicked.bind(this));
shader.addEventListener('click', this.cancelClicked.bind(this));
}
}, {
key: 'confirmClicked',
value: function confirmClicked(mouseEvent) {
mouseEvent.preventDefault();
console.log(ConfirmationController.button);
var form = this.findForm(ConfirmationController.button);
console.log(form);
if (form) form.submit();
this.showConfirmationPrompt(false);
}
}, {
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
*/
}, {
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}
*/
}, {
key: 'createPrompt',
value: function createPrompt() {
var html = '<div class="model">' + '<div class="header"><h4>' + ConfirmationController.headerText + '</h4></div>' + '<div class="body">' + '<p class="message">' + ConfirmationController.message + '</p>' + '<div class="buttons">' + '<button class="confirm">' + ConfirmationController.confirmText + '</button>' + '<button class="cancel">' + ConfirmationController.cancelText + '</button>' + '</div>' + '</div>' + '</div>' + '</div>' + '<div class="shader"></div>';
var promptElement = document.createElement("div");
promptElement.setAttribute('id', 'confirmBox');
promptElement.innerHTML = html;
return promptElement;
}
/**
* Show or hides the confirmation prompt by adding a hidden class to it.
*/
}, {
key: 'showConfirmationPrompt',
value: function showConfirmationPrompt(show) {
ConfirmationController.promptElement.classList.add('show');
if (!show) ConfirmationController.promptElement.classList.remove('show');
}
/**
* Returns true when a selector references a button tag or an input tag with type button.
* false if not
*
* @param selector
* @returns {boolean}
*/
}], [{
key: 'selectorValid',
value: function selectorValid(selector) {
var button = document.querySelector(selector);
if (!button) return false;
if (button.tagName !== 'BUTTON' && button.tagName !== 'INPUT') return false;
if (button.tagName === "INPUT") if (button.getAttribute('type').toLowerCase() !== 'button' && button.getAttribute('type').toLowerCase() !== 'submit') return false;
return true;
}
}]);
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 = 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._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) {
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')) {
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')) {
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
* @return {Initializer}
*/
}, {
key: 'bindSelectorToCallback',
value: function bindSelectorToCallback(selector, callback) {
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;
}
this._bindings[selector] = callback;
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;
}();
var TabsController = 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 stringseelc
* @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);
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) {
//Remove trailing / on the left hand side
tabSlug = this.removeLeftHandSlashInSlug(tabSlug);
if (!tabSlug) {
console.error("TabsController: Could not open tab with an empty slug");return;
}
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) {
$('.entity-tabs >ul >li').removeClass('active');
$('.entity-tabs >ul >li a[href="#' + slug + '"]').parent().addClass('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) {
var _this2 = this;
//Remove the active class from all tab content classes
document.querySelectorAll('.' + this.tabContentDivsClassAndIdPrefix).forEach(function (element) {
element.classList.remove(_this2.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);
}
/**
* 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);
return;
}
var tabSlugElement = document.querySelector(this.tabSlugInputId);
if (!tabSlugElement) return;
this.openTab(tabSlugElement.value);
}
}]);
return TabsController;
}();