PageRenderTime 46ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/sync-server/war/snippets/04/todolist.js

https://github.com/DamonOehlman/prowebapps-code
JavaScript | 383 lines | 267 code | 67 blank | 49 comment | 19 complexity | 5c15bafe78d88f56e56b2763a22ad1b9 MD5 | raw file
  1. TODOLIST = (function() {
  2. var MILLISECONDS_TO_DAYS = 86400000;
  3. function showTaskDetails(selector, task) {
  4. var container = jQuery(selector),
  5. daysDue = task.getDaysDue();
  6. // update the relevant task details
  7. container.find(".task-name").html(task.name);
  8. container.find(".task-description").html(task.description);
  9. if (daysDue < 0) {
  10. container.find(".task-daysleft").html("OVERDUE BY " + Math.abs(daysDue) + " DAYS").addClass("overdue");
  11. }
  12. else {
  13. container.find(".task-daysleft").html(daysDue + " days").removeClass("overdue");
  14. } // if..else
  15. container.slideDown();
  16. } // showTaskDetails
  17. function populateTaskList() {
  18. function pad(n) {
  19. return n<10 ? '0'+n : n;
  20. }
  21. var listHtml = "",
  22. monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"];
  23. // iterate through the current tasks
  24. for (var ii = 0; ii < currentTasks.length; ii++) {
  25. var dueDateHtml =
  26. "<ul class='calendar right'>" +
  27. "<li class='day'>" + pad(currentTasks[ii].due.getDate()) + "</li>" +
  28. "<li class='month'>" + monthNames[currentTasks[ii].due.getMonth()] + "</li>" +
  29. "<li class='year'>" + currentTasks[ii].due.getFullYear() + "</li>" +
  30. "</ul>";
  31. // add the list item for the task
  32. listHtml += "<li id='task_" + currentTasks[ii].id + "'>" + dueDateHtml +
  33. "<div class='task-header'>" + currentTasks[ii].name + "</div>" +
  34. "<div class='task-details'>" +
  35. currentTasks[ii].description + "<br />" +
  36. "<a href='#' class='task-complete right'>COMPLETE TASK</a>&nbsp;" +
  37. "</div>" +
  38. "</li>";
  39. } // for
  40. jQuery("ul#tasklist").html(listHtml);
  41. } // populateTaskList
  42. function toggleDetailsDisplay(listItem) {
  43. // slide up any active task details panes
  44. jQuery(".task-details").slideUp();
  45. // if the current item is selected implement a toggle
  46. if (activeItem == listItem) {
  47. activeItem = null;
  48. return;
  49. }
  50. // in the current list item toggle the display of the details pane
  51. jQuery(listItem).find(".task-details").slideDown();
  52. // update the active item
  53. activeItem = listItem;
  54. } // toggleDetailsDisplay
  55. // define an array that will hold the current tasks
  56. var currentTasks = [],
  57. activeItem = null;
  58. // define the module
  59. var module = {
  60. /* todo task */
  61. Task: function(params) {
  62. params = jQuery.extend({
  63. id: null,
  64. name: "",
  65. description: "",
  66. due: null
  67. }, params);
  68. // initialise self
  69. var self = {
  70. id: params.id,
  71. name: params.name,
  72. description: params.description,
  73. due: params.due ? new Date(params.due) : null,
  74. completed: null,
  75. complete: function() {
  76. self.completed = new Date();
  77. },
  78. getDaysDue: function() {
  79. return Math.floor((self.due - new Date()) / MILLISECONDS_TO_DAYS);
  80. }
  81. };
  82. return self;
  83. },
  84. /* storage module */
  85. Storage: (function() {
  86. // open / create a database for the application (expected size ~ 100K)
  87. var db = null;
  88. try {
  89. db = openDatabase("todolist", "1.1", "To Do List Database", 100 * 1024);
  90. // check that we have the required tables created
  91. db.transaction(function(transaction) {
  92. transaction.executeSql(
  93. "CREATE TABLE IF NOT EXISTS task(" +
  94. " name TEXT NOT NULL, " +
  95. " description TEXT, " +
  96. " due DATETIME, " +
  97. " completed DATETIME);");
  98. });
  99. }
  100. catch (e) {
  101. db = openDatabase("todolist", "1.0", "To Do List Database", 100 * 1024);
  102. // check that we have the required tables created
  103. db.transaction(function(transaction) {
  104. transaction.executeSql(
  105. "CREATE TABLE IF NOT EXISTS task(" +
  106. " name TEXT NOT NULL, " +
  107. " description TEXT, " +
  108. " due DATETIME);");
  109. });
  110. db.changeVersion("1.0", "1.1", function(transaction) {
  111. transaction.executeSql("ALTER TABLE task ADD completed DATETIME;");
  112. });
  113. }
  114. function getTasks(callback, extraClauses) {
  115. db.transaction(function(transaction) {
  116. transaction.executeSql(
  117. "SELECT rowid as id, * FROM task " + (extraClauses ? extraClauses : ""),
  118. [],
  119. function (transaction, results) {
  120. // initialise an array to hold the tasks
  121. var tasks = [];
  122. // read each of the rows from the db, and create tasks
  123. for (var ii = 0; ii < results.rows.length; ii++) {
  124. tasks.push(new module.Task(results.rows.item(ii)));
  125. } // for
  126. callback(tasks);
  127. }
  128. );
  129. });
  130. } // getTasks
  131. var subModule = {
  132. getIncompleteTasks: function(callback) {
  133. getTasks(callback, "WHERE completed IS NULL");
  134. },
  135. getTasksInPriorityOrder: function(callback) {
  136. subModule.getIncompleteTasks(function(tasks) {
  137. callback(tasks.sort(function(taskA, taskB) {
  138. return taskA.due - taskB.due;
  139. }));
  140. });
  141. },
  142. getMostImportantTask: function(callback) {
  143. subModule.getTasksInPriorityOrder(function(tasks) {
  144. callback(tasks.length > 0 ? tasks[0] : null);
  145. });
  146. },
  147. saveTask: function(task, callback) {
  148. db.transaction(function(transaction) {
  149. // if the task id is not assigned, then insert
  150. if (! task.id) {
  151. transaction.executeSql(
  152. "INSERT INTO task(name, description, due) VALUES (?, ?, ?);",
  153. [task.name, task.description, task.due],
  154. function(tx) {
  155. transaction.executeSql(
  156. "SELECT MAX(rowid) AS id from task",
  157. [],
  158. function (tx, results) {
  159. task.id = results.rows.item(0).id;
  160. if (callback) {
  161. callback();
  162. } // if
  163. }
  164. );
  165. }
  166. );
  167. }
  168. // otherwise, update
  169. else {
  170. transaction.executeSql(
  171. "UPDATE task " +
  172. "SET name = ?, description = ?, due = ?, completed = ? " +
  173. "WHERE rowid = ?;",
  174. [task.name, task.description, task.due, task.completed, task.id],
  175. function (tx) {
  176. if (callback) {
  177. callback();
  178. } // if
  179. }
  180. );
  181. } // if..else
  182. });
  183. }
  184. };
  185. return subModule;
  186. })(),
  187. /* validation module */
  188. Validation: (function() {
  189. var errors = {};
  190. return {
  191. displayErrors: function(newErrors) {
  192. // initialise variables
  193. var haveErrors = false;
  194. // update the errors with the new errors
  195. errors = newErrors;
  196. // remove the invalid class for all inputs
  197. $(":input.invalid").removeClass("invalid");
  198. // iterate through the fields specified in the errors array
  199. for (var fieldName in errors) {
  200. haveErrors = true;
  201. $("input[name='" + fieldName + "']").addClass("invalid");
  202. } // for
  203. // if we have errors, then add a message to the errors div
  204. $("#errors")
  205. .html(haveErrors ? "Errors were found." : "")
  206. .css("display", haveErrors ? "block" : "none");
  207. },
  208. displayFieldErrors: function(field) {
  209. var messages = errors[field.name];
  210. if (messages && (messages.length > 0)) {
  211. // find an existing error detail section for the field
  212. var errorDetail = $("#errordetail_" + field.id).get(0);
  213. // if it doesn't exist, then create it
  214. if (! errorDetail) {
  215. $(field).before("<ul class='errors-inline' id='errordetail_" + field.id + "'></ul>");
  216. errorDetail = $("#errordetail_" + field.id).get(0);
  217. } // if
  218. // add the various error messages to the div
  219. for (var ii = 0; ii < messages.length; ii++) {
  220. $(errorDetail).html('').append("<li>" + messages[ii] + "</li>");
  221. } // for
  222. } // if
  223. } // displayFieldErrors
  224. };
  225. })(),
  226. /* view activation handlers */
  227. activateMain: function() {
  228. TODOLIST.Storage.getMostImportantTask(function(task) {
  229. if (task) {
  230. // the no tasks message may be displayed, so remove it
  231. jQuery("#main .notasks").remove();
  232. // update the task details
  233. showTaskDetails("#main .task", task);
  234. // attach a click handler to the complete task button
  235. jQuery("#main .task-complete").unbind().click(function() {
  236. jQuery("#main .task").slideUp();
  237. // mark the task as complete
  238. task.complete();
  239. // save the task back to storage
  240. TODOLIST.Storage.saveTask(task, module.activateMain);
  241. });
  242. }
  243. else {
  244. jQuery("#main .notasks").remove();
  245. jQuery("#main .task").slideUp().after("<p class='notasks'>You have no tasks to complete</p>");
  246. }
  247. });
  248. },
  249. activateAllTasks: function() {
  250. TODOLIST.Storage.getTasksInPriorityOrder(function(tasks) {
  251. // update the current tasks
  252. currentTasks = tasks;
  253. populateTaskList();
  254. // refresh the task list display
  255. jQuery("ul#tasklist li").click(function() {
  256. toggleDetailsDisplay(this);
  257. });
  258. jQuery("ul#tasklist a.task-complete").click(function() {
  259. // complete the task
  260. alert("complete the task");
  261. });
  262. });
  263. }
  264. };
  265. // define the all tasks view
  266. PROWEBAPPS.ViewManager.define({
  267. id: "alltasks",
  268. actions: [
  269. new PROWEBAPPS.ChangeViewAction({
  270. target: "add",
  271. label: "Add"
  272. })
  273. ]
  274. });
  275. return module;
  276. })();
  277. $(document).ready(function() {
  278. /* validation code */
  279. $(":input").focus(function(evt) {
  280. TODOLIST.Validation.displayFieldErrors(this);
  281. }).blur(function(evt) {
  282. $("#errordetail_" + this.id).remove();
  283. });
  284. $("#taskentry").validate({
  285. submitHandler: function(form) {
  286. // get the values from the form in hashmap
  287. var formValues = PROWEBAPPS.getFormValues(form);
  288. // create a new task to save to the database
  289. var task = new TODOLIST.Task(formValues);
  290. // now create a new task
  291. TODOLIST.Storage.saveTask(task, function() {
  292. // PROWEBAPPS.ViewManager.activate("main");
  293. PROWEBAPPS.ViewManager.back();
  294. });
  295. },
  296. showErrors: function(errorMap, errorList) {
  297. // initialise an empty errors map
  298. var errors = {};
  299. // iterate through the jQuery validation error map, and convert to
  300. // something we can use
  301. for (var elementName in errorMap) {
  302. if (! errors[elementName]) {
  303. errors[elementName] = [];
  304. } // if
  305. errors[elementName].push(errorMap[elementName]);
  306. } // for
  307. // now display the errors
  308. TODOLIST.Validation.displayErrors(errors);
  309. }
  310. });
  311. // bind activation handlers
  312. $("#main").bind("activated", TODOLIST.activateMain);
  313. $("#alltasks").bind("activated", TODOLIST.activateAllTasks);
  314. // initialise the main view
  315. PROWEBAPPS.ViewManager.activate("main");
  316. });