File: D:/HostingSpaces/SBogers10/douven.komma.pro/wwwroot/js/kms/kms.js
'use strict';
//inject directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('fileUploadController', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) {
console.log('fileuploadcontroller loading');
//The files list
//Todo, extend with the files form the database
$scope.files = [];
//These are the fileIds, used for saving
$scope.dynamic = false;
$scope.uploadSizes = '';
$scope.maxImages;
$scope.errorMsg = {};
$scope.subFolder = false;
$scope.attributeKey = '';
$scope.forModel;
$scope.forModelId;
$scope.canUploadNewImage;
/**
* This function uploads the new files
* @param files
* @param errFiles
* @param maximages
* @param blockId
* @param tabId optional
*/
$scope.uploadFiles = function (files, errFiles, maximages, blockId, tabId) {
console.log("upload for block with blockId: "+blockId+" and tabKey: "+tabId);
console.log("uploadedFiles files:");
console.log(files);
//Get the model and the id where we are uploading images for. Should flow via a service but due to bad design and an upcoming newer angular version does not. todo: improve me
var htmlElement = document.querySelector("[ng-controller=KmsAttributeDynamicController]");
$scope.forModel = htmlElement.dataset.forModel;
$scope.forModelId = htmlElement.dataset.forModelId;
console.log("fileUploadController uploading files For model: "+$scope.forModel+" with id: "+$scope.forModelId+" and blockId: "+blockId);
if (typeof $scope.files == 'undefined') $scope.files = [];
if ($scope.files.length >= maximages) {
console.log("File upload not allowed. "+$scope.files.length+"/"+maximages+" uploaded");
return false;
}
//Set the files
$scope.errFiles = errFiles;
//Foreach each file
angular.forEach(files, function (file) {
//Push to true adds image at the back, false at the front
var push = true;
//Add the placeholder
var loadingImage = {loading: true, thumb_image_url: '/images/kms/loading.gif'};
if (push == true) {
$scope.files.push(loadingImage);
var imageKey = $scope.files.length - 1
} else {
//Add at the front
$scope.files.unshift(loadingImage);
var imageKey = 0
}
//Upload to the server
file.upload = Upload.upload({
url: '/kms/upload',
data: {
//Sent the data and the options
file: file,
uploadSizes: $scope.uploadSizes,
dynamic: $scope.dynamic,
subFolder: $scope.subFolder,
attribute_key: $scope.attributeKey,
forModelName: $scope.forModel,
forModelId: $scope.forModelId,
forBlockId: blockId,
forTabId: tabId
}
});
//After uploading
file.upload.then(function (response) {
$timeout(function () {
// delete $scope.files.loading
console.log(response);
//Set the response data
// file.result = response.data;
// file.result = response.data.uploaded[0];
// console.log("Thumb: "+response.data.uploaded.[0].thumb);
console.log("Image Id: "+response.data.uploaded[0].id);
//Add the file to the current files
$scope.files[imageKey] = response.data.uploaded[0];
console.log("$scope.files["+ imageKey +"] =");
console.log($scope.files[imageKey]);
//Reverse push
//Add the file to the current files
// $scope.files.unshift(file.result);
// $scope.setFileIds()
//Update fileids
var forBlockId = response.data.uploaded[0].forBlockId;
var blocksDataObject = $scope.getBlocksData(); //Defined in the KmsAttributeDynamicController
for(var key in blocksDataObject)
{
console.log("Looping over blocks to find a block with id: "+forBlockId+". Current block id: "+blocksDataObject[key].blockId);
// console.log(blocksDataObject[key]);
if(blocksDataObject.hasOwnProperty(key) === false || blocksDataObject[key].hasOwnProperty("blockId") === false) {
console.warn("Detected block data without a block id. Because of this, it is not possible to upload a file for that block. This does not mean that the uploaded image can be linked to the block your uploading currently for");
continue;
}
if(blocksDataObject[key].blockId == forBlockId) {
console.log("found block with id: "+forBlockId);
var data = [];
if(response.data.uploaded[0].forTabId) {
console.log("Uploaded an image for tab: "+response.data.uploaded[0].forTabId);
data = response.data.uploaded[0].id+"#"+response.data.uploaded[0].forTabId; //imageid#multipleimagesslot
} else {
console.log("Uploaded an image without tab: "+response.data.uploaded[0].id);
data = response.data.uploaded[0].id;
}
var fileIds = [];
if(blocksDataObject[key].hasOwnProperty('fileIds')) {
var length = blocksDataObject[key]['fileIds'].length;
//Determine if it is a single image or multiple images.
blocksDataObject[key]['fileIds'][length] = data;
console.log("Updating fileIds with image id: "+data);
} else {
console.log("Setting fileIds to a new array with image id: "+data);
blocksDataObject[key].fileIds = [data];
}
console.log("files.length = "+blocksDataObject[key].fileIds.length+" maxImages = "+$scope.maxImages);
$scope.canUploadNewImage = blocksDataObject[key].fileIds.length < $scope.maxImages;
}
$scope.setBlocksData(blocksDataObject);
}
});
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
console.error("Error: "+$scope.errorMsg);
}, function (evt) {
file.progress = Math.min(100, parseInt(100.0 *
evt.loaded / evt.total));
});
});
};
$scope.deleteImage = function (index, blockId, tab_key) {
var idToDelete = $scope.files[index].id;
console.log("idToDelete: "+idToDelete+" from blockId: "+blockId);
//Delete the image
$scope.files.splice(index, 1);
console.log("Current image amount: "+$scope.files.length);
//Delete the image id
// $scope.setFileIds()
var blocksDataObject = $scope.getBlocksData(); //Defined in the KmsAttributeDynamicController
for(var blockIndex in blocksDataObject)
{
if(blocksDataObject.hasOwnProperty(blockIndex) === false || blocksDataObject[blockIndex].hasOwnProperty("blockId") === false) {
console.warn("Detected block data without a block id. Because of this, it is not possible to upload a file for that block. This does not mean that the uploaded image can be linked to the block your uploading currently for");
continue;
}
if(blocksDataObject[blockIndex].blockId == blockId) {
console.log("found block with id: " + blockId);
if(blocksDataObject[blockIndex].hasOwnProperty('fileIdsToDelete') === false) blocksDataObject[blockIndex].fileIdsToDelete = [];
console.log("Current files to delete: "+blocksDataObject[blockIndex].fileIdsToDelete);
idToDelete = (typeof tab_key !== 'undefined') ? idToDelete+"#"+tab_key : idToDelete ;
for(var index in blocksDataObject[blockIndex].fileIds)
{
var currentId = blocksDataObject[blockIndex].fileIds[index];
if(typeof tab_key !== 'undefined') {
console.log("Looping over the file ids to find an image with id: "+idToDelete+" for tab: "+tab_key+" ("+currentId+")");
} else {
console.log("Looping over the file ids to find an image with id: "+idToDelete);
}
if(currentId == idToDelete) {
console.log("Image marked to delete on save: "+idToDelete);
blocksDataObject[blockIndex].fileIdsToDelete[blocksDataObject[blockIndex].fileIdsToDelete.length] = idToDelete;
blocksDataObject[blockIndex].fileIds.splice(index, 1);
if(typeof tab_key === 'undefined') {
$scope.canUploadNewImage = blocksDataObject[blockIndex].fileIds.length < $scope.maxImages;
} else {
var count = 0;
for(var index in blocksDataObject[blockIndex].fileIds) {
if(blocksDataObject[blockIndex].fileIds[index].split("#")[1] == tab_key) {
count++;
}
}
$scope.canUploadNewImage = count < $scope.maxImages;
}
break;
}
}
}
}
$scope.setBlocksData(blocksDataObject);
delete $scope.errorMsg.toMany;
};
// $scope.moveImage = function (index, direction) {
// var currentFile = $scope.files[index]
// var currentFileId = $scope.fileIds[index]
//
// if (direction == 'left') {
// $scope.files[index] = $scope.files[index - 1];
// $scope.files[index - 1] = currentFile;
// } else {
// $scope.files[index] = $scope.files[index + 1];
// $scope.files[index + 1] = currentFile;
// }
// $scope.setFileIds()
// };
$scope.initFiles = function (files, maxImages, tab_key) {
console.log("fileupload:initFiles: "+files+" maxImages: "+maxImages);
if(files) {
// //Strip out the image ids that do not belong to the tab we are processing for
var filesToUse = [];
if(typeof tab_key !== 'undefined') {
for(var index in files) {
var split = files[index].split("#");
if(split.length !== 2) continue; //The imageId did not contain a #
if(split[1] != tab_key) {
console.log("fileupload:initFiles: Ignoring image with id: "+split[0]+" because it belongs to tab "+split[1]+" and we are processing images for tab "+tab_key);
} else {
filesToUse[filesToUse.length] = files[index];
}
}
} else {
filesToUse = files;
}
if(filesToUse.length > 0) {
$.ajax({
type: "POST",
url: "/kms/getimagedata",
data: {
fileIds: filesToUse
},
success: function (response) {
console.log("Retrieved files: ");
console.log(response);
// files must look like this files[index].id files[index].sizes
$scope.files = response;
// $scope.setFileIds();
if (typeof maxImages != 'undefined') {
$scope.maxImages = maxImages
}
console.log("files.length < $scope.maxImages: " + filesToUse.length < $scope.maxImages);
$scope.canUploadNewImage = filesToUse.length < $scope.maxImages;
$scope.$apply();
},
error: function () {
console.log("An error occured while retrieving image(s)")
},
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
dataType: 'json'
});
} else {
$scope.maxImages = maxImages;
$scope.canUploadNewImage = true;
$scope.files = [];
}
} else {
$scope.maxImages = maxImages;
$scope.canUploadNewImage = true;
$scope.files = [];
}
};
$scope.updateFiles = function(files) {
console.log("Updated files");
$scope.files = files;
};
// $scope.setFileIds = function () {
// var local_fileIds = [];
//
// angular.forEach($scope.files, function (file) {
// local_fileIds.push(file.id);
// });
//
// console.log("set file ids ($scope.fileIds):");
// console.log(local_fileIds);
// $scope.fileIds = local_fileIds
// };
// $scope.getFileCount = function () {
// return $scope.fileIds.length;
// };
$scope.sortableOptions = {
update: function (e, ui) {
ui.item.sortable.cancel();
var originalIndex = ui.item.sortable.index;
var newIndex = ui.item.sortable.dropindex
var oldImage = $scope.files[originalIndex]
$scope.files[originalIndex] = $scope.files[newIndex]
$scope.files[newIndex] = oldImage
$scope.setFileIds()
}
};
console.log('fileuploadcontroller initialized');
}]);
'use strict';
angular.element(document).ready(function () {
// angular.element(document.querySelector('[ng-controller]')).scope().debug();
// //console.log('page loading completed');
});
angular.module('kms.dynamic-attribute', [])
.controller('KmsAttributeDynamicController', function ($scope, $element, $compile, $timeout) {
//console.log('KmsAttributeDynamicController initializing');
// var htmlElement = document.querySelector("[ng-controller=KmsAttributeDynamicController]");
//console.log("Controller element");
//console.log($($element)[0]);
var htmlElement = $($element)[0];
//For which model and id of the model the dynamic attribute works
$scope.forModel = htmlElement.dataset.forModel;
$scope.forModelId = htmlElement.dataset.forModelId;
//console.log("KmsAttributeDynamicController further initializing For model: "+$scope.forModel+" with id: "+$scope.forModelId);
$scope.key = htmlElement.dataset.key;
//console.log("Controller key: "+$scope.key);
var removeBlocks = function () {
// Remove the block list for re-render
angular.element('#' + $scope.key + '_blocks .inner').remove();
};
// Check if any blocks exists to prevent JSON.parse error
// Default is an empty array
$scope.blocks = [];
var html = $('#' + $scope.key + '_data', $element).html();
//console.log("JSON to parse (in element: #"+$scope.key+"_data): "+html);
if (html != '' && html != '""' && html != undefined) {
$scope.blocks = JSON.parse(html);
} else {
//console.log("Element called: " + "#" + $scope.key + "_data does not have json data set");
}
//console.log('scope blocks');
//console.log($scope.blocks);
$scope.blockSettings = {};
var blockSettingsElement = document.getElementById('blockSettings');
if (blockSettingsElement) {
var block_settings = blockSettingsElement.value;
if (block_settings != '' && block_settings != '""') {
$scope.blockSettings = JSON.parse(block_settings);
}
} else {
//console.log("No blocksettings element");
}
//console.log("blocksettings");
//console.log($scope.blockSettings);
$scope.blockTypes = getBlockTypes($scope.blockSettings);
$scope.addBlock = function (blockType) {
if (!angular.isUndefined(blockType)) {
$scope.blocks.push(angular.copy(blockType));
}
};
$scope.removeBlock = function (block) {
startToMove();
var index = $scope.blocks.indexOf(block);
$scope.blocks.splice(index, 1);
stopToMove()
};
$scope.upBlock = function (block) {
//load index from the block
var index = $scope.blocks.indexOf(block);
//if the index is 0 return
if (index < 1) return;
//Remove the editor
startToMove()
//Set the block index-1 to current index
$scope.blocks[index] = $scope.blocks[index - 1];
//set the triggerd block to index-1
$scope.blocks[index - 1] = block;
//enable the editor
stopToMove()
};
$scope.downBlock = function (block) {
//load the index from the block
var index = $scope.blocks.indexOf(block);
//If the block is the last return false
if (index >= $scope.blocks.length - 1) return;
//Remove the editor
startToMove()
//set the block +1 to the current index
$scope.blocks[index] = $scope.blocks[index + 1];
//set the triggerd block to index +1
$scope.blocks[index + 1] = block;
//enable the editor
stopToMove()
};
$scope.deleteImage = function (direction, block_key, image_key, tab_key) {
if (tab_key == null) {
$scope.blocks[block_key].images.splice(image_key, 1)
} else {
$scope.blocks[block_key].tab[tab_key].images.splice(image_key, 1)
}
}
$scope.deleteFile = function (block_key) {
delete $scope.blocks[block_key].file
}
$scope.changeOrder = function (direction, block_key, image_key, tab_key) {
//load the index from the block
var index = image_key
if (tab_key == null) {
var temp_image = $scope.blocks[block_key].images[image_key];
switch (direction) {
case 'left':
if (index == 0) return;
$scope.blocks[block_key].images[image_key] = $scope.blocks[block_key].images[image_key - 1];
$scope.blocks[block_key].images[image_key - 1] = temp_image;
break;
case 'right' :
if (index + 1 == $scope.blocks[block_key].images.length) return;
$scope.blocks[block_key].images[image_key] = $scope.blocks[block_key].images[image_key + 1];
$scope.blocks[block_key].images[image_key + 1] = temp_image;
break;
}
} else {
var temp_image = $scope.blocks[block_key].tab[tab_key].images[image_key];
switch (direction) {
case 'left':
if (index == 0) return;
$scope.blocks[block_key].tab[tab_key].images[image_key] = $scope.blocks[block_key].tab[tab_key].images[image_key - 1];
$scope.blocks[block_key].tab[tab_key].images[image_key - 1] = temp_image
;
break;
case 'right' :
if (index + 1 == $scope.blocks[block_key].tab[tab_key].images.length) return;
$scope.blocks[block_key].tab[tab_key].images[image_key] = $scope.blocks[block_key].tab[tab_key].images[image_key + 1];
$scope.blocks[block_key].tab[tab_key].images[image_key + 1] = temp_image;
break;
}
}
}
/**
* Add new image tab
* @param block_index
*/
$scope.addImageTab = function (block_index) {
$scope.blocks[block_index].tab.push({title: ''})
var new_index = $scope.blocks[block_index].tab.length
//set the new tab as active (-1) arrays start at 0
$scope.setActiveTab(block_index, new_index - 1)
};
/**
* Remove image tab
* @param block_index
*/
$scope.removeImageTab = function (block_index, tab_index) {
$scope.blocks[block_index].tab.splice(tab_index, 1);
//set the active tab to 0
$scope.setActiveTab(block_index, 0)
};
$scope.setActiveTab = function (index, tab) {
$scope.blocks[index].active_tab = tab;
};
$scope.moveTab = function (direction, index, tab_index) {
var temptab = $scope.blocks[index].tab[tab_index]
switch (direction) {
case 'left':
if (tab_index == 0) return;
$scope.blocks[index].tab[tab_index] = $scope.blocks[index].tab[tab_index - 1]
$scope.blocks[index].tab[tab_index - 1] = temptab
break;
case 'right':
if (tab_index + 1 == $scope.blocks[index].tab.length) return;
$scope.blocks[index].tab[tab_index] = $scope.blocks[index].tab[tab_index + 1]
$scope.blocks[index].tab[tab_index + 1] = temptab
break;
}
};
$scope.setUniqueId = function (block_index) {
if (typeof $scope.blocks[block_index].blockId == 'undefined') {
var stamp = new Date().valueOf();
$scope.blocks[block_index].blockId = Math.round(stamp + Math.random() * 1000);
}
};
$scope.debug = function () {
//console.log('key: ' + $scope.key);
};
//console.log('KmsAttributeDynamicController initialized');
})
.directive('block', function ($compile) {
return {
restrict: 'E',
template: '<div class="header">' +
'<div class="start">' +
'<div class="block-title">{{block.typeName}}</div>' +
'<div class="buttons">' +
'<div class="position-button up" ng-click="upBlock(block)"></div>' +
'<div class="position-button down" ng-click="downBlock(block)"></div>' +
'</div></div>' +
'<div class="end">' +
'<div class="btn btn-danger btn-trash" ng-click="removeBlock(block)"> </div>' +
'</div></div>',
link: function (scope, element, attr) {
switch (scope.block.typeSlug) {
case 'content-block':
// //console.log('rendering a content block with this data: '+scope.block);
scope.imageBoxId = scope.key + '-image-box-' + scope.$index;
scope.textBoxId = scope.key + '-text-box-' + scope.$index;
//console.log("Rendering content-block (afbeelding en tekst)");
//console.log(scope.block);
element.append($compile("<content-block></content-block>")(scope));
break;
case 'view-block':
element.append($compile("<view-block></view-block>")(scope));
break;
case 'page-link-block':
element.append($compile("<page-link-block></page-link-block>")(scope));
break;
case 'image-slider-block':
element.append($compile("<image-slider-block></image-slider-block>")(scope));
break;
case 'video-block':
element.append($compile("<video-block></video-block>")(scope));
break;
case 'file-block':
element.append($compile("<file-block></file-block>")(scope));
break;
case 'two-column-block':
scope.textBoxIds = [];
scope.textBoxIds.left = scope.key + '-text-box-' + scope.$index + '-left';
scope.textBoxIds.right = scope.key + '-text-box-' + scope.$index + '-right';
element.append($compile("<two-column-block></two-column-block>")(scope));
break;
case 'full-text-block':
scope.textBoxId = scope.key + '-text-box-' + scope.$index;
element.append($compile("<full-text-block></full-text-block>")(scope));
break;
case 'full-image-block':
element.append($compile("<full-image-block></full-image-block>")(scope));
break;
case 'multiple-images-block':
element.append($compile("<multiple-images-block></multiple-images-block>")(scope));
break;
}
// //console.log("block data element: "+attr.dataElement);
scope.dataElement = document.querySelector(attr.dataElement);
scope.getBlocksData = function() {
return JSON.parse(scope.dataElement.getAttribute("value"));
};
scope.setBlocksData = function(data) {
scope.dataElement.setAttribute("value", JSON.stringify(data));
}
}
}
})
.directive('contentBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-page-content-block',
link: function (scope, element, attrs) {
scope.image = 'imageHolder';
scope.loopCount = count;
count++;
scope.loopCount = scope.key;
// //console.log("getting content block data");
// //console.log(scope.getBlocksData();
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100);
scope.openBox = function (imageBoxId) {
$.colorbox({
href: '/kms/elfinder/standalonepopup/' + imageBoxId,
fastIframe: true,
iframe: true,
width: '70%',
height: '520px'
});
};
}
};
})
.directive('viewBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-view',
link: function (scope) {
}
};
})
.directive('twoColumnBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/two-column-block',
link: function (scope, element, attrs) {
scope.image = 'imageHolder',
scope.loopCount = count
count++;
scope.loopCount = scope.key
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100)
scope.openBox = function (imageBoxId) {
$.colorbox({
href: '/kms/elfinder/standalonepopup/' + imageBoxId,
fastIframe: true,
iframe: true,
width: '70%',
height: '520px'
});
};
}
};
})
.directive('imageSliderBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-image-slider-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fullImageBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-full-image-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fullTextBlock', function ($timeout) {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-full-text-block',
link: function (scope) {
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100)
}
}
})
.directive('multipleImagesBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-multiple-images-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('pageLinkBlock', function ($http) {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-page-link-block',
link: function (scope, element, attr) {
scope.pages = [];
$http.get('/kms/api/pages').then(function (response) {
// Adopt data
var pages = [];
var getChildren = function (node) {
for (var child in node) {
if (node[child].title) {
pages.push({
value: node[child].id,
label: node[child].title
});
}
getChildren(node[child].children);
}
};
getChildren(response.data);
// Set the data
scope.pages = pages;
});
}
};
})
.directive('videoBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-video-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fileBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-file-block',
link: function (node) {
////console.log(node);
}
}
});
/**
* Callback function for ElFinder (hard to implement in angular?)
*
* @param filePath
* @param requestingElementId
*/
function processSelectedFile(filePath, requestingElementId) {
// Get the scope of the the element that is passed into ElFinder
var elementScope = $('#' + requestingElementId).parent().parent().scope();
elementScope.block.image = filePath;
elementScope.$apply();
}
/**
* This function will initiate the tinyMce editor on an block
* It the initiation will be delayed because of the angular
*
* @param $scope
* @param $timeout
*/
function initiateTinyMceWithDelay($scope, $timeout, delay) {
//if (tinymce) tinymce.remove();
$timeout(function () {
initiateTinyMce($scope, $scope.textBoxId)
if (typeof $scope.textBoxIds !== 'undefined') {
if ($scope.textBoxIds.left != null) initiateTinyMce($scope, $scope.textBoxIds.left, 'description', 'left')
if ($scope.textBoxIds.right != null) initiateTinyMce($scope, $scope.textBoxIds.right, 'description', 'right')
}
}, delay);
}
function initiateTinyMce($scope, field, blockElement, blockSubElement) {
if (typeof field === 'undefined') {
field = $scope.textboxId;
}
if (typeof blockElement === 'undefined') {
blockElement = 'description';
}
//textboxIds[] =
tinymce.init({
elements: field,
mode: 'exact',
skin: 'kms',
menubar: false,
statusbar: false,
plugins: ['code', 'paste', 'link', 'table'],
toolbar: 'styleselect | bold italic underline | bullist numlist table | link | code',
height: '200',
default_link_target: "_blank",
paste_as_text: true,
style_formats: [{title: 'Titel', block: 'h2'}],
link_list: "/kms/file-list?key=komma_kms",
setup: function (editor) {
editor.on('change', function (e) {
editor.save();
if (typeof blockSubElement === 'undefined') {
$scope.block[blockElement] = editor.getContent();
} else {
$scope.block[blockElement][blockSubElement] = editor.getContent();
}
if (!$scope.$$phase) {
$scope.$apply();
}
});
// Update model on keypress
editor.on('KeyUp', function (e) {
editor.save();
if (typeof blockSubElement === 'undefined') {
$scope.block[blockElement] = editor.getContent();
} else {
$scope.block[blockElement][blockSubElement] = editor.getContent();
}
if (!$scope.$$phase) {
$scope.$apply();
}
});
}
});
}
function startToMove() {
$(document).find('textarea').each(function () {
tinyMCE.execCommand('mceFocus', false, $(this).attr('id'));
tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id'));
});
}
function stopToMove() {
//todo: Reanbling ONLY works after a timeout.
setTimeout(function () {
$(document).find('textarea').each(function () {
tinymce.execCommand('mceAddEditor', true, $(this).attr('id'));
});
}, 100);
}
function getBlockTypes(blockSettings) {
var blocks = {
two_column_block: {
typeSlug: 'two-column-block',
code_name: '',
view: '',
typeName: 'Twee tekstkolommen',
status: true,
description: {left: '', right: ''},
textWidth: '50',
},
content_block: {
typeSlug: 'content-block',
code_name: '',
view: '',
typeName: 'Afbeelding en tekst',
status: true,
location: 'left',
textWidth: '66',
description: '',
subFolder: 'dynamic',
image: null,
link: '',
link_text: 'Lees meer',
},
view_block: {
typeSlug: 'view-block',
code_name: '',
view: '',
typeName: 'View',
status: true,
},
full_image_block: {
typeSlug: 'full-image-block',
code_name: '',
view: '',
typeName: '100% Afbeelding',
subFolder: 'dynamic',
max_images: 1,
status: true,
image: null
},
full_text_block: {
typeSlug: 'full-text-block',
code_name: '',
view: '',
typeName: 'Een tekstkolom',
description: '',
status: true,
link: '',
link_text: 'Lees meer'
},
multiple_images_block: {
typeSlug: 'multiple-images-block',
code_name: '',
view: '',
typeName: 'Meerdere afbeeldingen',
tab: [
{
title: 'Afbeelding 1', max_images: 1}, {
title: 'Afbeelding 2a',
max_images: 2
},
{
title: 'Afbeelding 2b (optioneel)',
max_images: 1}
],
status: true,
location: 'left',
subFolder: 'dynamic',
textWidth: '66',
},
page_link_block: {
typeSlug: 'page-link-block',
code_name: '',
view: '',
typeName: 'Page-link block',
link_text: 'Lees meer',
special: 0,
status: true,
pageId: null
},
image_slider_block: {
typeSlug: 'image-slider-block',
code_name: '',
view: '',
typeName: 'Image gallery',
tab: [{title: ''}],
subFolder: 'dynamic',
status: true,
pageId: null
},
video_block: {
typeSlug: 'video-block',
code_name: '',
view: '',
typeName: 'Video',
youtube: '',
status: true,
pageId: null
},
file_block: {
typeSlug: 'file-block',
code_name: '',
view: '',
typeName: 'Bestand block',
title: '',
special: 0,
download_text: 'Download',
file: '',
status: true,
pageId: null
}
};
var blockTypes = {};
angular.forEach(blockSettings, function (block, blockName) {
blockTypes[blockName] = angular.merge(blocks[blockName], block);
});
return blockTypes;
}
'use strict';
var attributes = {};
var app = angular.module('kms', [
'ui.bootstrap',
'ngSanitize',
'kms.dynamic-attribute',
'fileUpload'
// 'ui.select',
// 'ui.date',
// 'ui.tree',
// 'ui.sortable',
// 'ui.utils.masks',
]);
app.controller('KmsEntities',[ '$scope', '$element',function ($scope, $element) {
$scope.entities = [];
$scope.paginatedEntities = [];
$scope.isSorting = false;
$scope.isSearching = function () {
return $scope.searchEntitiesText.text.length > 1;
};
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
var entityList = $('.entities-list-items .entities-list-item', $element);
for (var i = 0; i < entityList.length; i++) {
var dataElement = entityList[i];
var dataObject = {
thumbHtml: $('.entities-item-pre', dataElement).html(),
text: $('.entities-item-text', dataElement).html(),
status: $('.entities-item-status', dataElement).html(),
link: $('a', dataElement).first().attr('href'),
active: $(dataElement).hasClass('active')
};
$scope.entities.push(dataObject);
}
$scope.activateSorting = function () {
$scope.isSorting = true;
};
$scope.deactivateSorting = function () {
$scope.isSorting = false;
};
$scope.$watch("currentPage + numPerPage", function () {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
$scope.paginatedEntities = $scope.entities.slice(begin, end);
});
}]);
app.controller('KmsEntity',[ '$scope', '$element',function ($scope, $element) {
// console.log("attaching save to scope");
$scope.save = function () {
var button = document.querySelector('input[value="submit-cloaked"]');
// console.log(button);
button.click();
};
}]);
app.controller('SortableTree',['$scope', '$http', '$attrs' ,function ($scope, $http, $attrs) {
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
$scope.data = [];
$scope.treeOptions = {
dropped: function (event) {
$http({
method: 'POST',
url: '/kms/api/' + $attrs.siteslug + '/' + $attrs.slug,
data: {"tree": angular.toJson($scope.data)}
}).then(function (response) {
});
}
};
$scope.openLink = function (id) {
window.location = $scope.url + '/' + id;
};
$scope.toggle = function (scope) {
scope.toggle();
};
var getRootNodesScope = function () {
return angular.element(document.getElementById('tree-root').scope());
};
$scope.collapseAll = function () {
var scope = getRootNodesScope();
scope.collapseAll();
};
$scope.expandAll = function () {
var scope = getRootNodesScope();
scope.expandAll();
};
// console.log('/kms/api/' + $attrs.siteslug + '/' + $attrs.slug);
$http.get('/kms/api/' + $attrs.siteslug + '/' + $attrs.slug)
.then(function (response) {
// console.log("Kms.js SortableTree controller got data from '"+'/kms/api/' + $attrs.siteslug + '/' + $attrs.slug+"':");
// console.log(angular.toJson(response.data));
$scope.data = response.data[0]['children'];
});
}]);
app.controller('KmsAttributes',['$scope', function ($scope) {
$scope.attributes = attributes;
// console.log("$scope.attributes (via KmsAttributes)");
// console.log($scope.attributes);
// $scope.createModelString = function (modelKey, $itemKey) {
//
//
// //Check if the modelKey is an array structure (field_name[x])
// var match = modelKey.match(/(.*)\[(.*)]/)
//
// //There is no match, so a basic key
// if (match == null) {
// //SEt the modelKey as modelString
// $scope.modelString = modelKey
// //ItemKy is null
// $scope.itemKey = null;
// //return and exit
// return $scope.modelString
// }
//
// //Set the match[2] as the itemKey
// var itemCounter = match[2];
//
// //If the itemKey is not undefined and itemkey is the string itemKey
// if (typeof($itemKey) != "undefined" && match[2] == 'itemKey') {
// //Set the $itemKey to the scope itemKey
// itemCounter = $itemKey
// }
// //Glue the parts together
// var modelString = match[1] + '_' + itemCounter
//
// //Set to the global modelString
// $scope.modelString = modelString;
// $scope.itemCounter = itemCounter;
//
// //Return the string
// return modelString
// }
/**
* Wrapper for Json.parse()
* So i can parse Json in blade
* @param value
*/
$scope.parse = function (value) {
//If there is no value return;
if (!value) {
console.log("Parse without a value");
return;
}
//If it is already an object, return the value
if (typeof(value) == 'object') {
console.log("Parse value: "+value);
return value;
}
//Parse the string to a Json object
console.log("Parse string to Json: "+JSON.parse(value));
return JSON.parse(value);
};
$scope.convertToSlug = function (str) {
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
var from = "ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;";
var to = "aaaaaeeeeeiiiiooooouuuunc------";
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
};
}]);
app.controller('KmsAttributeTextFieldController',['$scope', '$element' ,function ($scope, $element) {
$scope.attributes = attributes;
}]);
app.controller('KmsAttributePasswordController',['$scope', '$element' ,function ($scope, $element) {
$scope.attributes = attributes;
//Set enabler true (show enable bottom)
$scope.enabler = true
//Change the password field to a hidden field
$('.passwordField', $element).attr('type', 'hidden')
//Method called when clikced on the enable button
$scope.enablePasswordField = function () {
//Change the type of the password field to password
$('.passwordField', $element).attr('type', 'password')
//Disable the enable buttom
$scope.enabler = false
}
}]);
app.controller('KmsAttributeDateController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
$('input[ui-date]', $element).datepicker({
changeYear: true,
changeMonth: true,
firstDay: 1,
dateFormat: 'dd-mm-yy',
});
}]);
app.controller('KmsAttributeCurrencyFieldController',['$scope', '$element','$locale', function ($scope, $element, $locale) {
$scope.attributes = attributes;
$scope.initCurrencyField = function (dec_value, currency) {
if (typeof(currency) !== 'undefined') {
//Set currencySymbol
$locale.NUMBER_FORMATS.CURRENCY_SYM = currency;
}
//Set the value an de dec_value
attributes[$scope.modelString] = {"value": dec_value / 100, "dec_value": dec_value}
$scope.$watch('attributes.' + $scope.modelString + '.value', function (newValue, oldValue) {
if (newValue === oldValue) return;
attributes[$scope.modelString].dec_value = newValue * 100
});
}
}]);
app.controller('KmsAttributePercentageFieldController',['$scope', '$element','$timeout', function ($scope, $element, $timeout) {
$scope.attributes = attributes;
}]);
app.controller('KmsAttributeTextFieldCurrencyController',['$scope', '$element','$timeout', function ($scope, $element, $timeout) {
$scope.attributes = attributes;
var id = $('input', $element).attr('id'); // get the id of the hidden field
var taxSelectFieldId = $('[data-kms-tax-field]', $element).attr('data-kms-tax-field');
var taxFieldId = id + '_tax';
var noTaxFieldId = id + '_no_tax';
var parseCurrencyStringToCents = function (currencyString) {
var val = currencyString.toString();
val = val.replace(/[.]/g, '');
val = val.replace(/[,]/g, '.');
val = val.replace(/[^0-9\.]+/g, '');
val = val * 100;
return val;
};
var addTax = function (value) {
if (attributes[taxSelectFieldId].selected) {
var factor = 1 + (attributes[taxSelectFieldId].selected.fullValue.rate / 100);
return value * factor;
}
};
var subtractTax = function (value) {
if (attributes[taxSelectFieldId].selected) {
var factor = 1 + (attributes[taxSelectFieldId].selected.fullValue.rate / 100);
return value / factor;
}
};
$scope.$watch('attributes.' + taxFieldId, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.attributes[id] = parseCurrencyStringToCents(newValue);
}
});
$scope.$watch('attributes.' + noTaxFieldId, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.attributes[id] = Math.round(addTax(parseCurrencyStringToCents(newValue)));
}
});
var watcherMain = function (newValue, oldVal) {
if ($.isNumeric($scope.attributes[id])) {
var $taxField = $('#' + taxFieldId);
var $noTaxField = $('#' + noTaxFieldId);
if (!$taxField.is(':focus')) {
$taxField.val($scope.attributes[id] / 100)
}
if (!$noTaxField.is(':focus')) {
$noTaxField.val(Math.round(subtractTax($scope.attributes[id])) / 100)
}
}
};
// Clear fields on blur if no value
$('#' + taxFieldId + ', #' + noTaxFieldId).blur(function (e) {
if ($scope.attributes[id] == 0) {
$('#' + taxFieldId + ', #' + noTaxFieldId).val(0);
}
});
$scope.$watch('attributes.' + id, watcherMain);
$scope.$watch('attributes.' + taxSelectFieldId + '.selected', watcherMain);
$scope.attributes[id] = $('input', $element).attr('value'); // get the value of the hidden field
$timeout(watcherMain, 500);
}]);
app.controller('KmsAttributeMultiSelectController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
$scope.selectData = [];
/**
* Initialize the choices for ui-select
*
* @param choices | string
* @returns {*}
*/
$scope.initChoices = function (choices) {
//If there are no choices;
if (!choices) return;
//If it is not already an object, make one from json
if (typeof(choices) != 'object') choices = JSON.parse(choices)
//Set the choices to the selectData
$scope.selectData = choices
return choices
};
$scope.setValue = function (values) {
if (!values) return;
// compare "values" array and "$scope.selectData" array
var result = [];
for (var i = 0; i < $scope.selectData.length; i++) {
var valueInElement = false;
for (var j = 0; j < values.length; j++) {
if ($scope.selectData[i].value == values[j]) {
valueInElement = true;
break;
}
}
if (valueInElement) result.push($scope.selectData[i]);
}
// $scope.attributes[id] = angular.toJson(values);
// $scope.attributes[id + '_select'] = result;
return result;
};
}]);
app.controller('KmsAttributeSlugController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes
//Get the slugField key
var slugField = $('[data-kms-slug-field]', $element).attr('data-kms-slug-field');
//Change [[itemCounter]] with the $scope.itemCounter (fieldGroup)
slugField = slugField.replace('[[itemCounter]]', $scope.itemCounter);
//Watch the slugField
$scope.$watch('attributes.' + slugField, function (newValue, oldValue) {
//When nothing changes, do nothing
if (newValue === oldValue) return;
//Set the string converted to Slug to the field
attributes[$scope.modelString] = $scope.convertToSlug(attributes[slugField]);
});
}]);
app.controller('KmsAttributeFieldGroupController', ['$scope', '$element',function ($scope, $element) {
$scope.number = 0;
$scope.itemKey = 1;
$scope.maxFieldGroups = null;
$scope.getNumber = function () {
return new Array($scope.number);
}
$scope.addRow = function () {
if ($scope.itemKey >= $scope.maxFieldGroups) return
//Add one to the itemKey
$scope.itemKey++;
//Addd one to the numbers
$scope.number++;
}
/**
* Initialize the given values
* @param itemKey | Amount (-1) of items fieldGroups
* @param maxFieldGroups | max fieldgroups
*/
$scope.init = function (itemKey, maxFieldGroups) {
//Items in the fieldGroup
$scope.itemKey = itemKey
//Max items in fieldGroup
$scope.maxFieldGroups = maxFieldGroups
//If it is a new, add a row
if (itemKey == 0) $scope.addRow();
}
}]);
app.controller('KmsAttributeRouteController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
var id = $('input', $element).attr('id');
attributes[id] = $('input', $element).attr('value');
//Get the name of the slugField
var slugField = $('[data-kms-slug-field]', $element).attr('data-kms-slug-field');
//Change [[itemCounter]] with the $scope.itemCounter (fieldGroup)
slugField = slugField.replace('[[itemCounter]]', $scope.itemCounter)
//Get the name of the parentField
var parentField = $('[data-kms-parent-field]', $element).attr('data-kms-parent-field');
//Set the languageId
var languageId = $('[data-kms-language-id]', $element).attr('data-kms-language-id');
//Set the structure
var structure = $('[data-kms-structure]', $element).attr('data-kms-structure');
/**
* This function generates the correct Route
* when a watched field is changed
*
* @param newValue
* @param oldValue
*/
var parseStructure = function (newValue, oldValue) {
//If the value is the same, stop and return
if (newValue === oldValue) return;
//Create an empty parentSlug string
var parentSlug = '';
// todo: make this work
//Check if the parentField exist in then attributes
if (attributes[parentField]) {
//Check if the parentField has an selected.routes object
if (!attributes[parentField].selected.routes) {
//If not trow an error
console.error('Routes are not set in the ' + parentField + 'field')
}
//Set the routes for the current language to parentSlug
parentSlug = attributes[parentField].selected.routes[languageId];
}
//Create newRoute
var newRoute = '';
//Replace {{parentSlugs}} with the parentSlug
newRoute = structure.replace('{{parentSlugs}}', parentSlug);
//Replace {{slug}} with the Slug
newRoute = newRoute.replace('{{slug}}', $scope.convertToSlug(newValue));
//Set the new route to the attribute of the current $scope.modelString
attributes[$scope.modelString] = newRoute
};
//Set a watch on the slugField, and execute the parseStructure method on a change
$scope.$watch('attributes.' + slugField, parseStructure);
//If there is an parentField
if (attributes[parentField]) {
//Set a watch on the parentField, and execute the parseStructure method on a change
$scope.$watch('attributes.' + parentField + '.selected', parseStructure);
}
}]);
app.controller('KmsAttributeSorterController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
}]);
app.filter("multiWordFilter", ['$filter', function ($filter) {
console.log('hoi');
return function (inputArray, searchText) {
var wordArray = searchText ? searchText.toLowerCase().split(/\s+/) : [];
var wordCount = wordArray.length;
for (var i = 0; i < wordCount; i++) {
// Hack for filtering on text parameter: {text:wordArray[i]}
inputArray = $filter('filter')(inputArray, {text: wordArray[i]});
}
return inputArray;
}
}]);
app.filter('valuesToArrayFilter', function () {
return function (input) {
if (!input) return;
var output = input.map(function (a) {
return a.value;
});
return output
}
});
/**
* AngularJS default filter with the following expression:
* "person in people | filter: {name: $select.search, age: $select.search}"
* performs a AND between 'name: $select.search' and 'age: $select.search'.
* We want to perform a OR.
*/
app.filter('propsFilter', function () {
return function (items, props) {
var out = [];
if (angular.isArray(items)) {
items.forEach(function (item) {
var itemMatches = false;
var keys = Object.keys(props);
for (var i = 0; i < keys.length; i++) {
var prop = keys[i];
var text = props[prop].toLowerCase();
if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
itemMatches = true;
break;
}
}
if (itemMatches) {
out.push(item);
}
});
} else {
// Let the output be the input untouched
out = items;
}
return out;
};
});
var IsJsonString = function (str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
};
$(function(){
var activeListItem = $('#sidebar .navigation li.active');
var activeListItemParents = activeListItem.parents();
var activeListItemParentsLength = activeListItemParents.length;
for(var i = 0; i <= activeListItemParentsLength; i++){
var node = activeListItemParents[i];
if(node && node.tagName == 'LI' && node.classList.contains('has-sub-items')){
node.classList.add('active');
}
}
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 () {
this.classList.toggle('active');
});
}
});
(function ($) {
// Save button
// $('#save-button').click(function (e) {
// $('#entity-form').find('input, textarea').attr('disabled', false);
// $('#entity-form').submit();
// });
// let saveButton = document.querySelector('#save-button');
// if(saveButton)
// {
// saveButton.addEventListener('click', function() {
// if(!saveButton.classList.contains('disabled')) {
// let entityForm = document.querySelector('#entity-form');
// if(entityForm) {
// let inputsAndTextAreas = entityForm.querySelector('input, textarea');
// inputsAndTextAreas.forEach(function(element) {
// element.setActive('disabled', false);
// });
// } else {
// console.error('There is not #entity-form element present that should contain the form that this button tries to save');
// }
// } else {
// console.log('not saving because the save button is disabled')
// }
// })
// }
// TinyMCE
tinymce.init({
selector: 'textarea.tiny-mce',
skin: 'kms',
menubar: false,
statusbar: false,
plugins: ['code', 'paste', 'link'],
toolbar: 'styleselect | bold italic underline | bullist numlist | link image | code',
height: '200',
paste_as_text: true,
style_formats: [{title: 'Titel', block: 'h2'}]
});
// 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');
var $activeListItem = $('.entities-list-item.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 () {
$('#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(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() {
}
});
/* ajax form submission */
jQuery('#dd-form').bind('submit',function(e) {
if(e) e.preventDefault();
fnSubmit(true);
});
})(jQuery);
function toggleOnOff($id) {
let onOffBox = document.getElementById($id);
let onOffSwitch = document.getElementById($id + '-switch');
if(onOffBox.checked){
onOffBox.value = '1';
onOffSwitch.classList.add('on');
}
else{
onOffBox.value = '0';
onOffSwitch.classList.remove('on');
}
}
function toggleOnOffSwitch($id) {
let onOffBox = document.getElementById($id);
onOffBox.checked = (onOffBox.checked) ? onOffBox.checked = false : onOffBox.checked = true;
toggleOnOff($id);
}
class PasswordController {
/**
* Validates password fields.
* The password fields values wil be imploded with a pipe symbol
*
* @param firstPasswordInputSelector The css selector for selecting the first password input field
* @param secondPasswordInputSelector The css selector for selecting the second password input field
* @param wrapperSelector The css selector for the wrapper div / element that wraps the first and second input fields along the validation helper
* @param passwordDontMatchErrorText string
* @param saveButtonId The id of a save button that needs to be disabled when the passwords are not valid
* @param minPasswordLength int
* @param wrapperHasTitleAttributeAndErrorClass Must be set to true if the wrapper (selected with the wrapper selector) has the ability to have an error class and title attribute for displaying an error
*/
constructor(firstPasswordInputSelector, secondPasswordInputSelector, wrapperSelector, passwordDontMatchErrorText, saveButtonId, minPasswordLength = 6, wrapperHasTitleAttributeAndErrorClass = false) {
this.wrapperHasTitleAttributeAndErrorClass = wrapperHasTitleAttributeAndErrorClass;
this.wrapper = document.querySelector(wrapperSelector);
this.saveButton = document.querySelector("#"+saveButtonId);
console.log(this.saveButton);
this.firstPasswordInput = this.wrapper.querySelector(firstPasswordInputSelector);
this.secondPasswordInput = this.wrapper.querySelector(secondPasswordInputSelector);
this.validationMessageWrapperSelector = '.validationHelper';
this.validationMessageWrapper = this.wrapper.querySelector(this.validationMessageWrapperSelector);
this.minPasswordLength = minPasswordLength;
//Used for validation
this.letter = this.validationMessageWrapper.querySelector(".letter");
this.capital = this.validationMessageWrapper.querySelector(".capital");
this.number = this.validationMessageWrapper.querySelector(".number");
this.length = this.validationMessageWrapper.querySelector(".length");
this.match = this.validationMessageWrapper.querySelector(".match");
// this.noPipe = document.querySelector(this.validationMessageWrapperSelector + " .noPipe");
this.activateListeners(true);
}
/**
* Activates listening for keyup events on the password fields to that the passwordChanged method is triggered
*
* @param state
*/
activateListeners(state) {
let self = this;
let validationHelper = this.validationMessageWrapper;
if (state) {
this.firstPasswordInput.addEventListener('keyup',
self.debounce(function () {
self.passwordChanged()
}, 100)
);
this.secondPasswordInput.addEventListener('keyup',
self.debounce(function () {
self.passwordChanged()
}, 100)
);
this.firstPasswordInput.addEventListener('focus', function () {
if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active')
});
this.secondPasswordInput.addEventListener('focus', function () {
if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active')
});
this.firstPasswordInput.addEventListener('blur', function () {
if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active')
});
this.secondPasswordInput.addEventListener('blur', function () {
if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active')
});
} else {
this.firstPasswordInput.removeEventListener('keyup', self.debounce);
this.secondPasswordInput.removeEventListener('keyup', self.debounce);
this.firstPasswordInput.removeEventListener('focus');
this.secondPasswordInput.removeEventListener('focus');
this.firstPasswordInput.removeEventListener('blur');
this.secondPasswordInput.removeEventListener('blur');
}
}
/**
* Triggered when one of the password fields are changed and if the listeners for those fields are active
*/
passwordChanged() {
console.log('password changed');
let value1 = this.firstPasswordInput.value;
let value2 = this.secondPasswordInput.value;
let valid = (this.validate(value1, value2));
console.log(valid);
this.enableValidMessage(valid);
this.enableSaveButton(valid);
this.removeWrapperError();
}
enableValidMessage(enable)
{
let validationHelper = this.validationMessageWrapper;
if(enable) {
if(!validationHelper.classList.contains('valid')) validationHelper.classList.add('valid');
} else {
if(validationHelper.classList.contains('valid')) validationHelper.classList.remove('valid');
}
}
enableSaveButton(enable)
{
console.log(enable);
if(enable) {
if(this.saveButton.classList.contains('disabled')) this.saveButton.classList.remove('disabled');
} else {
if(!this.saveButton.classList.contains('disabled')) this.saveButton.classList.add('disabled');
}
}
/**
* Removes the error that may be set on the wrapper
*/
removeWrapperError()
{
if(!this.wrapperHasTitleAttributeAndErrorClass) return;
if(this.wrapper.hasAttribute('title')) this.wrapper.setAttribute('title', '');
if(this.wrapper.classList.contains('error')) this.wrapper.classList.remove('error');
}
/**
* Validate two values and return true or false if respectively valid or not
*
* @param value1
* @param value2
* @returns {boolean}
*/
validate(value1, value2)
{
let valid = true;
// Validate lowercase letters
let lowerCaseLetters = /[a-z]/g;
if(value1.match(lowerCaseLetters)) {
this.letter.classList.remove("invalid");
this.letter.classList.add("valid");
} else {
this.letter.classList.remove("valid");
this.letter.classList.add("invalid");
valid = false;
}
// Validate capital letters
let upperCaseLetters = /[A-Z]/g;
if(value1.match(upperCaseLetters)) {
this.capital.classList.remove("invalid");
this.capital.classList.add("valid");
} else {
this.capital.classList.remove("valid");
this.capital.classList.add("invalid");
valid = false;
}
// Validate numbers
let numbers = /[0-9]/g;
if(value1.match(numbers)) {
this.number.classList.remove("invalid");
this.number.classList.add("valid");
} else {
this.number.classList.remove("valid");
this.number.classList.add("invalid");
valid = false;
}
// Validate numbers
// let pipe = /\|/g;
// if(value1.match(pipe)) {
// this.noPipe.classList.remove("valid");
// this.noPipe.classList.add("invalid");
// valid = false;
// } else {
// this.noPipe.classList.remove("invalid");
// this.noPipe.classList.add("valid");
// }
// Validate length
if(value1.length >= this.minPasswordLength) {
this.length.classList.remove("invalid");
this.length.classList.add("valid");
} else {
this.length.classList.remove("valid");
this.length.classList.add("invalid");
valid = false;
}
// Validate password match
if(value1 === value2 && (value1 !== '' || value2 !== '')) {
this.match.classList.remove("invalid");
this.match.classList.add("valid");
} else {
this.match.classList.remove("valid");
this.match.classList.add("invalid");
valid = false;
}
return valid
}
/**
* Triggers the function after the wait time (milliseconds) has expired and only
* if the function is not triggered again before the wait time expired.
*
* @param func
* @param wait
* @param immediate
* @returns {Function}
*/
debounce(func, wait, immediate) {
let timeout;
return function() {
let context = this, args = arguments;
let later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
}
/**
* 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"
* }]
* }
* ]
* }
* ]
*/
class SearchController {
constructor(sectionId, selector, inputSelector, mainUlId, resultCounterId) {
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);
let 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 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: []
* }
* ]
* }]
* }
*/
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
*/
load() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let 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((response) => {
if (!response.data) {
reject('The sortable did not get any data from the api');
return;
}
// console.log(response);
/** @var Array[] menuItemArrays*/
response.data[0].children.forEach(function (itemObject) {
/** @var array htmlElements **/
let htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach((element) => {
document.querySelector(self.selector).append(element);
})
});
// console.log(document.querySelector(self.selector));
self.initializeSearch();
resolve(document.querySelector(self.selector));
}).catch((error) => {
reject(error);
});
} else if(self.apiUrl === '' && self.dataToLoad !== '') {
// console.log('data children');
// console.log(self.dataToLoad.data[0].children);
self.dataToLoad.data[0].children.forEach(function(itemObject) {
let htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach((element) => {
document.querySelector(self.selector).append(element);
});
});
self.initializeSearch();
resolve(document.querySelector(self.selector));
}
});
}
/**
* Initialize search functionality on the ul this searchable does its job for
*/
initializeSearch() {
let section = this.section;
let input = document.querySelector(this.inputSelector);
let searchUl = document.querySelector(this.selector);
let resultCounter = document.getElementById(this.resultCounterId);
//handles searching
input.addEventListener('keyup', (event) => {
let mainUl = document.getElementById(this.mainUlId);
let resultsCount = 0;
let filterValue = input.value.toLowerCase();
let noSearchValue = (filterValue == '') ? true : false;
let listItems = searchUl.querySelectorAll('li');
let listItemsCount = listItems.length;
// console.log(listItemsCount);
for (let i = 0; i < listItemsCount; i++) {
let itemValue = listItems[i].dataset.title.toLowerCase();
if(itemValue.indexOf(filterValue) > -1 && noSearchValue === false) {
//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}
*/
createHtmlElement(data, items = [], currentTreeLevel = '')
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let self = this;
let activeId = self.activeId;
let id = data.id;
let title = data.title;
let children = data.children;
let status = data.status;
let thumbnail = data.thumbnail;
let statusType = 0;
let breadcrumb = currentTreeLevel ;
let treeBreadcrumb = (currentTreeLevel !== "") ? currentTreeLevel + " | "+title : title ;
// console.log(breadcrumb);
// console.log(data);
//Render all child html items first
let childItems = [];
for(let object of children)
{
childItems.push(this.createHtmlElement(object, items, treeBreadcrumb));
}
switch(status){
//Green
case 'active':
statusType = 1;
break;
//Transparent
default:
break;
}
//Displays a red or green line in front of the item depending on if the status (class) is active or not
let colorStatusHtml = `
<span class="color-status" data-status-type="${statusType}">
</span>
`;
//Displays the items icon thumbnail OR the first character of its title
let iconHtml = `
<div class="icon" ${thumbnail ? `style="background-image: url('${thumbnail}');"` : ''} >
${thumbnail ? '' : `<span>${title ? title.substring(0,1): ``}</span>`}
</div>
`;
//The main item html that has all other items
let listIem = `
<li data-title="${title}" class="${this.listItemClass} ${ id == activeId ? 'active': '' }">
<a href="${this.editEntitiesUrl}/${id}">
${colorStatusHtml}
${iconHtml}
<p data-breadcrumb="${breadcrumb}">${title}</p>
</a>
</li>
`;
//Render it to a real html element
let domParser = new DOMParser();
let 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;
}
}
$( function() {
$(".select-menu").selectmenu({
width: '100%',
create: function ( event, ui ) {
buttonId = this.id + '-button';
menuId = this.id + '-menu';
},
open: function ( event, ui ) {
let button = document.getElementById(this.id + '-button');
let menu = document.getElementById(this.id + '-menu');
menu.style.width = button.offsetWidth + 'px';
menu.parentNode.style.width = button.offsetWidth + 'px';
button.classList.add('dropdown-open');
},
change: function( event, ui ) {
let button = document.getElementById(this.id + '-button');
let buttonText = button.querySelector('.ui-selectmenu-text').innerHTML;
button.querySelector('.ui-selectmenu-text').innerHTML = buttonText.replace(/ /gi, '');
},
close: function ( event, ui) {
let button = document.getElementById(this.id + '-button');
button.classList.remove('dropdown-open');
}
});
});
/**
* 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"
* }]
* }
* ]
* }
* ]
*/
class SortableController {
constructor(selector) {
this.apiUrl = '';
this.editEntitiesUrl = '';
this.initialized = false;
this.selector = selector;
this.disabled = true;
this.listItemClass = 'entities-list-item';
let 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
*/
init(apiUrl, editEntitiesUrl) {
this.apiUrl = apiUrl;
this.editEntitiesUrl = editEntitiesUrl;
let itemsThatHaveSubUl = document.querySelectorAll(this.selector+" li ul");
itemsThatHaveSubUl.forEach((node, index) => {
// console.log('clicked ul');
node.style.display = node.style.display !== 'none' ? 'none' : '';
});
this.initialized = true;
}
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
});
let itemsThatHaveSubUl = document.querySelectorAll(this.selector+" li ul");
itemsThatHaveSubUl.forEach((node, index) => {
// console.log('clicked ul');
// node.style.display = node.style.display !== 'none' ? 'none' : '';
});
}
/**
* Makes the rootUl sortable
*/
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
*/
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
*/
save() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let itemsJson = this.itemsToJson(document.querySelector(this.selector));
itemsJson = JSON.stringify(itemsJson);
let apiJson = {
"tree": itemsJson
};
axios.post(this.apiUrl, apiJson).then((response) => {
// console.log('Successfully stored the sort order to the api: ');
// console.log(response);
}).catch((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.
*/
itemsToJson(htmlElement) {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let jsonArray = [];
let error = false;
//find all child li items
let listItems = htmlElement.querySelectorAll(':scope > li.'+this.listItemClass);
for(let listItem of listItems)
{
let 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;
}
let currentItemJson = JSON.parse(listItem.dataset.json);
let 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
*/
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((response) => {
if (!response.data) {
reject('The sortable did not get any data from the api');
return;
}
/** @var HTMLElement[] menuItems*/
let 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((error) => {
reject(error);
});
});
}
/**
* Removes all children from the ul
*/
clearRootUl()
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let 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}
*/
createHtmlElement(data)
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
self = this;
let activeId = self.activeId;
let id = data.id;
let title = data.title;
let thumbnail = data.thumbnail;
let children = data.children;
let status = data.status;
let routes = data.routes;
let statusType = 0;
//Generate json data representing that element
let routesForElement = {};
for (let routeProperty in routes) {
if(!routes.hasOwnProperty(routeProperty)) continue;
routesForElement[routeProperty] = routes[routeProperty];
}
if(routesForElement == {}) routesForElement = [];
let 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
let childItems = [];
for(let object of children)
{
childItems.push(this.createHtmlElement(object));
}
switch(status){
//Green
case 'active':
statusType = 1;
break;
//Transparent
default:
break;
}
//Displays a red or green line in front of the item depending on if the status (class) is active or not
let colorStatusHtml = `
<span class="color-status" data-status-type="${statusType}">
</span>
`;
//Displays the items icon thumbnail OR the first character of its title
let iconHtml = `
<div class="icon" ${thumbnail ? `style="background-image: url('${thumbnail}');"` : ''} >
${thumbnail ? '' : `<span>${title ? title.substring(0,1): ``}</span>`}
</div>
`;
//The main item html that has all other items
let listIem = `
<li data-json='${elementJson}' class="${this.listItemClass} ${ id == activeId ? 'active': '' }">
<a href="${this.editEntitiesUrl}/${id}">
${colorStatusHtml}
${iconHtml}
${title}
</a>
</li>
`;
//Render it to a real html element
let domParser = new DOMParser();
let document = domParser.parseFromString(listIem, "text/html");
let node = document.body.firstChild;
//And add the children inside
if(childItems.length > 0)
{
let 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;
}
}
class TabsController {
/**
* 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
*/
constructor(tabContentIdAndClassAndPrefix = 'tab', tabButtonGroupSelector = '.tab-buttons', activeClass = 'active', reactToUrlHashChange = false, tabSlugInputSelector = undefined) {
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
*/
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
*/
removeLeftHandSlashInSlug(tabSlug) {
return tabSlug.replace(/^\/(.*)/, '$1');
}
/**
* Updates a usually hidden input field holding the tab slug with a new slug
*
* @param newSlug string
*/
updateTabSlugInput(newSlug)
{
if(this.tabSlugInputId === undefined) return;
let element = document.querySelector(this.tabSlugInputId);
if(element) element.value = newSlug;
}
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
*/
showTabContentForTabWithSlug(slug)
{
//Remove the active class from all tab content classes
document.querySelectorAll('.'+this.tabContentDivsClassAndIdPrefix).forEach((element) => {
element.classList.remove(this.activeClass)
});
//Add the active class to the tab that has the correct slug appended to the IdPrefix
let 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
*/
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)
*/
hashChanged(event)
{
let tabSlug = window.location.hash.substring(1);
if (tabSlug) {
this.openTab(tabSlug);
return;
}
let tabSlugElement = document.querySelector(this.tabSlugInputId);
if(!tabSlugElement) return;
this.openTab(tabSlugElement.value);
}
}