Refactoring/rewrite of modal boxes handling

This commit is contained in:
Frederic Guillot
2017-01-02 17:01:27 -05:00
parent d49ce63e51
commit 3833c12ccc
173 changed files with 1526 additions and 1654 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,28 +1,27 @@
KB.component('submit-cancel', function (containerElement, options) {
KB.component('confirm-buttons', function (containerElement, options) {
var isLoading = false;
function onSubmit() {
isLoading = true;
KB.find('#modal-submit-button').replace(buildButton());
KB.trigger('modal.submit');
KB.find('#modal-confirm-button').replace(buildButton());
window.location.href = options.url;
}
function onCancel() {
KB.trigger('modal.cancel');
_KB.get('Popover').close();
KB.trigger('modal.close');
}
function onStop() {
isLoading = false;
KB.find('#modal-submit-button').replace(buildButton());
KB.find('#modal-confirm-button').replace(buildButton());
}
function buildButton() {
var button = KB.dom('button')
.click(onSubmit)
.attr('id', 'modal-submit-button')
.attr('type', 'submit')
.attr('class', 'btn btn-blue');
.attr('id', 'modal-confirm-button')
.attr('type', 'button')
.attr('class', 'btn btn-red');
if (isLoading) {
button
@@ -32,6 +31,10 @@ KB.component('submit-cancel', function (containerElement, options) {
;
}
if (options.tabindex) {
button.attr('tabindex', options.tabindex);
}
return button
.text(options.submitLabel)
.build();

View File

@@ -0,0 +1,192 @@
KB.component('file-upload', function (containerElement, options) {
var inputFileElement = null;
var dropzoneElement = null;
var files = [];
var currentFileIndex = null;
function onProgress(e) {
if (e.lengthComputable) {
var progress = e.loaded / e.total;
var percentage = Math.floor(progress * 100);
KB.find('#file-progress-' + currentFileIndex).attr('value', progress);
KB.find('#file-percentage-' + currentFileIndex).replaceText('(' + percentage + '%)');
}
}
function onError() {
var errorElement = KB.dom('div').addClass('file-error').text(options.labelUploadError).build();
KB.find('#file-item-' + currentFileIndex).add(errorElement);
}
function onComplete() {
currentFileIndex++;
if (currentFileIndex < files.length) {
KB.http.uploadFile(options.url, files[currentFileIndex], onProgress, onComplete, onError);
} else {
KB.trigger('modal.stop');
KB.trigger('modal.hide');
var alertElement = KB.dom('div')
.addClass('alert')
.addClass('alert-success')
.text(options.labelSuccess)
.build();
var buttonElement = KB.dom('button')
.attr('type', 'button')
.addClass('btn')
.addClass('btn-blue')
.click(onCloseWindow)
.text(options.labelCloseSuccess)
.build();
KB.dom(dropzoneElement).replace(KB.dom('div').add(alertElement).add(buttonElement).build());
}
}
function onCloseWindow() {
window.location.reload();
}
function onSubmit() {
currentFileIndex = 0;
uploadFiles();
}
function onFileChange() {
files = inputFileElement.files;
showFiles();
}
function onClickFileBrowser() {
files = [];
currentFileIndex = 0;
inputFileElement.click();
}
function onDragOver(e) {
e.stopPropagation();
e.preventDefault();
}
function onDrop(e) {
e.stopPropagation();
e.preventDefault();
files = e.dataTransfer.files;
showFiles();
}
function uploadFiles() {
if (files.length > 0) {
KB.http.uploadFile(options.url, files[currentFileIndex], onProgress, onComplete, onError);
}
}
function showFiles() {
if (files.length > 0) {
KB.trigger('modal.enable');
KB.dom(dropzoneElement)
.empty()
.add(buildFileListElement());
} else {
KB.trigger('modal.disable');
KB.dom(dropzoneElement)
.empty()
.add(buildInnerDropzoneElement());
}
}
function buildFileInputElement() {
return KB.dom('input')
.attr('id', 'file-input-element')
.attr('type', 'file')
.attr('name', 'files[]')
.attr('multiple', true)
.on('change', onFileChange)
.hide()
.build();
}
function buildInnerDropzoneElement() {
var dropzoneLinkElement = KB.dom('a')
.attr('href', '#')
.text(options.labelChooseFiles)
.click(onClickFileBrowser)
.build();
return KB.dom('div')
.attr('id', 'file-dropzone-inner')
.text(options.labelDropzone + ' ' + options.labelOr + ' ')
.add(dropzoneLinkElement)
.build();
}
function buildDropzoneElement() {
var dropzoneElement = KB.dom('div')
.attr('id', 'file-dropzone')
.add(buildInnerDropzoneElement())
.build();
dropzoneElement.ondragover = onDragOver;
dropzoneElement.ondrop = onDrop;
dropzoneElement.ondragover = onDragOver;
return dropzoneElement;
}
function buildFileListItem(index) {
var isOversize = false;
var progressElement = KB.dom('progress')
.attr('id', 'file-progress-' + index)
.attr('value', 0)
.build();
var percentageElement = KB.dom('span')
.attr('id', 'file-percentage-' + index)
.text('(0%)')
.build();
var itemElement = KB.dom('li')
.attr('id', 'file-item-' + index)
.add(progressElement)
.text(' ' + files[index].name + ' ')
.add(percentageElement);
if (files[index].size > options.maxSize) {
itemElement.add(KB.dom('div').addClass('file-error').text(options.labelOversize).build());
isOversize = true;
}
if (isOversize) {
KB.trigger('modal.disable');
}
return itemElement.build();
}
function buildFileListElement() {
var fileListElement = KB.dom('ul')
.attr('id', 'file-list')
.build();
for (var i = 0; i < files.length; i++) {
fileListElement.appendChild(buildFileListItem(i));
}
return fileListElement;
}
this.render = function () {
KB.on('modal.submit', onSubmit);
inputFileElement = buildFileInputElement();
dropzoneElement = buildDropzoneElement();
containerElement.appendChild(inputFileElement);
containerElement.appendChild(dropzoneElement);
};
});

View File

@@ -15,19 +15,19 @@ KB.keyboardShortcuts = function () {
} else if (forms.length > 1) {
if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') {
$(document.activeElement).parents("form").submit();
} else if (_KB.get("Popover").isOpen()) {
$("#popover-container form").submit();
} else if (KB.modal.isOpen()) {
KB.modal.getForm().submit();
}
}
}
KB.onKey('?', function () {
_KB.get("Popover").open($("body").data("keyboard-shortcut-url"));
KB.modal.open(KB.find('body').data('keyboardShortcutUrl'));
});
KB.onKey('Escape', function () {
if (! KB.exists('#suggest-menu')) {
_KB.get("Popover").close();
KB.trigger('modal.close');
_KB.get("Dropdown").close();
}
});
@@ -49,25 +49,25 @@ KB.keyboardShortcuts = function () {
});
KB.onKey('n', function () {
_KB.get("Popover").open($("#board").data("task-creation-url"));
KB.modal.open(KB.find('#board').data('taskCreationUrl'), 'large', false);
});
}
if (KB.exists('#task-view')) {
KB.onKey('e', function () {
_KB.get("Popover").open(KB.find('#task-view').data('editUrl'));
KB.modal.open(KB.find('#task-view').data('editUrl'), 'large');
});
KB.onKey('c', function () {
_KB.get("Popover").open(KB.find('#task-view').data('commentUrl'));
KB.modal.open(KB.find('#task-view').data('commentUrl'));
});
KB.onKey('s', function () {
_KB.get("Popover").open(KB.find('#task-view').data('subtaskUrl'));
KB.modal.open(KB.find('#task-view').data('subtaskUrl'));
});
KB.onKey('l', function () {
_KB.get("Popover").open(KB.find('#task-view').data('internalLinkUrl'));
KB.modal.open(KB.find('#task-view').data('internalLinkUrl'));
});
}

View File

@@ -0,0 +1,29 @@
(function () {
function getLink(e) {
if (e.target.tagName === 'I') {
return e.target.parentNode.getAttribute('href');
}
return e.target.getAttribute('href')
}
KB.onClick('.js-modal-large', function (e) {
KB.modal.open(getLink(e), 'large', false);
});
KB.onClick('.js-modal-medium', function (e) {
KB.modal.open(getLink(e), 'medium', false);
});
KB.onClick('.js-modal-small', function (e) {
KB.modal.open(getLink(e), 'small', false);
});
KB.onClick('.js-modal-confirm', function (e) {
KB.modal.open(getLink(e), 'small');
});
KB.onClick('.js-modal-close', function () {
KB.modal.close();
});
}());

View File

@@ -0,0 +1,120 @@
KB.component('screenshot', function (containerElement) {
var pasteCatcher = null;
var inputElement = null;
function onPaste(e) {
// Firefox doesn't have the property e.clipboardData.items (only Chrome)
if (e.clipboardData && e.clipboardData.items) {
var items = e.clipboardData.items;
if (items) {
for (var i = 0; i < items.length; i++) {
// Find an image in pasted elements
if (items[i].type.indexOf("image") !== -1) {
var blob = items[i].getAsFile();
// Get the image as base64 data
var reader = new FileReader();
reader.onload = function (event) {
createImage(event.target.result);
};
reader.readAsDataURL(blob);
}
}
}
} else {
// Handle Firefox
setTimeout(checkInput, 100);
}
}
function initialize() {
destroy();
if (! window.Clipboard) {
// Insert the content editable at the top to avoid scrolling down in the board view
pasteCatcher = document.createElement('div');
pasteCatcher.id = 'screenshot-pastezone';
pasteCatcher.contentEditable = true;
pasteCatcher.style.opacity = 0;
pasteCatcher.style.position = 'fixed';
pasteCatcher.style.top = 0;
pasteCatcher.style.right = 0;
pasteCatcher.style.width = 0;
document.body.insertBefore(pasteCatcher, document.body.firstChild);
pasteCatcher.focus();
// Set the focus when clicked anywhere in the document
document.addEventListener('click', setFocus);
// Set the focus when clicked in screenshot dropzone
document.getElementById('screenshot-zone').addEventListener('click', setFocus);
}
window.addEventListener('paste', onPaste, false);
}
function destroy() {
if (pasteCatcher !== null) {
document.body.removeChild(pasteCatcher);
} else if (document.getElementById('screenshot-pastezone')) {
document.body.removeChild(document.getElementById('screenshot-pastezone'));
}
document.removeEventListener('click', setFocus);
pasteCatcher = null;
}
function setFocus() {
if (pasteCatcher !== null) {
pasteCatcher.focus();
}
}
function checkInput() {
var child = pasteCatcher.childNodes[0];
if (child) {
// If the user pastes an image, the src attribute
// will represent the image as a base64 encoded string.
if (child.tagName === 'IMG') {
createImage(child.src);
}
}
pasteCatcher.innerHTML = '';
}
function createImage(blob) {
var pastedImage = new Image();
pastedImage.src = blob;
// Send the image content to the form variable
pastedImage.onload = function() {
var sourceSplit = blob.split('base64,');
inputElement.value = sourceSplit[1];
};
var zone = document.getElementById('screenshot-zone');
zone.innerHTML = '';
zone.className = 'screenshot-pasted';
zone.appendChild(pastedImage);
destroy();
initialize();
}
this.render = function () {
inputElement = KB.dom('input')
.attr('type', 'hidden')
.attr('name', 'screenshot')
.build();
containerElement.appendChild(inputElement);
initialize();
};
});

View File

@@ -1,5 +1,24 @@
KB.component('select-dropdown-autocomplete', function(containerElement, options) {
var componentElement, inputElement, inputHiddenElement;
var componentElement, inputElement, inputHiddenElement, chevronIconElement, loadingIconElement;
function onLoadingStart() {
KB.dom(loadingIconElement).show();
KB.dom(chevronIconElement).hide();
}
function onLoadingStop() {
KB.dom(loadingIconElement).hide();
KB.dom(chevronIconElement).show();
}
function onScroll() {
var menuElement = KB.find('#select-dropdown-menu');
if (menuElement) {
var componentPosition = componentElement.getBoundingClientRect();
menuElement.style('top', (document.body.scrollTop + componentPosition.bottom) + 'px');
}
}
function onKeyDown(e) {
switch (KB.utils.getKey(e)) {
@@ -66,8 +85,10 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
destroyDropdownMenu();
if (options.redirect) {
var regex = new RegExp(options.redirect.regex, 'g');
window.location = options.redirect.url.replace(regex, value);
window.location = options.redirect.url.replace(new RegExp(options.redirect.regex, 'g'), value);
} else if (options.replace) {
onLoadingStart();
KB.modal.replace(options.replace.url.replace(new RegExp(options.replace.regex, 'g'), value));
}
}
@@ -158,7 +179,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
return KB.dom('ul')
.attr('id', 'select-dropdown-menu')
.style('top', componentPosition.bottom + 'px')
.style('top', (document.body.scrollTop + componentPosition.bottom) + 'px')
.style('left', componentPosition.left + 'px')
.style('width', componentPosition.width + 'px')
.style('maxHeight', (window.innerHeight - componentPosition.bottom - 20) + 'px')
@@ -203,11 +224,20 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
}
this.render = function () {
var dropdownIconElement = KB.dom('i')
KB.on('select.dropdown.loading.start', onLoadingStart);
KB.on('select.dropdown.loading.stop', onLoadingStop);
chevronIconElement = KB.dom('i')
.attr('class', 'fa fa-chevron-down select-dropdown-chevron')
.click(toggleDropdownMenu)
.build();
loadingIconElement = KB.dom('span')
.hide()
.addClass('select-loading-icon')
.add(KB.dom('i').attr('class', 'fa fa-spinner fa-pulse').build())
.build();
inputHiddenElement = KB.dom('input')
.attr('type', 'hidden')
.attr('name', options.name)
@@ -227,7 +257,8 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
.addClass('select-dropdown-input-container')
.add(inputHiddenElement)
.add(inputElement)
.add(dropdownIconElement)
.add(chevronIconElement)
.add(loadingIconElement)
.build();
containerElement.appendChild(componentElement);
@@ -237,5 +268,7 @@ KB.component('select-dropdown-autocomplete', function(containerElement, options)
KB.on(eventName, function() { inputElement.focus(); });
});
}
window.addEventListener('scroll', onScroll, false);
};
});

View File

@@ -0,0 +1,94 @@
KB.component('submit-buttons', function (containerElement, options) {
var isLoading = false;
var isDisabled = options.disabled || false;
var submitLabel = options.submitLabel;
var formActionElement = null;
function onSubmit() {
isLoading = true;
KB.find('#modal-submit-button').replace(buildButton());
KB.trigger('modal.submit');
}
function onCancel() {
KB.trigger('modal.close');
}
function onStop() {
isLoading = false;
KB.find('#modal-submit-button').replace(buildButton());
}
function onDisable() {
isLoading = false;
isDisabled = true;
KB.find('#modal-submit-button').replace(buildButton());
}
function onEnable() {
isLoading = false;
isDisabled = false;
KB.find('#modal-submit-button').replace(buildButton());
}
function onHide() {
KB.dom(formActionElement).hide();
}
function onUpdateSubmitLabel(eventData) {
submitLabel = eventData.submitLabel;
KB.find('#modal-submit-button').replace(buildButton());
}
function buildButton() {
var button = KB.dom('button')
.attr('id', 'modal-submit-button')
.attr('type', 'submit')
.attr('class', 'btn btn-' + (options.color || 'blue'));
if (KB.modal.isOpen()) {
button.click(onSubmit);
}
if (options.tabindex) {
button.attr('tabindex', options.tabindex);
}
if (isLoading) {
button
.disable()
.add(KB.dom('i').attr('class', 'fa fa-spinner fa-pulse').build())
.text(' ')
;
}
if (isDisabled) {
button.disable();
}
return button
.text(submitLabel)
.build();
}
this.render = function () {
KB.on('modal.stop', onStop);
KB.on('modal.disable', onDisable);
KB.on('modal.enable', onEnable);
KB.on('modal.hide', onHide);
KB.on('modal.submit.label', onUpdateSubmitLabel);
var formActionElementBuilder = KB.dom('div')
.attr('class', 'form-actions')
.add(buildButton());
if (KB.modal.isOpen()) {
formActionElementBuilder
.text(' ' + options.orLabel + ' ')
.add(KB.dom('a').attr('href', '#').click(onCancel).text(options.cancelLabel).build())
}
formActionElement = formActionElementBuilder.build();
containerElement.appendChild(formActionElement);
};
});

View File

@@ -62,7 +62,7 @@ KB.component('suggest-menu', function(containerElement, options) {
}
function getParentElement() {
var selectors = ['.popover-form', '#popover-content', 'body'];
var selectors = ['#modal-content form', '#modal-content', 'body'];
for (var i = 0; i < selectors.length; i++) {
var element = document.querySelector(selectors[i]);

View File

@@ -74,8 +74,6 @@ KB.component('task-move-position', function (containerElement, options) {
"column_id": getColumnId(),
"swimlane_id": getSwimlaneId(),
"position": position
}).success(function () {
window.location.reload(true);
}).error(function (response) {
if (response) {
onError(response.message);
@@ -150,7 +148,6 @@ KB.component('task-move-position', function (containerElement, options) {
KB.on('modal.submit', onSubmit);
var form = KB.dom('div')
.on('submit', onSubmit)
.add(KB.dom('div').attr('id', 'message-container').build())
.add(KB.html.label(options.swimlaneLabel, 'form-swimlanes'))
.add(buildSwimlaneSelect())

View File

@@ -22,9 +22,7 @@ KB.on = function (eventType, callback) {
KB.trigger = function (eventType, eventData) {
if (this.listeners.internals.hasOwnProperty(eventType)) {
for (var i = 0; i < this.listeners.internals[eventType].length; i++) {
if (! this.listeners.internals[eventType][i](eventData)) {
break;
}
this.listeners.internals[eventType][i](eventData);
}
}
};

View File

@@ -88,6 +88,11 @@ KB.dom = function (tag) {
return this;
};
this.replaceText = function (text) {
element.textContent = text;
return this;
};
this.addClass = function (className) {
element.classList.add(className);
return this;
@@ -122,6 +127,13 @@ KB.dom = function (tag) {
return this;
};
this.empty = function () {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
return this;
};
this.parent = function (selector) {
for (; element && element !== document; element = element.parentNode) {
if (element.matches(selector)) {

View File

@@ -3,11 +3,21 @@ KB.http.request = function (method, url, headers, body) {
var errorCallback = function() {};
function parseResponse(request) {
try {
return JSON.parse(request.responseText);
} catch (e) {
return request.responseText;
var redirect = request.getResponseHeader('X-Ajax-Redirect');
if (redirect === 'self') {
window.location.reload();
} else if (redirect && redirect.indexOf('#') > -1) {
window.location = redirect.split('#')[0];
} else if (redirect) {
window.location = redirect;
} else if (request.getResponseHeader('Content-Type') === 'application/json') {
try {
return JSON.parse(request.responseText);
} catch (e) {}
}
return request.responseText;
}
this.execute = function () {
@@ -64,3 +74,20 @@ KB.http.postJson = function (url, body) {
return (new KB.http.request('POST', url, headers, JSON.stringify(body))).execute();
};
KB.http.postForm = function (url, formElement) {
var formData = new FormData(formElement);
return (new KB.http.request('POST', url, {}, formData)).execute();
};
KB.http.uploadFile = function (url, file, onProgress, onComplete, onError) {
var fd = new FormData();
fd.append('files[]', file);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', onProgress);
xhr.upload.addEventListener('load', onComplete);
xhr.upload.addEventListener('error', onError);
xhr.open('POST', url, true);
xhr.send(fd);
};

172
assets/js/core/modal.js Normal file
View File

@@ -0,0 +1,172 @@
(function () {
var isOpen = false;
function onOverlayClick(e) {
if (e.target.matches('#modal-overlay')) {
e.stopPropagation();
e.preventDefault();
destroy();
}
}
function onCloseButtonClick() {
destroy();
}
function onFormSubmit() {
KB.trigger('modal.loading');
submitForm();
}
function getForm() {
return document.querySelector('#modal-content form');
}
function submitForm() {
var form = getForm();
if (form) {
var url = form.getAttribute('action');
if (url) {
KB.http.postForm(url, form).success(function (response) {
KB.trigger('modal.stop');
if (response) {
replace(response);
} else {
destroy();
}
});
}
}
}
function afterRendering() {
var formElement = KB.find('#modal-content form');
if (formElement) {
formElement.on('submit', onFormSubmit, false);
}
var autoFocusElement = document.querySelector('#modal-content input[autofocus]');
if (autoFocusElement) {
autoFocusElement.focus();
}
KB.render();
_KB.datePicker();
_KB.autoComplete();
_KB.tagAutoComplete();
_KB.get('Task').onPopoverOpened();
}
function replace(html) {
var contentElement = KB.find('#modal-content');
if (contentElement) {
contentElement.replace(KB.dom('div')
.attr('id', 'modal-content')
.html(html)
.build()
);
afterRendering();
}
}
function create(html, width, overlayClickDestroy) {
var closeButtonElement = KB.dom('a')
.attr('href', '#')
.attr('id', 'modal-close-button')
.html('<i class="fa fa-times"></i>')
.click(onCloseButtonClick)
.build();
var headerElement = KB.dom('div')
.attr('id', 'modal-header')
.add(closeButtonElement)
.build();
var contentElement = KB.dom('div')
.attr('id', 'modal-content')
.html(html)
.build();
var boxElement = KB.dom('div')
.attr('id', 'modal-box')
.style('width', width)
.add(headerElement)
.add(contentElement)
.build();
var overlayElement = KB.dom('div')
.attr('id', 'modal-overlay')
.add(boxElement)
.build();
if (overlayClickDestroy) {
overlayElement.addEventListener('click', onOverlayClick, false);
}
document.body.appendChild(overlayElement);
afterRendering();
}
function destroy() {
var overlayElement = KB.find('#modal-overlay');
if (overlayElement) {
overlayElement.remove();
}
}
function getWidth(size) {
var viewport = KB.utils.getViewportSize();
switch (size) {
case 'large':
return viewport.width < 1300 ? '95%' : '1300px';
case 'medium':
return viewport.width < 1024 ? '70%' : '1024px';
}
return viewport.width < 800 ? '75%' : '800px';
}
KB.on('modal.close', function () {
destroy();
});
KB.on('modal.submit', function () {
submitForm();
});
KB.modal = {
open: function (url, size, overlayClickDestroy) {
_KB.get('Dropdown').close();
destroy();
if (typeof overlayClickDestroy === 'undefined') {
overlayClickDestroy = true;
}
KB.http.get(url).success(function (response) {
isOpen = true;
create(response, getWidth(size), overlayClickDestroy);
});
},
close: function () {
isOpen = false;
destroy();
},
isOpen: function () {
return isOpen;
},
replace: function (url) {
KB.http.get(url).success(function (response) {
replace(response);
});
},
getForm: getForm
};
}());

View File

@@ -87,3 +87,10 @@ KB.utils.getKey = function (e) {
return e.key;
};
KB.utils.getViewportSize = function () {
return {
width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
};
};

View File

@@ -2,6 +2,7 @@ Kanboard.BoardTask = function(app) {
this.app = app;
};
// TODO: rewrite this code
Kanboard.BoardTask.prototype.listen = function() {
var self = this;

View File

@@ -2,6 +2,7 @@ Kanboard.Dropdown = function(app) {
this.app = app;
};
// TODO: rewrite this code
Kanboard.Dropdown.prototype.listen = function() {
var self = this;
@@ -9,10 +10,6 @@ Kanboard.Dropdown.prototype.listen = function() {
self.close();
});
$(document).on('click', '#popover-content', function() {
self.close();
});
$(document).on('click', '.dropdown-menu', function(e) {
e.preventDefault();
e.stopImmediatePropagation();
@@ -56,7 +53,3 @@ Kanboard.Dropdown.prototype.listen = function() {
Kanboard.Dropdown.prototype.close = function() {
$("#dropdown").remove();
};
Kanboard.Dropdown.prototype.onPopoverOpened = function() {
this.close();
};

View File

@@ -1,125 +0,0 @@
Kanboard.FileUpload = function(app) {
this.app = app;
this.files = [];
this.currentFile = 0;
};
Kanboard.FileUpload.prototype.onPopoverOpened = function() {
var dropzone = document.getElementById("file-dropzone");
var self = this;
if (dropzone) {
dropzone.ondragover = dropzone.ondragenter = function(e) {
e.stopPropagation();
e.preventDefault();
};
dropzone.ondrop = function(e) {
e.stopPropagation();
e.preventDefault();
self.files = e.dataTransfer.files;
self.show();
$("#file-error-max-size").hide();
};
$(document).on("click", "#file-browser", function(e) {
e.preventDefault();
$("#file-form-element").get(0).click();
});
$(document).on("click", "#file-upload-button", function(e) {
e.preventDefault();
self.currentFile = 0;
self.checkFiles();
});
$("#file-form-element").change(function() {
self.files = document.getElementById("file-form-element").files;
self.show();
$("#file-error-max-size").hide();
});
}
};
Kanboard.FileUpload.prototype.show = function() {
$("#file-list").remove();
if (this.files.length > 0) {
$("#file-upload-button").prop("disabled", false);
$("#file-dropzone-inner").hide();
var ul = jQuery("<ul>", {"id": "file-list"});
for (var i = 0; i < this.files.length; i++) {
var percentage = jQuery("<span>", {"id": "file-percentage-" + i}).append("(0%)");
var progress = jQuery("<progress>", {"id": "file-progress-" + i, "value": 0});
var li = jQuery("<li>", {"id": "file-label-" + i})
.append(progress)
.append("&nbsp;")
.append(this.files[i].name)
.append("&nbsp;")
.append(percentage);
ul.append(li);
}
$("#file-dropzone").append(ul);
} else {
$("#file-dropzone-inner").show();
}
};
Kanboard.FileUpload.prototype.checkFiles = function() {
var max = parseInt($("#file-dropzone").data("max-size"));
for (var i = 0; i < this.files.length; i++) {
if (this.files[i].size > max) {
$("#file-error-max-size").show();
$("#file-label-" + i).addClass("file-error");
$("#file-upload-button").prop("disabled", true);
return;
}
}
this.uploadFiles();
};
Kanboard.FileUpload.prototype.uploadFiles = function() {
if (this.files.length > 0) {
this.uploadFile(this.files[this.currentFile]);
}
};
Kanboard.FileUpload.prototype.uploadFile = function(file) {
var dropzone = document.getElementById("file-dropzone");
var url = dropzone.dataset.url;
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.upload.addEventListener("progress", this.updateProgress.bind(this));
xhr.upload.addEventListener("load", this.transferComplete.bind(this));
xhr.open("POST", url, true);
fd.append('files[]', file);
xhr.send(fd);
};
Kanboard.FileUpload.prototype.updateProgress = function(e) {
if (e.lengthComputable) {
$("#file-progress-" + this.currentFile).val(e.loaded / e.total);
$("#file-percentage-" + this.currentFile).text('(' + Math.floor((e.loaded / e.total) * 100) + '%)');
}
};
Kanboard.FileUpload.prototype.transferComplete = function() {
this.currentFile++;
if (this.currentFile < this.files.length) {
this.uploadFile(this.files[this.currentFile]);
} else {
var uploadButton = $("#file-upload-button");
uploadButton.prop("disabled", true);
uploadButton.parent().hide();
$("#file-done").show();
}
};

View File

@@ -1,161 +0,0 @@
Kanboard.Popover = function(app) {
this.app = app;
};
Kanboard.Popover.prototype.listen = function() {
var self = this;
$(document).on("click", ".popover", function(e) {
self.onClick(e);
});
$(document).on("click", ".close-popover", function(e) {
self.close(e);
});
$(document).on("click", "#popover-close-button", function(e) {
self.close(e);
});
$(document).on("click", "#popover-content", function(e) {
e.stopPropagation();
});
};
Kanboard.Popover.prototype.onClick = function(e) {
e.preventDefault();
e.stopPropagation();
var target = e.currentTarget || e.target;
var link = target.getAttribute("href");
if (! link) {
link = target.getAttribute("data-href");
}
if (link) {
this.open(link);
}
};
Kanboard.Popover.prototype.isOpen = function() {
return $('#popover-container').size() > 0;
};
Kanboard.Popover.prototype.open = function(link) {
var self = this;
if (!self.isOpen()) {
$.get(link, function(content) {
$("body").prepend(
'<div id="popover-container">' +
'<div id="popover-content">' +
'<div id="popover-content-header"><a href="#" id="popover-close-button"><i class="fa fa-times"></i></a></div>' +
content +
'</div>' +
'</div>'
);
self.executeOnOpenedListeners();
});
}
};
Kanboard.Popover.prototype.close = function(e) {
if (this.isOpen()) {
if (e) {
e.preventDefault();
}
$("#popover-container").remove();
this.executeOnClosedListeners();
}
};
Kanboard.Popover.prototype.ajaxReload = function(data, request, self) {
var redirect = request.getResponseHeader("X-Ajax-Redirect");
if (redirect === 'self') {
window.location.reload();
} else if (redirect && redirect.indexOf('#') > -1) {
window.location = redirect.split('#')[0];
} else if (redirect) {
window.location = redirect;
} else {
$("#popover-content").html(data);
$("#popover-content input[autofocus]").focus();
self.executeOnOpenedListeners();
}
};
Kanboard.Popover.prototype.executeOnOpenedListeners = function() {
for (var className in this.app.controllers) {
var controller = this.app.get(className);
if (typeof controller.onPopoverOpened === "function") {
controller.onPopoverOpened();
}
}
this.afterOpen();
};
Kanboard.Popover.prototype.executeOnClosedListeners = function() {
for (var className in this.app.controllers) {
var controller = this.app.get(className);
if (typeof controller.onPopoverClosed === "function") {
controller.onPopoverClosed();
}
}
};
Kanboard.Popover.prototype.afterOpen = function() {
var self = this;
var popoverForm = $("#popover-content .popover-form");
// Submit forms with Ajax request
if (popoverForm) {
popoverForm.on("submit", function(e) {
e.preventDefault();
$.ajax({
type: "POST",
url: popoverForm.attr("action"),
data: popoverForm.serialize(),
success: function(data, textStatus, request) {
self.ajaxReload(data, request, self);
},
beforeSend: function() {
var button = $('.popover-form button[type="submit"]');
button.html('<i class="fa fa-spinner fa-pulse"></i> ' + button.html());
button.attr("disabled", true);
}
});
});
}
// Submit link with Ajax request
$(document).on("click", ".popover-link", function(e) {
e.preventDefault();
$.ajax({
type: "GET",
url: $(this).attr("href"),
success: function(data, textStatus, request) {
self.ajaxReload(data, request, self);
}
});
});
// Autofocus fields (html5 autofocus works only with page onload)
$("#popover-content input[autofocus]").each(function() {
$(this).focus();
});
this.app.datePicker();
this.app.autoComplete();
this.app.tagAutoComplete();
KB.render();
};

View File

@@ -1,134 +0,0 @@
Kanboard.Screenshot = function(app) {
this.app = app;
this.pasteCatcher = null;
};
Kanboard.Screenshot.prototype.onPopoverOpened = function() {
if (this.app.hasId("screenshot-zone")) {
this.initialize();
}
};
// Setup event listener and workarounds
Kanboard.Screenshot.prototype.initialize = function() {
this.destroy();
if (! window.Clipboard) {
// Create a contenteditable element
this.pasteCatcher = document.createElement("div");
this.pasteCatcher.id = "screenshot-pastezone";
this.pasteCatcher.contentEditable = "true";
// Insert the content editable at the top to avoid scrolling down in the board view
this.pasteCatcher.style.opacity = 0;
this.pasteCatcher.style.position = "fixed";
this.pasteCatcher.style.top = 0;
this.pasteCatcher.style.right = 0;
this.pasteCatcher.style.width = 0;
document.body.insertBefore(this.pasteCatcher, document.body.firstChild);
// Set focus on the contenteditable element
this.pasteCatcher.focus();
// Set the focus when clicked anywhere in the document
document.addEventListener("click", this.setFocus.bind(this));
// Set the focus when clicked in screenshot dropzone (popover)
document.getElementById("screenshot-zone").addEventListener("click", this.setFocus.bind(this));
}
window.addEventListener("paste", this.pasteHandler.bind(this));
};
// Destroy contentEditable element
Kanboard.Screenshot.prototype.destroy = function() {
if (this.pasteCatcher !== null) {
document.body.removeChild(this.pasteCatcher);
}
else if (document.getElementById("screenshot-pastezone")) {
document.body.removeChild(document.getElementById("screenshot-pastezone"));
}
document.removeEventListener("click", this.setFocus.bind(this));
this.pasteCatcher = null;
};
// Set focus on contentEditable element
Kanboard.Screenshot.prototype.setFocus = function() {
if (this.pasteCatcher !== null) {
this.pasteCatcher.focus();
}
};
// Paste event callback
Kanboard.Screenshot.prototype.pasteHandler = function(e) {
// Firefox doesn't have the property e.clipboardData.items (only Chrome)
if (e.clipboardData && e.clipboardData.items) {
var items = e.clipboardData.items;
if (items) {
for (var i = 0; i < items.length; i++) {
// Find an image in pasted elements
if (items[i].type.indexOf("image") !== -1) {
var blob = items[i].getAsFile();
// Get the image as base64 data
var reader = new FileReader();
var self = this;
reader.onload = function(event) {
self.createImage(event.target.result);
};
reader.readAsDataURL(blob);
}
}
}
}
else {
// Handle Firefox
setTimeout(this.checkInput.bind(this), 100);
}
};
// Parse the input in the paste catcher element
Kanboard.Screenshot.prototype.checkInput = function() {
var child = this.pasteCatcher.childNodes[0];
if (child) {
// If the user pastes an image, the src attribute
// will represent the image as a base64 encoded string.
if (child.tagName === "IMG") {
this.createImage(child.src);
}
}
this.pasteCatcher.innerHTML = "";
};
// Creates a new image from a given source
Kanboard.Screenshot.prototype.createImage = function(blob) {
var pastedImage = new Image();
pastedImage.src = blob;
// Send the image content to the form variable
pastedImage.onload = function() {
var sourceSplit = blob.split("base64,");
var sourceString = sourceSplit[1];
$("input[name=screenshot]").val(sourceString);
};
var zone = document.getElementById("screenshot-zone");
zone.innerHTML = "";
zone.className = "screenshot-pasted";
zone.appendChild(pastedImage);
this.destroy();
this.initialize();
};

View File

@@ -15,6 +15,7 @@ Kanboard.Search.prototype.focus = function() {
});
};
// TODO: rewrite this code
Kanboard.Search.prototype.listen = function() {
$(document).on("click", ".filter-helper", function (e) {
e.preventDefault();

View File

@@ -2,9 +2,9 @@ Kanboard.Task = function(app) {
this.app = app;
};
// TODO: rewrite this code
Kanboard.Task.prototype.onPopoverOpened = function() {
var self = this;
var reloadingProjectId = 0;
self.renderColorPicker();
@@ -19,29 +19,6 @@ Kanboard.Task.prototype.onPopoverOpened = function() {
$(dropdownId).val(currentId);
}
});
// Reload page when a destination project is changed
$(document).on("change", "select.task-reload-project-destination", function() {
if (reloadingProjectId > 0) {
$(this).val(reloadingProjectId);
}
else {
reloadingProjectId = $(this).val();
var url = $(this).data("redirect").replace(/PROJECT_ID/g, reloadingProjectId);
$(".loading-icon").show();
$.ajax({
type: "GET",
url: url,
success: function(data, textStatus, request) {
reloadingProjectId = 0;
$(".loading-icon").hide();
self.app.get("Popover").ajaxReload(data, request, self.app.get("Popover"));
}
});
}
});
};
Kanboard.Task.prototype.renderColorPicker = function() {