Initial import
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
|
||||
function updateStatus(e) {
|
||||
const url = window.location.search ? window.location.href + '&o=u' : window.location.href + '?o=u';
|
||||
const postData = e.dataset;
|
||||
postData.value = e.checked ? 1 : null;
|
||||
const msgYes = e.dataset.yes;
|
||||
const msgNo = e.dataset.no;
|
||||
const blinkUpdate = one(`#blink_${e.dataset.id}`);
|
||||
//const
|
||||
if (blinkUpdate) {
|
||||
if (e.checked) {
|
||||
if(blinkUpdate.dataset.published) {
|
||||
blinkUpdate.innerHTML = blinkUpdate.dataset.published;
|
||||
}
|
||||
blinkUpdate.classList.remove('blink-green')
|
||||
} else {
|
||||
if(blinkUpdate.dataset.unpublished) {
|
||||
blinkUpdate.innerHTML = blinkUpdate.dataset.unpublished;
|
||||
}
|
||||
blinkUpdate.classList.add('blink-green')
|
||||
}
|
||||
}
|
||||
request({
|
||||
url: url,
|
||||
post: postData,
|
||||
done: data => {
|
||||
if (data.successMsg) {
|
||||
if (e.checked) {
|
||||
flash.success(msgYes, true)
|
||||
} else {
|
||||
flash.success(msgNo, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function symbolInputProtect(e) {
|
||||
e.value = e.value.replace(':', '');
|
||||
e.value = e.value.replace('\'', '');
|
||||
e.value = e.value.replace('\"', '');
|
||||
}
|
||||
|
||||
function searchInCheckList(e) {
|
||||
all('.check-list label').forEach(l => {
|
||||
console.log(l.dataset.name);
|
||||
if (l.dataset.name.indexOf(e.value) !== -1) {
|
||||
l.style.display = 'block';
|
||||
} else {
|
||||
l.style.display = 'none';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setSingleFileImage(e) {
|
||||
const [defaultIcon, add, edit, remove] = [
|
||||
appendElem('i', e.dataset.default, e.parentNode),
|
||||
appendElem('i', 'la la-plus', e),
|
||||
appendElem('i', 'la la-pencil', e),
|
||||
appendElem('i', 'la la-remove', e),
|
||||
];
|
||||
|
||||
let [canvas, img] = [null, null];
|
||||
if (e.dataset.src) {
|
||||
add.addClass('hidden');
|
||||
defaultIcon.addClass('hidden');
|
||||
img = addImg('appear', e.parentNode, e.dataset.src, 'prepend');
|
||||
} else {
|
||||
edit.addClass('hidden');
|
||||
remove.addClass('hidden');
|
||||
}
|
||||
|
||||
if (add) {
|
||||
add.title = e.dataset.add;
|
||||
add.addEventListener('click', function () {
|
||||
chooseSingleFileImage(add, e.dataset.imageName, function (c) {
|
||||
defaultIcon.addClass('hidden');
|
||||
add.addClass('hidden');
|
||||
edit.removeClass('hidden');
|
||||
remove.removeClass('hidden');
|
||||
canvas = c;
|
||||
flash.warning(e.dataset.warning)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (edit) {
|
||||
edit.title = e.dataset.edit;
|
||||
edit.addEventListener('click', function () {
|
||||
chooseSingleFileImage(edit, e.dataset.imageName, function (c) {
|
||||
defaultIcon.addClass('hidden');
|
||||
add.addClass('hidden');
|
||||
edit.removeClass('hidden');
|
||||
remove.removeClass('hidden');
|
||||
canvas = c;
|
||||
flash.warning(e.dataset.warning)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
remove.title = e.dataset.remove;
|
||||
remove.addEventListener('click', function () {
|
||||
modal.confirm(e.dataset.removeMsg, function () {
|
||||
if (canvas) {
|
||||
canvas.remove();
|
||||
}
|
||||
if (img) {
|
||||
img.remove();
|
||||
}
|
||||
defaultIcon.removeClass('hidden');
|
||||
add.removeClass('hidden');
|
||||
edit.addClass('hidden');
|
||||
remove.addClass('hidden');
|
||||
if (e.dataset.src !== '') {
|
||||
//TODO SEARCH IMAGE AND DELETE IT
|
||||
request({
|
||||
url: '/remove-img/',
|
||||
post: {
|
||||
hash: e.dataset.src
|
||||
},
|
||||
done: r => {
|
||||
console.log(r);
|
||||
flash.success(e.dataset.removeReady);
|
||||
}
|
||||
})
|
||||
} else {
|
||||
flash.success(e.dataset.removeReady);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function chooseSingleFileImage(e, name, callback) {
|
||||
const bufferContent = e.innerHTML;
|
||||
const file = document.createElement('input');
|
||||
file.type = 'file';
|
||||
file.accept = 'image/jpeg, image/png';
|
||||
file.click();
|
||||
file.addEventListener('change', function () {
|
||||
const img = document.createElement('img');
|
||||
img.src = URL.createObjectURL(file.files[0]);
|
||||
img.style.height = '100%';
|
||||
img.style.width = 'auto';
|
||||
const cropper = new Cropper(img, {
|
||||
autoCrop: false,
|
||||
aspectRatio: 1,
|
||||
ready() {
|
||||
this.cropper.crop();
|
||||
},
|
||||
});
|
||||
modal.confirm(img,
|
||||
() => {
|
||||
e = e.parentNode.parentNode;
|
||||
const canvas = cropper.getCroppedCanvas();
|
||||
const img = e.querySelector('img');
|
||||
if (img)
|
||||
img.remove();
|
||||
e.prepend(canvas);
|
||||
canvas.toBlob(function (blob) {
|
||||
bufferFiles[name] = new File([blob], name + '.png', {type: 'image/png'});
|
||||
}, 'image/png');
|
||||
callback(canvas);
|
||||
},
|
||||
() => {
|
||||
|
||||
},
|
||||
{
|
||||
width: '50%',
|
||||
height: '70%'
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function setHistoryButton(e) {
|
||||
e.addEventListener('click', function () {
|
||||
all('.history-box', box => {
|
||||
if (e.hasClass('la-history')) {
|
||||
const b = box.parentNode.querySelector('.historyButton');
|
||||
b.switchClasses('la-remove', 'la-history');
|
||||
}
|
||||
box.remove();
|
||||
});
|
||||
|
||||
if (e.hasClass('la-history')) {
|
||||
e.switchClasses('la-history', 'la-remove');
|
||||
const container = e.parentNode;
|
||||
container.style.position = 'relative';
|
||||
const historyBox = one('history-box', container);
|
||||
request({
|
||||
url: '/get-history/',
|
||||
post: {
|
||||
table: e.dataset.table,
|
||||
id: e.dataset.id
|
||||
},
|
||||
done: data => {
|
||||
data.forEach(row => {
|
||||
one('history-row', historyBox).innerHTML = row;
|
||||
});
|
||||
historyBox.addClass('open');
|
||||
|
||||
}
|
||||
})
|
||||
} else {
|
||||
e.switchClasses('la-remove', 'la-history');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function setPositionButton(e) {
|
||||
e.addEventListener('click', function () {
|
||||
all('.position-box', box => {
|
||||
if (e.hasClass('la-stream')) {
|
||||
const b = box.parentNode.querySelector('.positionButton');
|
||||
b.switchClasses('la-remove', 'la-stream');
|
||||
}
|
||||
box.remove();
|
||||
});
|
||||
|
||||
if (e.hasClass('la-stream')) {
|
||||
e.switchClasses('la-stream', 'la-remove');
|
||||
const container = e.parentNode;
|
||||
container.style.position = 'relative';
|
||||
const positionBox = one('position-box', container);
|
||||
request({
|
||||
url: '/get-article-positions/',
|
||||
post: {
|
||||
article_key: e.dataset.articleKey
|
||||
},
|
||||
done: data => {
|
||||
data.forEach(groupRow => {
|
||||
const row = one('position-row', positionBox);
|
||||
var positions = '';
|
||||
groupRow.positions.forEach((p, i) => {
|
||||
const c = p.checked ? 'checked' : '';
|
||||
const cfa = p.checked_fa ? 'class="busy" title="Позицията е заета"' : ''
|
||||
positions += `<label onclick="updateArticlePosition(this)" ${cfa} style="margin-right: 5px"><input ${c} data-id="${p.id}" data-article-key="${e.dataset.articleKey}" type="checkbox"><span>${i + 1}</span></label>`;
|
||||
});
|
||||
|
||||
row.innerHTML = `
|
||||
<div class="group-row">
|
||||
<div class="group-name">${groupRow.group}</div>
|
||||
<div class="group-positions flex-wrap">
|
||||
${positions}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
//console.log(data);
|
||||
positionBox.addClass('open');
|
||||
|
||||
}
|
||||
})
|
||||
} else {
|
||||
e.switchClasses('la-remove', 'la-stream');
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function updateArticlePosition(e) {
|
||||
e.removeClass('busy');
|
||||
const check = e.querySelector('input');
|
||||
|
||||
request({
|
||||
url: '/update-article-position/',
|
||||
post: {
|
||||
id: check.dataset.id,
|
||||
article_key: check.dataset.articleKey,
|
||||
checked: check.checked,
|
||||
},
|
||||
done: r => {
|
||||
flash.success(r.message);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function saveAndBackToList(el) {
|
||||
event.preventDefault();
|
||||
const container = el.parentNode;
|
||||
const back = btoa(container.querySelector('a').href);
|
||||
history.pushState({}, null, window.location.href + `&back=${back}`);
|
||||
container.querySelector('#save').click();
|
||||
}
|
||||
|
||||
function updateLabelRequire(e, id) {
|
||||
if (e.value !== '') {
|
||||
one(`${id}`).addClass('require')
|
||||
} else {
|
||||
one(`${id}`).removeClass('require')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
const mkPicker = {
|
||||
setForAll: selector => {
|
||||
|
||||
const css = `
|
||||
.mkPicker {width: 100%; background: #ccc; height: 300px}
|
||||
.hide {display: none}
|
||||
`;
|
||||
const style = document.createElement('style')
|
||||
style.innerText = css;
|
||||
document.head.appendChild(style)
|
||||
|
||||
const createPicker = (c, o) => {
|
||||
|
||||
}
|
||||
|
||||
const createInline = (e, o) => {
|
||||
const c = document.createElement('div')
|
||||
c.className = 'mkPicker'
|
||||
e.parentNode.appendChild(c)
|
||||
createPicker(c, o)
|
||||
}
|
||||
const create = e => {
|
||||
var o = {}
|
||||
try {
|
||||
o = JSON.parse(e.dataset.mkPicker)
|
||||
} catch (exp) {
|
||||
alert('There is [data-mk-picker] with invalid JSON format')
|
||||
}
|
||||
o.type = o.type || 'popup'
|
||||
if(o.colorContainer)
|
||||
switch (o.type) {
|
||||
case 'inline':
|
||||
createInline(e, o)
|
||||
break;
|
||||
case 'popup':
|
||||
break;
|
||||
case 'modal':
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
document.querySelectorAll(selector).forEach(i => {
|
||||
create(i)
|
||||
});
|
||||
}
|
||||
}
|
||||
mkPicker.setForAll('[data-mk-picker]')
|
||||
@@ -0,0 +1,763 @@
|
||||
var bufferFiles = {};
|
||||
|
||||
//region [PROTOTYPES]
|
||||
Element.prototype.addClass = function (name) {
|
||||
this.classList.add(name)
|
||||
}
|
||||
Element.prototype.removeClass = function (name) {
|
||||
this.classList.remove(name)
|
||||
}
|
||||
Element.prototype.toggleClass = function (name) {
|
||||
if (this.classList.contains(name)) {
|
||||
this.classList.remove(name)
|
||||
} else {
|
||||
this.classList.add(name)
|
||||
}
|
||||
}
|
||||
Element.prototype.switchClasses = function (class1, class2) {
|
||||
if (this.classList.contains(class1)) {
|
||||
console.log(class1, class2);
|
||||
this.classList.remove(class1)
|
||||
this.classList.add(class2)
|
||||
} else if (this.classList.contains(class2)) {
|
||||
this.classList.remove(class2)
|
||||
this.classList.add(class1)
|
||||
}
|
||||
}
|
||||
Element.prototype.hasClass = function (className) {
|
||||
return this.classList.contains(className);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
|
||||
const all = (s, f) => {
|
||||
const elements = document.querySelectorAll(s);
|
||||
if (f) {
|
||||
elements.forEach(e => {
|
||||
f(e);
|
||||
})
|
||||
} else {
|
||||
return elements;
|
||||
}
|
||||
};
|
||||
|
||||
/** @return Element **/
|
||||
const one = (s, c) => {
|
||||
if (c) {
|
||||
let e = document.createElement('div');
|
||||
e.className = s;
|
||||
c.appendChild(e);
|
||||
return e;
|
||||
}
|
||||
return document.querySelector(s);
|
||||
};
|
||||
const appendElem = (t, c, p) => {
|
||||
let e = document.createElement(t);
|
||||
e.className = c + ' appear';
|
||||
p.appendChild(e);
|
||||
setTimeout(t => {
|
||||
e.addClass('visible');
|
||||
}, 10)
|
||||
return e;
|
||||
};
|
||||
|
||||
const prependElem = (t, c, p) => {
|
||||
let e = document.createElement(t);
|
||||
e.className = c;
|
||||
p.prepend(e);
|
||||
setTimeout(t => {
|
||||
e.addClass('visible');
|
||||
}, 10)
|
||||
return e;
|
||||
};
|
||||
|
||||
const addImg = (c, p, src, type) => {
|
||||
let e = document.createElement('img');
|
||||
e.className = c;
|
||||
e.src = '/get-img/' + src;
|
||||
if (type === 'prepend') {
|
||||
p.prepend(e);
|
||||
} else {
|
||||
p.appendChild(e);
|
||||
}
|
||||
e.addEventListener('load', function () {
|
||||
setTimeout(t => {
|
||||
e.addClass('visible');
|
||||
}, 10)
|
||||
});
|
||||
|
||||
return e;
|
||||
};
|
||||
|
||||
|
||||
const request = obj => {
|
||||
let serializer = d => {
|
||||
return Object.entries(d).map(([key, val]) => `${key}=${val}`).join('&');
|
||||
};
|
||||
obj.post = obj.post || '';
|
||||
obj.get = obj.get ? '?' + serializer(obj.get) : '';
|
||||
obj.url = obj.url || window.location.href;
|
||||
obj.dataType = obj.dataType || 'json';
|
||||
obj.done = obj.done || null;
|
||||
const type = obj.post !== '' ? 'post' : 'get';
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(type, obj.url + obj.get);
|
||||
if (!(obj.post instanceof FormData) && type === 'post') {
|
||||
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
|
||||
obj.post = serializer(obj.post);
|
||||
}
|
||||
xhr.upload.onprogress = function (e) {
|
||||
if (e.lengthComputable) {
|
||||
var percentage = Math.round((e.loaded / e.total) * 100);
|
||||
if (typeof obj.progress !== 'undefined') {
|
||||
obj.progress(percentage)
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.onload = () => {
|
||||
//console.log(xhr.responseText)
|
||||
if (typeof obj.done !== 'undefined') {
|
||||
if (obj.dataType === 'json') {
|
||||
try {
|
||||
obj.done(JSON.parse(xhr.responseText));
|
||||
} catch (e) {
|
||||
}
|
||||
} else {
|
||||
obj.done(xhr.responseText);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(obj.post);
|
||||
};
|
||||
|
||||
|
||||
const error = (name, message) => {
|
||||
all('.error-bubble', function (e) {
|
||||
e.remove();
|
||||
});
|
||||
const element = one('[name="' + name + '"]');
|
||||
element.parentNode.classList.add('form-error');
|
||||
element.focus();
|
||||
const elementYOffset = element.parentNode.offsetTop;
|
||||
window.scroll(0, elementYOffset - 100);
|
||||
console.log(elementYOffset);
|
||||
//window.pageYOffset = element.parentNode.offsetTop;
|
||||
const errorMsg = one('error-bubble', element.parentNode);
|
||||
errorMsg.innerText = message;
|
||||
setTimeout(() => {
|
||||
if (errorMsg) {
|
||||
errorMsg.remove();
|
||||
}
|
||||
}, 5000);
|
||||
element.oninput = function () {
|
||||
if (errorMsg) {
|
||||
errorMsg.remove();
|
||||
element.parentNode.classList.remove('form-error');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const errorElement = (element, message) => {
|
||||
all('.error-bubble', function (e) {
|
||||
e.remove();
|
||||
});
|
||||
element.parentNode.classList.add('form-error');
|
||||
element.focus();
|
||||
const elementYOffset = element.parentNode.offsetTop;
|
||||
window.scroll(0, elementYOffset - 100);
|
||||
console.log(elementYOffset);
|
||||
//window.pageYOffset = element.parentNode.offsetTop;
|
||||
const errorMsg = one('error-bubble', element.parentNode);
|
||||
errorMsg.innerText = message;
|
||||
setTimeout(() => {
|
||||
if (errorMsg) {
|
||||
errorMsg.remove();
|
||||
}
|
||||
}, 5000);
|
||||
element.oninput = function () {
|
||||
if (errorMsg) {
|
||||
errorMsg.remove();
|
||||
element.parentNode.classList.remove('form-error');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ready = () => {
|
||||
all('form').forEach(e => {
|
||||
e.onsubmit = () => {
|
||||
if (e.dataset.type !== 'search') {
|
||||
event.preventDefault();
|
||||
|
||||
if (typeof CKEDITOR !== "undefined") {
|
||||
for (let instanceName in CKEDITOR.instances) {
|
||||
CKEDITOR.instances[instanceName].updateElement();
|
||||
}
|
||||
}
|
||||
|
||||
e.addClass('form-wait');
|
||||
const waitingType = !one('.waiting-tape') ? one('waiting-tape', document.body) : null;
|
||||
const formData = new FormData(e);
|
||||
|
||||
|
||||
Object.keys(bufferFiles).forEach(f => {
|
||||
formData.append(f, bufferFiles[f]);
|
||||
});
|
||||
|
||||
request({
|
||||
post: formData,
|
||||
done: data => {
|
||||
console.log(data);
|
||||
if (data.error) {
|
||||
error(data.field, data.message);
|
||||
}
|
||||
if (data.html) {
|
||||
const htmlContent = one('#html_content');
|
||||
htmlContent.innerHTML = data.html;
|
||||
}
|
||||
if (data.redirect) {
|
||||
window.location.href = data.redirect;
|
||||
}
|
||||
if (data.reload) {
|
||||
window.location.reload();
|
||||
}
|
||||
const smartRedirect = function (model_id) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
if (params.get('back')) {
|
||||
window.location.href = atob(params.get('back'));
|
||||
} else {
|
||||
if (params.get('id')) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
window.location.href = `${window.location.href}&id=${model_id}`
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(data, 'test');
|
||||
if (data.smart_redirect && data.model_id) {
|
||||
smartRedirect(data.model_id)
|
||||
} else {
|
||||
e.removeClass('form-wait');
|
||||
if (waitingType)
|
||||
waitingType.remove()
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
event.preventDefault();
|
||||
const formData = new FormData(e);
|
||||
const d = [];
|
||||
for (let key of formData.keys()) {
|
||||
const value = formData.get(key);
|
||||
if (value) {
|
||||
d.push(key + ':' + formData.get(key))
|
||||
}
|
||||
}
|
||||
const queryStr = d.join('|');
|
||||
if (queryStr) {
|
||||
window.location.href = '?q=' + d.join('|');
|
||||
} else {
|
||||
window.location.href = window.location.href.split('?')[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
all('[data-image-name]', e => {
|
||||
setSingleFileImage(e);
|
||||
});
|
||||
all('.historyButton', e => {
|
||||
setHistoryButton(e);
|
||||
})
|
||||
all('.positionButton', e => {
|
||||
setPositionButton(e);
|
||||
})
|
||||
|
||||
dataFormat();
|
||||
|
||||
document.querySelectorAll('[data-pdf]').forEach(pdf => {
|
||||
pdf.classList.add('pointer');
|
||||
pdf.addEventListener('click', () => {
|
||||
modal.modalFrame(pdf.dataset.pdf);
|
||||
})
|
||||
})
|
||||
document.querySelectorAll('[data-model-3d]').forEach(model3d => {
|
||||
model3d.classList.add('pointer');
|
||||
model3d.addEventListener('click', () => {
|
||||
modal.modalFrame(model3d.getAttribute('data-model-3d'));
|
||||
})
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
|
||||
window.getUri = function (query) {
|
||||
if (window.location.search) {
|
||||
return window.location.href + '&' + query;
|
||||
} else {
|
||||
return window.location.href + '?' + query;
|
||||
}
|
||||
}
|
||||
|
||||
window.onload = ready;
|
||||
|
||||
const modal = {};
|
||||
modal.sessionLeft = (msg, time, resetLabel, ev, leftEv) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal', modalBackground);
|
||||
setTimeout(() => {
|
||||
modal.addClass('modal-open');
|
||||
}, 0)
|
||||
const message = one('modal-message ct', modal);
|
||||
if (typeof msg === 'string' || msg instanceof String) {
|
||||
message.innerHTML = msg;
|
||||
} else {
|
||||
message.style.height = 'calc(100% - 40px)';
|
||||
message.appendChild(msg);
|
||||
}
|
||||
const timer = document.createElement('span');
|
||||
timer.innerHTML = time;
|
||||
const sec = document.createElement('span');
|
||||
sec.innerHTML = ' сек.';
|
||||
var timeOut;
|
||||
const timeRun = () => {
|
||||
let ct = parseInt(timer.innerHTML);
|
||||
timeOut = setTimeout(() => {
|
||||
ct--;
|
||||
if (ct === 0) {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(() => {
|
||||
modalBackground.remove();
|
||||
}, 400)
|
||||
setTimeout(() => {
|
||||
modal.remove();
|
||||
}, 1000);
|
||||
if (leftEv) {
|
||||
clearTimeout(timeOut)
|
||||
leftEv();
|
||||
}
|
||||
} else {
|
||||
timer.innerHTML = ct;
|
||||
timeRun();
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
timeRun();
|
||||
|
||||
message.appendChild(timer)
|
||||
message.appendChild(sec)
|
||||
|
||||
const modalButtons = one('modal-buttons ct', modal)
|
||||
const refresh = one('modal-button-confirm btn-ib btn-default', modalButtons)
|
||||
resetLabel = resetLabel || 'Обнови сесията'
|
||||
refresh.innerHTML = '<i class="la la-redo-alt"></i> ' + resetLabel;
|
||||
refresh.addEventListener('click', () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(() => {
|
||||
modalBackground.remove();
|
||||
}, 400)
|
||||
setTimeout(() => {
|
||||
modal.remove();
|
||||
}, 1000);
|
||||
if (ev) {
|
||||
clearTimeout(timeOut)
|
||||
ev();
|
||||
}
|
||||
})
|
||||
}
|
||||
modal.confirm = (msg, yes, no, free, onload) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal', modalBackground);
|
||||
if (free) {
|
||||
Object.keys(free).forEach(key => {
|
||||
modal.style[key] = free[key];
|
||||
})
|
||||
}
|
||||
setTimeout(() => {
|
||||
modal.addClass('modal-open');
|
||||
}, 0)
|
||||
const message = one('modal-message ct', modal);
|
||||
if (typeof msg === 'string' || msg instanceof String) {
|
||||
message.innerHTML = msg;
|
||||
} else {
|
||||
message.style.height = 'calc(100% - 40px)';
|
||||
message.appendChild(msg);
|
||||
}
|
||||
|
||||
const modalButtons = one('modal-buttons ct', modal)
|
||||
const cancel = one('modal-button-cancel btn-ib btn-default', modalButtons)
|
||||
const confirm = one('modal-button-confirm btn-ib btn-default', modalButtons)
|
||||
cancel.innerHTML = '<i class="la la-ban"></i> Отказ';
|
||||
confirm.innerHTML = '<i class="la la-check"></i> Потвърждение';
|
||||
if (typeof onload !== 'undefined') {
|
||||
onload();
|
||||
}
|
||||
addEventListener('keydown', e => {
|
||||
if (e.keyCode === 27) {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(() => {
|
||||
modalBackground.remove();
|
||||
}, 400)
|
||||
if (no) {
|
||||
no();
|
||||
}
|
||||
}
|
||||
})
|
||||
cancel.onclick = () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(() => {
|
||||
modalBackground.remove();
|
||||
}, 400)
|
||||
if (no) {
|
||||
no();
|
||||
}
|
||||
};
|
||||
confirm.onclick = () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(() => {
|
||||
modalBackground.remove();
|
||||
}, 400)
|
||||
setTimeout(() => {
|
||||
modal.remove();
|
||||
}, 1000);
|
||||
if (yes) {
|
||||
yes();
|
||||
}
|
||||
}
|
||||
};
|
||||
modal.player = (videoSrc) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal-video', modalBackground);
|
||||
const closeButton = one('modal-video-close', modal);
|
||||
closeButton.innerHTML = '<i class="la la-times"></i>';
|
||||
setTimeout(function () {
|
||||
modal.addClass('modal-open');
|
||||
}, 100)
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(function () {
|
||||
modalBackground.remove();
|
||||
}, 350)
|
||||
})
|
||||
|
||||
var video = document.createElement('video');
|
||||
video.setAttribute('controls', true)
|
||||
//
|
||||
// First check for native browser HLS support
|
||||
//
|
||||
if (video.canPlayType('application/vnd.apple.mpegurl')) {
|
||||
video.src = videoSrc;
|
||||
//
|
||||
// If no native HLS support, check if HLS.js is supported
|
||||
//
|
||||
alert('application/vnd.apple.mpegurl')
|
||||
|
||||
modal.appendChild(video);
|
||||
video.play();
|
||||
} else if (Hls.isSupported()) {
|
||||
var hls = new Hls();
|
||||
hls.loadSource(videoSrc);
|
||||
hls.attachMedia(video);
|
||||
modal.appendChild(video);
|
||||
video.play();
|
||||
}
|
||||
|
||||
}
|
||||
modal.modalFrame = (src) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal-video', modalBackground);
|
||||
modal.classList.add('frame')
|
||||
const closeButton = one('modal-video-close', modal);
|
||||
closeButton.innerHTML = '<i class="la la-times"></i>';
|
||||
setTimeout(function () {
|
||||
modal.addClass('modal-open');
|
||||
}, 100)
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(function () {
|
||||
modalBackground.remove();
|
||||
}, 350)
|
||||
})
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('style', 'border: 0; width: 100%; height: 100%')
|
||||
iframe.src = src
|
||||
|
||||
modal.appendChild(iframe);
|
||||
}
|
||||
|
||||
|
||||
modal.modalPreview = (src) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal-video', modalBackground);
|
||||
modal.classList.add('modalImg')
|
||||
const closeButton = one('modal-video-close', modal);
|
||||
closeButton.innerHTML = '<i class="la la-times"></i>';
|
||||
setTimeout(function () {
|
||||
modal.addClass('modal-open');
|
||||
}, 100)
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.removeClass('modal-open');
|
||||
setTimeout(function () {
|
||||
modalBackground.remove();
|
||||
}, 350)
|
||||
})
|
||||
|
||||
var img = document.createElement('img');
|
||||
img.setAttribute('style', 'height: 100%; display: block')
|
||||
img.src = src
|
||||
|
||||
modal.appendChild(img);
|
||||
}
|
||||
|
||||
modal.modalFrame = (src) => {
|
||||
const modalBackground = one('modal-background', document.body);
|
||||
const modal = one('modal-video', modalBackground);
|
||||
modal.classList.add('frame')
|
||||
const closeButton = one('modal-video-close', modal);
|
||||
closeButton.innerHTML = '<i class="la la-times"></i>';
|
||||
document.body.classList.add('hideScrollbar');
|
||||
setTimeout(function () {
|
||||
modal.classList.add('modal-open');
|
||||
}, 100)
|
||||
closeButton.addEventListener('click', () => {
|
||||
modal.classList.remove('modal-open');
|
||||
setTimeout(function () {
|
||||
modalBackground.remove();
|
||||
document.body.classList.remove('hideScrollbar');
|
||||
}, 350)
|
||||
})
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('style', 'border: 0; width: 100%; height: 100%')
|
||||
iframe.setAttribute('allow', 'autoplay; fullscreen')
|
||||
iframe.src = src
|
||||
modal.appendChild(iframe);
|
||||
}
|
||||
|
||||
|
||||
const flash = {};
|
||||
flash.success = (msg, noTopScroll) => {
|
||||
if (!one('.flash')) {
|
||||
const flash = one('flash', document.body);
|
||||
flash.innerHTML = msg;
|
||||
setTimeout(() => {
|
||||
flash.addClass('success');
|
||||
}, 10)
|
||||
if (noTopScroll === undefined) {
|
||||
setTimeout(() => {
|
||||
window.scroll(0, 0);
|
||||
}, 100)
|
||||
}
|
||||
setTimeout(() => {
|
||||
all('.flash').forEach(f => {
|
||||
f.removeClass('success');
|
||||
setTimeout(() => {
|
||||
f.remove();
|
||||
}, 500);
|
||||
})
|
||||
}, 4000)
|
||||
}
|
||||
}
|
||||
|
||||
flash.warning = msg => {
|
||||
if (!one('.flash')) {
|
||||
const flash = one('flash', document.body);
|
||||
flash.innerHTML = msg;
|
||||
setTimeout(() => {
|
||||
flash.addClass('warning');
|
||||
}, 10)
|
||||
setTimeout(() => {
|
||||
all('.flash').forEach(f => {
|
||||
f.removeClass('warning');
|
||||
setTimeout(() => {
|
||||
f.remove();
|
||||
}, 500);
|
||||
})
|
||||
}, 7000)
|
||||
}
|
||||
}
|
||||
|
||||
flash.error = msg => {
|
||||
if (!one('.flash')) {
|
||||
const flash = one('flash', document.body);
|
||||
flash.innerHTML = msg;
|
||||
setTimeout(() => {
|
||||
flash.addClass('error');
|
||||
}, 10)
|
||||
setTimeout(() => {
|
||||
all('.flash').forEach(f => {
|
||||
f.removeClass('warning');
|
||||
setTimeout(() => {
|
||||
f.remove();
|
||||
}, 500);
|
||||
})
|
||||
}, 7000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const timeRange = (s, o) => {
|
||||
const createElement = (tag, className, appendTo) => {
|
||||
var element = document.createElement(tag)
|
||||
if (className)
|
||||
element.className = className;
|
||||
if (appendTo)
|
||||
appendTo.appendChild(element);
|
||||
return element;
|
||||
}
|
||||
const generate = e => {
|
||||
o = o || {}
|
||||
const
|
||||
dataset = e.dataset || {},
|
||||
minH = parseInt(o.minH) || parseInt(dataset.minH) || 0,
|
||||
maxH = parseInt(o.maxH) || parseInt(dataset.maxH) || 23,
|
||||
minutesStep = parseInt(o.minutesStep) || parseInt(dataset.minutesStep) || 5,
|
||||
cnt = e.parentNode,
|
||||
hc = createElement('div', 'time-range', cnt),
|
||||
h1 = createElement('select', 'time-select', hc),
|
||||
sh1 = createElement('div', 'sh', hc),
|
||||
m1 = createElement('select', 'time-select', hc),
|
||||
d = createElement('div', 'd', hc),
|
||||
h2 = createElement('select', 'time-select', hc),
|
||||
sh2 = createElement('div', 'sh', hc),
|
||||
m2 = createElement('select', 'time-select', hc),
|
||||
updateValuesFromInput = () => {
|
||||
if (e.value) {
|
||||
let t1 = e.value.split('-')[0].split(':');
|
||||
let t2 = e.value.split('-')[1].split(':');
|
||||
h1.value = parseInt(t1[0])
|
||||
m1.value = parseInt(t1[1])
|
||||
h2.value = parseInt(t2[0])
|
||||
m2.value = parseInt(t2[1])
|
||||
check()
|
||||
}
|
||||
},
|
||||
updateInput = (v) => {
|
||||
e.value = v
|
||||
},
|
||||
check = () => {
|
||||
let
|
||||
vH1 = parseInt(h1.value),
|
||||
vM1 = parseInt(m1.value),
|
||||
vH2 = parseInt(h2.value),
|
||||
vM2 = parseInt(m2.value)
|
||||
if (vH1 === vH2 && vM1 >= vM2)
|
||||
m2.value = (vM1 + minutesStep).toString();
|
||||
if (vH1 > vH2)
|
||||
h2.value = vH1.toString()
|
||||
if (h1.value && m1.value && h2.value && m2.value) {
|
||||
let
|
||||
vH1F = String(parseInt(h1.value)).padStart(2, '0'),
|
||||
vM1F = String(parseInt(m1.value)).padStart(2, '0'),
|
||||
vH2F = String(parseInt(h2.value)).padStart(2, '0'),
|
||||
vM2F = String(parseInt(m2.value)).padStart(2, '0')
|
||||
updateInput(`${vH1F}:${vM1F}-${vH2F}:${vM2F}`)
|
||||
cnt.classList.remove('form-error')
|
||||
} else {
|
||||
updateInput('');
|
||||
}
|
||||
}
|
||||
|
||||
createElement('option', null, h1).setAttribute('disabled', '')
|
||||
createElement('option', null, h2).setAttribute('disabled', '')
|
||||
createElement('option', null, m1).setAttribute('disabled', '')
|
||||
createElement('option', null, m2).setAttribute('disabled', '')
|
||||
for (let i = minH; i <= maxH; i++) {
|
||||
let opt1 = createElement('option', null, h1)
|
||||
opt1.innerHTML = String(i).padStart(2, '0')
|
||||
opt1.setAttribute('value', i)
|
||||
let opt2 = createElement('option', null, h2)
|
||||
opt2.innerHTML = String(i).padStart(2, '0')
|
||||
opt2.setAttribute('value', i)
|
||||
}
|
||||
for (let i = 0; i <= 59; i += minutesStep) {
|
||||
let opt1 = createElement('option', null, m1)
|
||||
opt1.innerHTML = String(i).padStart(2, '0')
|
||||
opt1.setAttribute('value', i)
|
||||
let opt2 = createElement('option', null, m2)
|
||||
opt2.innerHTML = String(i).padStart(2, '0')
|
||||
opt2.setAttribute('value', i);
|
||||
}
|
||||
h1.addEventListener('change', check)
|
||||
m1.addEventListener('change', check)
|
||||
h2.addEventListener('change', check)
|
||||
m2.addEventListener('change', check)
|
||||
updateValuesFromInput();
|
||||
}
|
||||
document.querySelectorAll(s).forEach(e => generate(e));
|
||||
}
|
||||
|
||||
const dataFormat = () => {
|
||||
all('input[data-format]', e => {
|
||||
e.min = e.min || 0;
|
||||
|
||||
const formatNumber = type => {
|
||||
|
||||
var elem = null;
|
||||
|
||||
e.addEventListener('focus', () => {
|
||||
e.select();
|
||||
const elem_offset = document.getSelection().anchorOffset;
|
||||
elem = document.getSelection().anchorNode.childNodes[elem_offset];
|
||||
})
|
||||
e.addEventListener('keydown', () => {
|
||||
let kc = event.keyCode;
|
||||
|
||||
if (e === elem) {
|
||||
e.value = '';
|
||||
elem = null;
|
||||
}
|
||||
switch (true) {
|
||||
case kc > 47 && kc < 58 || kc === 8 || kc === 190:
|
||||
if (kc === 190 && type === 'integer')
|
||||
event.preventDefault();
|
||||
if (kc !== 190 && kc !== 8 && e.value === '0')
|
||||
event.preventDefault()
|
||||
if (kc === 190 && e.value.indexOf('.') > -1)
|
||||
event.preventDefault()
|
||||
if (type === 'money' && kc !== 8 && e.value.charAt(e.value.length - 3) === '.')
|
||||
event.preventDefault();
|
||||
break;
|
||||
case kc === 13:
|
||||
event.preventDefault();
|
||||
blur(type);
|
||||
break;
|
||||
default:
|
||||
event.preventDefault();
|
||||
}
|
||||
})
|
||||
e.addEventListener('blur', () => {
|
||||
blur(type);
|
||||
})
|
||||
e.addEventListener('keyup', (elem) => {
|
||||
//blur(type);
|
||||
|
||||
if (e.dataset.max && type === 'integer') {
|
||||
if (e.value && parseInt(e.value) > parseInt(e.dataset.max)) {
|
||||
e.value = e.dataset.max
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
const blur = (type) => {
|
||||
if (type === 'money' && e.value) {
|
||||
let n = e.value.split('.');
|
||||
if (n[1]) {
|
||||
e.value = n[0] + '.' + String(parseInt(n[1])).padEnd(2, '0')
|
||||
} else {
|
||||
e.value = n[0] + '.00';
|
||||
}
|
||||
}
|
||||
e.blur();
|
||||
}
|
||||
|
||||
|
||||
if (e.dataset && e.dataset.format) {
|
||||
if (['integer', 'double', 'money'].indexOf(e.dataset.format) > -1)
|
||||
formatNumber(e.dataset.format);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
class DocumentsModule {
|
||||
|
||||
|
||||
documentRow() {
|
||||
return `
|
||||
<td>
|
||||
<select oninput="updateInput(this)" data-input="document_type">
|
||||
<option value=""></option>
|
||||
<option value="pptx">Презентация (pptx)</option>
|
||||
<option value="pdf">Документ (pdf)</option>
|
||||
<option value="docx">Документ (docx)</option>
|
||||
<option value="xlsx">Таблица (xlsx)</option>
|
||||
<option value="other">Други</option>
|
||||
</select>
|
||||
</td>
|
||||
<td><input oninput="updateInput(this)" data-input="name"></td>
|
||||
<td><input oninput="updateInput(this)" data-input="name_en"></td>
|
||||
<td class="ct"><button class="btn-ib btn-default" data-upload-field="file_name" onclick="attachAndUploadFile(this)"><i class="la la-file-upload"></i> Качи файл</button></td>
|
||||
<td class="ct"><button class="btn-ib btn-default" data-upload-field="file_name_en" onclick="attachAndUploadFile(this)"><i class="la la-file-upload"></i> Качи файл</button></td>
|
||||
<td><button onclick="deleteDocument(this)" class="btn btn-red btn-ico center"><i class="la la-trash font-20"></i></button></td>
|
||||
`;
|
||||
}
|
||||
|
||||
addDocument(documentKey, modelClass, modelId) {
|
||||
const
|
||||
tbody = document.querySelector('.docs-table tbody'),
|
||||
tr = document.createElement('tr')
|
||||
tr.innerHTML = this.documentRow()
|
||||
|
||||
this.generateRows();
|
||||
|
||||
this.postRequest('/file/document-add/', {documentKey, modelClass, modelId}, r => {
|
||||
console.log(r)
|
||||
if (r.document_id)
|
||||
tr.setAttribute('data-document-id', r.document_id)
|
||||
})
|
||||
if (tbody.querySelector('tr.empty')) {
|
||||
tbody.innerHTML = ''
|
||||
tbody.appendChild(tr)
|
||||
} else {
|
||||
const first = tbody.querySelector('tr:first-child');
|
||||
tbody.insertBefore(tr, first)
|
||||
}
|
||||
}
|
||||
|
||||
postRequest(url, data, done) {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('post', url)
|
||||
xhr.onload = () => {
|
||||
done(xhr.response)
|
||||
}
|
||||
xhr.send(JSON.stringify(data));
|
||||
}
|
||||
|
||||
getRequest(url, data, done) {
|
||||
data = data || {}
|
||||
let query = [];
|
||||
Object.keys(data).forEach(d => {
|
||||
query.push(d + '=' + data[d])
|
||||
})
|
||||
if (query.length > 0) url = `${url}?${query.join('&')}`
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('get', url)
|
||||
xhr.onload = () => {
|
||||
done(xhr.response)
|
||||
}
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
uploadFile(url, data, done) {
|
||||
data = data || {}
|
||||
const formData = new FormData();
|
||||
Object.keys(data).forEach(d => {
|
||||
formData.append(d, data[d]);
|
||||
})
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('post', url)
|
||||
xhr.onload = () => {
|
||||
done(xhr.response)
|
||||
}
|
||||
xhr.send(formData);
|
||||
}
|
||||
|
||||
generateRows() {
|
||||
document.querySelectorAll('[data-input]').forEach(e => {
|
||||
this.updateInput(e)
|
||||
})
|
||||
}
|
||||
|
||||
updateInput(e) {
|
||||
if (e.tagName === 'SELECT') {
|
||||
e.addEventListener('change', () => {
|
||||
let id = e.parentNode.parentNode.dataset.documentId;
|
||||
this.postRequest('/file/document-update/', {id: id, key: e.dataset.input, value: e.value}, done => {
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (e.tagName === 'INPUT') {
|
||||
e.addEventListener('keypress', () => {
|
||||
let id = e.parentNode.parentNode.dataset.documentId;
|
||||
this.postRequest('/file/document-update/', {id: id, key: e.dataset.input, value: e.value}, done => {
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const documentObject = new DocumentsModule();
|
||||
|
||||
function addDocument(e) {
|
||||
documentObject.addDocument(e.dataset.documentKey, e.dataset.modelClass, e.dataset.modelId)
|
||||
}
|
||||
|
||||
function updateInput(e) {
|
||||
documentObject.updateInput(e)
|
||||
}
|
||||
|
||||
function attachAndUploadFile(e) {
|
||||
const fileInput = document.createElement('input')
|
||||
fileInput.setAttribute('type', 'file')
|
||||
fileInput.addEventListener('change', () => {
|
||||
let id = e.parentNode.parentNode.dataset.documentId;
|
||||
let update = e.dataset.uploadField;
|
||||
let file = fileInput.files[0];
|
||||
let tdContainer = e.parentNode;
|
||||
|
||||
documentObject.uploadFile('/file/document-file-upload/', {
|
||||
id,
|
||||
update,
|
||||
file
|
||||
}, data => {
|
||||
console.log(data);
|
||||
if(data.id && data.file_path) {
|
||||
if (update === 'file_name') {
|
||||
tdContainer.innerHTML = `<a href="${data.file_path}" class="download-link"><span class="extension"><i class="la la-download"></i> ${data.extension} сваляне</span></a>
|
||||
<span data-document-id="${id}" data-delete-field="file_name" onclick="deleteUploadedFile(this)" class="delete-link"><span class="extension"><i class="la la-trash"></i> изтриване</span></span>`;
|
||||
} else if (update === 'file_name_en') {
|
||||
tdContainer.innerHTML = `<a href="${data.file_path}" class="download-link"><span class="extension"><i class="la la-download"></i> ${data.extension} сваляне</span></a>
|
||||
<span data-document-id="${id}" data-delete-field="file_name_en" onclick="deleteUploadedFile(this)" class="delete-link"><span class="extension"><i class="la la-trash"></i> изтриване</span></span>`;
|
||||
}
|
||||
flash.success('Файлът е качен');
|
||||
} else {
|
||||
flash.error('Грешка при качване на файла');
|
||||
}
|
||||
})
|
||||
})
|
||||
fileInput.click()
|
||||
}
|
||||
|
||||
function deleteUploadedFile(e) {
|
||||
let td = e.parentNode;
|
||||
let id = e.dataset.documentId;
|
||||
let deleteField = e.dataset.deleteField;
|
||||
modal.confirm('Сигурни ли сте, че искате да изтриете този файл?', function () {
|
||||
documentObject.postRequest('/file/document-file-delete/', {id, deleteField}, done => {
|
||||
td.innerHTML = `<button class="btn-ib btn-default" data-upload-field="${deleteField}" onclick="attachAndUploadFile(this)"><i class="la la-file-upload"></i> Качи файл</button>`
|
||||
flash.success('Файлът е изтрит');
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function deleteDocument(e) {
|
||||
let id = e.parentNode.parentNode.dataset.documentId;
|
||||
console.log(id);
|
||||
if(id) {
|
||||
modal.confirm('Сигурни ли сте, че искате да изтриете този документ?', function () {
|
||||
let tr = e.parentNode.parentNode;
|
||||
documentObject.postRequest('/file/document-delete/', {id}, done => {
|
||||
tr.remove();
|
||||
flash.success('Документът е изтрит');
|
||||
let rows = document.querySelectorAll('.docs-table tbody tr');
|
||||
if(rows.length === 0) {
|
||||
let tbody = document.querySelector('.docs-table tbody');
|
||||
tbody.innerHTML =
|
||||
`<tr class="empty">
|
||||
<td colspan="7" class="ct p20" style="">
|
||||
<strong>Няма добавени документи</strong>
|
||||
</td>
|
||||
</tr>`;
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
documentObject.generateRows()
|
||||
})
|
||||
@@ -0,0 +1,454 @@
|
||||
var newFiles = {};
|
||||
var sl = {};
|
||||
|
||||
function checkAll(el) {
|
||||
const objectKey = el.dataset.objectKey;
|
||||
sl[objectKey] = [];
|
||||
all('#' + objectKey + ' .file-img [data-del]', e => {
|
||||
e.checked = el.checked === true;
|
||||
if (el.checked) {
|
||||
if (e.dataset.del) {
|
||||
sl[objectKey].push(e.dataset.del);
|
||||
}
|
||||
e.parentNode.addClass('checked');
|
||||
} else {
|
||||
e.parentNode.removeClass('checked');
|
||||
}
|
||||
})
|
||||
updateActionButtons(objectKey);
|
||||
}
|
||||
|
||||
function checkSingle(e) {
|
||||
const objectKey = e.dataset.objectKey;
|
||||
sl[objectKey] = sl[objectKey] || [];
|
||||
if (e.checked) {
|
||||
if (e.dataset.del) {
|
||||
sl[objectKey].push(e.dataset.del);
|
||||
}
|
||||
e.parentNode.addClass('checked');
|
||||
} else {
|
||||
one('#' + e.dataset.objectKey + ' .all-files input').checked = false;
|
||||
e.parentNode.removeClass('checked');
|
||||
if (e.dataset.del) {
|
||||
const f = sl[objectKey].indexOf(e.dataset.del);
|
||||
sl[objectKey].splice(f, 1);
|
||||
}
|
||||
}
|
||||
updateActionButtons(objectKey);
|
||||
}
|
||||
|
||||
function updateActionButtons(id) {
|
||||
let checkedCounter = 0;
|
||||
let checkedCounterUploaded = 0;
|
||||
all('#' + id + ' .file-img input', c => {
|
||||
if (c.checked === true)
|
||||
checkedCounter++;
|
||||
|
||||
if (c.checked === true && c.dataset.del)
|
||||
checkedCounterUploaded++;
|
||||
})
|
||||
const btnDelete = one('#' + id + ' .btnDelete');
|
||||
const delCount = one('#' + id + ' .delCount');
|
||||
const btnEdit = one('#' + id + ' .btnEdit');
|
||||
if (btnDelete) {
|
||||
if (checkedCounter > 0) {
|
||||
btnDelete.removeClass('disabled');
|
||||
delCount.innerHTML = '(' + checkedCounter + ')'
|
||||
} else {
|
||||
btnDelete.addClass('disabled');
|
||||
delCount.innerHTML = '(0)'
|
||||
}
|
||||
}
|
||||
if (btnEdit) {
|
||||
if (checkedCounterUploaded === 1) {
|
||||
btnEdit.removeClass('disabled');
|
||||
} else {
|
||||
btnEdit.addClass('disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addFileImage(e) {
|
||||
const max_file_size = one('#' + e.dataset.objectKey + ' .max_file_size').value;
|
||||
const error_message = one('#' + e.dataset.objectKey + ' .error_message').value;
|
||||
const file = document.createElement('input');
|
||||
let filesContainer = one('#' + e.dataset.objectKey + ' .files-container');
|
||||
newFiles[e.dataset.objectKey] = newFiles[e.dataset.objectKey] || [];
|
||||
file.type = 'file';
|
||||
file.name = e.dataset.objectKey
|
||||
file.multiple = !e.dataset.singleFile;
|
||||
//file.accept = 'image/jpeg, image/png'
|
||||
file.click();
|
||||
file.addEventListener('change', function () {
|
||||
var cIndex = 0;
|
||||
for (let i = 0; i < this.files.length; i++) {
|
||||
const f = this.files[i];
|
||||
if (f.size > max_file_size * (1024 * 1024)) {
|
||||
flash.error(error_message);
|
||||
}
|
||||
else if(f.type !== 'image/jpeg' && f.type !== 'image/jpg' && f.type !== 'image/png') {
|
||||
flash.error('Позволените формати са jpeg и png')
|
||||
}
|
||||
else {
|
||||
filesContainer.style.display = 'flex';
|
||||
const fileContainer = one('file-img', filesContainer);
|
||||
const imgId = e.dataset.objectKey + '_' + cIndex;
|
||||
cIndex++;
|
||||
const img = new Image();
|
||||
img.src = URL.createObjectURL(f);
|
||||
img.id = imgId;
|
||||
const check = document.createElement('input')
|
||||
check.type = 'checkbox';
|
||||
check.setAttribute('data-del', '');
|
||||
check.setAttribute('data-id', imgId);
|
||||
check.setAttribute('data-object-key', e.dataset.objectKey)
|
||||
check.addEventListener('click', e => {
|
||||
checkSingle(check);
|
||||
})
|
||||
fileContainer.appendChild(img);
|
||||
fileContainer.appendChild(check);
|
||||
one('crops', fileContainer);
|
||||
const progress = one('progress', fileContainer);
|
||||
img.addEventListener('load', function () {
|
||||
fileContainer.addClass('visible');
|
||||
check.focus();
|
||||
});
|
||||
img.addEventListener('click', () => {
|
||||
editSingleFileImage(img);
|
||||
})
|
||||
orderItem(fileContainer, function (img_id, order_index) {
|
||||
newFiles[img_id][3] = order_index.toString();
|
||||
})
|
||||
newFiles[imgId] = [f, progress, e.dataset.objectKey, fileContainer.dataset.index, check];
|
||||
if (e.dataset.singleFile) {
|
||||
e.addClass('disabled');
|
||||
}
|
||||
}
|
||||
}
|
||||
uploadAllFilesByAttach(e.dataset.objectKey, cIndex, e.dataset.editMode);
|
||||
});
|
||||
}
|
||||
|
||||
function removeFileImage(el) {
|
||||
let checkedCounter = 0;
|
||||
let forDelete = [];
|
||||
all('#' + el.dataset.objectKey + ' .file-img input', c => {
|
||||
if (c.checked === true) {
|
||||
checkedCounter++;
|
||||
forDelete.push(c);
|
||||
}
|
||||
});
|
||||
let message = '';
|
||||
if (checkedCounter > 1) {
|
||||
message = 'Сигурни ли сте, че истате да премахнете тези ' + checkedCounter + ' изображения';
|
||||
} else {
|
||||
message = 'Сигурни ли сте, че искате да премахнете 1 изображение';
|
||||
}
|
||||
modal.confirm(message, function () {
|
||||
forDelete.forEach(fd => {
|
||||
fd.parentNode.remove();
|
||||
delete newFiles[fd.dataset.id];
|
||||
});
|
||||
el.querySelector('.delCount').innerHTML = '(0)';
|
||||
el.addClass('disabled');
|
||||
one('#' + el.dataset.objectKey + ' .btnEdit').addClass('disabled');
|
||||
if (sl[el.dataset.objectKey].length > 0) {
|
||||
let del_ids = JSON.stringify(sl[el.dataset.objectKey]);
|
||||
request({
|
||||
url: '/delete-file-cms/',
|
||||
post: {
|
||||
del_ids: del_ids
|
||||
},
|
||||
done: r => {
|
||||
if (r.success) {
|
||||
flash.success(r.success, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
const addButton = one('#' + el.dataset.objectKey + ' [data-single-file]');
|
||||
if (all('#' + el.dataset.objectKey + ' .file-img input').length === 0) {
|
||||
one('#' + el.dataset.objectKey + ' .files-container').style.display = 'none';
|
||||
if (addButton.dataset.singleFile) {
|
||||
addButton.removeClass('disabled');
|
||||
}
|
||||
}
|
||||
one('#' + el.dataset.objectKey + ' .all-files input').checked = false;
|
||||
})
|
||||
}
|
||||
|
||||
function uploadAllFilesByAttach(object_key, for_upload, editMode) {
|
||||
|
||||
console.log(newFiles);
|
||||
const
|
||||
newFilesIds = Object.keys(newFiles),
|
||||
media_key = one('[name="media_key"]').value,
|
||||
model_class = one('[name="model_class"]').value,
|
||||
model_id = one('[name="model_id"]').values
|
||||
|
||||
if (newFilesIds.length > 0) {
|
||||
var cc = 0;
|
||||
all(`#${object_key} .file-img`).forEach(e => {
|
||||
e.addClass('no-events');
|
||||
})
|
||||
newFilesIds.forEach(id => {
|
||||
if (newFiles[id][5] === undefined) {
|
||||
const f = newFiles[id][0];
|
||||
const progressBar = newFiles[id][1];
|
||||
const object_key = newFiles[id][2];
|
||||
const order_index = newFiles[id][3];
|
||||
const check = newFiles[id][4];
|
||||
let formData = new FormData();
|
||||
console.log(object_key);
|
||||
formData.append('media_key', media_key);
|
||||
formData.append('model_class', model_class);
|
||||
formData.append('object_key', object_key);
|
||||
formData.append('order_index', order_index);
|
||||
formData.append('model_id', model_id);
|
||||
formData.append(id, f);
|
||||
if (editMode) {
|
||||
formData.append('has_article', 1)
|
||||
}
|
||||
request({
|
||||
url: '/upload-file-cms/',
|
||||
post: formData,
|
||||
progress: percentage => {
|
||||
if (progressBar) {
|
||||
progressBar.style.width = percentage + '%'
|
||||
if (percentage < 100) {
|
||||
progressBar.parentNode.addClass('disabled')
|
||||
} else {
|
||||
if (progressBar) {
|
||||
if (progressBar.parentNode)
|
||||
progressBar.parentNode.removeClass('disabled')
|
||||
progressBar.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
done: data => {
|
||||
check.setAttribute('data-del', data.id);
|
||||
check.setAttribute('data-file-type', data.fileType)
|
||||
check.setAttribute('data-src-raw', data.srcRaw)
|
||||
check.setAttribute('data-media-key', data.mediaKey)
|
||||
check.setAttribute('data-file-name', data.fileName)
|
||||
check.setAttribute('data-crop-files', '[]')
|
||||
newFiles[id][5] = 'uploaded';
|
||||
cc++
|
||||
if (for_upload === cc) {
|
||||
all(`#${object_key} .file-img`).forEach(e => {
|
||||
e.removeClass('disabled');
|
||||
e.removeClass('no-events');
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
function editSingleFileImage(e) {
|
||||
const check = e.parentNode.querySelector('input');
|
||||
editFileSelectedImage(check, check.dataset.objectKey)
|
||||
}
|
||||
function editFileImage(e) {
|
||||
const check = one('#' + e.dataset.objectKey + ' .file-img input:checked');
|
||||
editFileSelectedImage(check, e.dataset.objectKey)
|
||||
}
|
||||
function editFileSelectedImage(check, objectKey) {
|
||||
const bufferFiles = {};
|
||||
const allowed_resolutions = JSON.parse(one('#' + objectKey + ' .allowed_resolutions').value);
|
||||
const cropFiles = JSON.parse(check.dataset.cropFiles);
|
||||
const img = document.createElement('img');
|
||||
img.src = check.dataset.srcRaw;
|
||||
img.style.height = '100%';
|
||||
img.style.width = 'auto';
|
||||
let aspectRation = 1;
|
||||
let rez = '1:1';
|
||||
if (allowed_resolutions.length > 0) {
|
||||
aspectRation = allowed_resolutions[0][0] / allowed_resolutions[0][1];
|
||||
rez = allowed_resolutions[0][0] + ':' + allowed_resolutions[0][1];
|
||||
}
|
||||
|
||||
|
||||
const cropContainer = document.createElement('div');
|
||||
cropContainer.addClass('crop-container');
|
||||
const cropImgContainer = one('crop-container-img', cropContainer);
|
||||
const cropsContainer = one('crops-container', cropContainer);
|
||||
const select = document.createElement('select');
|
||||
|
||||
cropImgContainer.addClass('invisible')
|
||||
const options = [];
|
||||
allowed_resolutions.forEach(r => {
|
||||
options.push('<option data-rez="' + r[0] + ':' + r[1] + '" value="' + (r[0] / r[1]) + '">Резолюция ' + r[0] + ':' + r[1] + '</option>');
|
||||
});
|
||||
select.innerHTML = options.join('');
|
||||
cropsContainer.innerHTML = '';
|
||||
|
||||
select.addEventListener('change', function () {
|
||||
cropper.setAspectRatio(parseFloat(this.value));
|
||||
const s = select.querySelector('option[value="' + this.value + '"]');
|
||||
rez = s.dataset.rez;
|
||||
});
|
||||
cropsContainer.appendChild(select);
|
||||
const cropBtn = one('btn-ib btn-default', cropsContainer);
|
||||
cropBtn.innerHTML = '<i class="la la-crop"></i> Изрежи';
|
||||
cropImgContainer.appendChild(img);
|
||||
const cropsImgContainer = one('crops-img-container', cropsContainer);
|
||||
|
||||
cropsContainer.addClass('disabled');
|
||||
const cropper = new Cropper(img, {
|
||||
autoCrop: false,
|
||||
aspectRatio: aspectRation,
|
||||
viewMode: 0,
|
||||
ready() {
|
||||
this.cropper.crop();
|
||||
cropImgContainer.removeClass('invisible')
|
||||
cropImgContainer.removeClass('invisible')
|
||||
cropsContainer.removeClass('disabled')
|
||||
},
|
||||
});
|
||||
|
||||
cropBtn.addEventListener('click', function () {
|
||||
const modalBtnConfirm = one('.modal-button-confirm');
|
||||
modalBtnConfirm.removeClass('disabled');
|
||||
const existCrop = one('[data-row-rez="' + rez + '"]');
|
||||
if (existCrop) {
|
||||
if (existCrop.querySelector('img'))
|
||||
existCrop.querySelector('img').remove();
|
||||
if (existCrop.querySelector('canvas'))
|
||||
existCrop.querySelector('canvas').remove();
|
||||
const canvas = cropper.getCroppedCanvas();
|
||||
existCrop.prepend(canvas);
|
||||
canvas.toBlob(function (blob) {
|
||||
bufferFiles[rez] = new File([blob], check.dataset.fileName, {type: check.dataset.fileType});
|
||||
}, check.dataset.fileType, 0.5)
|
||||
} else {
|
||||
createCanvasCropRow(cropper, cropsImgContainer, rez, canvas => {
|
||||
canvas.toBlob(function (blob) {
|
||||
bufferFiles[rez] = new File([blob], check.dataset.fileName, {type: check.dataset.fileType});
|
||||
}, check.dataset.fileType, 0.5)
|
||||
}, del_rez => {
|
||||
delete bufferFiles[rez];
|
||||
if (Object.keys(bufferFiles).length === 0) {
|
||||
modalBtnConfirm.addClass('disabled');
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
Object.keys(cropFiles).forEach(k => {
|
||||
createImageCropRow(cropFiles[k], cropsImgContainer, k, check);
|
||||
})
|
||||
modal.confirm(cropContainer,
|
||||
() => {
|
||||
const keyRez = Object.keys(bufferFiles);
|
||||
check.checked = false;
|
||||
check.parentNode.removeClass('checked');
|
||||
updateActionButtons(check.dataset.objectKey);
|
||||
const form = one('form');
|
||||
form.addClass('form-wait');
|
||||
const waitingType = !one('.waiting-tape') ? one('waiting-tape', document.body) : null;
|
||||
const fd = new FormData();
|
||||
fd.append('object_key', check.dataset.objectKey);
|
||||
fd.append('media_key', check.dataset.mediaKey);
|
||||
fd.append('file_name', check.dataset.fileName);
|
||||
fd.append('resolutions', JSON.stringify(keyRez));
|
||||
keyRez.forEach(rez => {
|
||||
const file = bufferFiles[rez];
|
||||
fd.append(rez, file);
|
||||
});
|
||||
request({
|
||||
url: '/upload-crop/',
|
||||
post: fd,
|
||||
done: r => {
|
||||
if (r.resolutions) {
|
||||
const crops = check.parentNode.querySelector('.crops');
|
||||
crops.innerHTML = '';
|
||||
r.resolutions.forEach(rez => {
|
||||
const cr = document.createElement('span');
|
||||
cr.innerHTML = rez;
|
||||
crops.appendChild(cr);
|
||||
})
|
||||
}
|
||||
if (r.files) {
|
||||
check.setAttribute('data-crop-files', JSON.stringify(r.files))
|
||||
}
|
||||
form.removeClass('form-wait');
|
||||
waitingType.remove();
|
||||
flash.success('Изображението с резолюцията ' + keyRez.join(', ') + ' е актуализирано успешно', true);
|
||||
}
|
||||
})
|
||||
},
|
||||
() => {
|
||||
},
|
||||
{
|
||||
width: '80%',
|
||||
height: '75%'
|
||||
},
|
||||
() => {
|
||||
const modalBtnConfirm = one('.modal-button-confirm');
|
||||
modalBtnConfirm.addClass('disabled');
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
function createCanvasCropRow(cropper, cropsImgContainer, rez, callback, callbackDel) {
|
||||
const canvas = cropper.getCroppedCanvas();
|
||||
const cropsImgRow = one('crops-img-row', cropsImgContainer);
|
||||
const cropsImgRez = one('crops-img-rez', cropsImgRow);
|
||||
cropsImgRez.innerHTML = rez;
|
||||
const cropsImgDel = one('crops-img-del btn-ib btn-default', cropsImgRow);
|
||||
cropsImgDel.innerHTML = '<i class="la la-trash"></i>';
|
||||
cropsImgDel.title = 'Изтрий резолюцията';
|
||||
cropsImgDel.addEventListener('click', function () {
|
||||
cropsImgDel.parentNode.remove();
|
||||
callbackDel(rez);
|
||||
flash.success('Резолюцията е изтрита');
|
||||
})
|
||||
cropsImgRow.prepend(canvas);
|
||||
cropsImgRow.setAttribute('data-row-rez', rez);
|
||||
callback(canvas);
|
||||
}
|
||||
|
||||
function createImageCropRow(src, cropsImgContainer, rez, check) {
|
||||
const img = new Image();
|
||||
img.src = src + '?t=' + (new Date().getTime());
|
||||
const cropsImgRow = one('crops-img-row', cropsImgContainer);
|
||||
const cropsImgRez = one('crops-img-rez', cropsImgRow);
|
||||
cropsImgRez.innerHTML = rez;
|
||||
const cropsImgDel = one('crops-img-del btn-ib btn-default', cropsImgRow);
|
||||
cropsImgDel.innerHTML = '<i class="la la-trash"></i>';
|
||||
cropsImgDel.title = 'Изтрий резолюцията';
|
||||
cropsImgDel.addEventListener('click', function () {
|
||||
cropsImgDel.parentNode.remove();
|
||||
request({
|
||||
url: '/delete-crop/',
|
||||
post: {
|
||||
del_id: check.dataset.del,
|
||||
rez: rez
|
||||
},
|
||||
done: r => {
|
||||
if (r.resolutions) {
|
||||
const crops = check.parentNode.querySelector('.crops');
|
||||
crops.innerHTML = '';
|
||||
r.resolutions.forEach(rez => {
|
||||
const cr = document.createElement('span');
|
||||
cr.innerHTML = rez;
|
||||
crops.appendChild(cr);
|
||||
})
|
||||
}
|
||||
if (r.files) {
|
||||
check.setAttribute('data-crop-files', JSON.stringify(r.files))
|
||||
}
|
||||
flash.success('Резолюцията е изтрита', true);
|
||||
}
|
||||
})
|
||||
})
|
||||
cropsImgRow.prepend(img);
|
||||
cropsImgRow.setAttribute('data-row-rez', rez);
|
||||
}
|
||||
|
||||
//region [ORDER]
|
||||
orderItems('.file-img', '/update-file-indexes/');
|
||||
//endregion
|
||||
@@ -0,0 +1,361 @@
|
||||
class SelectorBase {
|
||||
find(selector, parent) {
|
||||
const p = parent || document
|
||||
return parent.querySelector(selector)
|
||||
}
|
||||
|
||||
findAll(selector, parent, callback) {
|
||||
const p = parent || document,
|
||||
result = parent.querySelectorAll(selector)
|
||||
result.forEach((e, i) => {
|
||||
callback(e, i)
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
set(obj) {
|
||||
obj = obj || {};
|
||||
const element = document.createElement(obj.tagName || 'div')
|
||||
if (obj.type) element.type = obj.type
|
||||
if (obj.className) element.className = obj.className
|
||||
if (obj.innerHTML) element.innerHTML = obj.innerHTML
|
||||
if (obj.name) element.name = obj.name
|
||||
if (obj.events) {
|
||||
Object.keys(obj.events).forEach(event => {
|
||||
element.addEventListener(event, e => {
|
||||
obj.events[event](element)
|
||||
})
|
||||
})
|
||||
}
|
||||
if (obj.attributes) {
|
||||
Object.keys(obj.attributes).forEach(attr => {
|
||||
element.setAttribute(attr, obj.attributes[attr])
|
||||
})
|
||||
}
|
||||
if (obj.parent) obj.parent.appendChild(element)
|
||||
return element
|
||||
}
|
||||
|
||||
toggleClass(e, classFrom, classTo) {
|
||||
e.classList.remove(classFrom)
|
||||
e.classList.add(classTo)
|
||||
}
|
||||
}
|
||||
|
||||
class InnerTableItems extends SelectorBase {
|
||||
updateUrl
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.setDefault()
|
||||
}
|
||||
|
||||
setChangeToSelect(e) {
|
||||
this.typeValueSelectorChange(e, e.name.replace('user_interface_type', 'select_data'))
|
||||
}
|
||||
|
||||
setChangeDataType(e) {
|
||||
console.log(e.value)
|
||||
}
|
||||
|
||||
setUpdateUrl(url) {
|
||||
this.updateUrl = url;
|
||||
}
|
||||
|
||||
setDefault() {
|
||||
this.findAll('.type-value-selector', document, e => {
|
||||
e.addEventListener('change', () => {
|
||||
this.typeValueSelectorChange(e, e.name.replace('user_interface_type', 'select_data'))
|
||||
})
|
||||
})
|
||||
this.findAll('.inner-td-table .add-row', document, e => {
|
||||
e.addEventListener('click', () => {
|
||||
this.addNewRow(e)
|
||||
})
|
||||
})
|
||||
this.findAll('.inner-td-table .remove', document, e => {
|
||||
e.addEventListener('click', () => {
|
||||
this.deleteRow(e)
|
||||
})
|
||||
})
|
||||
this.findAll('.inner-td-table .add-bg, .inner-td-table .add-en', document, e => {
|
||||
e.addEventListener('keydown', () => {
|
||||
this.addNewRow(e)
|
||||
})
|
||||
})
|
||||
this.findAll('.inner-td-table .update-bg, .inner-td-table .update-en', document, e => {
|
||||
e.addEventListener('keydown', () => {
|
||||
this.updateRow(e)
|
||||
})
|
||||
e.addEventListener('blur', () => {
|
||||
this.updateRow(e)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
addNewRow(e) {
|
||||
const erd = this.eventRetrieveData(e)
|
||||
if (erd) {
|
||||
const lg = !erd.inputEn ? 1 : null;
|
||||
const row = this.setRow(erd.tbody, erd.dataTable, 'update', lg)
|
||||
row.inputBg.value = erd.inputBg.value
|
||||
if (row.inputEn && erd.inputEn) {
|
||||
row.inputEn.value = erd.inputEn.value
|
||||
}
|
||||
if(erd.inputBg.value !== '') {
|
||||
if (erd.tbody.dataset.id !== 'undefined') {
|
||||
console.log(erd.tbody.dataset.id)
|
||||
if (this.updateUrl && erd.tbody.dataset.id) {
|
||||
erd.tr.classList.add('disabled')
|
||||
this.request({
|
||||
url: this.updateUrl,
|
||||
data: {
|
||||
parent_id: erd.tbody.dataset.id,
|
||||
bg: erd.inputBg.value,
|
||||
en: erd.inputEn ? erd.inputEn.value : '',
|
||||
},
|
||||
success: r => {
|
||||
if (r.id) {
|
||||
row.tr.setAttribute('data-option-id', r.id);
|
||||
erd.tbody.insertBefore(row.tr, erd.tr)
|
||||
erd.inputBg.value = ''
|
||||
if (erd.inputEn) erd.inputEn.value = ''
|
||||
erd.tr.classList.remove('disabled')
|
||||
erd.inputBg.focus()
|
||||
this.setData(erd.tbody, erd.hidden)
|
||||
flash.success('Опцията е добавена успешно')
|
||||
} else {
|
||||
flash.error('Опцията не беше добавена поради грешка в системата')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
erd.tbody.insertBefore(row.tr, erd.tr)
|
||||
erd.inputBg.value = ''
|
||||
if (erd.inputEn) erd.inputEn.value = ''
|
||||
erd.tr.classList.remove('disabled')
|
||||
erd.inputBg.focus()
|
||||
this.setData(erd.tbody, erd.hidden)
|
||||
}
|
||||
} else {
|
||||
errorElement(erd.inputBg, 'Стойността не може да бъде празна')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateRow(e) {
|
||||
const erd = this.eventRetrieveData(e)
|
||||
if (erd) {
|
||||
if(erd.tr && erd.tr.dataset.optionId) {
|
||||
erd.tr.classList.add('disabled');
|
||||
this.request({
|
||||
url: this.updateUrl,
|
||||
data: {
|
||||
id: erd.tr.dataset.optionId,
|
||||
bg: erd.inputBg.value,
|
||||
en: erd.inputEn ? erd.inputEn.value : '',
|
||||
},
|
||||
success: r => {
|
||||
if(r.id) {
|
||||
this.setData(erd.tbody, erd.hidden)
|
||||
flash.success('Опцията е актуализирана успешно')
|
||||
} else {
|
||||
flash.error('Опцията не беше актуализирана поради грешка в системата')
|
||||
}
|
||||
erd.tr.classList.remove('disabled');
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deleteRow(e) {
|
||||
const erd = this.eventRetrieveData(e)
|
||||
if (erd) {
|
||||
erd.tr.classList.add('disabled');
|
||||
if(erd.tr && erd.tr.dataset.optionId) {
|
||||
this.request({
|
||||
url: this.updateUrl,
|
||||
data: {
|
||||
id: erd.tr.dataset.optionId,
|
||||
remove: 1
|
||||
},
|
||||
success: r => {
|
||||
if(r.success) {
|
||||
erd.tr.remove()
|
||||
this.setData(erd.tbody, erd.hidden)
|
||||
flash.success('Опцията е премахната успешно')
|
||||
} else {
|
||||
flash.error('Опцията не беше премахната поради грешка в системата')
|
||||
erd.tr.classList.remove('disabled')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRow(tbody, dataTable, type, lg) {
|
||||
const trObj = {tagName: 'tr', attributes: {"data-table": dataTable}};
|
||||
if (type === 'add')
|
||||
trObj.parent = tbody
|
||||
const
|
||||
tr = this.set(trObj),
|
||||
tdBg = this.set({tagName: 'td', parent: tr}),
|
||||
inputBg = this.set({
|
||||
tagName: 'input',
|
||||
parent: tdBg,
|
||||
className: `${type}-bg`,
|
||||
events: {
|
||||
keydown: () => {
|
||||
if (type === 'add')
|
||||
this.addNewRow(inputBg)
|
||||
if (type === 'update')
|
||||
this.updateRow(inputBg)
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
tdEn = this.set({tagName: 'td', parent: tr}),
|
||||
inputEn = this.set({
|
||||
tagName: 'input',
|
||||
parent: tdEn,
|
||||
className: `${type}-en`,
|
||||
events: {
|
||||
keydown: () => {
|
||||
if (type === 'add')
|
||||
this.addNewRow(inputEn)
|
||||
if (type === 'update')
|
||||
this.updateRow(inputEn)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (lg === 1) tdEn.remove()
|
||||
|
||||
|
||||
const button = this.set({
|
||||
tagName: 'td',
|
||||
className: type === 'add' ? 'add-row' : 'remove',
|
||||
parent: tr,
|
||||
innerHTML: `<i class="la la-${type === 'add' ? 'plus' : 'remove'}"></i>`,
|
||||
events: {
|
||||
click: () => {
|
||||
if (type === 'add')
|
||||
this.addNewRow(button)
|
||||
if (type === 'update')
|
||||
this.deleteRow(button)
|
||||
}
|
||||
}
|
||||
})
|
||||
if (type === 'add')
|
||||
inputBg.focus()
|
||||
if (type === 'update') {
|
||||
tr.className = 'added-row'
|
||||
tr.setAttribute('data-table', dataTable)
|
||||
}
|
||||
return {tr, inputBg, inputEn};
|
||||
|
||||
}
|
||||
|
||||
eventRetrieveData(e) {
|
||||
if (event.keyCode && event.keyCode !== 13) return
|
||||
var tr = "";
|
||||
if (event.keyCode === 13) event.preventDefault()
|
||||
if (e.tagName === "INPUT") tr = e.parentNode.parentNode
|
||||
if (e.tagName === "TD") tr = e.parentNode
|
||||
const
|
||||
dataTable = tr.dataset.table,
|
||||
tbody = document.querySelector(`.inner-td-table[data-table="${dataTable}"] tbody`),
|
||||
hidden = document.querySelector(`input[name="${dataTable}"]`),
|
||||
button = this.find('.add-row', tr),
|
||||
inputBg = this.find('td:nth-child(1) input', tr),
|
||||
inputEn = this.find('td:nth-child(2) input', tr)
|
||||
return {dataTable, tr, inputBg, inputEn, tbody, hidden, button}
|
||||
}
|
||||
|
||||
setData(tbody, hidden) {
|
||||
const data = []
|
||||
this.findAll('.added-row', tbody, (e, i) => {
|
||||
e.setAttribute('data-index', i)
|
||||
const objectRow = {};
|
||||
const bg = this.find('td:nth-child(1) input', e)
|
||||
const en = this.find('td:nth-child(2) input', e)
|
||||
if(e.dataset.optionId) objectRow['id'] = e.dataset.optionId
|
||||
if (bg) objectRow['bg'] = bg.value
|
||||
if (en) objectRow['en'] = en.value
|
||||
data.push(objectRow)
|
||||
})
|
||||
hidden.value = JSON.stringify(data)
|
||||
return data
|
||||
}
|
||||
|
||||
request(o) {
|
||||
console.log(o);
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.responseType = 'json'
|
||||
xhr.onload = () => {
|
||||
if(o.success) {
|
||||
o.success(xhr.response)
|
||||
}
|
||||
}
|
||||
xhr.open('post', o.url)
|
||||
const fd = new FormData()
|
||||
if(o.data) {
|
||||
Object.keys(o.data).forEach(key => {
|
||||
fd.append(key, o.data[key])
|
||||
})
|
||||
}
|
||||
xhr.send(fd)
|
||||
}
|
||||
|
||||
typeValueSelectorChange(elem, dataTable) {
|
||||
const
|
||||
existHidden = elem.parentNode.querySelector('input[type="hidden"]'),
|
||||
existTable = elem.parentNode.querySelector('table.inner-td-table')
|
||||
|
||||
if(existHidden) existHidden.remove()
|
||||
if(existTable) existTable.remove()
|
||||
|
||||
if (elem.value === 'list') {
|
||||
const
|
||||
hidden = this.set({tagName: 'input', type: 'hidden', name: dataTable}),
|
||||
table = this.set({
|
||||
tagName: 'table',
|
||||
className: 'inner-td-table',
|
||||
innerHTML:
|
||||
`<thead>
|
||||
<tr><td>стойност (bg)</td><td>стойност (en)</td><td></td></tr>
|
||||
</thead>`,
|
||||
attributes: {"data-table": dataTable}
|
||||
}),
|
||||
tbody = this.set({tagName: 'tbody', parent: table, attributes: {"data-id": elem.dataset.id}})
|
||||
this.setRow(tbody, dataTable, 'add')
|
||||
elem.parentNode.appendChild(hidden)
|
||||
elem.parentNode.appendChild(table)
|
||||
} else if (elem.value === 'list_simple') {
|
||||
const
|
||||
hidden = this.set({tagName: 'input', type: 'hidden', name: dataTable}),
|
||||
table = this.set({
|
||||
tagName: 'table',
|
||||
className: 'inner-td-table',
|
||||
innerHTML:
|
||||
`<thead>
|
||||
<tr><td>стойност</td></tr>
|
||||
</thead>`,
|
||||
attributes: {"data-table": dataTable}
|
||||
}),
|
||||
tbody = this.set({tagName: 'tbody', parent: table, attributes: {"data-id": elem.dataset.id}})
|
||||
this.setRow(tbody, dataTable, 'add', 1)
|
||||
elem.parentNode.appendChild(hidden)
|
||||
elem.parentNode.appendChild(table)
|
||||
} else {
|
||||
const
|
||||
hidden = this.find('input[type="hidden"]', elem.parentNode),
|
||||
table = this.find('table.inner-td-table', elem.parentNode)
|
||||
if (hidden) hidden.remove()
|
||||
if (table) table.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
var orderIndexUrl = ''
|
||||
function orderItems(className, orderUrl, callback) {
|
||||
document.querySelectorAll(className).forEach(item => {
|
||||
orderItem(item, callback)
|
||||
})
|
||||
orderIndexUrl = orderUrl
|
||||
}
|
||||
|
||||
function orderItem(draggable, callback) {
|
||||
draggable.setAttribute('draggable', true)
|
||||
draggable.querySelectorAll('input').forEach(input => {
|
||||
input.addEventListener('focus', () => {
|
||||
draggable.removeAttribute('draggable')
|
||||
})
|
||||
input.addEventListener('blur', () => {
|
||||
draggable.setAttribute('draggable', true)
|
||||
})
|
||||
})
|
||||
|
||||
draggable.classList.add('draggable')
|
||||
draggable.addEventListener('dragstart', () => {
|
||||
//console.log(event.target);
|
||||
//return;
|
||||
draggable.classList.add('dragging');
|
||||
})
|
||||
|
||||
const orderElements = [...draggable.parentNode.querySelectorAll('.draggable')];
|
||||
orderElements.forEach((e, i) => {
|
||||
if (typeof e.dataset.fileId === 'undefined') {
|
||||
e.setAttribute('data-index', i);
|
||||
}
|
||||
});
|
||||
draggable.addEventListener('dragend', (e) => {
|
||||
e.preventDefault()
|
||||
draggable.classList.remove('dragging');
|
||||
const reorderElements = [...container.querySelectorAll('.draggable')];
|
||||
const orderList = [];
|
||||
reorderElements.forEach((e, i) => {
|
||||
e.setAttribute('data-index', i);
|
||||
const check = e.querySelector('input');
|
||||
if (typeof callback !== 'undefined') {
|
||||
callback(check.dataset.id, i);
|
||||
}
|
||||
orderList.push({
|
||||
order_index: i,
|
||||
id: check.dataset.del
|
||||
})
|
||||
});
|
||||
requestOrderIndexes(orderList);
|
||||
if(callback)
|
||||
callback()
|
||||
})
|
||||
const container = draggable.parentNode
|
||||
container.addEventListener('dragover', e => {
|
||||
e.preventDefault()
|
||||
const draggable = document.querySelector('.dragging')
|
||||
const setElement = getDragAfterElement(container, e.clientX, e.clientY)
|
||||
if (setElement !== null) {
|
||||
container.insertBefore(draggable, setElement)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function requestOrderIndexes(indexes) {
|
||||
if (indexes.length > 0 && orderIndexUrl) {
|
||||
request({
|
||||
url: orderIndexUrl,
|
||||
post: {
|
||||
indexes: JSON.stringify(indexes)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function getDragAfterElement(container, x, y) {
|
||||
const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')]
|
||||
var child = null;
|
||||
draggableElements.forEach(e => {
|
||||
var box = e.getBoundingClientRect();
|
||||
if (x > box.left && x < box.left + box.width / 2) {
|
||||
if (y > box.top && y < box.top + box.height) {
|
||||
child = e;
|
||||
return child;
|
||||
}
|
||||
}
|
||||
})
|
||||
return child;
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/* Copyright 2012 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line max-len
|
||||
/** @typedef {import("./display/api").PDFDocumentLoadingTask} PDFDocumentLoadingTask */
|
||||
/** @typedef {import("./display/api").PDFDocumentProxy} PDFDocumentProxy */
|
||||
/** @typedef {import("./display/api").PDFPageProxy} PDFPageProxy */
|
||||
/** @typedef {import("./display/api").RenderTask} RenderTask */
|
||||
/** @typedef {import("./display/display_utils").PageViewport} PageViewport */
|
||||
// eslint-disable-next-line max-len
|
||||
/** @typedef {import("./display/text_layer").TextLayerRenderTask} TextLayerRenderTask */
|
||||
|
||||
import {
|
||||
AnnotationEditorParamsType,
|
||||
AnnotationEditorType,
|
||||
AnnotationMode,
|
||||
CMapCompressionType,
|
||||
createPromiseCapability,
|
||||
createValidAbsoluteUrl,
|
||||
InvalidPDFException,
|
||||
MissingPDFException,
|
||||
OPS,
|
||||
PasswordResponses,
|
||||
PermissionFlag,
|
||||
shadow,
|
||||
UnexpectedResponseException,
|
||||
UNSUPPORTED_FEATURES,
|
||||
Util,
|
||||
VerbosityLevel,
|
||||
} from "./shared/util.js";
|
||||
import {
|
||||
build,
|
||||
getDocument,
|
||||
LoopbackPort,
|
||||
PDFDataRangeTransport,
|
||||
PDFWorker,
|
||||
setPDFNetworkStreamFactory,
|
||||
version,
|
||||
} from "./display/api.js";
|
||||
import {
|
||||
getFilenameFromUrl,
|
||||
getPdfFilenameFromUrl,
|
||||
getXfaPageViewport,
|
||||
isPdfFile,
|
||||
isValidFetchUrl,
|
||||
loadScript,
|
||||
PDFDateString,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
} from "./display/display_utils.js";
|
||||
import { AnnotationEditorLayer } from "./display/editor/annotation_editor_layer.js";
|
||||
import { AnnotationEditorUIManager } from "./display/editor/tools.js";
|
||||
import { AnnotationLayer } from "./display/annotation_layer.js";
|
||||
import { GlobalWorkerOptions } from "./display/worker_options.js";
|
||||
import { isNodeJS } from "./shared/is_node.js";
|
||||
import { renderTextLayer } from "./display/text_layer.js";
|
||||
import { SVGGraphics } from "./display/svg.js";
|
||||
import { XfaLayer } from "./display/xfa_layer.js";
|
||||
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const pdfjsVersion =
|
||||
typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_VERSION") : void 0;
|
||||
/* eslint-disable-next-line no-unused-vars */
|
||||
const pdfjsBuild =
|
||||
typeof PDFJSDev !== "undefined" ? PDFJSDev.eval("BUNDLE_BUILD") : void 0;
|
||||
|
||||
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
|
||||
const streamsPromise = Promise.all([
|
||||
import("pdfjs/display/network.js"),
|
||||
import("pdfjs/display/fetch_stream.js"),
|
||||
]);
|
||||
|
||||
setPDFNetworkStreamFactory(async params => {
|
||||
const [{ PDFNetworkStream }, { PDFFetchStream }] = await streamsPromise;
|
||||
if (isValidFetchUrl(params.url)) {
|
||||
return new PDFFetchStream(params);
|
||||
}
|
||||
return new PDFNetworkStream(params);
|
||||
});
|
||||
} else if (PDFJSDev.test("GENERIC || CHROME")) {
|
||||
if (PDFJSDev.test("GENERIC") && isNodeJS) {
|
||||
const { PDFNodeStream } = require("./display/node_stream.js");
|
||||
|
||||
setPDFNetworkStreamFactory(params => {
|
||||
return new PDFNodeStream(params);
|
||||
});
|
||||
} else {
|
||||
const { PDFNetworkStream } = require("./display/network.js");
|
||||
const { PDFFetchStream } = require("./display/fetch_stream.js");
|
||||
|
||||
setPDFNetworkStreamFactory(params => {
|
||||
if (isValidFetchUrl(params.url)) {
|
||||
return new PDFFetchStream(params);
|
||||
}
|
||||
return new PDFNetworkStream(params);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
AnnotationEditorLayer,
|
||||
AnnotationEditorParamsType,
|
||||
AnnotationEditorType,
|
||||
AnnotationEditorUIManager,
|
||||
AnnotationLayer,
|
||||
AnnotationMode,
|
||||
build,
|
||||
CMapCompressionType,
|
||||
createPromiseCapability,
|
||||
createValidAbsoluteUrl,
|
||||
getDocument,
|
||||
getFilenameFromUrl,
|
||||
getPdfFilenameFromUrl,
|
||||
getXfaPageViewport,
|
||||
GlobalWorkerOptions,
|
||||
InvalidPDFException,
|
||||
isPdfFile,
|
||||
loadScript,
|
||||
LoopbackPort,
|
||||
MissingPDFException,
|
||||
OPS,
|
||||
PasswordResponses,
|
||||
PDFDataRangeTransport,
|
||||
PDFDateString,
|
||||
PDFWorker,
|
||||
PermissionFlag,
|
||||
PixelsPerInch,
|
||||
RenderingCancelledException,
|
||||
renderTextLayer,
|
||||
shadow,
|
||||
SVGGraphics,
|
||||
UnexpectedResponseException,
|
||||
UNSUPPORTED_FEATURES,
|
||||
Util,
|
||||
VerbosityLevel,
|
||||
version,
|
||||
XfaLayer,
|
||||
};
|
||||
@@ -0,0 +1,215 @@
|
||||
class SelectorBase {
|
||||
find(selector, parent) {
|
||||
const p = parent || document
|
||||
return parent.querySelector(selector)
|
||||
}
|
||||
|
||||
findAll(selector, parent, callback) {
|
||||
const p = parent || document,
|
||||
result = parent.querySelectorAll(selector)
|
||||
result.forEach((e, i) => {
|
||||
callback(e, i)
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
set(obj) {
|
||||
obj = obj || {};
|
||||
const element = document.createElement(obj.tagName || 'div')
|
||||
if (obj.type) element.type = obj.type
|
||||
if (obj.className) element.className = obj.className
|
||||
if (obj.innerHTML) element.innerHTML = obj.innerHTML
|
||||
if (obj.name) element.name = obj.name
|
||||
if (obj.value) element.value = obj.value
|
||||
if (obj.events) {
|
||||
Object.keys(obj.events).forEach(event => {
|
||||
element.addEventListener(event, e => {
|
||||
obj.events[event](element)
|
||||
})
|
||||
})
|
||||
}
|
||||
if (obj.attributes) {
|
||||
Object.keys(obj.attributes).forEach(attr => {
|
||||
element.setAttribute(attr, obj.attributes[attr])
|
||||
})
|
||||
}
|
||||
if (obj.parent) obj.parent.appendChild(element)
|
||||
return element
|
||||
}
|
||||
|
||||
toggleClass(e, classFrom, classTo) {
|
||||
e.classList.remove(classFrom)
|
||||
e.classList.add(classTo)
|
||||
}
|
||||
|
||||
toggleOpen(e) {
|
||||
if (e.classList.contains('open-autocomplete')) {
|
||||
e.classList.remove('open-autocomplete')
|
||||
} else {
|
||||
e.classList.add('open-autocomplete')
|
||||
}
|
||||
}
|
||||
|
||||
request(url, query, callback) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('get', `${url}?${query}`)
|
||||
xhr.onload = () => {
|
||||
callback(xhr.response)
|
||||
}
|
||||
xhr.send()
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBoxRemote extends SelectorBase {
|
||||
constructor(selector, obj) {
|
||||
super();
|
||||
this.set({
|
||||
tagName: 'link',
|
||||
parent: document.head,
|
||||
attributes: {href: '/_public/assets/css/search-box-remote.css', rel: 'stylesheet'}
|
||||
})
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
new SearchBoxRemoteInitial(element, obj)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBoxRemoteInitial extends SelectorBase {
|
||||
url
|
||||
q
|
||||
id
|
||||
columns
|
||||
selected
|
||||
hidden
|
||||
searchBoxContainer
|
||||
inputSearch
|
||||
autocompleteList
|
||||
|
||||
constructor(selector, obj) {
|
||||
super();
|
||||
obj = obj || {};
|
||||
this.url = obj.url || null
|
||||
this.q = obj.q || 'q'
|
||||
this.id = obj.id || 'id'
|
||||
this.columns = obj.columns || null
|
||||
this.init(selector)
|
||||
}
|
||||
|
||||
init(e) {
|
||||
if (e.dataset && e.dataset.sbrName) {
|
||||
this.hidden = this.set({
|
||||
tagName: 'input',
|
||||
type: 'hidden',
|
||||
name: e.dataset.sbrName,
|
||||
value: e.dataset.sbrValue,
|
||||
parent: e.parentNode
|
||||
})
|
||||
e.setAttribute('readonly', true)
|
||||
e.classList.add('pointer')
|
||||
e.parentNode.classList.add('relative')
|
||||
this.selected = e
|
||||
e.addEventListener('click', () => {
|
||||
if(!event.target.classList.contains('clear-button') && !event.target.classList.contains('add-button')) {
|
||||
this.setSearchBox(e.parentNode)
|
||||
}
|
||||
})
|
||||
this.setDefaultSelected()
|
||||
}
|
||||
}
|
||||
|
||||
setSearchBox(container) {
|
||||
if (container.classList.contains('open-autocomplete')) {
|
||||
container.classList.remove('open-autocomplete')
|
||||
container.querySelector('.search-box-container').remove()
|
||||
} else {
|
||||
this.searchBoxContainer = this.set({className: 'search-box-container', parent: container})
|
||||
this.inputSearch = this.set({
|
||||
tagName: 'input', attributes: {
|
||||
class: 'input-search',
|
||||
placeholder: 'Търси ...',
|
||||
}, parent: this.searchBoxContainer
|
||||
})
|
||||
this.inputSearch.focus();
|
||||
if ((window.innerHeight - this.searchBoxContainer.getBoundingClientRect().top) < 500) {
|
||||
this.searchBoxContainer.classList.add('up-position')
|
||||
this.setAutocompleteList('prepend')
|
||||
} else {
|
||||
this.searchBoxContainer.classList.add('down-position')
|
||||
this.setAutocompleteList('append')
|
||||
}
|
||||
container.classList.add('open-autocomplete')
|
||||
}
|
||||
}
|
||||
|
||||
setAutocompleteList(type) {
|
||||
this.autocompleteList = this.set({className: 'autocomplete-list autocomplete-image-list'})
|
||||
if (type === 'prepend') this.searchBoxContainer.insertBefore(this.autocompleteList, this.inputSearch)
|
||||
if (type === 'append') this.searchBoxContainer.append(this.autocompleteList)
|
||||
this.inputSearch.addEventListener('keydown', () => {
|
||||
this.keyDown()
|
||||
})
|
||||
this.inputSearch.addEventListener('keyup', () => {
|
||||
this.keyUp()
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultSelected() {
|
||||
if(this.hidden.value) {
|
||||
this.request(this.url, `${this.id}=${this.hidden.value}`, e => {
|
||||
if(e.settlement_id) {
|
||||
this.selected.value = `${e.name}`;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setAutocompleteValues(data) {
|
||||
const $this = this
|
||||
this.autocompleteList.innerHTML = ''
|
||||
data.forEach(e => {
|
||||
const resultRow = this.set({
|
||||
className: 'autocomplete-value',
|
||||
innerHTML: `${e.name}`,
|
||||
parent: this.autocompleteList,
|
||||
events: {
|
||||
click: function () {
|
||||
$this.hidden.value = e.object_id;
|
||||
$this.searchBoxContainer.parentNode.classList.remove('open-autocomplete')
|
||||
$this.searchBoxContainer.remove();
|
||||
$this.selected.innerHTML = resultRow.innerHTML + '<div class="flex flex-center top5">' +
|
||||
'<div class="add-button" onclick="addSelected()"><i class="la la-plus-circle"></i> Добави</div>' +
|
||||
'<div class="clear-button" onclick="clearSelected()"><i class="la la-remove"></i>Изчисти</div></div>'
|
||||
$this.selected.querySelector('.row-result').classList.add('panel-result-selected')
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
keyDown() {
|
||||
this.autocompleteList.innerHTML = ''
|
||||
}
|
||||
|
||||
keyUp() {
|
||||
if (this.inputSearch.value.length > 0) {
|
||||
this.request(this.url, `${this.q}=${this.inputSearch.value}`, data => {
|
||||
if (data && data.length > 0) {
|
||||
this.setAutocompleteValues(data)
|
||||
this.autocompleteList.classList.add('visible')
|
||||
} else {
|
||||
this.closeAutocomplete()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.closeAutocomplete()
|
||||
}
|
||||
}
|
||||
|
||||
closeAutocomplete() {
|
||||
this.autocompleteList.classList.remove('visible')
|
||||
this.autocompleteList.innerHTML = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
class SelectorBase {
|
||||
find(selector, parent) {
|
||||
const p = parent || document
|
||||
return parent.querySelector(selector)
|
||||
}
|
||||
|
||||
findAll(selector, parent, callback) {
|
||||
const p = parent || document,
|
||||
result = parent.querySelectorAll(selector)
|
||||
result.forEach((e, i) => {
|
||||
callback(e, i)
|
||||
})
|
||||
return result;
|
||||
}
|
||||
|
||||
set(obj) {
|
||||
obj = obj || {};
|
||||
const element = document.createElement(obj.tagName || 'div')
|
||||
if (obj.type) element.type = obj.type
|
||||
if (obj.className) element.className = obj.className
|
||||
if (obj.innerHTML) element.innerHTML = obj.innerHTML
|
||||
if (obj.name) element.name = obj.name
|
||||
if (obj.value) element.value = obj.value
|
||||
if (obj.events) {
|
||||
Object.keys(obj.events).forEach(event => {
|
||||
element.addEventListener(event, e => {
|
||||
obj.events[event](element)
|
||||
})
|
||||
})
|
||||
}
|
||||
if (obj.attributes) {
|
||||
Object.keys(obj.attributes).forEach(attr => {
|
||||
element.setAttribute(attr, obj.attributes[attr])
|
||||
})
|
||||
}
|
||||
if (obj.parent) obj.parent.appendChild(element)
|
||||
return element
|
||||
}
|
||||
|
||||
toggleClass(e, classFrom, classTo) {
|
||||
e.classList.remove(classFrom)
|
||||
e.classList.add(classTo)
|
||||
}
|
||||
|
||||
toggleOpen(e) {
|
||||
if (e.classList.contains('open-autocomplete')) {
|
||||
e.classList.remove('open-autocomplete')
|
||||
} else {
|
||||
e.classList.add('open-autocomplete')
|
||||
}
|
||||
}
|
||||
|
||||
request(url, query, callback) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('get', `${url}?${query}`)
|
||||
xhr.onload = () => {
|
||||
callback(xhr.response)
|
||||
}
|
||||
xhr.send()
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBoxRemote extends SelectorBase {
|
||||
constructor(selector, obj) {
|
||||
super();
|
||||
this.set({
|
||||
tagName: 'link',
|
||||
parent: document.head,
|
||||
attributes: {href: '/_public/assets/css/search-box-remote.css', rel: 'stylesheet'}
|
||||
})
|
||||
document.querySelectorAll(selector).forEach(element => {
|
||||
new SearchBoxRemoteInitial(element, obj)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class SearchBoxRemoteInitial extends SelectorBase {
|
||||
url
|
||||
q
|
||||
id
|
||||
columns
|
||||
selected
|
||||
hidden
|
||||
searchBoxContainer
|
||||
inputSearch
|
||||
autocompleteList
|
||||
|
||||
constructor(selector, obj) {
|
||||
super();
|
||||
obj = obj || {};
|
||||
this.url = obj.url || null
|
||||
this.q = obj.q || 'q'
|
||||
this.id = obj.id || 'id'
|
||||
this.columns = obj.columns || null
|
||||
this.init(selector)
|
||||
}
|
||||
|
||||
init(e) {
|
||||
if (e.dataset && e.dataset.sbrName) {
|
||||
this.hidden = this.set({
|
||||
tagName: 'input',
|
||||
type: 'hidden',
|
||||
name: e.dataset.sbrName,
|
||||
value: e.dataset.sbrValue,
|
||||
parent: e.parentNode
|
||||
})
|
||||
e.setAttribute('readonly', true)
|
||||
e.classList.add('pointer')
|
||||
e.parentNode.classList.add('relative')
|
||||
this.selected = e
|
||||
e.addEventListener('click', () => {
|
||||
this.setSearchBox(e.parentNode)
|
||||
})
|
||||
this.setDefaultSelected()
|
||||
}
|
||||
}
|
||||
|
||||
setSearchBox(container) {
|
||||
if (container.classList.contains('open-autocomplete')) {
|
||||
container.classList.remove('open-autocomplete')
|
||||
container.querySelector('.search-box-container').remove()
|
||||
} else {
|
||||
this.searchBoxContainer = this.set({className: 'search-box-container', parent: container})
|
||||
this.inputSearch = this.set({
|
||||
tagName: 'input', attributes: {
|
||||
class: 'input-search',
|
||||
placeholder: 'Търси ...',
|
||||
}, parent: this.searchBoxContainer
|
||||
})
|
||||
this.inputSearch.focus();
|
||||
if ((window.innerHeight - this.searchBoxContainer.getBoundingClientRect().top) < 500) {
|
||||
this.searchBoxContainer.classList.add('up-position')
|
||||
this.setAutocompleteList('prepend')
|
||||
} else {
|
||||
this.searchBoxContainer.classList.add('down-position')
|
||||
this.setAutocompleteList('append')
|
||||
}
|
||||
container.classList.add('open-autocomplete')
|
||||
}
|
||||
}
|
||||
|
||||
setAutocompleteList(type) {
|
||||
this.autocompleteList = this.set({className: 'autocomplete-list'})
|
||||
if (type === 'prepend') this.searchBoxContainer.insertBefore(this.autocompleteList, this.inputSearch)
|
||||
if (type === 'append') this.searchBoxContainer.append(this.autocompleteList)
|
||||
this.inputSearch.addEventListener('keydown', () => {
|
||||
this.keyDown()
|
||||
})
|
||||
this.inputSearch.addEventListener('keyup', () => {
|
||||
this.keyUp()
|
||||
})
|
||||
}
|
||||
|
||||
setDefaultSelected() {
|
||||
if(this.hidden.value) {
|
||||
this.request(this.url, `${this.id}=${this.hidden.value}`, e => {
|
||||
if(e.settlement_id) {
|
||||
this.selected.value = `${e.name}`;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
setAutocompleteValues(data) {
|
||||
const $this = this
|
||||
this.autocompleteList.innerHTML = ''
|
||||
data.forEach(e => {
|
||||
const resultRow = this.set({
|
||||
className: 'autocomplete-value',
|
||||
innerHTML: `${e.name}`,
|
||||
parent: this.autocompleteList,
|
||||
events: {
|
||||
click: function () {
|
||||
$this.hidden.value = e.settlement_id;
|
||||
$this.searchBoxContainer.parentNode.classList.remove('open-autocomplete')
|
||||
$this.searchBoxContainer.remove();
|
||||
$this.selected.value = resultRow.innerHTML
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
keyDown() {
|
||||
this.autocompleteList.innerHTML = ''
|
||||
}
|
||||
|
||||
keyUp() {
|
||||
if (this.inputSearch.value.length > 0) {
|
||||
this.request(this.url, `${this.q}=${this.inputSearch.value}`, data => {
|
||||
if (data && data.length > 0) {
|
||||
this.setAutocompleteValues(data)
|
||||
this.autocompleteList.classList.add('visible')
|
||||
} else {
|
||||
this.closeAutocomplete()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.closeAutocomplete()
|
||||
}
|
||||
}
|
||||
|
||||
closeAutocomplete() {
|
||||
this.autocompleteList.classList.remove('visible')
|
||||
this.autocompleteList.innerHTML = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
class SearchBox {
|
||||
container
|
||||
box = document.createElement('div')
|
||||
list = document.createElement('div')
|
||||
listItems = document.createElement('div')
|
||||
selected = document.createElement('div')
|
||||
searchField = document.createElement('input')
|
||||
cloneListItems = this.listItems
|
||||
callback
|
||||
dynamic = {}
|
||||
constructor(selector) {
|
||||
if (typeof selector === "string") {
|
||||
this.container = document.querySelector(selector)
|
||||
} else {
|
||||
this.container = selector;
|
||||
}
|
||||
if(this.container) {
|
||||
if (this.container.nodeName === 'SELECT') {
|
||||
this.dataRetrieve()
|
||||
} else {
|
||||
alert('Html element SELECT is required')
|
||||
}
|
||||
addEventListener('keydown', ({keyCode}) => {
|
||||
if (keyCode === 27) {
|
||||
this.clear()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.box.classList.remove('open')
|
||||
this.searchField.value = ''
|
||||
this.listItems.querySelectorAll('.list-item').forEach(e => {
|
||||
e.classList.add('visible')
|
||||
e.classList.remove('hover')
|
||||
e.style.display = 'block'
|
||||
})
|
||||
}
|
||||
|
||||
getNextSibling(elem, selector) {
|
||||
var sibling = elem.nextElementSibling;
|
||||
while (sibling) {
|
||||
if (sibling.matches(selector)) return sibling;
|
||||
sibling = sibling.nextElementSibling
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPreviousSibling(elem, selector) {
|
||||
var sibling = elem.previousElementSibling;
|
||||
if (!selector) return sibling;
|
||||
while (sibling) {
|
||||
if (sibling.matches(selector)) return sibling;
|
||||
sibling = sibling.previousElementSibling;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyDown() {
|
||||
this.searchField.addEventListener('keydown', ({keyCode}) => {
|
||||
if (keyCode === 38 || keyCode === 40) {
|
||||
event.preventDefault()
|
||||
const visibleList = this.listItems.querySelectorAll('.visible:not(.hover)');
|
||||
const firstItem = visibleList[0];
|
||||
const lastItem = this.listItems.querySelectorAll('.visible:not(.hover)')[visibleList.length - 1];
|
||||
if (this.listItems.querySelectorAll('.visible.hover').length === 0) {
|
||||
if (keyCode === 38 && lastItem)
|
||||
lastItem.classList.add('hover')
|
||||
if (keyCode === 40 && firstItem)
|
||||
firstItem.classList.add('hover')
|
||||
} else {
|
||||
const hoverElement = this.listItems.querySelector('.visible.hover')
|
||||
hoverElement.classList.remove('hover')
|
||||
//up
|
||||
if (keyCode === 38) {
|
||||
const prev = this.getPreviousSibling(hoverElement, '.visible')
|
||||
if (prev && prev.classList.contains('visible')) {
|
||||
prev.classList.add('hover')
|
||||
console.log(prev.offsetTop)
|
||||
this.listItems.scrollTo({
|
||||
top: prev.offsetTop - 160,
|
||||
})
|
||||
} else {
|
||||
lastItem.classList.add('hover')
|
||||
this.listItems.scrollTo({
|
||||
top: lastItem.offsetTop - 160,
|
||||
})
|
||||
}
|
||||
}
|
||||
//down
|
||||
if (keyCode === 40) {
|
||||
const next = this.getNextSibling(hoverElement, '.visible')
|
||||
if (next && next.classList.contains('visible')) {
|
||||
next.classList.add('hover')
|
||||
this.listItems.scrollTo({
|
||||
top: next.offsetTop - 160
|
||||
})
|
||||
} else {
|
||||
firstItem.classList.add('hover')
|
||||
this.listItems.scrollTo({
|
||||
top: firstItem.offsetTop - 160,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (keyCode === 13) {
|
||||
event.preventDefault()
|
||||
const currentSelected = this.listItems.querySelector('.hover');
|
||||
if (currentSelected) {
|
||||
const selected = this.listItems.querySelector('.selected');
|
||||
if (!this.container.hasAttribute('multiple')) {
|
||||
if (selected) {
|
||||
selected.classList.remove('selected')
|
||||
}
|
||||
currentSelected.classList.add('selected')
|
||||
this.selected.innerText = currentSelected.innerHTML
|
||||
this.container.value = currentSelected.dataset.select
|
||||
if (this.dynamic.change) {
|
||||
this.dynamic.change(this.container.value)
|
||||
}
|
||||
} else {
|
||||
this.multipleSelected(currentSelected, this.container.querySelector(`option[value="${currentSelected.dataset.select}"]`))
|
||||
}
|
||||
this.container.parentNode.classList.remove('form-error')
|
||||
this.clear()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
multipleSelected(listItem, e) {
|
||||
if (!this.selected.querySelector(`.selected-box[data-id="${e.value}"]`)) {
|
||||
const sb = document.createElement('div')
|
||||
const close = document.createElement('div')
|
||||
sb.className = 'selected-box'
|
||||
close.className = 'close'
|
||||
sb.innerText = e.innerText
|
||||
sb.setAttribute('data-id', e.value)
|
||||
const sbExists = this.selected.querySelector('.selected-box')
|
||||
if (!sbExists) {
|
||||
this.selected.innerHTML = '';
|
||||
}
|
||||
sb.appendChild(close)
|
||||
this.selected.appendChild(sb)
|
||||
listItem.classList.add('selected')
|
||||
e.setAttribute('selected', true)
|
||||
sb.addEventListener('click', () => {
|
||||
event.preventDefault()
|
||||
e.removeAttribute('selected')
|
||||
const listItemSelected = this.listItems.querySelector(`.list-item[data-select="${e.value}"]`)
|
||||
listItemSelected.classList.remove('selected')
|
||||
sb.remove()
|
||||
const sbExists = this.selected.querySelector('.selected-box')
|
||||
if (!sbExists) {
|
||||
var s = this.container.querySelector('option:first-child')
|
||||
if (s)
|
||||
this.selected.innerHTML = s.innerText;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
dataRetrieve() {
|
||||
this.box.classList.add('search-box')
|
||||
this.list.classList.add('list')
|
||||
this.selected.classList.add('selected')
|
||||
this.listItems.classList.add('list-items')
|
||||
this.list.appendChild(this.searchField)
|
||||
this.list.appendChild(this.listItems)
|
||||
this.box.appendChild(this.selected)
|
||||
this.box.appendChild(this.list)
|
||||
if (this.container.hasClass('disabled')) {
|
||||
this.box.classList.add('disabled')
|
||||
}
|
||||
this.box.addEventListener('click', e => {
|
||||
if (
|
||||
!event.target.classList.contains('selected-box') &&
|
||||
!event.target.classList.contains('close')
|
||||
) {
|
||||
if (this.box.classList.contains('open')) {
|
||||
this.clear()
|
||||
} else {
|
||||
document.querySelectorAll('.search-box').forEach(b => {
|
||||
b.classList.remove('open')
|
||||
})
|
||||
this.box.classList.add('open')
|
||||
this.searchField.focus()
|
||||
}
|
||||
}
|
||||
})
|
||||
this.keyDown()
|
||||
this.updateFromOptionList()
|
||||
this.searchField.addEventListener('keyup', e => {
|
||||
this.listItems.querySelectorAll('.list-item').forEach((e, i) => {
|
||||
if (e.innerText.toLowerCase().indexOf(this.searchField.value.toLowerCase()) !== -1 || this.searchField.value === '') {
|
||||
e.style.display = 'block'
|
||||
e.classList.add('visible')
|
||||
} else {
|
||||
e.style.display = 'none'
|
||||
e.classList.remove('visible')
|
||||
e.classList.remove('hover')
|
||||
}
|
||||
})
|
||||
})
|
||||
this.container.parentNode.appendChild(this.box);
|
||||
}
|
||||
|
||||
updateFromOptionList() {
|
||||
this.listItems.innerHTML = '';
|
||||
this.container.querySelectorAll('option').forEach((e, i) => {
|
||||
if (i === 0) {
|
||||
//if (this.selected.innerText !== '')
|
||||
this.selected.innerText = e.innerText
|
||||
}
|
||||
if (e.value) {
|
||||
const listItem = document.createElement('div');
|
||||
listItem.className = 'list-item visible'
|
||||
listItem.setAttribute('data-select', e.value)
|
||||
listItem.innerText = e.innerText;
|
||||
this.listItems.appendChild(listItem)
|
||||
if (e.hasAttribute('selected') && !this.container.hasAttribute('multiple')) {
|
||||
this.selected.innerText = e.innerText
|
||||
listItem.classList.add('selected')
|
||||
this.listItems.scrollTo({
|
||||
top: listItem.offsetTop - 160,
|
||||
})
|
||||
}
|
||||
if (e.hasAttribute('selected') && this.container.hasAttribute('multiple')) {
|
||||
this.multipleSelected(listItem, e)
|
||||
}
|
||||
listItem.addEventListener('click', l => {
|
||||
if (!this.container.hasAttribute('multiple')) {
|
||||
this.container.value = e.value
|
||||
this.selected.innerText = e.innerText
|
||||
if (this.dynamic.change) {
|
||||
this.dynamic.change(this.container.value)
|
||||
}
|
||||
} else {
|
||||
this.multipleSelected(listItem, e)
|
||||
}
|
||||
this.container.parentNode.classList.remove('form-error')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
updateFromObject(object) {
|
||||
const $this = this;
|
||||
object.searchBox.dynamic.change = function (value) {
|
||||
$this.request(object.url, `${object.queryId}=${value}`, data => {
|
||||
//console.log(data);
|
||||
$this.container.innerHTML = $this.container.querySelector('option:first-child').outerHTML
|
||||
data.forEach(o => {
|
||||
if (o[object.data.id] && o[object.data.name]) {
|
||||
const option = document.createElement('option')
|
||||
option.value = o[object.data.id];
|
||||
option.innerText = o[object.data.name]
|
||||
$this.container.appendChild(option)
|
||||
}
|
||||
})
|
||||
if (data.length > 0) {
|
||||
$this.updateFromOptionList()
|
||||
$this.container.classList.remove('disabled')
|
||||
$this.box.classList.remove('disabled')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
request(url, query, callback) {
|
||||
const xhr = new XMLHttpRequest()
|
||||
xhr.responseType = 'json'
|
||||
xhr.open('get', `${url}?${query}`)
|
||||
xhr.onload = () => {
|
||||
callback(xhr.response)
|
||||
}
|
||||
xhr.send()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
function idleLogout() {
|
||||
var t;
|
||||
var sleep = false;
|
||||
window.onload = resetTimer;
|
||||
window.onmousemove = resetTimer;
|
||||
window.onmousedown = resetTimer; // catches touchscreen presses as well
|
||||
window.ontouchstart = resetTimer; // catches touchscreen swipes as well
|
||||
window.ontouchmove = resetTimer; // required by some devices
|
||||
window.onclick = resetTimer; // catches touchpad clicks as well
|
||||
window.onkeydown = resetTimer;
|
||||
window.addEventListener('scroll', resetTimer, true); // improved; see comments
|
||||
|
||||
function yourFunction() {
|
||||
clearTimeout(t);
|
||||
sleep = true;
|
||||
modal.sessionLeft('Вашата сесия ще изтече след ', 60, null, () => {
|
||||
sleep = false;
|
||||
resetTimer();
|
||||
}, () => {
|
||||
let key = document.getElementById('user_type_key');
|
||||
window.location.href = `/${key.value}/logout/`
|
||||
});
|
||||
}
|
||||
|
||||
function resetTimer() {
|
||||
if(!sleep) {
|
||||
clearTimeout(t);
|
||||
t = setTimeout(yourFunction, 9*60*1000); // time is in milliseconds
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//idleLogout();
|
||||
|
||||
function checkActivity() {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('get', '/remote/check-activity/')
|
||||
xhr.responseType = 'json';
|
||||
xhr.onload = () => {
|
||||
if (xhr.response && xhr.response.status) {
|
||||
if (xhr.response.status === 'inactive') {
|
||||
window.location.href = xhr.response.redirect;
|
||||
//console.log(xhr.response)
|
||||
} else {
|
||||
console.log(xhr.response)
|
||||
setTimeout(function () {
|
||||
checkActivity()
|
||||
}, 200000)
|
||||
}
|
||||
}
|
||||
}
|
||||
xhr.send();
|
||||
|
||||
}
|
||||
|
||||
checkActivity();
|
||||
Reference in New Issue
Block a user