var PluginDo = { /******************************* * General functions * *******************************/ /** * Create a floating, draggable overlay * * @param {string} title The title line * @param {string} id The id to assign to the overlay * @param {string} fieldsetcontent Content of the fieldset as HTML * @param {string} submitcaption Label for the submit button * @param {Function} submitaction callback What to do when submit is pressed * @returns HTMLElement The created overlay DOMObject */ createOverlay: function (title, id, fieldsetcontent, submitcaption, submitaction) { // create overlay div var $dialog = jQuery(document.createElement('div')) .dialog({ autoOpen: false, draggable: true, title: title, resizable: false }) .html('
' + fieldsetcontent + '

' + '
') .parent() .attr('id', id) .addClass('plugin_do_popup') .hide() .appendTo('.dokuwiki:first'); // add event handlers jQuery('#' + id + ' .ui-dialog-titlebar-close').click(function () { // close button $dialog.toggle(); }); $dialog .keydown(function (e) { if (e.keyCode !== 13) { //Enter return true; } submitaction(); $dialog.hide(); e.preventDefault(); e.stopPropagation(); return false; }) .find('.plugin_do_closetask').click(function (e) { submitaction(); $dialog.hide(); e.preventDefault(); return false; }); return $dialog; }, /******************************************** * functions for interactive tasks in pages * ********************************************/ /** * Toggle status of task, when symbol of task is clicked * * @param event * @returns {boolean} */ toggle_status: function (event) { if (jQuery(this).parent().hasClass('plugin_do_done')) { //mark undone PluginDo.save_update_SingleTask(this); } else { //mark done, popup for commitmessage var $popup = jQuery('#do__commit_popup') .toggle() .css({ 'position': 'absolute', 'top': (event.pageY + 10) + 'px', 'left': (event.pageX - 150) + 'px' }); $popup[0].__me = this; jQuery('#do__popup_msg').focus(); } event.preventDefault(); event.stopPropagation(); return false; }, /** * callback performs close action of Close dialog */ closeSingleTask: function () { PluginDo.save_update_SingleTask(jQuery('#do__commit_popup')[0].__me); jQuery('#do__popup_msg').val(''); }, /** * Switch the status of a image src * * @param {boolean} done true for done tasks, false for undone * @param {jQuery} $applyto jQuery object with DOM elements where to update images */ switchDoNr: function (done, $applyto) { var newImg = done ? 'undone.png' : 'done.png'; $applyto.find('img').attr('src', DOKU_BASE + 'lib/plugins/do/pix/' + newImg); }, /** * Set the task title * * @param {Array} assignees assignees for the task, undefined for autodetect * @param {string} due the task's due date, undefined for autodetect * @param {string} closedby who closed the task, undefined for not closed, yet * @param {string} closedon when was the task closed, undefined for not closed, yet * @param {jQuery} $applyto jQuery object with DOM elements where to add the title tag, undefined for rootNode */ buildTitle: function ($applyto, assignees, due, closedby, closedon) { var titelNo = 4; // determine assignees if (!assignees || !assignees.length) { //take the assignees of the first task or table row when tasks are duplicated var $assigneeobjs = $applyto.first().find('span.plugin_do_meta_user'); if ($assigneeobjs.length === 0) { $assigneeobjs = $applyto.parent('td').parent().first().find('td.plugin_do_assignee'); } assignees = []; $assigneeobjs.each(function (i, assignee) { assignees.push(PluginDo.stripTags(jQuery(assignee).html())); }); } if (assignees.length > 0) { titelNo -= 2; } // determine due date if (!due) { var $due = $applyto.first().find('span.plugin_do_meta_date'); if ($due.length === 0) { $due = $applyto.parent('td').parent().first().find('td.plugin_do_date'); } due = PluginDo.stripTags($due.length ? $due.html() : ''); } if (due !== '') { titelNo -= 1; } var newTitle = PluginDo.getLang('title' + titelNo, assignees.join(', '), due); // is closed? if (closedon) { newTitle += ' ' + PluginDo.getLang('done', closedon); } // who closed it? if (closedby || closedby === '') { if (closedby === '') closedby = LANG.plugins['do'].by_unknown; newTitle += ' ' + PluginDo.getLang('closedby', closedby); } // apply the title $applyto.attr('title', newTitle); }, /** * get a localized string by name. * * if a arg is given it replaces %s with arg * * @return {string} localized text. */ getLang: function (name, arg1, arg2) { var lang = LANG.plugins['do'][name]; if (arg1 === null) { return lang; } else if (arg2 === null) { return lang.replace(/%(1\$)?(s|d)/, arg1); } else { return lang.replace(/%(1\$)?(s|d)/, arg1) .replace(/%(2\$)?(s|d)/, arg2); } }, /** * Escapes html entities from a string. */ hsc: function (text) { return text .replace('&', '&') .replace('<', '<') .replace('>', '>') .replace('"', '"'); }, /** * Removes tags from string * * @param {string} text * @returns {string} untagged text */ stripTags: function (text) { return text.replace(/(<([^>]+)>)/ig, ""); }, /** * Determine if a element is late * * @param {HTMLElement} ele span with due date * @returns {boolean} whether task is still open and exceed due date */ isLate: function (ele) { if (typeof ele.parentNode == 'undefined') { return false; } var $ele = jQuery(ele); if ($ele.parent().parent().hasClass('plugin_do_done')) { return false; } var currentdate = new Date(), duedate = new Date($ele.html()); return duedate.getTime() < currentdate.getTime(); }, /** * Returns value of requested url parameter * * @param {string} url * @param {string} keyname * @returns {*} */ urlParam: function (url, keyname) { var results = new RegExp('[\\?&]' + keyname + '=([^&#]*)').exec(url); if (results === null) { return null; } else { return results[1] || 0; } }, /** * Update statistics in the page task status view * * done: All tasks done * undone: There are %1$d open tasks * late: There are %1$d open tasks, %2$d are late.' * * @param {string} response result return by ajax toggle request * @param {jQuery} $itemspan */ updatePageTaskView: function (response, $itemspan) { var $pagestat = jQuery('.plugin__do_pagetasks'); if ($pagestat.length > 0) { var count = parseInt($pagestat.children().first().html(), 10), latecount = 0, newClass, oldClass = $pagestat.children().first().attr('class'), $cdate = $itemspan.find('span.plugin_do_meta_date'); if (response) { // task is marked done if (count == 1) { newClass = 'do_done'; } else if (oldClass != 'do_late') { newClass = 'do_undone'; } else { newClass = 'do_undone'; jQuery('.plugin_do_meta_date') .each(function (i, doitem) { if (PluginDo.isLate(doitem)) { newClass = 'do_late'; latecount++; } }); } count -= 1; } else { //task is marked undone if (count === 0) { if ($cdate.length && PluginDo.isLate($cdate[0])) { newClass = 'do_late'; latecount++; } else { newClass = 'do_undone'; } } else { newClass = 'do_undone'; jQuery('.plugin_do_meta_date') .each(function (i, doitem) { if (PluginDo.isLate(doitem)) { newClass = 'do_late'; latecount++; } }); } count += 1; } var title = PluginDo.getLang('title_' + newClass.substr(3), count, latecount); $pagestat .attr('title', title) .children().first() .html(count) .removeClass().addClass(newClass); } }, /** * Save the task change to wiki and update the html in the page * * @param {HTMLElement} me clicked url element */ save_update_SingleTask: function (me) { var $me = jQuery(me), $itemspan = $me.parent(), md5v = $itemspan.attr('class').match(/plugin_do_([a-f0-9]{32})/)[1], $dotags = jQuery('.plugin_do_' + md5v), done = $itemspan.hasClass('plugin_do_done'), param = { call: 'plugin_do', do_page: decodeURIComponent(PluginDo.urlParam(me.search.substring(1), 'do_page')), do_md5: decodeURIComponent(PluginDo.urlParam(me.search.substring(1), 'do_md5')), do_commit: jQuery('#do__popup_msg').val() }; if (!done) { var commitmsg = PluginDo.hsc(param.do_commit); //update table(s) $dotags.parent('td').parent().find('td.plugin_do_commit').html(commitmsg ? commitmsg : ''); //update task (inclusive duplicates) commitmsg = (commitmsg ? ' (' + PluginDo.getLang("note_done") + commitmsg + ')' : ''); $dotags.find('span.plugin_do_commit').html(commitmsg); } else { $dotags.parent('td').parent().find('td.plugin_do_commit').html(''); $dotags.find('span.plugin_do_commit').html(''); } var $image = $me.find('img'); var donr = !$image.is("img[src*='undone.png']"); $image.attr('src', DOKU_BASE + 'lib/images/throbber.gif'); /** * callback to update task when it is toggled * @param response */ var updateSingleTask = function (response) { var closedby = null, closedon = null; if (response == "-1" || response == "-2") { var langkey = 'notallowed'; if (response == "-1") { langkey = "notloggedin"; } alert(PluginDo.getLang(langkey)); //remove throbber PluginDo.switchDoNr(!donr, $me); return; } if (response) { $dotags.addClass('plugin_do_done'); if (JSINFO.plugin_do_user_name) { $dotags.parent('td').parent().find('td.plugin_do_status span span').html(JSINFO.plugin_do_user_name); } closedby = JSINFO.plugin_do_user_clean; closedon = response; } else { $dotags.removeClass('plugin_do_done'); $dotags.parent('td').parent().find('td.plugin_do_status span span').html(' '); } PluginDo.switchDoNr(donr, $dotags); PluginDo.buildTitle($dotags, [], '', closedby, closedon); //update statistics in the page task status view PluginDo.updatePageTaskView(response, $itemspan); }; //save changes, ajax returns data to mark fail or success jQuery.ajax({ type: "POST", url: DOKU_BASE + 'lib/exe/ajax.php', data: param, success: updateSingleTask, dataType: 'json' }); }, /** * callback which updates do's in the page * so that task changes after last page render are displayed correctly * * @param {Array} doStates ajax response with info from sqlite about tasks in this page */ updateItems: function (doStates) { jQuery.each(doStates, function (i, state) { var $dotags = jQuery('.plugin_do_' + state.md5); PluginDo.buildTitle($dotags, [], '', state.closedby, state.status); PluginDo.switchDoNr(!state.status, $dotags); if (state.status) { $dotags.addClass('plugin_do_done'); } if (state.msg) { var msg = PluginDo.hsc(state.msg); $dotags.find('span.plugin_do_commit').html(' (' + PluginDo.getLang("note_done") + msg + ')'); } }); }, /******************************************** * Toolbar functions * ********************************************/ old_select: null, $toolbardialog: null, textarea: null, initDialog: function (edid) { PluginDo.textarea = jQuery('#' + edid)[0]; var fieldsetcontent = ''; jQuery.each(['assign', 'date'], function (i, input) { fieldsetcontent += '

' + '' + '' + '

'; }); // prepare hidden overlay PluginDo.$toolbardialog = PluginDo.createOverlay( LANG.plugins['do'].popup_title, 'do__popup', fieldsetcontent, LANG.plugins['do'].popup_submit, PluginDo.insertDoSyntax ); jQuery('#do__popup_date').datepicker({ dateFormat: "yy-mm-dd", changeMonth: true, changeYear: true }); // if the bureaucracy plugin is installed, use its user autocompletion jQuery('#do__popup_assign').addClass('userspicker'); }, toggleToolbarDialog: function (e) { PluginDo.old_select = DWgetSelection(PluginDo.textarea); var $popup_date = jQuery('#do__popup_date'); var $popup_assign = jQuery('#do__popup_assign'); //check if a task was selected and load it's data var txt = PluginDo.old_select.getText(); $popup_date.val(''); $popup_assign.val(''); var m = txt.match(/]*)>[\s\S]*<\/do>/); if (m) { var users = m[1]; m = users.match(/\d\d\d\d-\d\d-\d\d/); if (m) { var date = m[0]; users = users.replace(date, ''); $popup_date.val(date); } users = users.replace(/^\s+/, ''); users = users.replace(/\s+$/, ''); $popup_assign.val(users); } PluginDo.$toolbardialog.css({ 'position': 'absolute', 'top': (e.pageY + 10) + 'px', 'left': (e.pageX - 100) + 'px' }); return PluginDo.$toolbardialog.toggle(); }, /** * The submit action of toolbar dialog */ insertDoSyntax: function () { // Validate data var $popup_date = jQuery('#do__popup_date'); var $popup_assign = jQuery('#do__popup_assign'); var pre = ']*)>[\s\S]*<\/do>/); if (m) { // we have previous tags, replace them stxt = stxt.replace(/]*)>/, pre); } else { // no selection or previous tags, add them stxt = pre + stxt + ''; } pasteText(sel, stxt); $popup_date.val(''); $popup_assign.val(''); } };