PageRenderTime 48ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/public/js/list.js

https://bitbucket.org/schonben/list
JavaScript | 435 lines | 367 code | 41 blank | 27 comment | 72 complexity | c6b632d340dd27fb08c1b3c88dc76269 MD5 | raw file
  1. var listId = "";
  2. var syncTimer;
  3. var syncAction = "getAll";
  4. var syncStatus = 0;
  5. var syncTimeout = 10;
  6. var block = false;
  7. var categoryList;
  8. var itemList;
  9. /*
  10. * Functions to update localStorage object.
  11. */
  12. function populateList(json,id) { // Takes a list object, and merges into the localStorage object.
  13. if(typeof id === "undefined") { id = listId; }
  14. var list = getList(id);
  15. list.name = json.name;
  16. list.description = json.description;
  17. list.showCat = json.showCat;
  18. list.showQty = json.showQty;
  19. list.lastupdate = getTime();
  20. $.each(json.items,function(index, item){
  21. updateItem(item,list.items);
  22. });
  23. setList(list);
  24. }
  25. function updateItem(item,target) { // Adds or syncs an item to the localStorage object.
  26. // Find the target
  27. var index = findIndex(item.id,target);
  28. if (index === false) {
  29. // Add the new item to the array
  30. target.push(item);
  31. } else {
  32. var t = target[index];
  33. var types = ["cat","so","value","qty","done"];
  34. for (i=0;i<types.length;i++) { // Check each element and update if newer.
  35. var type = types[i];
  36. if (typeof item[type] !== "undefined") {
  37. if (typeof t[type] === "undefined") {
  38. t[type] = item[type];
  39. } else if (item[type].time > t[type].time) {
  40. t[type] = item[type];
  41. }
  42. }
  43. }
  44. }
  45. }
  46. function updRow(item,id,type) { // Update row in localStorage object from the app. Operates on only one field at the time.
  47. var list = getList();
  48. var index = findIndex(id,list.items);
  49. var refresh = false;
  50. list.items[index].updated = getTime();
  51. switch(type) {
  52. case "cat":
  53. list.items[index].cat.value = item.trim();
  54. list.items[index].cat.time = getTime();
  55. refresh = true;
  56. break;
  57. case "value":
  58. item = item.trim();
  59. if (item.length > 0) { // Has text in value field
  60. list.items[index].value.value = item;
  61. list.items[index].value.time = getTime();
  62. } else { // Empty field, delete item instead.
  63. list.items[index].done.value = 2;
  64. list.items[index].done.time = getTime();
  65. refresh = true;
  66. }
  67. break;
  68. case "value":
  69. list.items[index].value.value = Number(item);
  70. list.items[index].value.time = getTime();
  71. break;
  72. case "done":
  73. list.items[index].done.value = (list.items[index].done.value == 1)?0:1;
  74. list.items[index].done.time = getTime();
  75. if (list.items[index].done.value == 1) {
  76. $("#"+id).addClass("done");
  77. } else {
  78. $("#"+id).removeClass("done");
  79. }
  80. refresh = true;
  81. break;
  82. }
  83. $("#sync").removeClass();
  84. $("#sync").addClass("sync_old");
  85. syncStatus = 1;
  86. block = false;
  87. setList(list);
  88. if (refresh) buildList();
  89. }
  90. function addItem(value) { // Add a new row to the object
  91. if (typeof value === "undefined") {
  92. value = $("#new_value").val().trim()
  93. }
  94. var cat = $("#new_cat").val().trim();
  95. $("#new_value").val("");
  96. $("#new_cat").val("");
  97. var list = getList();
  98. if (value != "") {
  99. if (!rescurrectItem(list,value,$("#new_cat").val())) {
  100. // Create the new item.
  101. var item = new Object();
  102. item.id = createGuid();
  103. item.cat = new Object();
  104. if (cat != "") {
  105. item.cat.value = cat;
  106. $("#new_cat").attr("placeholder", cat);
  107. } else if ($("#new_cat").attr("placeholder") != "Category") {
  108. item.cat.value = $("#new_cat").attr("placeholder");
  109. } else {
  110. item.cat.value = "";
  111. }
  112. item.cat.time = getTime();
  113. item.value = new Object();
  114. item.value.value = value;
  115. item.value.time = getTime();
  116. item.qty = new Object();
  117. item.qty.value = value;
  118. item.qty.time = getTime();
  119. item.done = new Object();
  120. item.done.value = 0;
  121. item.done.time = getTime();
  122. item.so = new Object();
  123. item.so.value = 0;
  124. item.so.time = getTime();
  125. // Push the item onto the list.
  126. list.items.push(item);
  127. }
  128. $("#sync").removeClass();
  129. $("#sync").addClass("sync_old");
  130. syncStatus = 1;
  131. }
  132. setList(list);
  133. saveOrder();
  134. syncJSON('updList');
  135. // Redraw the list
  136. buildList();
  137. }
  138. function rescurrectItem(list,value,cat) {
  139. var match = false;
  140. $.each(list.items,function (index,item) {
  141. if (item.done.value > 1 && item.value.value == value) { // Match deleted item
  142. item.done.value = 0;
  143. item.done.time = getTime();
  144. item.so.value = 0;
  145. item.so.time = getTime();
  146. if (cat != "" && item.cat.value != cat) {
  147. item.cat.value = cat;
  148. item.cat.time = getTime();
  149. }
  150. match = true;
  151. return false;
  152. }
  153. });
  154. return match;
  155. }
  156. /*
  157. Functions to draw the list on the doc.
  158. */
  159. function buildList() { // Update name and empty the list
  160. var list = getList();
  161. var inSync = true;
  162. $("#listname").html(list.name);
  163. // Only if not blocked
  164. if (block == false) {
  165. $("#listbody").html("");
  166. var catTmp = "";
  167. $(list.items).sort(dynamicSortMultiple("cat","done","so")).each(function(index,element) {
  168. addCategoryList(element.cat.value);
  169. addItemList(element.value.value);
  170. if (element.done.value <= 1) {
  171. if (list.lastsync < element.updated) {
  172. inSync = false;
  173. }
  174. if (catTmp != element.cat.value) {
  175. catTmp = element.cat.value;
  176. $("#listbody").append('<tr id="catRow_'+catTmp+'"><th colspan="3">'+catTmp+'</th></tr>');
  177. }
  178. addRow(index,element);
  179. }
  180. });
  181. $('#new_cat').autocomplete("option", { source: categoryList });
  182. $('#new_value').autocomplete("option", { source: itemList });
  183. }
  184. if (!inSync) {
  185. $("#sync").removeClass();
  186. $("#sync").addClass("sync_old");
  187. syncStatus = 1;
  188. }
  189. }
  190. function addRow (index,element) { // Draw a row
  191. var cl = "";
  192. if (element.done.value > 0) {
  193. cl = ' class="done"';
  194. }
  195. $("#listbody").append('<tr id="'+ element.id +'"'+cl+'>' +
  196. '<td class="checkbox" onclick="updRow(1,\''+ element.id +'\',\'done\')" id="check_'+ element.id +'"></td>' +
  197. '<td colspan="2"><input type="hidden" id="cat_'+ element.id +'" value="' + element.cat.value + '" /><input type="text" onkeyup="blockDraw(\''+ element.id +'\');" onchange="updRow(this.value,\''+ element.id +'\',\'value\')" name="value_'+ element.id +'" value="' + HTMLEncode(element.value.value) + '" tabindex="'+(20000+element.so.value)+'"/></td>' +
  198. '<td><img src="img/move.png" alt="Move" class="handle"/></td>' +
  199. "</tr>");
  200. }
  201. function buildIndex (items) { // Draw index page
  202. $(".lists").html("");
  203. var lists = $(readLists()).sort(dynamicSortMultiple("name"));
  204. $.each(lists,function(id,list) {
  205. var html = '<li><a href="#list/'+list.id+'" onclick="openList(\''+list.id+'\')">'+list.name;
  206. if (items != false) {
  207. html += ' ('+showItemCount(list.items)+')';
  208. }
  209. html += '</a></li>';
  210. $(".lists").append(html);
  211. });
  212. }
  213. function showItemCount(items) {
  214. var total = 0;
  215. var pending = 0;
  216. $.each(items,function (index,item) {
  217. if (item.done.value < 2) total++;
  218. if (item.done.value == 0) pending++;
  219. });
  220. return pending+" / "+total;
  221. }
  222. function openList (id) { // Set the list, draw and show it.
  223. $("#settingsLink").attr("href",rpath+"settings/list-"+id);
  224. listId = id; // Set the list id
  225. categoryList = []; // Empty the datalist
  226. itemList = []; // Empty the datalist
  227. $("#listid").val(id); // Set id in form as well, in case it is used.
  228. buildIndex(false); // Draw the index.
  229. buildList(); // Draw the list.
  230. syncAction = "updList";
  231. syncJSON('updList'); // Call a list sync
  232. // Finally show the list
  233. $("#index").addClass("shaded");
  234. $("#list").show();
  235. }
  236. function closeList (sync) { // Close the list view, and return to index.
  237. $("#settingsLink").attr("href",rpath+"settings");
  238. if (sync !== false) {
  239. syncJSON('updList'); // Call a list sync before closing.
  240. }
  241. listId = ""; // Unset the list id
  242. $('#categories').html(""); // Empty the datalist
  243. $("#listid").val(""); // Unset form id.
  244. $(".lists").html(""); // Empty the HTML, just in case.
  245. buildIndex(); // Draw the index.
  246. syncAction = "getAll";
  247. syncJSON('getAll'); // Call an index sync
  248. // Finally show the list
  249. $("#list").hide();
  250. $("#index").removeClass("shaded");
  251. }
  252. function addCategoryList(cat) { // Add category to array if not already there.
  253. var exists = false;
  254. for (i=0;i<categoryList.length;i++) {
  255. if (categoryList[i] == cat) exists = true;
  256. }
  257. if (!exists)
  258. categoryList.push(cat);
  259. }
  260. function addItemList(item) { // Add item to list if not already there.
  261. var exists = false;
  262. for (i=0;i<itemList.length;i++) {
  263. if (itemList[i] == item) exists = true;
  264. }
  265. if (!exists)
  266. itemList.push(item);
  267. }
  268. function delChecked () {
  269. var list = getList();
  270. $.each(list.items,function (index,item) {
  271. if (item.done.value == 1) {
  272. item.done.value = 2;
  273. item.done.time = getTime();
  274. }
  275. });
  276. setList(list);
  277. buildList();
  278. }
  279. /*
  280. * Block drawing of the list.
  281. * This is so that half edited rows will not be lost.
  282. * This could be made to block only a specific row, but requires that the drawing be redone.
  283. */
  284. function blockDraw(id) {
  285. block = id;
  286. }
  287. // Save item order on reordering.
  288. function saveOrder(ui) {
  289. var list = getList();
  290. var so = 0;
  291. var cat = "";
  292. $("#listbody tr").each(function(){
  293. var id = $(this).attr("id");
  294. if (id.substr(0,6) == "catRow") {
  295. cat = id.substr(7);
  296. } else {
  297. $.each(list.items,function(){
  298. if (this.id == id) {
  299. so = so + 10;
  300. this.so.value = so;
  301. this.so.time = getTime();
  302. if (this.cat.value != cat) {
  303. this.cat.value = cat;
  304. this.cat.time = getTime();
  305. }
  306. return true;
  307. }
  308. });
  309. }
  310. });
  311. setList(list);
  312. $("#sync").removeClass();
  313. $("#sync").addClass("sync_old");
  314. syncStatus = 1;
  315. block = false;
  316. buildList();
  317. }
  318. /*
  319. * Sync data to server.
  320. */
  321. function syncJSON(action) {
  322. if(typeof action === "undefined") { action = syncAction; }
  323. if (navigator.onLine !== false){
  324. var json = "{}";
  325. switch (action) {
  326. case "updList":
  327. var list = getList();
  328. list.lastsent = getTime();
  329. json = JSON.stringify(list);
  330. setList(list);
  331. break;
  332. }
  333. var token = getCookie("userToken");
  334. $("#sync").removeClass();
  335. $("#sync").addClass("sync_on");
  336. syncStatus = 2;
  337. syncTimer = setTimeout(function(){ syncFail() },syncTimeout * 1000);
  338. $.ajax({
  339. type: "POST",
  340. url: jsonURL,
  341. data: {token: token, action: action, json: json},
  342. success: parseData,
  343. dataType: "json"
  344. });
  345. }
  346. }
  347. // AJAX return handler
  348. function parseData(data) {
  349. $("#sync").removeClass();
  350. $("#sync").addClass("sync");
  351. syncStatus = 0;
  352. clearTimeout(syncTimer);
  353. switch (data.status) {
  354. case 0:
  355. setCookie("userToken",data.token);
  356. switch (data.action) {
  357. case "update":
  358. populateList(data.data,data.id);
  359. buildList();
  360. break;
  361. case "index":
  362. $.each(data.data,function (id,list) {
  363. populateList(list,list.id);
  364. });
  365. // Clean up deleted lists
  366. $.each(readLists(),function (index,list){
  367. if (list !== null && findIndex(list.id,data.data) === false) { // If the list is not in the returned data, nuke it.
  368. deleteList(list.id);
  369. }
  370. })
  371. buildIndex();
  372. break;
  373. }
  374. break;
  375. case 1:
  376. //window.location.assign("/login");
  377. break;
  378. case 18: // List does not exist
  379. closeList(false);
  380. break;
  381. default:
  382. syncFail();
  383. alert("Unhandled Error #"+data.status+": "+data.errmsg);
  384. break;
  385. }
  386. }
  387. // Sync timed out or failed.
  388. function syncFail () {
  389. if (syncStatus == 2) {
  390. $("#sync").removeClass();
  391. $("#sync").addClass("sync_error");
  392. syncStatus = 3
  393. }
  394. }