Guest User

Untitled

a guest
Jun 20th, 2018
67
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.80 KB | None | 0 0
  1. <!DOCTYPE HTML>
  2. <html>
  3. <head>
  4. <title></title>
  5. <style>
  6. .clearfix:after {clear: both; content: "."; display: block; height: 0; line-height: 0; visibility: hidden;}
  7. .clearfix {display: inline-block;} * html .clearfix {height: 1%;} .clearfix {display: block;} html[xmlns] .clearfix {display:block;}
  8. body {
  9. background: rgba(237, 243, 247, 1);
  10. color: rgba(0, 0, 0, .7);
  11. font-family: Arial, Verdana, sans-serif;
  12. font-size: 62.5%;
  13. margin: 0;
  14. padding: 0;
  15. }
  16. h1 {
  17. color: rgba(0, 0, 0, .2);
  18. font-size: 1.5em;
  19. margin: 40px auto 0;
  20. padding: 0 20px 1px 0;
  21. text-align: right;
  22. text-transform: uppercase;
  23. width: 740px;
  24. }
  25. ul, li {list-style: none; margin: 0; padding: 0;}
  26. li {clear: both; font-size: 1.2em; line-height: 1.2; margin: 3px 0; padding: 2px 0;}
  27. li input {display: block; float: left; margin-right: 8px; margin-top: 0;}
  28. h2 {font-size: 1.5em;}
  29. #wrapper {margin: 0 auto; width: 740px;}
  30. nav {float: left; margin: 3px 0 0; width: 240px;}
  31. nav li {margin: 1px 0;}
  32. nav li a, nav li a:hover {
  33. background: rgba(255, 255, 255, .5);
  34. border: 4px solid rgba(212, 218, 222, 1);
  35. border-right: none;
  36. clear: right;
  37. color: rgba(100, 110, 103, .6);
  38. display: block;
  39. padding: 8px 12px;
  40. text-decoration: none;
  41. border-bottom-left-radius: 6px;
  42. border-top-left-radius: 6px;
  43. }
  44. nav a em {display: block; float: right; font-style: normal;}
  45. nav li a.active, nav li a:hover {background: rgba(255, 255, 255, 1); color: rgba(100, 110, 103, 1);}
  46. #tasks-wrapper {
  47. background: rgb(255, 255, 255);
  48. border: 1px solid rgba(212, 218, 222, .9);
  49. border-top: 4px solid rgba(212, 218, 222, .9);
  50. float: left;
  51. width: 67%;
  52. }
  53. #archive-list li {border-bottom: 1px solid rgba(217, 223, 227, .5); padding-bottom: 3px;}
  54. #date-picker {color: rgba(80, 180, 80, 1); display: none; margin: 3em auto 0; text-indent: 1em; width: 420px;}
  55. #today-wrapper, #archive-wrapper {min-height: 75px; padding: 0 20px 20px;}
  56. #new-task {margin-bottom: 12px; padding: 5px;}
  57. #task-list li span, #archive-list li span {
  58. color: rgba(0, 0, 0, .35); /* set this based on user prefs. must set in dom creation */
  59. float: right;
  60. font-size: 11px;
  61. font-weight: 600;
  62. margin: 0 6px 0 0;
  63. text-align: right;
  64. }
  65. .cat-1, .cat-2, .cat-3 {
  66. padding: 4px 0;
  67. }
  68. .cat-1 {background: rgba(220, 230, 215, .6);}
  69. .cat-1.complete {background: rgba(220, 230, 215, .25); color: rgba(0, 0, 0, .2)}
  70. .cat-2 {background: rgba(220, 220, 230, .6);}
  71. .cat-2.complete {background: rgba(220, 220, 230, .25); color: rgba(0, 0, 0, .2);}
  72. .cat-3 {background: rgba(220, 210, 195, .6);}
  73. </style>
  74. </head>
  75. <body>
  76. <h1>Memero</h1>
  77. <div class="clearfix" id="wrapper">
  78. <nav>
  79. <ul></ul>
  80. </nav>
  81. <div id="tasks-wrapper"></div>
  82. </div>
  83. <script>
  84. (function() {
  85. var $ = {};
  86. $.props = {
  87. days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
  88. html: {
  89. checkboxClass: 'task-checkbox',
  90. completeClass: 'complete',
  91. dateHeadline: 'date-headline',
  92. archiveWrapper: 'archive-wrapper',
  93. archiveList: 'archive-list',
  94. taskInput: 'new-task',
  95. taskList: 'task-list',
  96. tasksWrapper: 'tasks-wrapper',
  97. todayWrapper: 'today-wrapper'
  98. },
  99. months:['January', 'February', 'March', 'April', 'May', 'June', 'July',
  100. 'August', 'September', 'October', 'November', 'December'],
  101. nav: {
  102. ul: document.getElementsByTagName('nav')[0].getElementsByTagName('ul')[0],
  103. tagsId: 'tags-tab',
  104. todayId: 'today-tab',
  105. previousDayId: 'previous-day-tab',
  106. lastWorkdayText: 'completed tasks',
  107. todayText: 'Todays Tasks',
  108. tagsText: 'Tags'
  109. },
  110. taskCheckbox: document.getElementsByClassName('task-checkbox')
  111. };
  112. $.find = function(id) {
  113. return document.getElementById(id);
  114. };
  115. $.capture = function(id, eventType, callback) { // Find dynamically created object by binding event
  116. var elem;
  117. window.document.addEventListener(eventType, function(e) {
  118. if (e.target.getAttribute('id') === id) {
  119. elem = e.target;
  120. callback(elem);
  121. e.preventDefault();
  122. }
  123. }, false);
  124. };
  125. $.Helpers = {
  126. changeClass: function(elem, newClassName) {
  127. var classes = [], _change;
  128. _change = function(elem, newClassName, index) {
  129. if (elem.className) { // Has a class already
  130. classes = elem.className.split(' ');
  131. }
  132. if (!$.Helpers.contains(classes, newClassName) && index === 0) { // Doesn't have this class assigned
  133. classes.push(newClassName);
  134. elem.setAttribute('class', classes.join(' '));
  135. } else { // Does have this class, so remove it
  136. elem.removeAttribute('class');
  137. if (classes.length > 0) {
  138. classes.splice(classes.indexOf(newClassName));
  139. elem.setAttribute('class', classes.join(' '))
  140. }
  141. }
  142. };
  143. return function() {
  144. if (elem instanceof Array) {
  145. for (var i=0; i<elem.length; i++) {
  146. _change(elem[i][0], elem[i][1], i);
  147. }
  148. } else {
  149. _change(elem, newClassName, 0);
  150. }
  151. }();
  152. },
  153. contains: function(arr, matcher) {
  154. if (typeof arr === 'object') {
  155. for (var i=0; i<arr.length; i++) {
  156. if (arr[i] === matcher) {
  157. return true;
  158. }
  159. }
  160. }
  161. return false;
  162. },
  163. toObject: function(array) {
  164. var size = array.length,
  165. obj = {};
  166. for (var i=0; i<size; i++) {
  167. obj[i+1] = array[i];
  168. }
  169. return obj;
  170. }
  171. };
  172. $.Storage = {
  173. has: function(obj) {
  174. if (window.localStorage) {
  175. if (localStorage[obj]) {
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. } else {
  181. return false;
  182. }
  183. },
  184. totalItems: function(obj) {
  185. var ln = 0;
  186. if (obj === undefined) {
  187. return 0;
  188. }
  189. if (typeof obj === 'string') {
  190. obj = JSON.parse(obj);
  191. }
  192. for (o in obj) {
  193. ln += 1;
  194. }
  195. return ln;
  196. }
  197. };
  198. $.Tag = function(name) {
  199. this.id = 0;
  200. this.name = name;
  201. };
  202. $.Tag.prototype.save = function() {
  203. var allTags;
  204. if ($.Tag.find(this.name)) {return this;}
  205. this.id = $.Storage.totalItems(localStorage.tags) + 1;
  206. if ($.Storage.has('tags')) {
  207. allTags = JSON.parse(localStorage.tags);
  208. } else {
  209. allTags = {};
  210. }
  211. allTags[this.id] = this;
  212. localStorage.setItem('tags', JSON.stringify(allTags));
  213. return this;
  214. };
  215. $.Tag.exp = /\[[\w*(\s|\-)?]*\]$/;
  216. $.Tag.getTag = function(taskName) {
  217. return taskName.match($.Tag.exp)[0].slice(1, -1);
  218. };
  219. $.Tag.find = function(name) {
  220. var tags, tag;
  221. if ($.Storage.has('tags')) {
  222. tags = JSON.parse(localStorage.tags);
  223. for (t in tags) {
  224. if (tags[t].name.toLowerCase() === name.toLowerCase()) {
  225. tag = new $.Tag(tags[t].name);
  226. tag.id = tags[t].id;
  227. tag.colorCode = tags[t].colorCode;
  228. return tag;
  229. }
  230. }
  231. }
  232. return false;
  233. };
  234. $.Tag.trimTagFromTaskName = function(taskName) {
  235. var pos = taskName.search(this.exp),
  236. cleanName = taskName.substr(0, pos);
  237. if (cleanName.charAt(cleanName.length-1) == ' ') {
  238. cleanName = cleanName.substr(0, cleanName.length-1);
  239. }
  240. return cleanName;
  241. };
  242. $.Tag.tallyTags = function() {
  243. var totalTags = $.Storage.totalItems(localStorage.tags);
  244. return totalTags;
  245. };
  246.  
  247. $.Task = function(id, name, tag) {
  248. this.name = name;
  249. this.complete = false;
  250. this.id = id;
  251. this.tag = tag;
  252. };
  253. $.Task.prototype.save = function() {
  254. var allTasks;
  255. if (!$.Storage.has('tasks')) {
  256. allTasks = {};
  257. this.id = 1;
  258. } else {
  259. allTasks = JSON.parse(localStorage.tasks);
  260. this.id = $.Storage.totalItems(localStorage.tasks) + 1;
  261. }
  262. allTasks[this.id] = this;
  263. localStorage.setItem('tasks', JSON.stringify(allTasks));
  264. return this;
  265. };
  266. $.Task.prototype.addToList = function() {
  267. var task = {}, indicator, tasks;
  268. task[this.id] = this;
  269. tasks = $.View.renderTask(task, true);
  270. $.find($.props.html.taskList).appendChild(tasks);
  271. $.find($.props.html.taskInput).value = '';
  272. indicator = $.find('today-tab').getElementsByTagName('em')[0];
  273. indicator.textContent = $.Storage.totalItems(localStorage.tasks);
  274. return this;
  275. };
  276. $.Task.prototype.markComplete = function() {
  277. var tasks;
  278. if (window.localStorage) {
  279. if (this.complete) {
  280. this.complete = false;
  281. } else {
  282. this.complete = true;
  283. }
  284. tasks = JSON.parse(localStorage.tasks);
  285. tasks[this.id] = this;
  286. try {
  287. localStorage.setItem('tasks', JSON.stringify(tasks));
  288. return true;
  289. } catch(ex) {
  290. return false;
  291. }
  292. }
  293. };
  294. $.Task.find = function(id) {
  295. var tasks, task;
  296. if (window.localStorage) {
  297. tasks = JSON.parse(localStorage.tasks);
  298. if (tasks[id]) {
  299. task = new $.Task(id, tasks[id].name, tasks[id].tag);
  300. task.complete = tasks[id].complete;
  301. }
  302. return task;
  303. }
  304. };
  305. $.Task.OrganizeTasks = function() {
  306. var completedTasks = [],
  307. unfinishedTasks = [],
  308. tasks;
  309. if ($.Storage.has('tasks')) {
  310. tasks = JSON.parse(localStorage.tasks);
  311. for (task in tasks) { // iterate through tasks
  312. if (tasks[task].complete) { // store completed tasks in new obj
  313. completedTasks.push(tasks[task]);
  314. } else {
  315. unfinishedTasks.push(tasks[task]);
  316. }
  317. }
  318. if (completedTasks.length > 0) {
  319. localStorage.archive = JSON.stringify($.Helpers.toObject(completedTasks));
  320. } else {
  321. localStorage.removeItem('archive');
  322. }
  323. if (unfinishedTasks.length > 0) {
  324. localStorage.tasks = JSON.stringify($.Helpers.toObject(unfinishedTasks));
  325. } else {
  326. localStorage.removeItem('tasks');
  327. }
  328. }
  329. };
  330. $.Task.TallyTasks = function(tasks) {
  331. var totalTasks = $.Storage.totalItems(tasks);
  332. return totalTasks;
  333. };
  334. $.Time = {
  335. isNewDay: function(lastLoginDate) {
  336. var now = new Date();
  337. if (!lastLoginDate) {return true;}
  338. if (typeof lastLoginDate == 'string') {
  339. lastLoginDate = new Date(lastLoginDate);
  340. }
  341. return now.getDate() !== lastLoginDate.getDate();
  342. },
  343. todaysDate: function() {
  344. var today = new Date(),
  345. d = today.getDay(),
  346. m = today.getMonth(),
  347. year = today.getFullYear();
  348. return $.props.days[d] + ', ' + $.props.months[m] + ' ' + today.getDate() + ' ' + year;
  349. },
  350. lastWorkday: function() {
  351. var workday = new Date(localStorage.previousWorkday),
  352. d = workday.getDay(),
  353. m = workday.getMonth(),
  354. year = workday.getFullYear();
  355. return $.props.days[d] + ', ' + $.props.months[m] + ' ' + workday.getDate() + ' ' + year;
  356. }
  357. };
  358. $.View = {
  359. navigation: {
  360. renderArchive: function(numOfTasks, isActive) {
  361. var li, a, text, em, lastWorkday;
  362. li = document.createElement('li');
  363. a = document.createElement('a');
  364. a.setAttribute('href', '#');
  365. a.setAttribute('id', $.props.nav.previousDayId);
  366. if (isActive) {
  367. a.setAttribute('class', 'active');
  368. }
  369. lastWorkday = new Date(localStorage.previousWorkday);
  370. text = document.createTextNode($.props.days[lastWorkday.getDay()] + 's ' +$.props.nav.lastWorkdayText);
  371. em = document.createElement('em');
  372. em.textContent = numOfTasks || 0;
  373. a.appendChild(text);
  374. a.appendChild(em);
  375. li.appendChild(a);
  376. $.props.nav.ul.appendChild(li);
  377. },
  378. renderTags: function(numOfTags, isActive) {
  379. var li, a, text, em;
  380. li = document.createElement('li');
  381. a = document.createElement('a');
  382. a.setAttribute('href', '#');
  383. a.setAttribute('id', $.props.nav.tagsId);
  384. if (isActive) {
  385. a.setAttribute('class', 'active');
  386. }
  387. text = document.createTextNode($.props.nav.tagsText);
  388. em = document.createElement('em');
  389. em.textContent = numOfTags;
  390. a.appendChild(text);
  391. a.appendChild(em);
  392. li.appendChild(a);
  393. $.props.nav.ul.appendChild(li);
  394. },
  395. renderToday: function(numOfTasks, isActive) {
  396. var li, a, text, em;
  397. li = document.createElement('li');
  398. a = document.createElement('a');
  399. a.setAttribute('href', '#');
  400. a.setAttribute('id', $.props.nav.todayId);
  401. if (isActive) {
  402. a.setAttribute('class', 'active');
  403. }
  404. text = document.createTextNode($.props.nav.todayText);
  405. em = document.createElement('em');
  406. em.textContent = numOfTasks;
  407. a.appendChild(text);
  408. a.appendChild(em);
  409. li.appendChild(a);
  410. $.props.nav.ul.appendChild(li);
  411. }
  412. },
  413. renderArchivePanel: function() {
  414. var div, h2, input, ul,
  415. tasksFrag = document.createDocumentFragment(),
  416. parentElem = document.getElementById($.props.html.tasksWrapper);
  417. div = document.createElement('div');
  418. div.setAttribute('id', $.props.html.archiveWrapper);
  419. h2 = document.createElement('h2');
  420. h2.setAttribute('id', $.props.html.dateHeadline);
  421. h2.textContent = 'Completed tasks from ' + $.Time.lastWorkday() ;
  422. ul = document.createElement('ul');
  423. ul.setAttribute('id', $.props.html.archiveList);
  424. div.appendChild(h2);
  425. div.appendChild(ul);
  426. tasksFrag.appendChild(div);
  427. if (parentElem.hasChildNodes()) {
  428. parentElem.textContent = '';
  429. }
  430. parentElem.appendChild(tasksFrag);
  431. },
  432. renderTagPanel: function() {
  433. var div, h2, input, ul,
  434. tasksFrag = document.createDocumentFragment(),
  435. parentElem = document.getElementById($.props.html.tasksWrapper);
  436. div = document.createElement('div');
  437. div.setAttribute('id', $.props.html.archiveWrapper);
  438. h2 = document.createElement('h2');
  439. h2.textContent = 'Tags for your tasks'
  440. div.appendChild(h2);
  441. tasksFrag.appendChild(div);
  442. if (parentElem.hasChildNodes()) {
  443. parentElem.textContent = '';
  444. }
  445. parentElem.appendChild(tasksFrag);
  446. },
  447. renderTodayPanel: function() {
  448. var div, h2, input, ul,
  449. tasksFrag = document.createDocumentFragment(),
  450. parentElem = document.getElementById($.props.html.tasksWrapper);
  451. div = document.createElement('div');
  452. div.setAttribute('id', $.props.html.todayWrapper);
  453. h2 = document.createElement('h2');
  454. h2.setAttribute('id', $.props.html.dateHeadline);
  455. h2.textContent = 'Tasks for ' + $.Time.todaysDate() ;
  456. input = document.createElement('input');
  457. input.setAttribute('id', $.props.html.taskInput);
  458. input.setAttribute('placeholder', 'What are you doing today?');
  459. input.setAttribute('size', '40');
  460. input.setAttribute('type', 'text');
  461. ul = document.createElement('ul');
  462. ul.setAttribute('id', $.props.html.taskList);
  463. div.appendChild(h2);
  464. div.appendChild(input);
  465. div.appendChild(ul);
  466. tasksFrag.appendChild(div);
  467. if (parentElem.hasChildNodes()) {
  468. parentElem.textContent = '';
  469. }
  470. parentElem.appendChild(tasksFrag);
  471. },
  472. renderTask: function(tasks, editable) {
  473. var li, checkbox, text, frag, tagSpan;
  474. frag = document.createDocumentFragment();
  475. if (typeof tasks !== 'object') {
  476. tasks = JSON.parse(tasks);
  477. }
  478. for (t in tasks) {
  479. task = tasks[t];
  480. li = document.createElement('li');
  481. text = document.createTextNode(task.name);
  482. if (editable) {
  483. checkbox = document.createElement('input');
  484. checkbox.setAttribute('type', 'checkbox');
  485. checkbox.setAttribute('id', t); // May need to refactor object/id
  486. if (task.complete) {
  487. li.className = 'complete';
  488. checkbox.setAttribute('checked', 'checked');
  489. }
  490. li.appendChild(checkbox);
  491. //li.setAttribute('contenteditable', '');
  492. }
  493. li.appendChild(text);
  494. if (task.tag) {
  495. li.setAttribute('class', 'cat-'+$.Tag.find(task.tag).id);
  496. tagSpan = document.createElement('span');
  497. tagSpan.textContent = task.tag;
  498. li.appendChild(tagSpan);
  499. }
  500. frag.appendChild(li);
  501. }
  502. return frag;
  503. },
  504. renderTasks: function(tasks, parentElementID, editable) {
  505. var storedTasks = localStorage[tasks],
  506. parentElem = $.find(parentElementID);
  507. var tasksHTML = this.renderTask(storedTasks, editable);
  508. parentElem.appendChild(tasksHTML);
  509. }
  510. };
  511. $.initApp = function(callback) {
  512. if (!localStorage.lastLogin) { // Set today as current day if it doesn't exist
  513. localStorage.lastLogin = new Date();
  514. }
  515. if ($.Time.isNewDay(localStorage.lastLogin)) { // Setup storage for new work day
  516. localStorage.previousWorkday = localStorage.lastLogin;
  517. localStorage.lastLogin = new Date();
  518. $.Task.OrganizeTasks();
  519. }
  520. $.View.renderTodayPanel();
  521. $.View.navigation.renderToday($.Task.TallyTasks(localStorage.tasks), true);
  522. if ($.Storage.has('tasks')) {
  523. $.View.renderTasks('tasks', $.props.html.taskList, true);
  524. }
  525. if ($.Storage.has('archive')) {
  526. $.View.navigation.renderArchive($.Task.TallyTasks(localStorage.archive), false);
  527. }
  528. if ($.Storage.has('tags')) {
  529. $.View.navigation.renderTags($.Tag.tallyTags(), false);
  530. }
  531. document.title = 'Memero - ' + $.Time.todaysDate() ;
  532. callback.call();
  533. };
  534. $.initApp(function() {
  535. window.document.addEventListener('keypress', function(e) {
  536. var task,
  537. taskName,
  538. tag,
  539. tagName,
  540. input = document.getElementById($.props.html.taskInput);
  541. if (e.target.id === $.props.html.taskInput) {
  542. if (e.keyCode == '13' && input.value !== '') {
  543. taskName = input.value;
  544. if (taskName.match($.Tag.exp)) { // Task is tagged
  545. tag = new $.Tag($.Tag.getTag(taskName));
  546. tag.save();
  547. taskName = $.Tag.trimTagFromTaskName(taskName);
  548. tagName = tag.name;
  549. }
  550. task = new $.Task(0, taskName, tagName);
  551. task.save().addToList();
  552. }
  553. }
  554. }, false);
  555. window.document.addEventListener('click', function(e) {
  556. if (e.target.getAttribute('type') == 'checkbox') {
  557. taskId = e.target.getAttribute('id');
  558. task = $.Task.find(taskId);
  559. if (task.markComplete()) {
  560. $.Helpers.changeClass(e.target.parentNode, $.props.html.completeClass);
  561. }
  562. }
  563. }, false);
  564. $.capture($.props.nav.previousDayId, 'click', function(elem) {
  565. if (elem.className !== 'active') {
  566. $.Helpers.changeClass([[elem, 'active'], [$.find($.props.nav.todayId), 'active'], [$.find($.props.nav.tagsId), 'active']]);
  567.  
  568. // $.Helpers.changeClass();
  569.  
  570. $.View.renderArchivePanel();
  571. $.View.renderTasks('archive', $.props.html.archiveList, false);
  572. }
  573. });
  574. $.capture($.props.nav.todayId, 'click', function(elem) {
  575. if (elem.className !== 'active') {
  576. $.Helpers.changeClass([[elem, 'active'], [$.find($.props.nav.previousDayId), 'active'], [$.find($.props.nav.tagsId), 'active']]);
  577. $.View.renderTodayPanel();
  578. $.View.renderTasks('tasks', $.props.html.taskList, true);
  579. }
  580. });
  581. $.capture($.props.nav.tagsId, 'click', function(elem) {
  582. if (elem.className !== 'active') {
  583. $.Helpers.changeClass([[elem, 'active'], [$.find($.props.nav.previousDayId), 'active'], [$.find($.props.nav.todayId), 'active']]);
  584. $.View.renderTagPanel();
  585. }
  586. });
  587. });
  588. })();
  589. </script>
  590. </body>
  591. </html>
Add Comment
Please, Sign In to add comment