Files
server/usr/share/psa-horde/nag/js/smartmobile.js
2026-01-07 20:52:11 +01:00

500 lines
14 KiB
JavaScript

/**
* Base smartmobile application logic for Nag.
*
* Copyright 2011-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (GPL). If you
* did not receive this file, see http://www.horde.org/licenses/gpl.
*
* @author Michael J Rubinsky <mrubinsk@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/gpl GPL
* @package Nag
*/
var NagMobile = {
tasklists: {},
tasks: {},
currentList: undefined,
/**
* Toggle the completion status of the task.
*
* @param object d The data object.
*/
toggleComplete: function(d)
{
var parsed = d.options.parsedUrl;
HordeMobile.doAction(
'smartmobileToggle',
{
task: parsed.params.task_id,
tasklist: parsed.params.tasklist
},
function(r) { NagMobile.toggleCompleteCallback(r, d.options.data) }
);
},
/**
* Callback for the toggleComplete action
*
* @param object r The response object.
* @param object elt The element containing the task.
*/
toggleCompleteCallback: function(r, elt)
{
if (!r.data) {
return;
}
switch (r.data) {
case 'complete':
NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')].cp = true;
if (Nag.conf.showCompleted == 'incomplete' ||
Nag.conf.showCompleted == 'future-incomplete') {
// Hide the task
elt.parent().remove();
} else {
elt.jqmData('icon', 'check')
.find('span.ui-icon')
.removeClass('ui-icon-nag-unchecked')
.addClass('ui-icon-check');
NagMobile.styleTask(elt, NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')]);
}
break;
default:
NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')].cp = false;
if (Nag.conf.showCompleted == 'complete') {
// Hide the task
elt.parent().remove();
} else {
elt.jqmData('icon', 'minus')
.find('span.ui-icon')
.removeClass('ui-icon-check')
.addClass('ui-icon-nag-unchecked');
NagMobile.styleTask(elt, NagMobile.tasks[elt.jqmData('tasklist')][elt.jqmData('task_id')]);
}
}
},
/**
* Get a task from the server.
*
* @param object d The data object.
*/
getTask: function(d)
{
var parsed = d.options.parsedUrl;
HordeMobile.doAction(
'getTask',
{
task: parsed.params.task_id,
tasklist: parsed.params.tasklist
},
NagMobile.getTaskCallback
);
$('#nag-taskform-view a[href^="#task-delete"]').show();
HordeMobile.changePage('nag-taskform-view', d);
},
/**
* Callback for the getTask action.
*
* @param object r The response object.
*/
getTaskCallback: function(r)
{
if (!r.task) {
return;
}
var task = r.task,
f = $('form')[0];
f.reset();
$("#task_title").val(task.n);
$("#task_desc").val(task.de);
$("#task_assignee").val(task.as);
$("task_private").prop("checked", task.pr).checkboxradio("refresh");
if (task.dd) {
$("#task_due").val(Date.parse(task.dd).toString('yyyy-MM-dd'));
}
if (task.s) {
$("#task_start").val(Date.parse(task.s).toString('yyyy-MM-dd'));
}
var myselect = $("#task_priority");
myselect[0].selectedIndex = task.pr - 1;
myselect.selectmenu("refresh");
$("#task_completed").prop("checked", task.cp).checkboxradio("refresh");
$("#task_estimate").val(task.e);
$("#task_id").val(task.id);
$("#tasklist").val(task.l);
},
/**
* Get a list of tasklists from the server and display the nag-lists view.
*
* @param object d The data object.
*/
toLists: function(d)
{
HordeMobile.changePage('nag-lists', d);
HordeMobile.doAction(
'getTaskLists',
{},
NagMobile.getTasklistsCallback
);
},
/**
* Callback for the getTaskLists action
*
* @param object r The response object.
*/
getTasklistsCallback: function(r)
{
if (!r.tasklists) {
return;
}
var list = $('#nag-lists :jqmData(role="listview")'),
count = 0;
list.empty();
$.each(r.tasklists, function(i, l) {
count = count + l.count;
NagMobile.insertTasklist(list, l, false);
});
NagMobile.insertTasklist(
list,
{
'name': Nag.strings.all,
'count': count
},
true
);
list.listview('refresh');
},
/**
* Insert a tasklist element into the tasklist list.
*
* @param object el The UL element.
* @param object l The list hash.
* @param boolean top Place new list at top of list if true.
*/
insertTasklist: function(el, l, top)
{
var url = HordeMobile.createUrl('nag-list', { tasklist: l.id }),
list;
NagMobile.tasklists[l.id] = l;
list = $('<li>').append($('<a>').attr({ href: url }).addClass('nag-tasklist')
.append($('<img>').attr({ 'src': Nag.conf.icons[(l.smart ? 'smartlist' : 'tasklist')] }).addClass('ui-li-icon'))
.append($('<h3>').text(l.name))
.append($('<span>').addClass('ui-li-count' + (l.overdue ? ' overdue' : '')).text(l.count))
);
if (top) {
el.prepend(list);
} else {
el.append(list);
}
},
/**
* Retrieve a tasklist from the server and display the nag-list view.
*
* @param object d The data object.
*/
toList: function(d)
{
var params = d.options.parsedUrl.params;
HordeMobile.doAction(
'listTasks',
{ tasklist: params.tasklist },
NagMobile.listTasksCallback
);
$('#nag-list .smartmobile-title')
.text(NagMobile.tasklists[params.tasklist].name);
NagMobile.currentList = params.tasklist;
HordeMobile.changePage('nag-list', d);
},
/**
* Callback for the listTasks action.
*
* @param object r The response object.
*/
listTasksCallback: function(r)
{
if (!r.tasks) {
return;
}
NagMobile.tasks = {};
$.each(r.tasks, function(i, t) {
if (!NagMobile.tasks[t.l]) {
NagMobile.tasks[t.l] = {};
}
NagMobile.tasks[t.l][t.id] = t;
});
NagMobile.buildTaskList();
if (NagMobile.tasklists[NagMobile.currentList].smart == 1) {
$('#nag-list :jqmData(role="footer") a[href^="#nag-taskform-view"]').hide();
} else {
$('#nag-list :jqmData(role="footer") a[href^="#nag-taskform-view"]').show();
}
},
/**
* Build the complete tasklist
*/
buildTaskList: function()
{
var list = $('#nag-list :jqmData(role="listview")'),
count = 0;
list.empty();
$.each(NagMobile.tasks, function (i, l) {
$.each(l, function (i, t) {
count++;
NagMobile.insertTask(list, t);
});
});
if (count > 0) {
$('#nag-notasks').hide();
} else {
$('#nag-notasks').show();
}
list.listview('refresh');
},
/**
* Insert task into the view.
*
* @param object l The UL element.
* @param object t The task hash.
*/
insertTask: function(l, t)
{
var params = {
task_id: t.id,
tasklist: t.l
}, item, view_link, toggle_link;
view_link = $('<a>');
if (!!t.vl) {
view_link = view_link.attr({
href: t.vl,
'data-ajax': 'false'
});
} else {
view_link = view_link.attr({
href: HordeMobile.createUrl('nag-taskform-view', params)
});
}
view_link = view_link.addClass('nag-task');
toggle_link = $('<a>');
if (!!t.cl) {
toggle_link = toggle_link.attr({
href: t.cl,
'data-ajax': 'false'
});
} else {
toggle_link = toggle_link.attr({
href: HordeMobile.createUrl('nag-toggle', params)
});
}
item = $('<li>').jqmData('icon', t.cp ? 'check' : 'nag-unchecked')
.append(
view_link
.append(
$('<h3>').text(t.n)
).append(
$('<p>').addClass('ui-li-aside')
.text(t.dd)
).append(
$('<p>').text((t.de ? t.de : ''))
)
).append(
toggle_link
);
item.jqmData('task_id', t.id);
item.jqmData('tasklist', t.l);
NagMobile.styleTask(item, t);
l.append(item);
},
/**
* Handler for pageBeforeChange event
*
* @param object e The event object.
* @param object data The data object.
*/
toPage: function(e, data)
{
switch (data.options.parsedUrl.view) {
case 'nag-list':
NagMobile.toList(data);
e.preventDefault();
break;
case 'nag-taskform-view':
if (data.options.parsedUrl.params.task_id) {
NagMobile.getTask(data);
} else {
HordeMobile.changePage('nag-taskform-view', data);
$('#nag-taskform-view .smartmobile-title').text(Nag.strings.newTask);
}
e.preventDefault();
break;
case 'nag-lists':
NagMobile.toLists(data);
e.preventDefault();
break;
case 'nag-toggle':
NagMobile.toggleComplete(data);
e.preventDefault();
break;
}
},
/**
* Add the appropriate CSS classes to the task element based on the task's
* completion, due date etc...
*
* @param object l The tasks's LI element.
* @param object t The task hash.
*/
styleTask: function(l, t)
{
var task_due = Date.parse(t.dd),
task_overdue = task_due ? (task_due.compareTo(new Date()) < 0 ? true : false) : false;
if (!t.cp) {
l.removeClass('closed');
if (!task_overdue) {
l.removeClass('overdue');
} else {
l.addClass('overdue');
}
} else {
l.addClass('closed');
l.removeClass('overdue');
}
},
/**
* Prepare the nag-taskform-view for entering a new task.
*/
prepareFormForNew: function()
{
$('#nag-task-form')[0].reset();
// Must explicitly call refresh when the selectedIndex changes
// programmatically or the UI won't reflect the new value.
try {
$("#task_priority").selectmenu("refresh");
} catch(e) {}
$('#nag-task-form #tasklist').val('');
$('#nag-task-form #task_id').val('');
$('#nag-taskform-view a[href^="#task-delete"]').hide();
},
handleSubmit: function(e)
{
var form = $('#nag-task-form'),
data = HordeJquery.formToObject(form);
if (!data.hasOwnProperty('task_completed')) {
data.task_completed = 'off';
}
HordeMobile.doAction('saveTask', data, NagMobile.handleSubmitCallback);
},
handleSubmitCallback: function(r)
{
if (!r.task) {
return;
}
NagMobile.tasks[r.task.l][r.task.id] = r.task;
NagMobile.buildTaskList();
HordeMobile.changePage('nag-list');
},
handleCancel: function(e)
{
HordeMobile.changePage('nag-list');
},
handleDelete: function(e)
{
var taskid = $('#nag-taskform-view #task_id').val(),
tasklist = $('#nag-taskform-view #tasklist').val();
if (taskid && tasklist) {
HordeMobile.doAction('deleteTask', {
'task_id': taskid,
'tasklist': tasklist,
},
NagMobile.handleDeleteCallback
);
}
},
handleDeleteCallback: function(r)
{
if (!r.l) {
return;
}
if (r.deleted) {
delete NagMobile.tasks[r.l][r.deleted];
}
NagMobile.buildTaskList();
HordeMobile.changePage('nag-list');
},
onDocumentReady: function()
{
$(document).bind('pagebeforechange', NagMobile.toPage);
// Capture task completed clicks to add the current LI element to
// the page change data.
$('#nag-list :jqmData(role="listview")').on('click', 'li', function(e) {
var a = $(e.target).closest('a[href^="#nag-toggle"]');
if (a.length) {
$.mobile.changePage(a.attr('href'), { data: $(e.currentTarget) });
return false;
}
});
// Capture new task clicks.
$('#nag-list :jqmData(role="footer") a[href^="#nag-taskform-view"]').on('click', NagMobile.prepareFormForNew);
$('#nag-taskform-view a[href^="#task-submit"]').on('click', NagMobile.handleSubmit);
$('#nag-taskform-view a[href^="#task-cancel"]').on('click', NagMobile.handleCancel);
$('#nag-taskform-view a[href^="#task-delete"]').on('click', NagMobile.handleDelete);
NagMobile.tasklists = Nag.tasklists;
NagMobile.tasklists[undefined] = { 'name': Nag.strings.all };
}
};
$(NagMobile.onDocumentReady);