Rewrite task move position component and remove Vuejs
This commit is contained in:
@@ -2,45 +2,22 @@
|
|||||||
<h2><?= t('Move task to another position on the board') ?></h2>
|
<h2><?= t('Move task to another position on the board') ?></h2>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script type="x/template" id="template-task-move-position">
|
<form>
|
||||||
<?= $this->form->label(t('Swimlane'), 'swimlane') ?>
|
|
||||||
<select v-model="swimlaneId" @change="onChangeSwimlane()" id="form-swimlane">
|
|
||||||
<option v-for="swimlane in board" v-bind:value="swimlane.id">
|
|
||||||
{{ swimlane.name }}
|
|
||||||
</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<div v-if="columns.length > 0">
|
<?= $this->app->component('task-move-position', array(
|
||||||
<?= $this->form->label(t('Column'), 'column') ?>
|
'saveUrl' => $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
|
||||||
<select v-model="columnId" @change="onChangeColumn()" id="form-column">
|
'board' => $board,
|
||||||
<option v-for="column in columns" v-bind:value="column.id">
|
'swimlaneLabel' => t('Swimlane'),
|
||||||
{{ column.title }}
|
'columnLabel' => t('Column'),
|
||||||
</option>
|
'positionLabel' => t('Position'),
|
||||||
</select>
|
'beforeLabel' => t('Insert before this task'),
|
||||||
</div>
|
'afterLabel' => t('Insert after this task'),
|
||||||
|
)) ?>
|
||||||
|
|
||||||
<div v-if="tasks.length > 0">
|
<?= $this->app->component('submit-cancel', array(
|
||||||
<?= $this->form->label(t('Position'), 'position') ?>
|
'submitLabel' => t('Save'),
|
||||||
<select v-model="position" id="form-position">
|
'orLabel' => t('or'),
|
||||||
<option v-for="task in tasks" v-bind:value="task.position">#{{ task.id }} - {{ task.title }}</option>
|
'cancelLabel' => t('cancel'),
|
||||||
</select>
|
)) ?>
|
||||||
<label><input type="radio" value="before" v-model="positionChoice"><?= t('Insert before this task') ?></label>
|
|
||||||
<label><input type="radio" value="after" v-model="positionChoice"><?= t('Insert after this task') ?></label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="errorMessage">
|
</form>
|
||||||
<div class="alert alert-error">{{ errorMessage }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<submit-cancel
|
|
||||||
label-button="<?= t('Save') ?>"
|
|
||||||
label-or="<?= t('or') ?>"
|
|
||||||
label-cancel="<?= t('cancel') ?>"
|
|
||||||
:callback="onSubmit">
|
|
||||||
</submit-cancel>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<task-move-position
|
|
||||||
save-url="<?= $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
|
|
||||||
:board='<?= json_encode($board, JSON_HEX_APOS) ?>'
|
|
||||||
></task-move-position>
|
|
||||||
|
|||||||
4
assets/js/app.min.js
vendored
4
assets/js/app.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,6 +3,6 @@ KB.onClick('.accordion-toggle', function(e) {
|
|||||||
|
|
||||||
if (section) {
|
if (section) {
|
||||||
KB.dom(section).toggleClass('accordion-collapsed');
|
KB.dom(section).toggleClass('accordion-collapsed');
|
||||||
KB.dom(KB.dom(section).child('.accordion-content')).toggle();
|
KB.dom(KB.dom(section).find('.accordion-content')).toggle();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,35 +1,52 @@
|
|||||||
Vue.component('submit-cancel', {
|
KB.component('submit-cancel', function (containerElement, options) {
|
||||||
props: ['labelButton', 'labelOr', 'labelCancel', 'callback'],
|
var isLoading = false;
|
||||||
template: '<div class="form-actions">' +
|
|
||||||
'<button type="button" class="btn btn-blue" @click="onSubmit" :disabled="isLoading">' +
|
function onSubmit() {
|
||||||
'<span v-show="isLoading"><i class="fa fa-spinner fa-pulse"></i> </span>' +
|
isLoading = true;
|
||||||
'{{ labelButton }}' +
|
KB.find('#modal-submit-button').replace(buildButton());
|
||||||
'</button> ' +
|
KB.trigger('modal.submit');
|
||||||
'{{ labelOr }} <a href="#" v-on:click.prevent="onCancel">{{ labelCancel }}</a>' +
|
|
||||||
'</div>'
|
|
||||||
,
|
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
loading: false
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isLoading: function () {
|
|
||||||
return this.loading;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onSubmit: function () {
|
|
||||||
this.loading = true;
|
|
||||||
this.callback();
|
|
||||||
},
|
|
||||||
onCancel: function () {
|
|
||||||
_KB.get('Popover').close();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
events: {
|
|
||||||
'submitCancelled': function() {
|
|
||||||
this.loading = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onCancel() {
|
||||||
|
KB.trigger('modal.cancel');
|
||||||
|
_KB.get('Popover').close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onStop() {
|
||||||
|
isLoading = false;
|
||||||
|
KB.find('#modal-submit-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');
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
button
|
||||||
|
.disable()
|
||||||
|
.add(KB.dom('i').attr('class', 'fa fa-spinner fa-pulse').build())
|
||||||
|
.text(' ')
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return button
|
||||||
|
.text(options.submitLabel)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render = function () {
|
||||||
|
KB.on('modal.stop', onStop);
|
||||||
|
|
||||||
|
var element = KB.dom('div')
|
||||||
|
.attr('class', 'form-actions')
|
||||||
|
.add(buildButton())
|
||||||
|
.text(' ' + options.orLabel + ' ')
|
||||||
|
.add(KB.dom('a').attr('href', '#').click(onCancel).text(options.cancelLabel).build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
containerElement.appendChild(element);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,86 +1,164 @@
|
|||||||
Vue.component('task-move-position', {
|
KB.component('task-move-position', function (containerElement, options) {
|
||||||
props: ['board', 'saveUrl'],
|
|
||||||
template: '#template-task-move-position',
|
function getSelectedValue(id) {
|
||||||
data: function () {
|
var element = KB.dom(document).find('#' + id);
|
||||||
return {
|
|
||||||
swimlaneId: 0,
|
if (element) {
|
||||||
columnId: 0,
|
return parseInt(element.options[element.selectedIndex].value);
|
||||||
position: 1,
|
|
||||||
columns: [],
|
|
||||||
tasks: [],
|
|
||||||
positionChoice: 'before',
|
|
||||||
errorMessage: ''
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
ready: function () {
|
|
||||||
this.columns = this.board[0].columns;
|
|
||||||
this.columnId = this.columns[0].id;
|
|
||||||
this.tasks = this.columns[0].tasks;
|
|
||||||
this.errorMessage = '';
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
onChangeSwimlane: function () {
|
|
||||||
var self = this;
|
|
||||||
this.columnId = 0;
|
|
||||||
this.position = 1;
|
|
||||||
this.columns = [];
|
|
||||||
this.tasks = [];
|
|
||||||
this.positionChoice = 'before';
|
|
||||||
|
|
||||||
this.board.forEach(function(swimlane) {
|
return null;
|
||||||
if (swimlane.id === self.swimlaneId) {
|
|
||||||
self.columns = swimlane.columns;
|
|
||||||
self.tasks = self.columns[0].tasks;
|
|
||||||
self.columnId = self.columns[0].id;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChangeColumn: function () {
|
|
||||||
var self = this;
|
|
||||||
this.position = 1;
|
|
||||||
this.tasks = [];
|
|
||||||
this.positionChoice = 'before';
|
|
||||||
|
|
||||||
this.columns.forEach(function(column) {
|
|
||||||
if (column.id == self.columnId) {
|
|
||||||
self.tasks = column.tasks;
|
|
||||||
|
|
||||||
if (self.tasks.length > 0) {
|
|
||||||
self.position = parseInt(self.tasks[0]['position']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSubmit: function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
if (this.positionChoice == 'after') {
|
|
||||||
this.position++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
cache: false,
|
|
||||||
url: this.saveUrl,
|
|
||||||
contentType: "application/json",
|
|
||||||
type: "POST",
|
|
||||||
processData: false,
|
|
||||||
data: JSON.stringify({
|
|
||||||
"column_id": this.columnId,
|
|
||||||
"swimlane_id": this.swimlaneId,
|
|
||||||
"position": this.position
|
|
||||||
}),
|
|
||||||
statusCode: {
|
|
||||||
200: function() {
|
|
||||||
window.location.reload(true);
|
|
||||||
},
|
|
||||||
403: function(jqXHR) {
|
|
||||||
var response = JSON.parse(jqXHR.responseText);
|
|
||||||
self.errorMessage = response.message;
|
|
||||||
|
|
||||||
self.$broadcast('submitCancelled');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getSwimlaneId() {
|
||||||
|
var swimlaneId = getSelectedValue('form-swimlanes');
|
||||||
|
return swimlaneId === null ? options.board[0].id : swimlaneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getColumnId() {
|
||||||
|
var columnId = getSelectedValue('form-columns');
|
||||||
|
return columnId === null ? options.board[0].columns[0].id : columnId;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPosition() {
|
||||||
|
var position = getSelectedValue('form-position');
|
||||||
|
return position === null ? 1 : position;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPositionChoice() {
|
||||||
|
var element = KB.find('input[name=positionChoice]:checked');
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
return element.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'before';
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSwimlaneChanged() {
|
||||||
|
var columnSelect = KB.dom(document).find('#form-columns');
|
||||||
|
KB.dom(columnSelect).replace(buildColumnSelect());
|
||||||
|
|
||||||
|
var taskSection = KB.dom(document).find('#form-tasks');
|
||||||
|
KB.dom(taskSection).replace(buildTasks());
|
||||||
|
}
|
||||||
|
|
||||||
|
function onColumnChanged() {
|
||||||
|
var taskSection = KB.dom(document).find('#form-tasks');
|
||||||
|
KB.dom(taskSection).replace(buildTasks());
|
||||||
|
}
|
||||||
|
|
||||||
|
function onError(message) {
|
||||||
|
KB.trigger('modal.stop');
|
||||||
|
|
||||||
|
KB.find('#message-container')
|
||||||
|
.replace(KB.dom('div')
|
||||||
|
.attr('id', 'message-container')
|
||||||
|
.attr('class', 'alert alert-error')
|
||||||
|
.text(message)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSubmit() {
|
||||||
|
var position = getPosition();
|
||||||
|
var positionChoice = getPositionChoice();
|
||||||
|
|
||||||
|
if (positionChoice === 'after') {
|
||||||
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
|
KB.find('#message-container').replace(KB.dom('div').attr('id', 'message-container').build());
|
||||||
|
|
||||||
|
KB.http.postJson(options.saveUrl, {
|
||||||
|
"column_id": getColumnId(),
|
||||||
|
"swimlane_id": getSwimlaneId(),
|
||||||
|
"position": position
|
||||||
|
}).success(function () {
|
||||||
|
window.location.reload(true);
|
||||||
|
}).error(function (response) {
|
||||||
|
if (response) {
|
||||||
|
onError(response.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildSwimlaneSelect() {
|
||||||
|
var swimlanes = [];
|
||||||
|
|
||||||
|
options.board.forEach(function(swimlane) {
|
||||||
|
swimlanes.push({'value': swimlane.id, 'text': swimlane.name});
|
||||||
|
});
|
||||||
|
|
||||||
|
return KB.dom('select')
|
||||||
|
.attr('id', 'form-swimlanes')
|
||||||
|
.change(onSwimlaneChanged)
|
||||||
|
.for('option', swimlanes)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildColumnSelect() {
|
||||||
|
var columns = [];
|
||||||
|
var swimlaneId = getSwimlaneId();
|
||||||
|
|
||||||
|
options.board.forEach(function(swimlane) {
|
||||||
|
if (swimlaneId === swimlane.id) {
|
||||||
|
swimlane.columns.forEach(function(column) {
|
||||||
|
columns.push({'value': column.id, 'text': column.title});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return KB.dom('select')
|
||||||
|
.attr('id', 'form-columns')
|
||||||
|
.change(onColumnChanged)
|
||||||
|
.for('option', columns)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTasks() {
|
||||||
|
var tasks = [];
|
||||||
|
var swimlaneId = getSwimlaneId();
|
||||||
|
var columnId = getColumnId();
|
||||||
|
var container = KB.dom('div').attr('id', 'form-tasks');
|
||||||
|
|
||||||
|
options.board.forEach(function(swimlane) {
|
||||||
|
if (swimlaneId === swimlane.id) {
|
||||||
|
swimlane.columns.forEach(function(column) {
|
||||||
|
if (columnId === column.id) {
|
||||||
|
column.tasks.forEach(function(task) {
|
||||||
|
tasks.push({'value': task.position, 'text': '#' + task.id + ' - ' + task.title});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (tasks.length > 0) {
|
||||||
|
container
|
||||||
|
.add(KB.html.label(options.positionLabel, 'form-position'))
|
||||||
|
.add(KB.dom('select').attr('id', 'form-position').for('option', tasks).build())
|
||||||
|
.add(KB.html.radio(options.beforeLabel, 'positionChoice', 'before'))
|
||||||
|
.add(KB.html.radio(options.afterLabel, 'positionChoice', 'after'))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
return container.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.render = function () {
|
||||||
|
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())
|
||||||
|
.add(KB.html.label(options.columnLabel, 'form-columns'))
|
||||||
|
.add(buildColumnSelect())
|
||||||
|
.add(buildTasks())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
containerElement.appendChild(form);
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ KB.component('text-editor', function (containerElement, options) {
|
|||||||
.attr('tabindex', options.tabindex || '-1')
|
.attr('tabindex', options.tabindex || '-1')
|
||||||
.attr('required', options.required || false)
|
.attr('required', options.required || false)
|
||||||
.attr('autofocus', options.autofocus || null)
|
.attr('autofocus', options.autofocus || null)
|
||||||
.attr('placeholder', options.placeholder || '')
|
.text(options.text) // Order is important for IE11
|
||||||
.text(options.text)
|
.attr('placeholder', options.placeholder || null)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
return KB.dom('div')
|
return KB.dom('div')
|
||||||
@@ -124,7 +124,7 @@ KB.component('text-editor', function (containerElement, options) {
|
|||||||
document.execCommand('ms-beginUndoUnit');
|
document.execCommand('ms-beginUndoUnit');
|
||||||
} catch (error) {}
|
} catch (error) {}
|
||||||
|
|
||||||
textarea.value = replaceTextRange(text, textarea.selectionStart, textarea.selectionEnd, replacedText);
|
textarea.value = replaceTextRange(textarea.value, textarea.selectionStart, textarea.selectionEnd, replacedText);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
document.execCommand('ms-endUndoUnit');
|
document.execCommand('ms-endUndoUnit');
|
||||||
|
|||||||
70
assets/js/core/base.js
Normal file
70
assets/js/core/base.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
var KB = {
|
||||||
|
components: {},
|
||||||
|
utils: {},
|
||||||
|
html: {},
|
||||||
|
http: {},
|
||||||
|
listeners: {
|
||||||
|
clicks: {},
|
||||||
|
internals: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.on = function (eventType, callback) {
|
||||||
|
if (! this.listeners.internals.hasOwnProperty(eventType)) {
|
||||||
|
this.listeners.internals[eventType] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.listeners.internals[eventType].push(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.onClick = function (selector, callback) {
|
||||||
|
this.listeners.clicks[selector] = callback;
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.listen = function () {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
function onClick(e) {
|
||||||
|
for (var selector in self.listeners.clicks) {
|
||||||
|
if (self.listeners.clicks.hasOwnProperty(selector) && e.target.matches(selector)) {
|
||||||
|
e.preventDefault();
|
||||||
|
self.listeners.clicks[selector](e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('click', onClick, false);
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.component = function (name, object) {
|
||||||
|
this.components[name] = object;
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.getComponent = function (name, containerElement, options) {
|
||||||
|
var object = this.components[name];
|
||||||
|
return new object(containerElement, options);
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.render = function () {
|
||||||
|
for (var name in this.components) {
|
||||||
|
var elementList = document.querySelectorAll('.js-' + name);
|
||||||
|
|
||||||
|
for (var i = 0; i < elementList.length; i++) {
|
||||||
|
if (this.components.hasOwnProperty(name)) {
|
||||||
|
var component = KB.getComponent(name, elementList[i], JSON.parse(elementList[i].dataset.params));
|
||||||
|
component.render();
|
||||||
|
elementList[i].className = elementList[i].className + '-rendered';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
KB.dom = function (tag) {
|
KB.dom = function (tag) {
|
||||||
|
|
||||||
function DomManipulation(tag) {
|
function DomManipulation(tag) {
|
||||||
@@ -31,19 +30,33 @@ KB.dom = function (tag) {
|
|||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.click = function (callback) {
|
this.on = function (eventName, callback) {
|
||||||
element.onclick = function (e) {
|
element.addEventListener(eventName, function (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
callback();
|
callback(e.target);
|
||||||
};
|
});
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.click = function (callback) {
|
||||||
|
return this.on('click', callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.change = function (callback) {
|
||||||
|
return this.on('change', callback);
|
||||||
|
};
|
||||||
|
|
||||||
this.add = function (node) {
|
this.add = function (node) {
|
||||||
element.appendChild(node);
|
element.appendChild(node);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.replace = function (node) {
|
||||||
|
element.parentNode.replaceChild(node, element);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
this.html = function (html) {
|
this.html = function (html) {
|
||||||
element.innerHTML = html;
|
element.innerHTML = html;
|
||||||
return this;
|
return this;
|
||||||
@@ -73,6 +86,16 @@ KB.dom = function (tag) {
|
|||||||
return element.classList.contains(className);
|
return element.classList.contains(className);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
this.disable = function () {
|
||||||
|
element.disabled = true;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.enable = function () {
|
||||||
|
element.disabled = false;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
this.parent = function (selector) {
|
this.parent = function (selector) {
|
||||||
for (; element && element !== document; element = element.parentNode) {
|
for (; element && element !== document; element = element.parentNode) {
|
||||||
if (element.matches(selector)) {
|
if (element.matches(selector)) {
|
||||||
@@ -83,7 +106,7 @@ KB.dom = function (tag) {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.child = function (selector) {
|
this.find = function (selector) {
|
||||||
return element.querySelector(selector);
|
return element.querySelector(selector);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -118,3 +141,13 @@ KB.dom = function (tag) {
|
|||||||
|
|
||||||
return new DomManipulation(tag);
|
return new DomManipulation(tag);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
KB.find = function (selector) {
|
||||||
|
var element = document.querySelector(selector);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
return KB.dom(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|||||||
25
assets/js/core/html.js
Normal file
25
assets/js/core/html.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
KB.html.label = function (label, id) {
|
||||||
|
return KB.dom('label').attr('for', id).text(label).build();
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.html.radio = function (label, name, value) {
|
||||||
|
return KB.dom('label')
|
||||||
|
.add(KB.dom('input')
|
||||||
|
.attr('type', 'radio')
|
||||||
|
.attr('name', name)
|
||||||
|
.attr('value', value)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.text(label)
|
||||||
|
.build();
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.html.radios = function (items) {
|
||||||
|
var html = KB.dom('div');
|
||||||
|
|
||||||
|
for (var item in items) {
|
||||||
|
if (items.hasOwnProperty(item)) {
|
||||||
|
html.add(KB.html.radio(item.label, item.name, item.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
66
assets/js/core/http.js
Normal file
66
assets/js/core/http.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
KB.http.request = function (method, url, headers, body) {
|
||||||
|
var successCallback = function() {};
|
||||||
|
var errorCallback = function() {};
|
||||||
|
|
||||||
|
function parseResponse(request) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(request.responseText);
|
||||||
|
} catch (e) {
|
||||||
|
return request.responseText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.execute = function () {
|
||||||
|
var request = new XMLHttpRequest();
|
||||||
|
request.open(method, url, true);
|
||||||
|
request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||||
|
|
||||||
|
for (var header in headers) {
|
||||||
|
if (headers.hasOwnProperty(header)) {
|
||||||
|
request.setRequestHeader(header, headers[header]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
request.onerror = function() {
|
||||||
|
errorCallback();
|
||||||
|
};
|
||||||
|
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState === XMLHttpRequest.DONE) {
|
||||||
|
var response = parseResponse(request);
|
||||||
|
|
||||||
|
if (request.status === 200) {
|
||||||
|
successCallback(response);
|
||||||
|
} else {
|
||||||
|
errorCallback(response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
request.send(body);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.success = function (callback) {
|
||||||
|
successCallback = callback;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.error = function (callback) {
|
||||||
|
errorCallback = callback;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.http.get = function (url) {
|
||||||
|
return (new KB.http.request('GET', url)).execute();
|
||||||
|
};
|
||||||
|
|
||||||
|
KB.http.postJson = function (url, body) {
|
||||||
|
var headers = {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (new KB.http.request('POST', url, headers, JSON.stringify(body))).execute();
|
||||||
|
};
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
var KB = {
|
|
||||||
components: {},
|
|
||||||
utils: {},
|
|
||||||
clickEvents: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
KB.onClick = function (selector, callback) {
|
|
||||||
this.clickEvents[selector] = callback;
|
|
||||||
};
|
|
||||||
|
|
||||||
KB.listen = function () {
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
function onClick(e) {
|
|
||||||
for (var selector in self.clickEvents) {
|
|
||||||
if (self.clickEvents.hasOwnProperty(selector) && e.target.matches(selector)) {
|
|
||||||
e.preventDefault();
|
|
||||||
self.clickEvents[selector](e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('click', onClick, false);
|
|
||||||
};
|
|
||||||
|
|
||||||
KB.component = function (name, object) {
|
|
||||||
this.components[name] = object;
|
|
||||||
};
|
|
||||||
|
|
||||||
KB.render = function () {
|
|
||||||
for (var name in this.components) {
|
|
||||||
var elementList = document.querySelectorAll('.js-' + name);
|
|
||||||
|
|
||||||
for (var i = 0; i < elementList.length; i++) {
|
|
||||||
if (this.components.hasOwnProperty(name)) {
|
|
||||||
var object = this.components[name];
|
|
||||||
var component = new object(elementList[i], JSON.parse(elementList[i].dataset.params));
|
|
||||||
|
|
||||||
component.render();
|
|
||||||
elementList[i].className = elementList[i].className + '-rendered';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
KB.utils.formatDuration = function (d) {
|
|
||||||
if (d >= 86400) {
|
|
||||||
return Math.round(d/86400) + "d";
|
|
||||||
}
|
|
||||||
else if (d >= 3600) {
|
|
||||||
return Math.round(d/3600) + "h";
|
|
||||||
}
|
|
||||||
else if (d >= 60) {
|
|
||||||
return Math.round(d/60) + "m";
|
|
||||||
}
|
|
||||||
|
|
||||||
return d + "s";
|
|
||||||
};
|
|
||||||
13
assets/js/core/utils.js
Normal file
13
assets/js/core/utils.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
KB.utils.formatDuration = function (d) {
|
||||||
|
if (d >= 86400) {
|
||||||
|
return Math.round(d/86400) + "d";
|
||||||
|
}
|
||||||
|
else if (d >= 3600) {
|
||||||
|
return Math.round(d/3600) + "h";
|
||||||
|
}
|
||||||
|
else if (d >= 60) {
|
||||||
|
return Math.round(d/60) + "m";
|
||||||
|
}
|
||||||
|
|
||||||
|
return d + "s";
|
||||||
|
};
|
||||||
14
assets/js/polyfills/matches.js
Normal file
14
assets/js/polyfills/matches.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
if (!Element.prototype.matches) {
|
||||||
|
Element.prototype.matches =
|
||||||
|
Element.prototype.matchesSelector ||
|
||||||
|
Element.prototype.mozMatchesSelector ||
|
||||||
|
Element.prototype.msMatchesSelector ||
|
||||||
|
Element.prototype.oMatchesSelector ||
|
||||||
|
Element.prototype.webkitMatchesSelector ||
|
||||||
|
function(s) {
|
||||||
|
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
|
||||||
|
i = matches.length;
|
||||||
|
while (--i >= 0 && matches.item(i) !== this) {}
|
||||||
|
return i > -1;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -157,9 +157,5 @@ Kanboard.Popover.prototype.afterOpen = function() {
|
|||||||
this.app.autoComplete();
|
this.app.autoComplete();
|
||||||
this.app.tagAutoComplete();
|
this.app.tagAutoComplete();
|
||||||
|
|
||||||
new Vue({
|
|
||||||
el: '#popover-container'
|
|
||||||
});
|
|
||||||
|
|
||||||
KB.render();
|
KB.render();
|
||||||
};
|
};
|
||||||
|
|||||||
7
assets/js/vendor.min.js
vendored
7
assets/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
12
gulpfile.js
12
gulpfile.js
@@ -7,8 +7,9 @@ var strip = require('gulp-strip-comments');
|
|||||||
|
|
||||||
var src = {
|
var src = {
|
||||||
js: [
|
js: [
|
||||||
'assets/js/core/kb.js',
|
'assets/js/polyfills/*.js',
|
||||||
'assets/js/core/dom.js',
|
'assets/js/core/base.js',
|
||||||
|
'assets/js/core/!(base|bootstrap)*.js',
|
||||||
'assets/js/components/*.js',
|
'assets/js/components/*.js',
|
||||||
'assets/js/core/bootstrap.js',
|
'assets/js/core/bootstrap.js',
|
||||||
'assets/js/src/Namespace.js',
|
'assets/js/src/Namespace.js',
|
||||||
@@ -69,13 +70,6 @@ gulp.task('bower', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('vendor', function() {
|
gulp.task('vendor', function() {
|
||||||
gulp.src('node_modules/vue/dist/vue.min.js')
|
|
||||||
.pipe(strip({trim: true}))
|
|
||||||
.pipe(gulp.dest('node_modules/vue/dist/'))
|
|
||||||
;
|
|
||||||
|
|
||||||
vendor.js.push('node_modules/vue/dist/vue.min.js');
|
|
||||||
|
|
||||||
gulp.src(vendor.js)
|
gulp.src(vendor.js)
|
||||||
.pipe(concat('vendor.min.js'))
|
.pipe(concat('vendor.min.js'))
|
||||||
.pipe(gulp.dest(dist.js))
|
.pipe(gulp.dest(dist.js))
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kanboard",
|
"name": "kanboard",
|
||||||
"dependencies": {
|
"devDependencies": {
|
||||||
"bower": "^1.7.9",
|
"bower": "^1.7.9",
|
||||||
"gulp": "^3.9.1",
|
"gulp": "^3.9.1",
|
||||||
"gulp-bower": "0.0.13",
|
"gulp-bower": "0.0.13",
|
||||||
@@ -9,9 +9,6 @@
|
|||||||
"gulp-sass": "^2.3.2",
|
"gulp-sass": "^2.3.2",
|
||||||
"gulp-strip-comments": "^2.4.3",
|
"gulp-strip-comments": "^2.4.3",
|
||||||
"gulp-uglify": "^1.5.3",
|
"gulp-uglify": "^1.5.3",
|
||||||
"vue": "1.0.26-csp"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"jshint": "^2.9.4"
|
"jshint": "^2.9.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user