500 lines
14 KiB
JavaScript
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);
|