mytinytodo/src/content/mytinytodo.js

3082 lines
97 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
This file is a part of myTinyTodo.
(C) Copyright 2009-2010,2020-2023 Max Pozdeev <maxpozdeev@gmail.com>
Licensed under the GNU GPL version 2 or any later. See file COPYRIGHT for details.
*/
(function(){
"use strict";
var taskList = new Array(), taskOrder = new Array();
var filter = { compl:0, search:'', due:'' };
var sortOrder; //save task order before dragging
var searchTimer;
var objPrio = {};
var lastClickedNodeId = 0;
var flag = {
needAuth: false,
isLogged: false,
tagsChanged: true,
readOnly: false,
editFormChanged: false,
firstLoad: true,
dontChangeHistoryOnce: false,
showTagsFromAllLists: false
};
var taskCnt = { total:0, past: 0, today:0, soon:0 };
var tabLists = {
_lists: {},
_length: 0,
_order: [],
_alltasks: {},
clear: function(){
this._lists = {}; this._length = 0; this._order = [];
this._alltasks = { id:-1, showCompl:0, sort:3, name:_mtt.lang.get('alltasks') };
},
length: function(){ return this._length; },
exists: function(id){ if(this._lists[id] || id==-1) return true; else return false; },
add: function(list){ this._lists[list.id] = list; this._length++; this._order.push(list.id); },
replace: function(list){ this._lists[list.id] = list; },
get: function(id){ if(id==-1) return this._alltasks; else return this._lists[id]; },
getAll: function(){ var r = []; for(var i in this._order) { r.push(this._lists[this._order[i]]); }; return r; },
reorder: function(order){ this._order = order; }
};
var curList = 0;
var tagsList = [];
var _mtt; /* internal alias for window.mytinytodo */
var mytinytodo = window.mytinytodo = _mtt = {
theme: {
newTaskFlashColor: '#ffffaa',
editTaskFlashColor: '#bbffaa',
deleteTaskFlashColor: '#ffaaaa',
msgFlashColor: '#ffffff'
},
actions: {},
menus: {},
mttUrl: '',
homeUrl: '',
apiUrl: '',
options: {
token: '',
title: '',
openList: 0,
autotag: false,
instantSearch: true,
tagPreview: true,
tagPreviewDelay: 700, //milliseconds
ajaxAnimationDelay: 200,
saveShowNotes: false,
firstdayofweek: 1,
touchDevice: false,
calendarIcon: 'calendar.png', // need themeUrl+icon
history: true,
markdown: true,
viewTaskOnClick: false,
},
timers: {
previewtag: 0,
ajaxAnimation: 0,
},
lang: {
__lang: null,
daysMin: [],
daysLong: [],
monthsShort: [],
monthsLong: [],
get: function(v) {
if(this.__lang[v]) return this.__lang[v];
else return v;
},
init: function(lang)
{
this.__lang = lang;
this.daysMin = this.__lang.daysMin;
this.daysLong = this.__lang.daysLong;
this.monthsShort = this.__lang.monthsShort;
this.monthsLong = this.__lang.monthsLong;
},
isRTL: function() {
return this.get('_rtl') > 0 ? true : false;
}
},
pages: {
current: null,
prev: []
},
pageDefault: {
page: 'tasks',
pageClass: '',
lastScrollTop: 0,
onOpen: function() { this.loadLists(); }
},
curList: function(){
return curList;
},
flag: flag,
lastHistoryState: null,
// procs
setApiDriver: function(driver)
{
this.db = new driver(_mtt);
return this;
},
init: function(options)
{
// required properties
if (options.hasOwnProperty('lang')) {
this.lang.init(options.lang);
delete options.lang;
}
if (options.hasOwnProperty('mttUrl')) {
this.mttUrl = options.mttUrl;
delete options.mttUrl;
}
if (options.hasOwnProperty('apiUrl')) {
this.apiUrl = options.apiUrl;
delete options.apiUrl;
}
else {
this.apiUrl = this.mttUrl + 'api.php?_path=/';
}
if (options.hasOwnProperty('db')) {
delete options.db;
}
if (options.hasOwnProperty('homeUrl')) {
this.homeUrl = options.homeUrl;
delete options.homeUrl;
}
else {
this.homeUrl = this.mttUrl;
}
if ( ! options.hasOwnProperty('touchDevice') ) {
this.options.touchDevice = ('ontouchend' in document);
}
jQuery.extend(this.options, options);
if (this.options.token) {
jQuery.ajaxSetup( { headers: { "MTT-Token": this.options.token } } )
}
flag.needAuth = options.needAuth ? true : false;
flag.isLogged = options.isLogged ? true : false;
if (this.options.showdate) {
$('#mtt').addClass('show-inline-date');
}
// handlers
$('.mtt-tabs-new-button').click(function(){
addList();
});
$('.mtt-tabs-select-button').click(function(event){
if (!_mtt.menus.selectlist) {
_mtt.menus.selectlist = new mttMenu( 'slmenucontainer', { onclick:slmenuSelect, alignRight: true } );
}
_mtt.menus.selectlist.show(this);
});
$('#newtask_form').submit(function(){
submitNewTask(this);
return false;
});
$('#newtask_submit').mousedown(function(e){
e.preventDefault(); //keep the focus in #task
$('#newtask_form').submit();
});
$('#newtask_adv').click(function(){
showEditForm(1);
return false;
});
$('#task').keydown(function(event){
if(event.keyCode == 27) {
$(this).val('');
}
}).focusin(function(){
$('#task_placeholder').removeClass('placeholding');
$('#toolbar').addClass('mtt-intask');
}).focusout(function(){
if('' == $(this).val()) $('#task_placeholder').addClass('placeholding');
$('#toolbar').removeClass('mtt-intask');
});
$('#search_close').click(function(){
liveSearchToggle(0);
return false;
});
$('#search').keyup(function(event){
if(event.keyCode == 27) return;
if($(this).val() == '') $('#search_close').hide(); //actual value is only on keyup
else $('#search_close').show();
if (_mtt.options.instantSearch) {
clearTimeout(searchTimer);
searchTimer = setTimeout(function(){searchTasks()}, 400);
}
})
.keydown(function(event){
if(event.keyCode == 27) { // cancel on Esc (NB: no esc event on keypress in Chrome and on keyup in Opera)
if($(this).val() != '') {
$(this).val('');
$('#search_close').hide();
searchTasks();
}
else {
liveSearchToggle(0);
}
return false; //need to return false in firefox (for AJAX?)
}
else if ( event.keyCode == 13 ) {
searchTasks(1);
return false;
}
}).focusin(function(){
$('#toolbar').addClass('mtt-insearch');
}).focusout(function(){
$('#toolbar').removeClass('mtt-insearch');
});
$('#taskview').click(function(){
if(!_mtt.menus.taskview) _mtt.menus.taskview = new mttMenu('taskviewcontainer');
_mtt.menus.taskview.show(this);
});
$('#mtt-tag-filters').on('click', '.mtt-filter-close', function(){
cancelTagFilter($(this).attr('tagid'));
});
$('#mtt-tag-toolbar-close').click(function(){
cancelTagFilter(0);
});
$('#tagcloudbtn').click(function(){
if (flag.readOnly) {
$('#tagcloudAllLists').prop('checked', false).prop('disabled', true);
}
else if (curList.id == -1) {
$('#tagcloudAllLists').prop('checked', true).prop('disabled', true);
}
else {
$('#tagcloudAllLists').prop('checked', flag.showTagsFromAllLists).prop('disabled', false);
}
if(!_mtt.menus.tagcloud) _mtt.menus.tagcloud = new mttMenu('tagcloud', {
beforeShow: function(){
if(flag.tagsChanged) {
$('#tagcloudcontent').html('');
$('#tagcloudload').show();
loadTags(curList.id, function(){$('#tagcloudload').hide();});
}
},
alignRight:true
});
_mtt.menus.tagcloud.show(this);
});
$('#tagcloudcancel').click(function(){
if(_mtt.menus.tagcloud) _mtt.menus.tagcloud.close();
});
$('#tagcloudcontent').on('click', '.tag', function(event){
//tag is not escaped
addFilterTag( this.dataset.tag, this.dataset.tagId, (event.metaKey || event.ctrlKey ? true : false) );
if(_mtt.menus.tagcloud) _mtt.menus.tagcloud.close();
return false;
});
$('#tagcloudAllLists').click(function(){
flag.showTagsFromAllLists = this.checked;
$('#tagcloudcontent').html('');
$('#tagcloudload').show();
loadTags(curList.id, function(){$('#tagcloudload').hide();});
});
$('#mtt-notes-show').click(function(e){
toggleAllNotes(1, e);
this.blur();
return false;
});
$('#mtt-notes-hide').click(function(e){
toggleAllNotes(0, e);
this.blur();
return false;
});
$('#taskviewcontainer li').click(function(){
if(this.id == 'view_tasks') setTaskview(0);
else if(this.id == 'view_past') setTaskview('past');
else if(this.id == 'view_today') setTaskview('today');
else if(this.id == 'view_soon') setTaskview('soon');
});
// Tabs
$('#lists').on('click', 'li.mtt-tab', function(event) {
var listId = this.id.split('_', 2)[1];
if (listId === 'all') listId = -1;
if(event.metaKey || event.ctrlKey) {
// hide the tab
hideList(listId);
return false;
}
tabSelect(listId);
return false;
});
$('#lists').on('click', 'li.mtt-tab .list-action', function(){
listMenu(this);
return false; //stop bubble to tab click
});
//Priority popup
$('#priopopup .prio-neg-1').click(function(){
prioClick(-1,this);
});
$('#priopopup .prio-zero').click(function(){
prioClick(0,this);
});
$('#priopopup .prio-pos-1').click(function(){
prioClick(1,this);
});
$('#priopopup .prio-pos-2').click(function(){
prioClick(2,this);
});
$('#priopopup').mouseleave(function(){
$(this).hide()}
);
// edit form handlers
$('#alltags_show').click(function(){
toggleEditAllTags(1);
return false;
});
$('#alltags_hide').click(function(){
toggleEditAllTags(0);
return false;
});
$('#taskedit_form').submit(function(){
return saveTask(this);
});
$('#alltags').on('click', '.tag', function(){
addEditTag(this.dataset.tag);
return false;
});
$("#duedate").datepicker({
dateFormat: _mtt.duedatepickerformat(),
firstDay: _mtt.options.firstdayofweek,
showOn: 'button',
buttonImage: _mtt.options.calendarIcon,
buttonImageOnly: true,
constrainInput: false,
duration:'',
dayNamesMin:_mtt.lang.daysMin,
dayNames:_mtt.lang.daysLong,
monthNamesShort:_mtt.lang.monthsShort,
monthNames:_mtt.lang.monthsLong,
isRTL: _mtt.lang.isRTL()
});
function ac_split( val ) {
return val.split( /,\s*/ );
}
function ac_extractLast( term ) {
return ac_split( term ).pop();
}
$("#edittags").autocomplete({
source: function(request, response) {
var taskId = document.getElementById('taskedit_form').id.value;
var listId = (taskId != '') ? taskList[taskId].listId : curList.id;
_mtt.db.request('suggestTags', {list:listId, q:ac_extractLast(request.term)}, function(json){
response(json);
})
},/*
search: function() {
// custom minLength
var term = ac_extractLast( this.value );
if ( term.length < 2 ) {
return false;
}
},*/
focus: function() {
// prevent value inserted on focus using keyboard
return false;
},
select: function( event, ui ) {
var terms = ac_split( this.value );
terms.pop(); // remove the current input
terms.push( ui.item.value ); // add the selected item
terms.push( "" ); // add placeholder to get the comma-and-space at the end
this.value = terms.join( ", " );
return false;
}
});
$('#taskedit_form').find('select,input,textarea').bind('change keypress', function(){
flag.editFormChanged = true;
});
$('#taskviewer_edit_btn').on('click', function() {
const id = document.getElementById('page_taskviewer').dataset.id;
editTask(id);
});
// tasklist handlers
$("#tasklist").on('click', '> li.task-row .task-middle', function(e) {
if ( findParentNode(e.target, 'A') ) {
return; //ignore clicks on links
}
var li = findParentNode(this, 'LI');
if (li && li.id) {
if (e.altKey) {
viewTask(li.dataset.id);
return;
}
if (lastClickedNodeId && li.id != lastClickedNodeId) {
$('#'+lastClickedNodeId).removeClass('clicked');
}
lastClickedNodeId = li.id;
$(li).toggleClass('clicked');
}
});
$('#tasklist').on('dblclick', '> li.task-row .task-middle, > li.task-row .task-note-block', function(){
let id = parseInt(getLiTaskId(this));
if (id) {
//clear selection
if (document.selection && document.selection.empty && document.selection.createRange().text)
document.selection.empty();
else if (window.getSelection)
window.getSelection().removeAllRanges();
editTask(id);
}
});
if (this.options.touchDevice) {
this.options.viewTaskOnClick = true;
}
if (this.options.viewTaskOnClick) {
$('#mtt').addClass('view-task-on-click');
$('#tasklist').on('click', '> li.task-row .task-title', function(){
let id = parseInt(getLiTaskId(this));
if (id) {
viewTask(id);
}
});
}
$('#tasklist').on('click', '.taskactionbtn', function(){
var id = parseInt(getLiTaskId(this));
if(id) taskContextMenu(this, id);
return false;
});
$('#tasklist').on('click', 'input[type=checkbox]', function(){
var id = parseInt(getLiTaskId(this));
if(id) completeTask(id, this);
//return false;
});
$('#tasklist').on('click', '.task-toggle', function(){
var id = getLiTaskId(this);
if(id) $('#taskrow_'+id).toggleClass('task-expanded');
return false;
});
$('#tasklist').on('click', '.tag', function(event){
clearTimeout(_mtt.timers.previewtag);
$('#tasklist li').removeClass('not-in-tagpreview');
//tag is not escaped
addFilterTag(this.dataset.tag, this.dataset.tagId, (event.metaKey || event.ctrlKey ? true : false) );
return false;
});
if(!this.options.touchDevice) {
$('#tasklist').on('mouseover mouseout', '.task-prio', function(event){
var id = parseInt(getLiTaskId(this));
if(!id) return;
if(event.type == 'mouseover') prioPopup(1, this, id);
else prioPopup(0, this);
});
}
$('#tasklist').on('click', '.mtt-action-note-cancel', function(){
var id = parseInt(getLiTaskId(this));
if(id) cancelTaskNote(id);
return false;
});
$('#tasklist').on('click', '.mtt-action-note-save', function(){
var id = parseInt(getLiTaskId(this));
if(id) saveTaskNote(id);
return false;
});
if (this.options.tagPreview && !this.options.touchDevice) {
$('#tasklist').on('mouseover mouseout', '.tag', function(event){
const cl = 'tag-id-' + this.dataset.tagId;
const sel = (event.metaKey || event.ctrlKey) ? 'li.'+cl : 'li:not(.'+cl+')';
if (event.type == 'mouseover') {
_mtt.timers.previewtag = setTimeout( function(){
$('#tasklist '+sel).addClass('not-in-tagpreview');
}, _mtt.options.tagPreviewDelay);
}
else {
clearTimeout(_mtt.timers.previewtag);
$('#tasklist li').removeClass('not-in-tagpreview');
}
});
}
$("#tasklist").sortable({
items: '> :not(.task-completed)',
cancel: 'span,input,a,textarea,.task-note-block',
delay: 150,
start: tasklistSortStart,
update: tasklistSortUpdated,
placeholder: 'mtt-task-placeholder',
cursor: 'grabbing'
});
$("#lists ul").sortable({
delay: 150,
update: listOrderChanged,
items: '> :not(#list_all)',
forcePlaceholderSize : true,
placeholder: 'mtt-tab mtt-tab-sort-placeholder',
cursor: 'grabbing'
});
if (this.options.touchDevice) {
$("#tasklist").disableSelection();
$("#tasklist").sortable('option', {
axis: 'y',
delay: 50,
cancel: 'input',
distance: 0
});
/*$('#cmenu_note').hide();*/
$("#lists ul").sortable('disable');
$("#mtt").addClass("touch-device");
}
// AJAX Errors
$(document).ajaxSend(function(r,s){
hideAlert();
clearTimeout(_mtt.timers.ajaxAnimation);
_mtt.timers.ajaxAnimation = setTimeout( function(){
$("#mtt").addClass("ajax-loading");
}, _mtt.options.ajaxAnimationDelay );
});
$(document).ajaxStop(function(r,s){
clearTimeout(_mtt.timers.ajaxAnimation);
$("#mtt").removeClass("ajax-loading");
});
$(document).ajaxError(function(event, request, settings){
var errtxt;
if (request.status == 0) errtxt = 'Bad connection';
else if(request.status == 403) errtxt = request.responseText;
else if (request.status != 200) errtxt = 'HTTP: '+request.status+'/'+request.statusText + "\n" + request.responseText;
else errtxt = request.responseText;
flashError(_mtt.lang.get('error'), errtxt);
});
// Error Message details
$("#msg>.msg-text").click(function(){
$("#msg>.msg-details").toggle();
});
// Authentication
$('#login_btn').click(function(){
showLogin();
return false;
});
$('#logout_btn').click(function(){
logout();
return false;
});
$('#login_form').submit(function(){
doAuth(this);
return false;
});
// Settings
$(document).on('click', 'a[data-settings-link]', function(event) {
var settingsPage = this.dataset.settingsLink;
if (settingsPage == 'index') {
showSettings( (event.metaKey || event.ctrlKey) ? 1 : 0 );
}
else if (settingsPage == 'ext-activate' || settingsPage == 'ext-deactivate') {
activateExtension(settingsPage == 'ext-activate' ? true : false, this.dataset.ext);
}
else if (settingsPage == 'ext-index') {
showExtensionSettings(this.dataset.ext);
}
return false;
});
$("#page_ajax").on('submit', '#settings_form', function() {
saveSettings(this);
return false;
});
$("#page_ajax").on('submit', '#ext_settings_form', function() {
saveExtensionSettings(this);
return false;
});
$(document).on('click', '.mtt-back-button', function() {
_mtt.pageBack(true);
this.blur();
return false;
});
$(window).bind('beforeunload', function() {
if (_mtt.pages.current && _mtt.pages.current.page == 'taskedit' && flag.editFormChanged) {
return _mtt.lang.get('confirmLeave');
}
});
$("#page_ajax").on('click', 'a[data-ext-settings-action],button[data-ext-settings-action]', function() {
extensionSettingsAction(this.dataset.extSettingsAction, this.dataset.ext);
return false;
});
// tab menu
this.addAction('listSelected', tabmenuOnListSelected);
// task context menu
this.addAction('listsLoaded', cmenuOnListsLoaded);
this.addAction('listRenamed', cmenuOnListRenamed);
this.addAction('listAdded', cmenuOnListAdded);
this.addAction('listSelected', cmenuOnListSelected);
this.addAction('listOrderChanged', cmenuOnListOrderChanged);
this.addAction('listHidden', cmenuOnListHidden);
// select list menu
this.addAction('listsLoaded', slmenuOnListsLoaded);
this.addAction('listRenamed', slmenuOnListRenamed);
this.addAction('listAdded', slmenuOnListAdded);
this.addAction('listSelected', slmenuOnListSelected);
this.addAction('listHidden', slmenuOnListHidden);
//History
if (this.options.history) {
window.onpopstate = historyOnPopState;
window.history.scrollRestoration = 'manual';
}
// Appearance mode for CSS
if (window.matchMedia) {
document.documentElement.setAttribute('data-system-appearance', window.matchMedia("(prefers-color-scheme: dark)").matches ? 'dark' : 'light');
// TODO: use MediaQueryList.onchange since Safari 14 (macos 10.14) is min target
window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
document.documentElement.setAttribute('data-system-appearance', e.matches ? 'dark' : 'light');
});
}
this.doAction( 'init' );
return this;
},
log: function(v)
{
console.log.apply(this, arguments);
},
addAction: function(action, proc)
{
if(!this.actions[action]) this.actions[action] = new Array();
this.actions[action].push(proc);
},
doAction: function(action, opts)
{
if(!this.actions[action]) return;
for(var i in this.actions[action]) {
this.actions[action][i](opts);
}
},
setOptions: function(opts) {
jQuery.extend(this.options, opts);
},
run: function()
{
var path = this.parseAnchor();
updateAccessStatus();
if (path.settings) {
showSettings(path.settings == 'json' ? 1 : 0);
}
else if (path.search && path.list) {
filter.search = path.search;
this.pageSet('tasks', '');
this.loadLists();
}
else {
this.pageSet('tasks', '');
this.loadLists();
}
},
loadLists: function()
{
if(filter.search != '') {
//filter.search = '' will be in tabSelect
$('#searchbarkeyword').text('');
$('#searchbar').hide();
}
$('#page_tasks').hide();
$('#tasklist').html('');
$('#tasks_info').hide();
tabLists.clear();
this.db.loadLists(null, function(res)
{
var ti = '';
var openListId = 0;
if (res && res.total && res.list)
{
// open required or last opened or first non-hidden list
let list;
if (_mtt.options.openList) {
list = res.list.find( item => _mtt.options.openList == item.id );
}
else {
const lastOpenList = getLocalStorageItem('lastList');
if (lastOpenList && !flag.readOnly) {
list = res.list.find( item => !item.hidden && lastOpenList == item.id );
}
if (!list) {
list = res.list.find( item => !item.hidden );
}
}
if (list) {
openListId = list.id;
}
res.list.forEach( (item) => {
if ( item.id == -1 ) {
tabLists._alltasks = item;
ti += prepareListHtml(item);
}
else {
tabLists.add(item);
ti += prepareListHtml(item);
}
});
}
if (openListId == 0) {
curList = 0;
}
if (_mtt.options.markdown == true) {
$('#mtt').addClass('markdown-enabled');
}
if (tabLists.length() > 0) {
$('#mtt').removeClass('no-lists');
}
else {
$('#mtt').addClass('no-lists');
}
if (_mtt.options.openList != 0 && openListId == 0) {
// cant open list - not found
$('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
}
else if (tabLists.length() == 0) {
if (flag.readOnly) $('#tasks_info .v').text(_mtt.lang.get('noPublicLists'));
else $('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
}
_mtt.options.openList = 0;
$('#lists .mtt-tab-selected').removeClass('mtt-tab-selected');
$('#mtt').addClass('no-list-selected');
$('#lists ul').html(ti);
$('#lists').show();
_mtt.doAction('listsLoaded');
if (tabLists.length() > 0 && openListId != 0) {
tabSelect(openListId);
}
$('#page_tasks').show();
});
},
duedatepickerformat: function()
{
if(!this.options.duedatepickerformat) return 'yy-mm-dd';
var s = this.options.duedatepickerformat.replace(/(.)/g, function(t,s) {
switch(t) {
case 'Y': return 'yy';
case 'y': return 'yy';
case 'd': return 'dd';
case 'j': return 'd';
case 'm': return 'mm';
case 'n': return 'm';
case '/':
case '.':
case '-': return t;
default: return '';
}
});
if(s == '') return 'yy-mm-dd';
return s;
},
errorDenied: function()
{
flashError(this.lang.get('denied'));
},
pageSet: function(page, pageClass)
{
if (this.pages.current) {
var prev = this.pages.current;
prev.lastScrollTop = $(window).scrollTop();
this.pages.prev.push(this.pages.current);
$('#mtt').removeClass('page-' + prev.page);
$('#page_'+ prev.page).removeClass('mtt-page-'+prev.page.pageClass).hide();
}
$(window).scrollTop(0);
this.pages.current = { page:page, pageClass:pageClass };
$('#mtt').addClass('page-' + page);
$('#page_'+ this.pages.current.page).show().addClass('mtt-page-'+ this.pages.current.pageClass);
},
pageBack: function(clicked)
{
hideAlert();
$(document).off('keydown.mttback');
// If clicked on back button in settings we'll use history navigation
if ( clicked && this.pages.current && this.pages.prev.length > 0 &&
((_mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') || _mtt.pages.current.page == 'taskviewer') ) {
window.history.back();
return;
}
if (this.pages.current.page == 'tasks') {
return;
}
if (this.pages.current) {
var prev = this.pages.current;
$('#mtt').removeClass('page-' + prev.page);
$('#page_'+ prev.page).removeClass('mtt-page-'+prev.page.pageClass);
$('#page_'+ prev.page).hide();
}
var cur = this.pages.prev.pop();
this.pages.current = cur ? cur : this.pageDefault;
$('#mtt').addClass('page-' + this.pages.current.page);
$('#page_'+ this.pages.current.page).addClass('mtt-page-'+ this.pages.current.pageClass).show();
$(window).scrollTop(this.pages.current.lastScrollTop);
if (!cur && this.pages.current.onOpen) {
this.pages.current.onOpen.call(this);
}
},
filter: {
_filters: [],
clear: function() {
this._filters = [];
$('#mtt-tag-toolbar').hide();
$('#mtt-tag-filters').html('');
},
addTag: function(tagId, tag, exclude)
{
tagId += 0;
for (let i in this._filters) {
if (this._filters[i].tagId && this._filters[i].tagId == tagId)
return false;
}
this._filters.push({tagId:tagId, tag:tag, exclude:exclude});
const tagHtml = this.prepareTagHtml(tagId, tag, ['tag-filter', 'tag-id-'+tagId, exclude ? 'tag-filter-exclude' : '']) ;
$('#mtt-tag-filters').append(tagHtml);
$('#mtt-tag-toolbar').show();
return true;
},
cancelTag: function(tagId)
{
for (let i in this._filters) {
if (this._filters[i].tagId && this._filters[i].tagId == tagId) {
this._filters.splice(i, 1);
$('#mtt-tag-filters .tag-filter.tag-id-'+tagId).remove();
if (this._filters.length == 0) {
$('#mtt-tag-toolbar').hide();
}
return true;
}
}
return false;
},
getTags: function(withExcluded)
{
let a = [];
for (let i in this._filters) {
if (this._filters[i].tagId) {
if (this._filters[i].exclude && withExcluded)
a.push('^'+ this._filters[i].tag);
else if (!this._filters[i].exclude)
a.push(this._filters[i].tag)
}
}
return a.join(', ');
},
prepareTagHtml: function(tagId, tag, classes)
{
// tag is not escaped
return '<span class="' + classes.join(' ') + ' mtt-filter-close" tagid="' + tagId + '">' + escapeHtml(tag) + '<span class="tag-filter-btn"></span></span>';
}
},
parseAnchor: function()
{
if(location.hash == '') return false;
var h = location.hash.substr(1);
var a = h.split("/");
var p = {};
var s = '';
for(var i=0; i<a.length; i++)
{
s = a[i];
switch(s) {
case "list": if(a[++i].match(/^-?\d+$/)) { p[s] = a[i]; } break;
case "alltasks": p.list = '-1'; break;
case "settings": p.settings = true; break;
case "settings.json": p.settings = 'json'; break;
case "search": p.search = decodeURIComponent(a[++i]); break;
}
}
if(p.list) this.options.openList = p.list;
return p;
},
urlForList: function(list)
{
var l = list || curList;
if (l === undefined) return '';
if (l.id == -1) return '#alltasks';
return '#list/' + l.id;
},
urlForExport: function(format, list)
{
var l = list || curList;
if (l === undefined) return '';
if (!format.match(/^[a-z0-9-]+$/i)) return '';
return this.mttUrl + 'export.php?list='+l.id +'&format='+format;
},
urlForFeed: function(list)
{
list = list || curList;
if (list === undefined) return '';
return _mtt.mttUrl + 'feed.php?list=' + list.id;
},
urlForSettings: function(json = 0)
{
if (json == 1) return '#settings.json';
return '#settings';
}
}; // End of mytinytodo object
function addList()
{
mttPrompt( _mtt.lang.get('addList'), _mtt.lang.get('addListDefault'), function(r)
{
_mtt.db.request('addList', {name:r}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
var i = tabLists.length();
tabLists.add(item);
if (i > 0) {
$('#lists ul').append(prepareListHtml(item));
mytinytodo.doAction('listAdded', item);
}
else {
_mtt.loadLists();
}
});
});
};
function renameCurList()
{
if (!curList) return;
mttPrompt( _mtt.lang.get('renameList'), dehtml(curList.name), function(r)
{
_mtt.db.request('renameList', {list:curList.id, name:r}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
curList = item;
tabLists.replace(item);
$('#lists ul>.mtt-tab-selected>a').attr('title', item.name).find('span').html(item.name); //FIXME: wft?
mytinytodo.doAction('listRenamed', item);
});
});
};
function deleteCurList()
{
if (!curList) return false;
mttConfirm( _mtt.lang.get('deleteList'), function()
{
_mtt.db.request('deleteList', {list:curList.id}, function(json){
if (!parseInt(json.total)) return;
_mtt.loadLists();
});
});
};
function publishCurList()
{
if(!curList) return false;
_mtt.db.request('publishList', { list:curList.id, publish:curList.published?0:1 }, function(json){
if(!parseInt(json.total)) return;
curList.published = curList.published?0:1;
if(curList.published) {
$('#btnPublish').addClass('mtt-item-checked');
$('#btnRssFeed').removeClass('mtt-item-disabled');
}
else {
$('#btnPublish').removeClass('mtt-item-checked');
$('#btnRssFeed').addClass('mtt-item-disabled');
}
});
};
function enableFeedKeyInCurList()
{
if (!curList) return false;
_mtt.db.request('enableFeedKey', {
list: curList.id,
enable: (curList.feedKey === undefined || curList.feedKey === '') ? 1 : 0
}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
curList.feedKey = item.feedKey;
if (curList.feedKey) {
$('#btnFeedKey').addClass('mtt-item-checked');
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
mttAlert(curList.feedKey);
}
else {
$('#btnFeedKey').removeClass('mtt-item-checked');
$('#btnShowFeedKey').addClass('mtt-item-disabled');
}
});
};
function showFeedKeyInCurList()
{
if (!curList) return false;
if (curList.feedKey === undefined || curList.feedKey === '') return false;
mttAlert(curList.feedKey);
};
function loadTasks(opts)
{
if(!curList) return false;
updateSortUI(curList.sort);
opts = opts || {};
if(opts.clearTasklist) {
$('#tasklist').html('');
$('#total').html('0');
}
_mtt.db.request('loadTasks', {
list: curList.id,
compl: curList.showCompl,
sort: curList.sort,
search: filter.search,
tag: _mtt.filter.getTags(true),
setCompl: opts.setCompl,
saveSort: opts.saveSort
}, function(json){
taskList.length = 0;
taskOrder.length = 0;
taskCnt.total = taskCnt.past = taskCnt.today = taskCnt.soon = 0;
var tasks = '';
$.each(json.list, function(i,item){
tasks += _mtt.prepareTaskStr(item);
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
changeTaskCnt(item, 1);
});
if(opts.beforeShow && opts.beforeShow.call) {
opts.beforeShow();
}
refreshTaskCnt();
$('#tasklist').html(tasks);
});
};
function prepareListHtml(list)
{
var opentag = '';
if (list.id == -1) {
opentag = '<li id="list_all" class="mtt-tab' + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
}
else {
opentag = '<li id="list_' + list.id + '" class="mtt-tab' + (list.hidden ? ' mtt-tab-hidden' : '') + '">';
}
return opentag +
'<a href="' + _mtt.urlForList(list) + '" title="' + list.name + '"><span>' + list.name + '</span>' +
'<div class="list-action mtt-img-button"><span></span></div></a></li>';
}
function prepareTaskStr(item, noteExp)
{
return '<li id="taskrow_'+item.id+'" ' + 'data-id="'+item.id + '" class="task-row ' + (item.compl?'task-completed ':'') + item.dueClass + (item.note!=''?' task-has-note':'') +
((curList.showNotes && item.note != '') || noteExp ? ' task-expanded' : '') + prepareDomClassOfTags(item.tags_ids) + '">' +
prepareTaskBlocks(item) + "</li>\n";
};
_mtt.prepareTaskStr = prepareTaskStr;
function prepareTaskBlocks(item)
{
var id = item.id;
var markdown = '';
if (_mtt.options.markdown == true) markdown = 'markdown-note';
return '' +
'<div class="task-block">' +
'<div class="task-left">' +
'<div class="task-toggle"></div>' +
'<label><input type="checkbox" '+(flag.readOnly?'disabled="disabled"':'')+(item.compl?'checked="checked"':'')+'></label>' +
"</div>\n" +
'<div class="task-middle">' +
'<div class="task-middle-top">' +
'<div class="task-through">' +
preparePrio(item.prio,id) +
'<span class="task-title">' + prepareTaskTitleInlineHtml(item.title) + '</span> ' +
(curList.id == -1 ? '<span class="task-listname">'+ tabLists.get(item.listId).name +'</span>' : '') +
'<span class="task-tags">' + prepareTagsStr(item) + '</span>' +
'</div>' +
'<div class="task-through-right">' + prepareDueDate(item) + "</div>" +
'</div>' +
'<div class="task-date">' +
prepareInlineDate(item) +
'</div>' +
"</div>" +
'<div class="task-actions"><div class="taskactionbtn"></div></div>' +
'</div>' +
'<div class="task-note-block">' +
'<div id="tasknote' + id + '" class="task-note ' + markdown + '">' + prepareTaskNoteInlineHtml(item.note, item.noteText) + '</div>' +
'<div id="tasknotearea'+id+'" class="task-note-area"><textarea id="notetext'+id+'"></textarea>'+
'<span class="task-note-actions"><a href="#" class="mtt-action-note-save">'+_mtt.lang.get('actionNoteSave') +
'</a> | <a href="#" class="mtt-action-note-cancel">'+_mtt.lang.get('actionNoteCancel')+'</a></span>' +
'</div>' +
'</div>';
};
_mtt.prepareTaskBlocks = prepareTaskBlocks;
function prepareTaskTitleInlineHtml(s)
{
// Task title is already escaped on php back-end
return s;
}
_mtt.prepareTaskTitleInlineHtml = prepareTaskTitleInlineHtml;
function prepareTaskNoteInlineHtml(s, rawText)
{
// Task note is already escaped on php back-end
return s;
};
_mtt.prepareTaskNoteInlineHtml = prepareTaskNoteInlineHtml;
function preparePrio(prio,id)
{
var cl =''; var v = '';
if(prio < 0) { cl = 'prio-neg prio-neg-'+Math.abs(prio); v = '&#8722;'+Math.abs(prio); } // &#8722; = &minus; =
else if(prio > 0) { cl = 'prio-pos prio-pos-'+prio; v = '+'+prio; }
else { cl = 'prio-zero'; v = '&#177;0'; } // &#177; = &plusmn; = ±
return '<span class="task-prio '+cl+'">'+v+'</span>';
};
_mtt.preparePrio = preparePrio;
function prepareTagsStr(item, delimiter = ', ')
{
if (!item.tags || item.tags == '') return '';
let a = item.tags.split(',');
if (!a.length) return '';
const b = item.tags_ids.split(',')
for (let i in a) {
a[i] = '<span class="tag" data-tag="'+a[i]+'" data-tag-id="'+b[i]+'">'+a[i]+'</span>';
}
return a.join(delimiter);
};
_mtt.prepareTagsStr = prepareTagsStr;
function prepareDomClassOfTags(ids)
{
if(!ids || ids == '') return '';
var a = ids.split(',');
if(!a.length) return '';
for(var i in a) {
a[i] = 'tag-id-'+a[i];
}
return ' '+a.join(' ');
};
_mtt.prepareDomClassOfTags = prepareDomClassOfTags;
function prepareDueDate(item)
{
if(!item.duedate) return '';
return '<span class="duedate" title="'+item.dueTitle+'">'+item.dueStr+'</span>';
};
_mtt.prepareDueDate = prepareDueDate;
function prepareInlineDate(item)
{
var inlineDate = item.dateInlineTitle;
var title = item.dateFull;
if (item.compl) {
inlineDate = item.dateCompletedInlineTitle;
title = item.dateCompletedFull;
}
else if ( item.isEdited && (curList.sort == 4 || curList.sort == 104) ) {
inlineDate = item.dateEditedInlineTitle;
title = item.dateEditedFull;
}
return '<span class="task-id">#' + item.id + '</span> <span title="' + title +'">' + inlineDate + '</span>';
}
_mtt.prepareInlineDate = prepareInlineDate;
function submitNewTask(form)
{
if(form.task.value == '') return false;
_mtt.db.request('newTask', { list:curList.id, title: form.task.value, tag:_mtt.filter.getTags() }, function(json){
if(!json.total) return;
$('#total').text( parseInt($('#total').text()) + 1 );
taskCnt.total++;
form.task.value = '';
var item = json.list[0];
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
$('#tasklist').append(_mtt.prepareTaskStr(item));
changeTaskOrder(item.id);
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.newTaskFlashColor}, 2000);
refreshTaskCnt();
});
flag.tagsChanged = true;
return false;
};
function changeTaskOrder(id)
{
id = parseInt(id);
if (taskOrder.length < 2) {
return;
}
if (id && (curList.sort == 5 || curList.sort == 105)) {
// re-sort the whole list in case of database sorting is not the same due to collation
changeTaskOrder();
}
const oldOrder = taskOrder.slice();
function firstNonZero(order, compl, ...args) {
const m = (order < 100) ? 1 : -1;
if (compl != 0) return compl;
for (const arg of args) {
if (arg != 0) return arg * m;
}
return 0;
}
// sortByHand
if (curList.sort == 0) {
taskOrder.sort( (a, b) => firstNonZero(
0,
taskList[a].compl - taskList[b].compl,
taskList[a].ow - taskList[b].ow
))
}
// sortByPrio and reverse
else if (curList.sort == 1 || curList.sort == 101) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[b].prio - taskList[a].prio,
taskList[a].dueInt - taskList[b].dueInt,
taskList[a].ow - taskList[b].ow
));
}
// sortByDueDate and reverse
else if (curList.sort == 2 || curList.sort == 102) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dueInt - taskList[b].dueInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
// sortByDateCreated and reverse
else if (curList.sort == 3 || curList.sort == 103) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dateInt - taskList[b].dateInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
));
}
// sortByDateModified and reverse
else if (curList.sort == 4 || curList.sort == 104) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].dateEditedInt - taskList[b].dateEditedInt,
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
// sortByTitle and reverse
else if (curList.sort == 5 || curList.sort == 105) {
taskOrder.sort( (a, b) => firstNonZero(
curList.sort,
taskList[a].compl - taskList[b].compl,
taskList[a].title.localeCompare(taskList[b].title, 'en', {sensitivity: 'base'}),
taskList[b].prio - taskList[a].prio,
taskList[a].ow - taskList[b].ow
))
}
else {
return;
}
if (oldOrder.toString() == taskOrder.toString()) {
return;
}
if (id && taskList[id]) {
// optimization: determine where to insert task: top or after some task
const indx = $.inArray(id, taskOrder);
if (indx == 0) {
$('#tasklist').prepend($('#taskrow_'+id))
} else {
const after = taskOrder[indx-1];
$('#taskrow_' + after).after($('#taskrow_'+id));
}
}
else {
const o = $('#tasklist');
for (const i in taskOrder) {
o.append($('#taskrow_' + taskOrder[i]));
}
}
};
function prioPopup(act, el, id)
{
if(act == 0) {
clearTimeout(objPrio.timer);
return;
}
var offset = $(el).offset();
$('#priopopup').css({ position: 'absolute', top: offset.top + 1, left: offset.left + 1 });
objPrio.taskId = id;
objPrio.el = el;
objPrio.timer = setTimeout("$('#priopopup').show()", 300);
};
function prioClick(prio, el)
{
el.blur();
prio = parseInt(prio);
$('#priopopup').fadeOut('fast'); //.hide();
setTaskPrio(objPrio.taskId, prio);
};
function setTaskPrio(id, prio)
{
_mtt.db.request('setTaskPriority', {id:id, priority:prio});
taskList[id].prio = prio;
var $t = $('#taskrow_'+id);
$t.find('.task-prio').replaceWith(preparePrio(prio, id));
if(curList.sort != 0) changeTaskOrder(id);
$t.effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal');
};
function setSort(v, init)
{
if (v < 0 || (v > 5 && v < 101) || v > 105) {
return;
}
curList.sort = v;
loadTasks({saveSort:1});
};
function updateSortUI(v)
{
$('#listmenucontainer .sort-item').removeClass('mtt-item-checked').children('.mtt-sort-direction').text('');
if (v == 0) $('#sortByHand').addClass('mtt-item-checked');
else if(v==1 || v==101) $('#sortByPrio').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==1 ? '↑' : '↓');
else if(v==2 || v==102) $('#sortByDueDate').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==2 ? '↑' : '↓');
else if(v==3 || v==103) $('#sortByDateCreated').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==3 ? '↓' : '↑');
else if(v==4 || v==104) $('#sortByDateModified').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==4 ? '↓' : '↑');
else if(v==5 || v==105) $('#sortByTitle').addClass('mtt-item-checked').children('.mtt-sort-direction').text(v==5 ? '↓' : '↑');
else return;
curList.sort = v;
if (v == 0 && !flag.readOnly) $("#tasklist").sortable('enable');
else $("#tasklist").sortable('disable');
};
function changeTaskCnt(task, dir, old)
{
if(dir > 0) dir = 1;
else if(dir < 0) dir = -1;
if(dir == 0 && old != null && task.dueClass != old.dueClass) //on saveTask
{
if(old.dueClass != '') taskCnt[old.dueClass]--;
if(task.dueClass != '') taskCnt[task.dueClass]++;
}
else if(dir == 0 && old == null) //on comleteTask
{
if(!curList.showCompl && task.compl) taskCnt.total--;
if(task.dueClass != '') taskCnt[task.dueClass] += task.compl ? -1 : 1;
}
if(dir != 0) {
if(task.dueClass != '' && !task.compl) taskCnt[task.dueClass] += dir;
taskCnt.total += dir;
}
};
function refreshTaskCnt()
{
$('#cnt_total').text(taskCnt.total);
$('#cnt_past').text(taskCnt.past);
$('#cnt_today').text(taskCnt.today);
$('#cnt_soon').text(taskCnt.soon);
if(filter.due == '') $('#total').text(taskCnt.total);
else if(taskCnt[filter.due] != null) $('#total').text(taskCnt[filter.due]);
};
function setTaskview(v)
{
if(v == 0)
{
if(filter.due == '') return;
$('#taskview .btnstr').text(_mtt.lang.get('tasks'));
$('#tasklist').removeClass('filter-'+filter.due);
filter.due = '';
$('#total').text(taskCnt.total);
}
else if(v=='past' || v=='today' || v=='soon')
{
if(filter.due == v) return;
else if(filter.due != '') {
$('#tasklist').removeClass('filter-'+filter.due);
}
$('#tasklist').addClass('filter-'+v);
$('#taskview .btnstr').text(_mtt.lang.get('f_'+v));
$('#total').text(taskCnt[v]);
filter.due = v;
}
};
function toggleAllNotes(show, event)
{
for (let id in taskList)
{
if (taskList[id].note == '') continue;
if (show) $('#taskrow_'+id).addClass('task-expanded');
else $('#taskrow_'+id).removeClass('task-expanded');
}
curList.showNotes = show;
if (_mtt.options.saveShowNotes || (event && (event.metaKey || event.ctrlKey)) ) {
_mtt.db.request('setShowNotesInList', {list:curList.id, shownotes:show}, function(json){});
}
};
function tabSelect(elementOrId)
{
var id;
if (typeof elementOrId == 'number') id = elementOrId;
else if(typeof elementOrId == 'string') id = parseInt(elementOrId);
else {
id = $(elementOrId).attr('id');
if (!id ) return;
id = id.split('_', 2)[1];
if (id === 'all') id = -1;
}
if ( !tabLists.exists(id) ) {
$('#tasks_info .v').text(_mtt.lang.get('listNotFound'))
$('#tasks_info').show();
$('.mtt-need-list').addClass('mtt-item-disabled');
return;
}
else {
$('#tasks_info').hide();
$('.mtt-need-list').removeClass('mtt-item-disabled');
$('#mtt').removeClass('no-list-selected');
}
var prevList = curList;
curList = tabLists.get(id);
$('#lists .mtt-tab-selected').removeClass('mtt-tab-selected');
if(id == -1) {
$('#list_all').addClass('mtt-tab-selected').removeClass('mtt-tab-hidden');
$('#listmenucontainer .mtt-need-real-list').addClass('mtt-item-hidden');
}
else {
$('#list_'+id).addClass('mtt-tab-selected').removeClass('mtt-tab-hidden');
$('#listmenucontainer .mtt-need-real-list').removeClass('mtt-item-hidden');
}
if (prevList.id != id) {
if (id == -1) $('#mtt').addClass('show-all-tasks');
else $('#mtt').removeClass('show-all-tasks');
if (filter.search != '') liveSearchToggle(0, 1);
mytinytodo.doAction('listSelected', tabLists.get(id));
}
var newTitle = curList.name + ' - ' + _mtt.options.title;
var isFirstLoad = flag.firstLoad;
updateHistoryState( { list:id }, _mtt.urlForList(curList), newTitle );
if (!flag.readOnly) {
setLocalStorageItem('lastList', ''+id);
}
if (curList.hidden && flag.readOnly != true) {
curList.hidden = false;
_mtt.db.request('setHideList', {list:curList.id, hide:0});
}
flag.tagsChanged = true;
cancelTagFilter(0, 1);
setTaskview(0);
if (isFirstLoad && filter.search != '') {
$('#search').val(filter.search);
$('#search_close').show();
searchTasks(true);
}
else {
filter.search = '';
loadTasks({clearTasklist:1});
}
};
function listMenu(el)
{
if(!mytinytodo.menus.listMenu) mytinytodo.menus.listMenu = new mttMenu('listmenucontainer', {onclick:listMenuClick, onhover:listMenuHover});
mytinytodo.menus.listMenu.show(el);
};
function listMenuClick(el, menu)
{
if(!el.id) return;
switch(el.id) {
case 'btnAddList': addList(); break;
case 'btnRenameList': renameCurList(); break;
case 'btnDeleteList': deleteCurList(); break;
case 'btnPublish': publishCurList(); break;
case 'btnFeedKey': enableFeedKeyInCurList(); break;
case 'btnShowFeedKey': showFeedKeyInCurList(); break;
case 'btnHideList': hideList(curList.id); break;
case 'btnExportCSV': return true;
case 'btnExportICAL': return true;
case 'btnRssFeed': return true;
case 'btnShowCompleted': showCompletedToggle(); break;
case 'btnClearCompleted': clearCompleted(); break;
case 'sortByHand': setSort(0); break;
case 'sortByPrio': setSort(curList.sort==1 ? 101 : 1); break;
case 'sortByDueDate': setSort(curList.sort==2 ? 102 : 2); break;
case 'sortByDateCreated': setSort(curList.sort==3 ? 103 : 3); break;
case 'sortByDateModified': setSort(curList.sort==4 ? 104 : 4); break;
case 'sortByTitle': setSort(curList.sort==5 ? 105 : 5); break;
}
return false;
};
function listMenuHover(el, menu)
{
if(!el.id) return;
switch(el.id) {
case 'btnExportCSV': $('#'+el.id+'>a').attr('href', _mtt.urlForExport('csv')) ; break;
case 'btnExportICAL': $('#'+el.id+'>a').attr('href', _mtt.urlForExport('ical')) ; break;
case 'btnRssFeed': $('#'+el.id+'>a').attr('href', _mtt.urlForFeed()) ; break;
}
}
function deleteTask(id)
{
mttConfirm( _mtt.lang.get('confirmDelete'), function()
{
flag.tagsChanged = true;
_mtt.db.request('deleteTask', {id:id}, function(json){
if (!parseInt(json.total)) return;
var item = json.list[0];
taskOrder.splice($.inArray(id,taskOrder), 1);
$('#taskrow_'+id).effect("highlight", {color:_mtt.theme.deleteTaskFlashColor}, 'normal', function(){ $(this).remove() });
changeTaskCnt(taskList[id], -1);
refreshTaskCnt();
delete taskList[id];
});
})
return false;
};
function completeTask(id, ch)
{
if(!taskList[id]) return; //click on already removed from the list while anim. effect
var compl = 0;
if(ch.checked) compl = 1;
_mtt.db.request('completeTask', {id:id, compl:compl, list:curList.id}, function(json){
if(!parseInt(json.total)) return;
var item = json.list[0];
if(item.compl) $('#taskrow_'+id).addClass('task-completed');
else $('#taskrow_'+id).removeClass('task-completed');
taskList[id] = item;
changeTaskCnt(taskList[id], 0);
if(item.compl && !curList.showCompl) {
delete taskList[id];
taskOrder.splice($.inArray(id,taskOrder), 1);
$('#taskrow_'+id).fadeOut('normal', function(){ $(this).remove() });
}
else if(curList.showCompl) {
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(taskList[id]));
$('#taskrow_'+id).fadeOut('fast', function(){
changeTaskOrder(id);
$(this).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
});
}
refreshTaskCnt();
});
return false;
};
function toggleTaskNote(id)
{
var aArea = '#tasknotearea'+id;
if($(aArea).css('display') == 'none')
{
$('#notetext'+id).val(taskList[id].noteText);
$(aArea).show();
$('#tasknote'+id).hide();
$('#taskrow_'+id).addClass('task-expanded');
$('#notetext'+id).focus();
} else {
cancelTaskNote(id)
}
return false;
};
function cancelTaskNote(id)
{
if(taskList[id].note == '') $('#taskrow_'+id).removeClass('task-expanded');
$('#tasknotearea'+id).hide();
$('#tasknote'+id).show();
return false;
};
function saveTaskNote(id)
{
_mtt.db.request('editNote', {id:id, note:$('#notetext'+id).val()}, function(json){
if(!parseInt(json.total)) return;
var item = json.list[0];
taskList[id].note = item.note;
taskList[id].noteText = item.noteText;
$('#tasknote'+id).html(prepareTaskNoteInlineHtml(item.note, item.noteText));
if(item.note == '') $('#taskrow_'+id).removeClass('task-has-note task-expanded');
else $('#taskrow_'+id).addClass('task-has-note task-expanded');
cancelTaskNote(id);
});
return false;
};
function fillTaskViewer(id)
{
const item = taskList[id];
if (!item) return false;
$('#page_taskviewer').attr('data-id', item.id);
$('#taskviewer_id').text('#' + item.id);
$('#page_taskviewer .title').html(item.title);
$('#page_taskviewer .note').html(item.note);
$('#page_taskviewer .prio .content').html(preparePrio(item.prio,item.id));
$('#page_taskviewer .due .content').html(item.duedate);
$('#page_taskviewer .tags .content').html(prepareTagsStr(item, ''));
$('#page_taskviewer .list .content').text(curList.name);
return item;
}
function viewTask(id)
{
const item = fillTaskViewer(id);
if (!item) return;
_mtt.pageSet('taskviewer');
updateHistoryState({ task: item.id, list: item.listId }, '#task/'+item.id, dehtml(item.title) + ' - ' + curList.name + ' - ' + _mtt.options.title);
}
function editTask(id)
{
var item = taskList[id];
if(!item) return false;
// no need to clear form
var form = document.getElementById('taskedit_form');
form.task.value = item.titleText;
form.note.value = item.noteText;
form.id.value = item.id;
form.tags.value = dehtml(item.tags).split(',').join(', ');
form.duedate.value = item.duedate;
form.prio.value = item.prio;
$('#taskedit_id').text('#' + item.id);
$('#taskedit_info .date-created-value').text(item.date).attr('title', item.dateFull);;
if (item.isEdited && !item.compl) {
$('#taskedit_info .date-edited-value').text(item.dateEdited).attr('title', item.dateEditedFull);
$('#taskedit_info .date-edited').show()
}
else {
$('#taskedit_info .date-edited').hide();
}
if (item.compl) {
$('#taskedit_info .date-completed-value').text(item.dateCompleted).attr('title', item.dateCompletedFull);;
$('#taskedit_info .date-completed').show()
}
else {
$('#taskedit_info .date-completed').hide();
}
toggleEditAllTags(0);
showEditForm();
return false;
};
function clearEditForm()
{
var form = document.getElementById('taskedit_form');
form.task.value = '';
form.note.value = '';
form.tags.value = '';
form.duedate.value = '';
form.prio.value = '0';
form.id.value = '';
toggleEditAllTags(0);
};
function showEditForm(isAdd)
{
let form = document.getElementById('taskedit_form');
if (isAdd)
{
clearEditForm();
$('#page_taskedit').removeClass('mtt-inedit').addClass('mtt-inadd');
form.isadd.value = 1;
if (_mtt.options.autotag) form.tags.value = _mtt.filter.getTags();
if ($('#task').val() != '')
{
_mtt.db.request('parseTaskStr', { list:curList.id, title:$('#task').val(), tag:_mtt.filter.getTags() }, function(json){
if(!json) return;
form.task.value = json.title
form.tags.value = (form.tags.value != '') ? form.tags.value +', '+ json.tags : json.tags;
form.prio.value = json.prio;
$('#task').val('');
});
}
}
else {
$('#page_taskedit').removeClass('mtt-inadd').addClass('mtt-inedit');
form.isadd.value = 0;
}
$(document).on('keydown.mttback', function(event) {
if (event.keyCode == 27) { //Esc pressed
_mtt.pageBack(true);
}
});
flag.editFormChanged = false;
_mtt.pageSet('taskedit');
};
function saveTask(form)
{
$("#edittags").autocomplete('close');
if (flag.readOnly)
return false;
if (form.isadd.value != 0)
return submitFullTask(form);
_mtt.db.request('editTask', {id:form.id.value, title: form.task.value, note:form.note.value,
prio:form.prio.value, tags:form.tags.value, duedate:form.duedate.value},
function(json) {
if (!parseInt(json.total))
return;
const item = json.list[0];
changeTaskCnt(item, 0, taskList[item.id]);
taskList[item.id] = item;
const noteExpanded = (item.note != '' && $('#taskrow_'+item.id).is('.task-expanded')) ? 1 : 0;
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(item, noteExpanded));
if (curList.sort != 0)
changeTaskOrder(item.id);
refreshTaskCnt();
_mtt.pageBack(); //back to list or viewer
if (_mtt.pages.current.page = 'taskviewer') {
fillTaskViewer(item.id);
}
else {
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
}
});
flag.tagsChanged = true;
return false;
};
function toggleEditAllTags(show)
{
if (show)
{
if (curList.id == -1) {
const taskId = document.getElementById('taskedit_form').id.value;
loadTags(taskList[taskId].listId, fillEditAllTags);
}
else if (flag.tagsChanged)
loadTags(curList.id, fillEditAllTags);
else
fillEditAllTags();
showhide($('#alltags_hide'), $('#alltags_show'));
}
else {
$('#alltags').hide();
showhide($('#alltags_show'), $('#alltags_hide'))
}
};
function fillEditAllTags()
{
const a = [];
tagsList.forEach( (item) => {
a.push('<span class="tag" data-tag="' + item.tag +'">' + item.tag + '</span>');
});
const content = (a.length == 0) ? _mtt.lang.get('noTags') : a.join('');
$('#alltags').html(content);
$('#alltags').show();
};
function addEditTag(tag)
{
var v = $('#edittags').val();
if(v == '') {
$('#edittags').val(tag);
return;
}
var r = v.search(new RegExp('(^|,)\\s*'+tag+'\\s*(,|$)'));
if(r < 0) $('#edittags').val(v+', '+tag);
};
function loadTags(listId, callback)
{
if (flag.showTagsFromAllLists) listId = -1;
_mtt.db.request('tagCloud', {list:listId}, function(json){
if (!parseInt(json.total)) tagsList = [];
else tagsList = json.items;
let cloud = '';
tagsList.forEach( item => {
// item.tag is escaped with htmlspecialchars()
cloud += ' <span class="tag" data-tag="' + item.tag + '" data-tag-id="' + item.id + '">' + item.tag + '</span>';
});
if (cloud == '') {
cloud = _mtt.lang.get('noTags');
}
$('#tagcloudcontent').html(cloud)
flag.tagsChanged = false;
callback();
});
};
function cancelTagFilter(tagId, dontLoadTasks)
{
if(tagId) _mtt.filter.cancelTag(tagId);
else _mtt.filter.clear();
if(dontLoadTasks==null || !dontLoadTasks) loadTasks();
};
function addFilterTag(tag, tagId, exclude)
{
if(!_mtt.filter.addTag(tagId, tag, exclude)) return false;
loadTasks();
};
function liveSearchToggle(toSearch, dontLoad)
{
if(toSearch)
{
$('#search').focus();
}
else
{
if($('#search').val() != '') {
filter.search = '';
$('#search').val('');
$('#searchbarkeyword').text('');
$('#searchbar').hide();
$('#search_close').hide();
if(!dontLoad) loadTasks();
}
$('#search').blur();
}
};
function searchTasks(force)
{
var newkeyword = $('#search').val();
if(newkeyword == filter.search && !force) return false;
filter.search = newkeyword;
if (filter.search != '') {
$('#searchbarkeyword').text(filter.search);
$('#searchbar').fadeIn('fast');
}
else $('#searchbar').fadeOut('fast');
loadTasks();
return false;
};
function submitFullTask(form)
{
if(flag.readOnly) return false;
_mtt.db.request( 'fullNewTask',
{
list: curList.id,
tag: _mtt.filter.getTags(),
title: form.task.value,
note: form.note.value,
prio: form.prio.value,
tags: form.tags.value,
duedate: form.duedate.value
},
function(json) {
if (!parseInt(json.total)) return;
form.task.value = '';
var item = json.list[0];
taskList[item.id] = item;
taskOrder.push(parseInt(item.id));
$('#tasklist').append(_mtt.prepareTaskStr(item));
changeTaskOrder(item.id);
_mtt.pageBack();
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.newTaskFlashColor}, 2000);
changeTaskCnt(item, 1);
refreshTaskCnt();
}
);
flag.tagsChanged = true;
return false;
};
function tasklistSortStart(event, ui)
{
// remember initial order before sorting
sortOrder = $(this).sortable('toArray');
};
function tasklistSortUpdated(event, ui)
{
if(!ui.item[0]) return;
var itemId = ui.item[0].id;
var n = $(this).sortable('toArray');
// remove possible empty id's
for(var i=0; i<sortOrder.length; i++) {
if(sortOrder[i] == '') { sortOrder.splice(i,1); i--; }
}
if(n.toString() == sortOrder.toString()) return;
// make index: id=>position
var h0 = {}; //before
for(var j=0; j<sortOrder.length; j++) {
h0[sortOrder[j]] = j;
}
var h1 = {}; //after
for(var j=0; j<n.length; j++) {
h1[n[j]] = j;
taskOrder[j] = parseInt(n[j].split('_')[1]);
}
// prepare param
var o = [];
var diff;
var replaceOW = taskList[sortOrder[h1[itemId]].split('_')[1]].ow;
for(var j in h0)
{
diff = h1[j] - h0[j];
if(diff != 0) {
var a = j.split('_');
if(j == itemId) diff = replaceOW - taskList[a[1]].ow;
o.push({id:a[1], diff:diff});
taskList[a[1]].ow += diff;
}
}
_mtt.db.request('changeOrder', {order:o});
};
function mttMenu(container, options)
{
var menu = this;
this.container = document.getElementById(container);
this.$container = $(this.container);
this.isOpen = false;
this.options = options || {};
this.submenu = [];
this.curSubmenu = null;
this.showTimer = null;
this.ts = (new Date).getTime();
this.container.mttmenu = this.ts;
if (!this.options.hasOwnProperty('isRTL')) {
this.options.isRTL = ($('body').css('direction') == 'rtl') ? true : false;
}
if (!this.options.hasOwnProperty('alignRight')) {
this.options.alignRight = false;
}
if (!this.options.hasOwnProperty('adjustWidth')) {
this.options.adjustWidth = true;
}
this.$container.find('li').click(function(){
var r = menu.onclick(this, menu);
return (typeof r === 'undefined') ? false : r;
})
.each(function(){
var submenu = 0;
if($(this).is('.mtt-menu-indicator'))
{
submenu = new mttMenu($(this).attr('submenu'), menu.options);
submenu.$caller = $(this);
submenu.parent = menu;
if(menu.root) submenu.root = menu.root; //!! be careful with circular references
else submenu.root = menu;
menu.submenu.push(submenu);
submenu.ts = submenu.container.mttmenu = submenu.root.ts;
}
$(this).hover(
function(){
if(!$(this).is('.mtt-menu-item-active')) menu.$container.find('li').removeClass('mtt-menu-item-active');
clearTimeout(menu.showTimer);
if(menu.hideTimer && menu.parent) {
clearTimeout(menu.hideTimer);
menu.hideTimer = null;
menu.$caller.addClass('mtt-menu-item-active');
clearTimeout(menu.parent.showTimer);
}
if(menu.curSubmenu && menu.curSubmenu.isOpen && menu.curSubmenu != submenu && !menu.curSubmenu.hideTimer)
{
menu.$container.find('li').removeClass('mtt-menu-item-active');
var curSubmenu = menu.curSubmenu;
curSubmenu.hideTimer = setTimeout(function(){
curSubmenu.hide();
curSubmenu.hideTimer = null;
}, 300);
}
if (menu.options.onhover) menu.options.onhover(this, menu);
if(!submenu || menu.curSubmenu == submenu && menu.curSubmenu.isOpen)
return;
menu.showTimer = setTimeout(function(){
menu.curSubmenu = submenu;
submenu.showSub();
}, 400);
},
function(){}
);
});
this.onclick = function(item, fromMenu)
{
if ($(item).is('.mtt-item-disabled,.mtt-menu-indicator,.mtt-item-hidden')) return;
var r = undefined;
if (this.options.onclick) r = this.options.onclick(item, fromMenu);
if (menu.root) menu.root.close();
else menu.close();
return r;
};
this.hide = function()
{
for(var i in this.submenu) this.submenu[i].hide();
clearTimeout(this.showTimer);
this.$container.hide();
this.$container.find('li').removeClass('mtt-menu-item-active');
this.isOpen = false;
};
this.close = function(event)
{
if(!this.isOpen) return;
if(event)
{
// ignore if event (click) was on caller or container
var t = event.target;
if(t == this.caller || (t.mttmenu && t.mttmenu == this.ts)) return;
while(t.parentNode) {
if(t.parentNode == this.caller || (t.mttmenu && t.mttmenu == this.ts)) return;
t = t.parentNode;
}
}
this.hide();
$(this.caller).removeClass('mtt-menu-button-active');
$(document).off('mousedown.mttmenu');
$(document).off('keydown.mttmenu');
// onClose trigger
if(this.options.onClose && this.options.onClose.call) {
this.options.onClose.call(this);
}
};
this.show = function(caller)
{
if(this.isOpen)
{
this.close();
if(this.caller && this.caller == caller) return;
}
$(document).triggerHandler('mousedown.mttmenu'); //close any other open menu
$(document).on('keydown.mttmenu', function(event) {
if (event.keyCode == 27) {
menu.close(); //close the menu on Esc pressed
}
});
this.caller = caller;
var $caller = $(caller);
// beforeShow trigger
if(this.options.beforeShow && this.options.beforeShow.call) {
this.options.beforeShow.call(this);
}
// adjust width
if (this.options.adjustWidth) {
this.$container.width('');
this.$container.removeClass('mtt-left-adjusted mtt-right-adjusted');
if ( this.$container.outerWidth(true) > $(window).width() ) {
this.$container.addClass('mtt-left-adjusted mtt-right-adjusted');
this.$container.width( $(window).width() - (this.$container.outerWidth(true) - this.$container.width()) );
}
}
//round the width to avoid overflow issues
this.$container.width( Math.ceil(this.$container.width()) );
$caller.addClass('mtt-menu-button-active');
var offset = $caller.offset();
var containerWidth = this.$container.outerWidth(true);
var alignRight = this.options.isRTL ^ this.options.alignRight; //alignRight is not for submenu
var x2 = $(window).width() + $(document).scrollLeft() - containerWidth - 1; // TODO: rtl?
var x = alignRight ? offset.left + $caller.outerWidth() - containerWidth : offset.left;
if (x > x2) {
x = x2; //move left if container overflows right edge
this.$container.addClass('mtt-right-adjusted');
}
if (x < 0) {
x = 0; //do not cross left edge
this.$container.addClass('mtt-left-adjusted');
}
var y = offset.top + caller.offsetHeight - 1;
if(y + this.$container.outerHeight(true) > $(window).height() + $(document).scrollTop()) y = offset.top - this.$container.outerHeight();
if(y<0) y=0;
this.$container.css({ position: 'absolute', top: y, left: x, width:this.$container.width() /*, 'min-width': $caller.width()*/ }).show();
var menu = this;
$(document).on('mousedown.mttmenu', function(e) { menu.close(e) });
this.isOpen = true;
};
this.showSub = function()
{
// adjust width
if (this.options.adjustWidth) {
this.$container.width('');
this.$container.removeClass('mtt-left-adjusted mtt-right-adjusted');
if ( this.$container.outerWidth(true) > $(window).width() ) {
this.$container.addClass('mtt-left-adjusted mtt-right-adjusted');
this.$container.width( $(window).width() - (this.$container.outerWidth(true) - this.$container.width()) );
}
}
//round the width to avoid overflow issues
this.$container.width( Math.ceil(this.$container.width()) );
this.$caller.addClass('mtt-menu-item-active');
var offset = this.$caller.offset();
var containerWidth = this.$container.outerWidth(true);
var x = 0;
if (this.options.isRTL) {
x = offset.left - containerWidth - 1;
if (x < 0) {
x = offset.left + this.$caller.outerWidth();
}
if ( x + containerWidth > $(window).width() + $(document).scrollLeft() ) {
x = $(window).width() + $(document).scrollLeft() - containerWidth; // TODO: rtl?
this.$container.addClass('mtt-right-adjusted');
}
}
else {
x = offset.left + this.$caller.outerWidth();
if ( x + containerWidth > $(window).width() + $(document).scrollLeft() ) { // TODO: rtl?
x = offset.left - containerWidth - 1;
}
if (x < 0) {
x = 0;
this.$container.addClass('mtt-left-adjusted');
}
}
var y = offset.top + this.parent.$container.offset().top-this.parent.$container.find('li:first').offset().top;
if(y + this.$container.outerHeight(true) > $(window).height() + $(document).scrollTop()) y = $(window).height() + $(document).scrollTop()- this.$container.outerHeight(true) - 1;
if(y<0) y=0;
this.$container.css({ position: 'absolute', top: y, left: x, width:this.$container.width() /*, 'min-width': this.$caller.outerWidth()*/ }).show();
this.isOpen = true;
};
this.destroy = function()
{
for(var i in this.submenu) {
this.submenu[i].destroy();
delete this.submenu[i];
}
this.$container.find('li').unbind(); //'click mouseenter mouseleave'
};
};
function taskContextMenu(el, id)
{
if(!_mtt.menus.cmenu) _mtt.menus.cmenu = new mttMenu('taskcontextcontainer', {
onclick: taskContextClick,
beforeShow: function() {
var taskId = this.tag;
$('#taskrow_'+taskId).addClass('menu-active');
$('#cmenupriocontainer li').removeClass('mtt-item-checked');
$('#cmenu_prio\\:'+ taskList[taskId].prio).addClass('mtt-item-checked');
},
onClose: function() {
$('#tasklist li').removeClass('menu-active');
},
alignRight: true
});
_mtt.menus.cmenu.tag = id;
_mtt.menus.cmenu.show(el);
};
function taskContextClick(el, menu)
{
if(!el.id) return;
var taskId = parseInt(_mtt.menus.cmenu.tag);
var id = el.id, value;
var a = id.split(':');
if(a.length == 2) {
id = a[0];
value = a[1];
}
switch(id) {
case 'cmenu_edit': editTask(taskId); break;
/*case 'cmenu_note': toggleTaskNote(taskId); break;*/
case 'cmenu_delete': deleteTask(taskId); break;
case 'cmenu_prio': setTaskPrio(taskId, parseInt(value)); break;
case 'cmenu_list':
if(menu.$caller && menu.$caller.attr('id')=='cmenu_move') moveTaskToList(taskId, value);
break;
}
};
function moveTaskToList(taskId, listId)
{
if(curList.id == listId) return;
_mtt.db.request('moveTask', {id:taskId, from:curList.id, to:listId}, function(json){
if(!parseInt(json.total)) return;
if(curList.id == -1)
{
// leave the task in current tab (all tasks tab)
var item = json.list[0];
changeTaskCnt(item, 0, taskList[item.id]);
taskList[item.id] = item;
var noteExpanded = (item.note != '' && $('#taskrow_'+item.id).is('.task-expanded')) ? 1 : 0;
$('#taskrow_'+item.id).replaceWith(_mtt.prepareTaskStr(item, noteExpanded));
if(curList.sort != 0) changeTaskOrder(item.id);
refreshTaskCnt();
$('#taskrow_'+item.id).effect("highlight", {color:_mtt.theme.editTaskFlashColor}, 'normal', function(){$(this).css('display','')});
}
else {
// remove the task from currrent tab
changeTaskCnt(taskList[taskId], -1)
delete taskList[taskId];
taskOrder.splice($.inArray(taskId,taskOrder), 1);
$('#taskrow_'+taskId).fadeOut('normal', function(){ $(this).remove() });
refreshTaskCnt();
}
});
flag.tagsChanged = true;
};
function cmenuOnListsLoaded()
{
if(_mtt.menus.cmenu) _mtt.menus.cmenu.destroy();
_mtt.menus.cmenu = null;
var s = '';
var all = tabLists.getAll();
for(var i in all) {
s += '<li id="cmenu_list:'+all[i].id+'" class="'+(all[i].hidden?'mtt-list-hidden':'')+'">'+all[i].name+'</li>';
}
$('#cmenulistscontainer ul').html(s);
};
function cmenuOnListAdded(list)
{
if(_mtt.menus.cmenu) _mtt.menus.cmenu.destroy();
_mtt.menus.cmenu = null;
$('#cmenulistscontainer ul').append('<li id="cmenu_list:'+list.id+'">'+list.name+'</li>');
};
function cmenuOnListRenamed(list)
{
$('#cmenu_list\\:'+list.id).text(list.name);
};
function cmenuOnListSelected(list)
{
$('#cmenulistscontainer li').removeClass('mtt-item-disabled');
$('#cmenu_list\\:'+list.id).addClass('mtt-item-disabled').removeClass('mtt-list-hidden');
};
function cmenuOnListOrderChanged()
{
cmenuOnListsLoaded();
$('#cmenu_list\\:'+curList.id).addClass('mtt-item-disabled');
};
function cmenuOnListHidden(list)
{
if (list.id == -1) return;
$('#cmenu_list\\:'+list.id).addClass('mtt-list-hidden');
};
function tabmenuOnListSelected(list)
{
if (list.published) {
$('#btnPublish').addClass('mtt-item-checked');
$('#btnRssFeed').removeClass('mtt-item-disabled');
}
else {
$('#btnPublish').removeClass('mtt-item-checked');
$('#btnRssFeed').addClass('mtt-item-disabled');
}
if (list.showCompl) {
$('#btnShowCompleted').addClass('mtt-item-checked');
}
else {
$('#btnShowCompleted').removeClass('mtt-item-checked');
}
if (list.feedKey !== undefined && list.feedKey !== '') {
$('#btnFeedKey').addClass('mtt-item-checked');
$('#btnShowFeedKey').removeClass('mtt-item-disabled');
}
else {
$('#btnFeedKey').removeClass('mtt-item-checked');
$('#btnShowFeedKey').addClass('mtt-item-disabled');
}
};
function listOrderChanged(event, ui)
{
var a = $(this).sortable("toArray");
var order = [];
for(var i in a) {
order.push(a[i].split('_')[1]);
}
tabLists.reorder(order);
_mtt.db.request('changeListOrder', {order:order});
_mtt.doAction('listOrderChanged', {order:order});
};
function showCompletedToggle()
{
var act = curList.showCompl ? 0 : 1;
curList.showCompl = tabLists.get(curList.id).showCompl = act;
if(act) $('#btnShowCompleted').addClass('mtt-item-checked');
else $('#btnShowCompleted').removeClass('mtt-item-checked');
loadTasks({setCompl:1});
};
function clearCompleted()
{
if (!curList) return false;
mttConfirm( _mtt.lang.get('clearCompleted'), function()
{
_mtt.db.request('clearCompletedInList', {list:curList.id}, function(json){
if(!parseInt(json.total)) return;
flag.tagsChanged = true;
if(curList.showCompl) loadTasks();
});
});
};
function showhide(a,b)
{
a.show();
b.hide();
};
function findParentNode(el, node)
{
// in html nodename is in uppercase, in xhtml nodename in in lowercase
if (el.nodeName.toUpperCase() == node) return el;
while (el.parentNode) {
el = el.parentNode;
if (el.nodeName.toUpperCase() == node) return el;
}
return null;
};
function getLiTaskId(el)
{
var li = findParentNode(el, 'LI');
if(!li || !li.id) return 0;
return li.id.split('_',2)[1];
};
function isParentId(el, id)
{
if(el.id && $.inArray(el.id, id) != -1) return true;
if(!el.parentNode) return null;
return isParentId(el.parentNode, id);
};
function dehtml(str)
{
return str.replace(/&quot;/g, '"').replace(/&#039;/g, "'").replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&amp;/g, '&');
};
function escapeHtml(str) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return str.replace(/[&<>"']/g, (m) => map[m]);
}
function slmenuOnListsLoaded()
{
if(_mtt.menus.selectlist) {
_mtt.menus.selectlist.destroy();
_mtt.menus.selectlist = null;
}
var s = '';
var all = tabLists.getAll();
for(var i in all) {
s += '<li id="slmenu_list:'+all[i].id+'" class="'+(all[i].id==curList.id?'mtt-item-checked':'')+' list-id-'+all[i].id+(all[i].hidden?' mtt-list-hidden':'')+'"><div class="menu-icon"></div><a href="'+ _mtt.urlForList(all[i])+ '">'+all[i].name+'</a></li>';
}
$('#slmenucontainer ul>.slmenu-lists-begin').nextAll().remove();
$('#slmenucontainer ul>.slmenu-lists-begin').after(s);
};
function slmenuOnListRenamed(list)
{
$('#slmenucontainer li.list-id-'+list.id).find('a').html(list.name);
};
function slmenuOnListAdded(list)
{
if(_mtt.menus.selectlist) {
_mtt.menus.selectlist.destroy();
_mtt.menus.selectlist = null;
}
$('#slmenucontainer ul').append('<li id="slmenu_list:'+list.id+'" class="list-id-'+list.id+'"><div class="menu-icon"></div><a href="'+ _mtt.urlForList(list)+ '">'+list.name+'</a></li>');
};
function slmenuOnListSelected(list)
{
$('#slmenucontainer li').removeClass('mtt-item-checked');
$('#slmenucontainer li.list-id-'+list.id).addClass('mtt-item-checked').removeClass('mtt-list-hidden');
};
function slmenuOnListHidden(list)
{
if (list.id == -1) return;
$('#slmenucontainer li.list-id-'+list.id).addClass('mtt-list-hidden');
};
function slmenuSelect(el, menu)
{
if(!el.id) return;
var id = el.id, value;
var a = id.split(':');
if(a.length == 2) {
id = a[0];
value = a[1];
}
if(id == 'slmenu_list') {
tabSelect(parseInt(value));
}
return false;
};
function hideList(listId)
{
if (typeof listId === 'string') {
listId = parseInt(listId);
}
else if (typeof listId !== 'number') {
return;
}
if(!tabLists.get(listId)) return false;
// if we hide current tab
var listIdToSelect = 0;
if(curList.id == listId) {
var all = tabLists.getAll();
for(var i in all) {
if(all[i].id != curList.id && !all[i].hidden) {
listIdToSelect = all[i].id;
break;
}
}
// do not hide the tab if others are hidden
if(!listIdToSelect) return false;
}
if(listId == -1) {
$('#list_all').addClass('mtt-tab-hidden').removeClass('mtt-tab-selected');
}
else {
$('#list_'+listId).addClass('mtt-tab-hidden').removeClass('mtt-tab-selected');
}
tabLists.get(listId).hidden = true;
_mtt.db.request('setHideList', {list:listId, hide:1});
_mtt.doAction('listHidden', tabLists.get(listId));
if(listIdToSelect) {
tabSelect(listIdToSelect);
}
}
function getLocalStorageItem(key)
{
try {
return localStorage.getItem(key);
}
catch (e) {
console.log(e);
}
return null;
}
function setLocalStorageItem(key, value)
{
try {
localStorage.setItem(key, value);
}
catch (e) {
console.log(e);
}
}
/*
Errors and Info messages
*/
function flashError(str, details)
{
if (details === undefined) details = '';
$("#msg>.msg-text").text(dehtml(str))
$("#msg>.msg-details").text(dehtml(details));
$("#loading").hide();
$("#msg").addClass('mtt-error').effect("highlight", {color:_mtt.theme.msgFlashColor}, 700);
}
function flashInfo(str, details)
{
if (details === undefined) details = '';
$("#msg>.msg-text").text(dehtml(str))
$("#msg>.msg-details").text(dehtml(details));
$("#loading").hide();
$("#msg").addClass('mtt-info').effect("highlight", {color:_mtt.theme.msgFlashColor}, 700);
}
function hideAlert()
{
$("#msg>.msg-text").text('');
$("#msg>.msg-details").text('');
$("#msg").hide().removeClass('mtt-error mtt-info').find('.msg-details').hide();
}
/*
Authorization
*/
function updateAccessStatus()
{
// flag.needAuth is not changed after pageload
if(flag.needAuth)
{
if (flag.isLogged) {
showhide( $("#logout_btn"), $("#login_btn") );
}
else {
showhide( $("#login_btn"), $("#logout_btn") );
}
}
else {
$('#mtt').addClass('no-need-auth');
}
if(flag.needAuth && !flag.isLogged) {
flag.readOnly = true;
$("#bar_public").show();
$('#mtt').addClass('readonly')
liveSearchToggle(1);
// remove some tab menu items
$('#btnRenameList,#btnDeleteList,#btnClearCompleted,#btnPublish').remove();
}
else {
flag.readOnly = false;
$('#mtt').removeClass('readonly')
$("#bar_public").hide();
liveSearchToggle(0);
}
$('#page_ajax').hide();
}
function showLogin()
{
if (_mtt.pages.current && _mtt.pages.current.page == 'login') {
return false;
}
_mtt.pageSet('login', '');
$('#password').val('').focus();
}
function doAuth(form)
{
_mtt.db.request( 'login', { password: form.password.value }, function(json) {
form.password.value = '';
if(json.logged)
{
flag.isLogged = true;
window.location.hash = '';
window.location.reload();
}
else {
flashError(_mtt.lang.get('invalidpass'));
$('#password').focus();
}
});
}
function logout()
{
_mtt.db.request( 'logout', {}, function(json) {
flag.isLogged = false;
window.location.hash = '';
window.location.reload();
});
return false;
}
/*
Settings
*/
function showSettings(json = 0)
{
var reload = false;
if (_mtt.pages.current && _mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') {
reload = true;
}
var jsonParam = (json == 1) ? '&json=1' : '';
$('#page_ajax').load(_mtt.mttUrl + 'settings.php?ajax=yes' + jsonParam, null, function(){
if (!reload) {
_mtt.pageSet('ajax','settings');
var newTitle = _mtt.lang.get('set_header') + ' - ' + _mtt.options.title;
updateHistoryState( { settings:1, settingsJson:json }, _mtt.urlForSettings(json), newTitle );
_mtt.doAction('settingsLoaded');
}
})
}
function saveSettings(frm)
{
if(!frm) return false;
var params = { save:'ajax' };
$(frm).find("input:hidden,input:text,input:password,input:checked,select,textarea").filter(":enabled").each(function() { params[this.name || '__'] = this.value; });
$(frm).find(":submit").attr('disabled','disabled').blur();
$.post(_mtt.mttUrl+'settings.php', params, function(json){
if(json.saved) {
flashInfo(_mtt.lang.get('settingsSaved'));
setTimeout( function(){
window.location.assign(_mtt.homeUrl); //window.location.reload();
}, 1000);
}
}, 'json');
}
function activateExtension(activate, ext)
{
var params = {
'activate': activate ? 1 : 0,
'ext': ext
}
$.post(_mtt.mttUrl+'settings.php', params, function(json){
if(json.saved) {
flashInfo(_mtt.lang.get('settingsSaved'));
showSettings(0);
}
}, 'json');
}
function showExtensionSettings(ext, callback)
{
if (_mtt.pages.current && _mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') {
$('#page_ajax').load(_mtt.apiUrl + 'ext-settings/' + ext, null, function() {
if (callback) callback();
});
}
}
function saveExtensionSettings(frm)
{
if (!frm) return false;
var ext = frm.dataset.ext;
var params = {};
$(frm).find("input:hidden,input:text,input:password,input:checked,select,textarea").filter(":enabled").each(function() { params[this.name || '__'] = this.value; });
$.ajax({
url: _mtt.apiUrl + 'ext-settings/' + ext,
method: 'PUT',
contentType : 'application/json',
data: JSON.stringify(params),
dataType: 'json',
success: function(json) {
if (json.saved) {
if (json.msg) showExtensionSettings(ext, function(){
flashInfo(json.msg);
});
else showExtensionSettings(ext);
}
else if (json.msg) {
flashError(json.msg);
}
}
});
}
function extensionSettingsAction(actionString, ext)
{
if (actionString === undefined || ext === undefined) return false;
var a = actionString.split(':', 2);
if (a.length !== 2) return false;
var method = a[0],
action = a[1];
$.ajax({
url: _mtt.apiUrl + 'ext/' + ext + '/' + action,
method: method.toUpperCase(),
contentType : 'application/json',
data: '{}',
dataType: 'json',
success: function(json) {
if (json.total && json.total > 0) {
const callback = function() {
if (json.msg) flashInfo(json.msg, json.details);
if (json.reload) {
setTimeout( function(){
//window.location.hash = '';
window.location.reload();
}, 1000);
}
}
showExtensionSettings(ext, callback);
}
else if (json.msg) {
flashInfo(json.msg, json.details);
}
}
});
}
/*
* Dialogs
*/
function mttConfirm(msg, callbackOk, callbackCancel)
{
mttModalDialog('confirm').message(msg).ok(callbackOk).cancel(callbackCancel).show();
}
function mttPrompt(msg, defaultValue, callbackOk, callbackCancel)
{
mttModalDialog('prompt').message(msg).default(defaultValue).ok(callbackOk).cancel(callbackCancel).show();
}
function mttAlert(msg, callbackOk)
{
mttModalDialog().ok(callbackOk).message(msg).show();
}
function mttModalDialog(dialogType = 'alert')
{
if ( ! (this instanceof mttModalDialog) ) return new mttModalDialog(dialogType);
let dialog = this;
this.type = dialogType;
let lastScrollTop = 0;
this.close = function() {
//restore scrolling
$('body').css({
'position': '',
'top': ''
});
window.scrollTo(window.pageXOffset, lastScrollTop);
$("html").removeClass('mtt-modal-dialog-active');
$("#modal_overlay, #modal").hide();
$("#btnModalOk").off('click');
$("#btnModalCancel").off('click');
$("#modalMessage").text('');
$("#modalTextInput").val('').off('keyup.mttmodal');
$(document).off('keydown.mttmodal');
} ;
this.ok = function(callback) {
$("#btnModalOk").on('click', function() {
const value = $("#modalTextInput").val();
dialog.close();
if (typeof callback === 'function')
callback( dialog.type === 'prompt' ? value : null );
});
return dialog;
};
this.cancel = function(callback) {
$("#btnModalCancel").on('click', function() {
dialog.close();
if (typeof callback === 'function')
callback();
});
return dialog;
};
this.message = function(msg = '') {
$("#modalMessage").text(msg);
return dialog;
};
this.default = function(value = '') {
$("#modalTextInput").val(value);
return dialog;
}
this.show = function() {
let modalOverlay = document.getElementById("modal_overlay");
if (!modalOverlay) {
modalOverlay = document.createElement("div");
modalOverlay.id = "modal_overlay";
modalOverlay.style.cssText = "position: fixed; z-index: 999; left: 0; top: 0; width: 100%; height: 100%; background-color: black; opacity: 0.6; display:none;";
document.getElementsByTagName('body')[0].appendChild(modalOverlay);
}
if (dialog.type === 'confirm') {
$("#btnModalCancel").show();
$("#modalTextInput").hide();
}
else if(dialog.type === 'prompt') {
$("#btnModalCancel").show();
$("#modalTextInput").show();
$("#modalTextInput").on("keyup.mttmodal", function(e) {
if (e.keyCode == 13) {
$("#btnModalOk").click();
}
});
}
else {
$("#btnModalCancel").hide();
$("#modalTextInput").hide();
}
$(document).on('keydown.mttmodal', function(event) {
if (event.keyCode == 27) {
dialog.close();
}
});
//disable background scrolling
lastScrollTop = window.pageYOffset;
$('body').css({
'position': 'fixed',
'top': `-${lastScrollTop}px`
})
$("html").addClass('mtt-modal-dialog-active');
$("#modal_overlay, #modal").show();
$("#modalTextInput").focus();
return dialog;
};
}
/*
* History and Hash change
*/
/**
* Manipulate browser history manually.
* //TODO: use window.location and hashchange event ?
* @param {object} state History Api state data
* @param {string} url document url. appended to the state.
* @param {string} title document title to set. appended to the state.
*/
function updateHistoryState(state, url, title)
{
if (!_mtt.options.history) {
document.title = title;
return;
}
if (flag.dontChangeHistoryOnce) {
flag.dontChangeHistoryOnce = false;
}
else {
if (_mtt.lastHistoryState) {
//_mtt.lastHistoryState.title = document.title;
window.history.pushState(_mtt.lastHistoryState, _mtt.lastHistoryState.title, _mtt.lastHistoryState.url);
}
state.url = url;
state.title = title;
window.history.replaceState(state, title, url); //also refresh visible URL
}
_mtt.lastHistoryState = history.state;
flag.firstLoad = false;
document.title = title;
}
function historyOnPopState(event)
{
if (!event.state) return;
if (event.state.list && _mtt.pages.current &&
((_mtt.pages.current.page == 'ajax' && _mtt.pages.current.pageClass == 'settings') || _mtt.pages.current.page == 'taskviewer') ) {
// Here we go back to tasklist from settings or view task, no reload.
// Just show and hide pages without history actions.
_mtt.pageBack();
flag.dontChangeHistoryOnce = true;
updateHistoryState( { list:event.state.list }, event.state.url, event.state.title );
}
else if (event.state.task) {
flag.dontChangeHistoryOnce = true;
viewTask(event.state.task);
}
else if (event.state.list) {
flag.dontChangeHistoryOnce = true;
tabSelect(event.state.list);
}
else if (event.state.settings) {
flag.dontChangeHistoryOnce = true;
showSettings(event.state.settingsJson);
}
else {
console.log("unexpected: nothing to pop");
}
}
})();