Dashboard sipadu mbip
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

note.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. ;(function($, db) {
  2. var Note; // Initialize note
  3. /**
  4. * Format timestamp in a specified format
  5. * @param {int} time Time in integer format
  6. * @return {string} Formated time string
  7. */
  8. function _formatDate(time) {
  9. return moment(Number(time)).format('DD MMMM YYYY, h:mm a');
  10. }
  11. /**
  12. * Escape HTML tags
  13. * @param {string} html Unescaped content
  14. * @return {string} HTML escaped content
  15. */
  16. function _escapeHtml(html) {
  17. return $('<div />').text(html).html();
  18. }
  19. // Debouncer to imporve performance
  20. function _debounce(func, wait, immediate) {
  21. var timeout;
  22. return function() {
  23. var context = this, args = arguments;
  24. var later = function() {
  25. timeout = null;
  26. if (!immediate) func.apply(context, args);
  27. };
  28. var callNow = immediate && !timeout;
  29. clearTimeout(timeout);
  30. timeout = setTimeout(later, wait);
  31. if (callNow) func.apply(context, args);
  32. };
  33. };
  34. Note = {
  35. // DOM selectors
  36. $list: $('#Note-list'), // Note list wrapper
  37. $pad: $('#Note-pad'), // Note writing box
  38. $created: $('#Note-created__date'), // Note created date holder
  39. $add: $('.Note-add'), // Note create button
  40. $search: $('#Note-search'), // Note search box
  41. _length: 0, // Length of notes
  42. _lastIndex: 0, // Last note index
  43. _PREFIX: 'TB', // DB record key prefix
  44. _UID_SEPERATOR: '-', // Seperator between prefix and index
  45. init: function() {
  46. // Event must be called before render
  47. Note.addEvents();
  48. Note.render();
  49. },
  50. /**
  51. * Render full note list
  52. * @return {object} Note module object
  53. */
  54. render: function() {
  55. var templates = [];
  56. Note._length = 0;
  57. /**
  58. * Loop through all DB entry and prepare only valid note for output
  59. * @param {string} key DB entry key
  60. * @param {object} entry DB entry object
  61. * @return {void}
  62. */
  63. db.forEach(function(key, entry) {
  64. if (Note._isValid(key)) {
  65. templates.unshift(Note._getNoteTemplate(entry));
  66. ++Note._length;
  67. if (entry.id > Note._lastIndex) {
  68. Note._lastIndex = entry.id;
  69. }
  70. }
  71. });
  72. Note._renderList(templates);
  73. return this;
  74. },
  75. /**
  76. * Bind event listeners to DOM elements
  77. */
  78. addEvents: function() {
  79. // Note open handler
  80. Note.$list.on('click', '.Note', function() {
  81. var note = $(this),
  82. noteWrapper = note.parent(),
  83. id = note.data('uid');
  84. if (id) Note.open(id);
  85. noteWrapper.addClass('active').siblings().removeClass('active');
  86. });
  87. // Note delete handler
  88. Note.$list.on('click', '.Note-delete', function(e) {
  89. e.preventDefault();
  90. Note.delete($(this).data('uid'));
  91. $(this).parent().remove();
  92. Note.$pad.val(' ');
  93. if (!Note._length) {
  94. Note.$created.text('');
  95. }
  96. });
  97. // Note create handler
  98. Note.$add.on('click', function(e) {
  99. e.preventDefault();
  100. Note.create({}).render();
  101. });
  102. // Create a note on focus if there is no note
  103. Note.$pad.on('focus', function() {
  104. if (!Note._length) {
  105. Note.create({}).render();
  106. }
  107. });
  108. // Update note on keyup
  109. Note.$pad.on('keyup', _debounce(function(e) {
  110. var title,
  111. content = Note.$pad.val(),
  112. $note = $('.Note[data-uid="' + Note.$pad.data('uid') + '"');
  113. if (-1 !== content.indexOf('\n')) {
  114. title = content.slice(0, content.indexOf('\n'));
  115. Note.update(Note.$pad.data('uid'), {title:title, content:content});
  116. $note.find('.Note__name').text(title);
  117. $note.find('.Note__desc').text(Note._getPartialContent(content, title));
  118. }
  119. }, 200));
  120. // Search note
  121. Note.$search.on('keyup', _debounce(function() {
  122. Note.search(Note.$search.val());
  123. }, 200));
  124. },
  125. /**
  126. * Create note
  127. * @param {object} data Note data object
  128. * @return {object} Note module object
  129. */
  130. create: function(data) {
  131. if ('object' !== $.type(data)) {
  132. return new Error('Invalid data. Object expected.');
  133. }
  134. var defaults = {
  135. id: ++Note._lastIndex,
  136. title: 'Note Title',
  137. content: '',
  138. time: new Date().getTime()
  139. };
  140. $.extend(defaults, data);
  141. store.set(Note._getUID(defaults.id), defaults);
  142. return this;
  143. },
  144. /**
  145. * Update note
  146. * @param {string} key Note record key
  147. * @param {object} note Note data object
  148. * @return {object} Note module object
  149. */
  150. update: function(key, note) {
  151. if (!Note._isValid(key)) {
  152. return new Error('Invalid note key.');
  153. }
  154. db.transact(key, function(old) {
  155. old = $.extend(old, note);
  156. });
  157. return this;
  158. },
  159. /**
  160. * Delete note
  161. * @param {string} key Note record key
  162. * @return {object} Note module object
  163. */
  164. delete: function(key) {
  165. if (!Note._isValid(key)) {
  166. return new Error('Invalid note key.');
  167. }
  168. store.remove(key);
  169. --Note._length;
  170. return this;
  171. },
  172. /**
  173. * Open note to writing box and setup UI
  174. * @param {string} key Note record key
  175. * @return {object} Note module object
  176. */
  177. open: function(key) {
  178. if (!Note._isValid(key)) {
  179. return new Error('Invalid note key.');
  180. }
  181. var note = db.get(key);
  182. Note.$pad.val(note.content).data('uid', Note._getUID(note.id));
  183. Note.$created.text(_formatDate(note.time));
  184. return this;
  185. },
  186. /**
  187. * Search note in DB and render search result
  188. * @param {string} term Search term given by user
  189. * @return {void}
  190. */
  191. search: function(term) {
  192. var templates = [], termRegx = new RegExp(term, 'gi');
  193. db.forEach(function(key, entry) {
  194. if (Note._isValid(key) && (termRegx.test(entry.content) || termRegx.test(entry.title))) {
  195. templates.unshift(Note._getNoteTemplate(entry));
  196. }
  197. });
  198. Note._renderList(templates);
  199. },
  200. /**
  201. * Render note list and select first note
  202. * @param {array} templates Array of complied note templates
  203. * @return {void}
  204. */
  205. _renderList: function(templates) {
  206. Note.$list.html(templates).children(':first-child').find('.Note').trigger('click');
  207. templates.length || Note.$pad.val(''); // Clear note pad when there is no note
  208. },
  209. /**
  210. * Get note list item HTML template
  211. * @param {object} data Single note object
  212. * @return {string} Output ready note list item
  213. */
  214. _getNoteTemplate: function(data) {
  215. var template = '<li class="list-group-item">'
  216. + '<button class="Note-delete" data-uid="' + Note._getUID(data.id) + '" type="button">x</button>'
  217. + '<div class="Note" data-uid="' + Note._getUID(data.id) + '">'
  218. + '<div class="Note__name">' + _escapeHtml(data.title) + '</div>'
  219. + '<div class="Note__desc">' + _escapeHtml(Note._getPartialContent(data.content, data.title)) + '</div>'
  220. + '<span class="Note__date">' + _formatDate(data.time) + '</span>'
  221. + '</div>'
  222. + '</li>';
  223. return template;
  224. },
  225. _getPartialContent: function(content, title) {
  226. return content.slice(title.length);
  227. },
  228. // Generate UID
  229. _getUID: function(id) {
  230. return Note._PREFIX + Note._UID_SEPERATOR + id;
  231. },
  232. /**
  233. * Check note entry validity
  234. * @param {string} key Entry key
  235. * @return {Boolean} Check if it is valid note entry
  236. */
  237. _isValid: function(key) {
  238. return key && (String(key).indexOf(Note._PREFIX) === 0);
  239. }
  240. };
  241. Note.init();
  242. /**
  243. * Only for demo purpose, feel free to delete
  244. */
  245. if (!db.get('demo_imported')) {
  246. var demoNotes = [{
  247. title: 'The Road Not Taken by Robert Frost',
  248. content: 'The Road Not Taken by Robert Frost\n\nTwo roads diverged in a yellow wood,\nAnd sorry I could not travel both\nAnd be one traveler, long I stood\nAnd looked down one as far as I could\nTo where it bent in the undergrowth;\nThen took the other, as just as fair,\nAnd having perhaps the better claim,\nBecause it was grassy and wanted wear;\nThough as for that the passing there\nHad worn them really about the same,\nAnd both that morning equally lay\nIn leaves no step had trodden black.\nOh, I kept the first for another day!\nYet knowing how way leads on to way,\nI doubted if I should ever come back.\nI shall be telling this with a sigh\nSomewhere ages and ages hence:\nTwo roads diverged in a wood, and I-\nI took the one less traveled by,\nAnd that has made all the difference.'
  249. },
  250. {
  251. title: 'To My Wife - With A Copy Of My Poems by Oscar Wilde',
  252. content: 'To My Wife - With A Copy Of My Poems by Oscar Wilde\n\nI can write no stately proem\nAs a prelude to my lay;\nFrom a poet to a poem\nI would dare to say.\n\nFor if of these fallen petals\nOne to you seem fair,\nLove will waft it till it settles\nOn your hair.\n\nAnd when wind and winter harden\nAll the loveless land,\nIt will whisper of the garden,\nYou will understand.'
  253. },
  254. {
  255. title: 'All the World\'s a Stage by William Shakespeare',
  256. content: 'All the World\'s a Stage by William Shakespeare\n\nAll the world\'s a stage,\nAnd all the men and women merely players;\nThey have their exits and their entrances,\nAnd one man in his time plays many parts,\nHis acts being seven ages. At first, the infant,\nMewling and puking in the nurse\'s arms.\nThen the whining schoolboy, with his satchel\nAnd shining morning face, creeping like snail\nUnwillingly to school. And then the lover,\nSighing like furnace, with a woeful ballad\nMade to his mistress\' eyebrow. Then a soldier,\nFull of strange oaths and bearded like the pard,\nJealous in honor, sudden and quick in quarrel,\nSeeking the bubble reputation\nEven in the cannon\'s mouth. And then the justice,\nIn fair round belly with good capon lined,\nWith eyes severe and beard of formal cut,\nFull of wise saws and modern instances;\nAnd so he plays his part. The sixth age shifts\nInto the lean and slippered pantaloon,\nWith spectacles on nose and pouch on side;\nHis youthful hose, well saved, a world too wide\nFor his shrunk shank, and his big manly voice,\nTurning again toward childish treble, pipes\nAnd whistles in his sound. Last scene of all,\nThat ends this strange eventful history,\nIs second childishness and mere oblivion,\nSans teeth, sans eyes, sans taste, sans everything.'
  257. }];
  258. demoNotes.forEach(function(demoNote) {
  259. Note.create(demoNote);
  260. });
  261. Note.render();
  262. db.set('demo_imported', true);
  263. }
  264. window.TB_Note = Note;
  265. })(jQuery, store);