Refactoring/rewrite of modal boxes handling
This commit is contained in:
2
assets/css/app.min.css
vendored
2
assets/css/app.min.css
vendored
File diff suppressed because one or more lines are too long
6
assets/js/app.min.js
vendored
6
assets/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -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();
|
||||
192
assets/js/components/file-upload.js
Normal file
192
assets/js/components/file-upload.js
Normal 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);
|
||||
};
|
||||
});
|
||||
@@ -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'));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
29
assets/js/components/modal.js
Normal file
29
assets/js/components/modal.js
Normal 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();
|
||||
});
|
||||
}());
|
||||
120
assets/js/components/screenshot.js
Normal file
120
assets/js/components/screenshot.js
Normal 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();
|
||||
};
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
});
|
||||
|
||||
94
assets/js/components/submit-buttons.js
Normal file
94
assets/js/components/submit-buttons.js
Normal 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);
|
||||
};
|
||||
});
|
||||
@@ -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]);
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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
172
assets/js/core/modal.js
Normal 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
|
||||
};
|
||||
}());
|
||||
@@ -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)
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@ Kanboard.BoardTask = function(app) {
|
||||
this.app = app;
|
||||
};
|
||||
|
||||
// TODO: rewrite this code
|
||||
Kanboard.BoardTask.prototype.listen = function() {
|
||||
var self = this;
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
|
||||
@@ -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(" ")
|
||||
.append(this.files[i].name)
|
||||
.append(" ")
|
||||
.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();
|
||||
}
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -65,11 +65,10 @@ td.board-column-task-collapsed
|
||||
.board-add-icon
|
||||
float: left
|
||||
padding: 0 5px
|
||||
a
|
||||
i
|
||||
text-decoration: none
|
||||
color: link-color('primary')
|
||||
font-size: size('xlarge')
|
||||
line-height: 70%
|
||||
font-size: size('large')
|
||||
&:focus, &:hover
|
||||
text-decoration: none
|
||||
color: red
|
||||
|
||||
@@ -26,6 +26,6 @@
|
||||
padding-bottom: 8px
|
||||
border-bottom: 1px dotted #ddd
|
||||
width: 95%
|
||||
&.file-error
|
||||
.file-error
|
||||
font-weight: bold
|
||||
color: color('error')
|
||||
|
||||
@@ -1,13 +1,19 @@
|
||||
@import variables
|
||||
@import mixins
|
||||
|
||||
form
|
||||
margin-bottom: 20px
|
||||
fieldset
|
||||
border: 1px solid #ccc
|
||||
margin-top: 20px
|
||||
|
||||
legend
|
||||
font-weight: 500
|
||||
font-size: size('medium')
|
||||
|
||||
label
|
||||
cursor: pointer
|
||||
display: block
|
||||
margin-top: 10px
|
||||
font-weight: 400
|
||||
|
||||
input
|
||||
&[type="number"], &[type="date"], &[type="email"], &[type="password"], &[type="text"]:not(.input-addon-field)
|
||||
@@ -19,7 +25,6 @@ input
|
||||
height: 25px
|
||||
padding-bottom: 0
|
||||
font-family: sans-serif
|
||||
margin-top: 10px
|
||||
+appearance
|
||||
@include placeholder
|
||||
color: color('lighter')
|
||||
|
||||
@@ -10,3 +10,6 @@ a
|
||||
&:hover
|
||||
color: link-color('hover')
|
||||
text-decoration: none
|
||||
.fa
|
||||
text-decoration: none
|
||||
color: color('primary')
|
||||
@@ -1,6 +1,7 @@
|
||||
@import variables
|
||||
|
||||
.text-editor
|
||||
margin-top: 10px
|
||||
a
|
||||
font-size: size('normal')
|
||||
color: color('light')
|
||||
@@ -10,7 +11,12 @@
|
||||
color: link-color('primary')
|
||||
.text-editor-preview-area
|
||||
border: 1px solid color('lighter')
|
||||
width: 400px
|
||||
height: 200px
|
||||
width: 700px
|
||||
max-width: 99%
|
||||
height: 250px
|
||||
overflow: auto
|
||||
padding: 2px
|
||||
textarea
|
||||
width: 700px
|
||||
max-width: 98%
|
||||
height: 250px
|
||||
@@ -1,7 +1,7 @@
|
||||
@import variables
|
||||
@import mixins
|
||||
|
||||
#popover-container
|
||||
#modal-overlay
|
||||
position: fixed
|
||||
top: 0
|
||||
left: 0
|
||||
@@ -11,25 +11,24 @@
|
||||
overflow: auto
|
||||
z-index: 100
|
||||
|
||||
#popover-content
|
||||
#modal-box
|
||||
position: fixed
|
||||
width: 950px
|
||||
max-width: 95%
|
||||
max-height: calc(100% - 50px)
|
||||
top: 5%
|
||||
top: 2%
|
||||
left: 50%
|
||||
transform: translateX(-50%)
|
||||
padding: 0 15px 15px
|
||||
background: #fff
|
||||
overflow: auto
|
||||
border-radius: 5px
|
||||
|
||||
#popover-content-header
|
||||
#modal-content
|
||||
padding: 0 5px 5px
|
||||
|
||||
#modal-header
|
||||
text-align: right
|
||||
padding-right: 5px
|
||||
|
||||
#popover-close-button
|
||||
#modal-close-button
|
||||
color: color('primary')
|
||||
&:hover
|
||||
color: color('error')
|
||||
|
||||
.popover-form
|
||||
margin-bottom: 0
|
||||
@@ -45,3 +45,8 @@
|
||||
top: 4px
|
||||
right: 5px
|
||||
cursor: pointer
|
||||
.select-loading-icon
|
||||
color: color('medium')
|
||||
position: absolute
|
||||
top: 4px
|
||||
right: 5px
|
||||
32
assets/sass/_task_form.sass
Normal file
32
assets/sass/_task_form.sass
Normal file
@@ -0,0 +1,32 @@
|
||||
@import variables
|
||||
|
||||
.task-form-container
|
||||
@include grid(100)
|
||||
|
||||
.task-form-main-column
|
||||
@include grid_width(60/100)
|
||||
@include custom-device(1000px)
|
||||
@include grid_width(1)
|
||||
|
||||
input[type="text"]
|
||||
width: 700px
|
||||
max-width: 99%
|
||||
|
||||
.task-form-secondary-column
|
||||
max-width: 250px
|
||||
min-width: 200px
|
||||
max-height: 600px
|
||||
padding-left: 10px
|
||||
overflow: auto
|
||||
@include grid_width(20/100)
|
||||
@include custom-device(1000px)
|
||||
@include grid_width(1)
|
||||
max-width: 99%
|
||||
max-height: none
|
||||
label:first-child
|
||||
margin-top: 0
|
||||
@include custom-device(1000px)
|
||||
margin-top: 10px
|
||||
|
||||
.task-form-bottom
|
||||
@include grid_width(1)
|
||||
@@ -15,7 +15,7 @@
|
||||
@import select_dropdown
|
||||
@import suggest_menu
|
||||
@import dialog_box
|
||||
@import popover
|
||||
@import modal
|
||||
@import pagination
|
||||
@import header
|
||||
@import logo
|
||||
@@ -41,6 +41,7 @@
|
||||
@import task_board_date
|
||||
@import task_tags
|
||||
@import task_summary
|
||||
@import task_form
|
||||
@import task_listing
|
||||
@import comment
|
||||
@import subtasks
|
||||
|
||||
Reference in New Issue
Block a user