1
0

notes.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. // Returns a function, that, as long as it continues to be invoked, will not
  2. // be triggered. The function will be called after it stops being called for
  3. // N milliseconds. If `immediate` is passed, trigger the function on the
  4. // leading edge, instead of the trailing.
  5. function debounce(func, wait, immediate) {
  6. var timeout;
  7. return function() {
  8. var context = this, args = arguments;
  9. var later = function() {
  10. timeout = null;
  11. if (!immediate) func.apply(context, args);
  12. };
  13. var callNow = immediate && !timeout;
  14. clearTimeout(timeout);
  15. timeout = setTimeout(later, wait);
  16. if (callNow) func.apply(context, args);
  17. };
  18. };
  19. function WebDavFileSystem(baseUrl) {
  20. this.baseUrl = baseUrl || "/storage";
  21. }
  22. WebDavFileSystem.prototype.list = function list(path) {
  23. var baseUrl = this.baseUrl;
  24. var url = baseUrl + path;
  25. return fetch(url, {method: "PROPFIND"})
  26. .then(function (r) { return r.text(); })
  27. .then(function (str) { return new window.DOMParser().parseFromString(str, "text/xml"); })
  28. .then(function (r) {
  29. console.log(r);
  30. var items = r.querySelectorAll("multistatus response");
  31. items = Array.prototype.slice.call(items);
  32. items = items.map(function (i) {
  33. var i = {
  34. path: i.querySelector("href").textContent,
  35. created_at: i.querySelector("prop creationdate").textContent,
  36. modified_at: i.querySelector("prop getlastmodified").textContent,
  37. length_bytes: i.querySelector("prop getcontentlength").textContent,
  38. display_name: i.querySelector("prop displayname").textContent,
  39. status: i.querySelector("status").textContent
  40. };
  41. if (i.path) {
  42. i.path = i.path.substr(baseUrl.length);
  43. }
  44. return i;
  45. });
  46. return items;
  47. });
  48. }
  49. WebDavFileSystem.prototype.upload = function upload(path, contentType, content) {
  50. var url = this.baseUrl + path;
  51. return fetch(url, {method: 'PUT', body: content});
  52. }
  53. WebDavFileSystem.prototype.download = function download(path) {
  54. var url = this.baseUrl + path;
  55. return fetch(url)
  56. .then(function (r) { return r.text(); })
  57. }
  58. var fs = new WebDavFileSystem();
  59. var notesStore;
  60. function saveNote() {
  61. // post note to /upload
  62. // post new .csv to /upload (logic should be on server)
  63. }
  64. function syncNotesFromDir() {
  65. fs.list("/").then(function (files) {
  66. var IDs = files.filter(function (f) {
  67. return f.path.endsWith(".txt");
  68. });
  69. var data = IDs.map(function (id) {
  70. return [id.path, id.created_at, id.modified_at];
  71. })
  72. console.log(data);
  73. //notesStore.clear();
  74. notesStore.loadData(data, true);
  75. });
  76. }
  77. var notesStore = new Ext.data.SimpleStore({
  78. fields: ["id", "created", "modified", "title", "body", "attachment_keys"],
  79. id: 0,
  80. data: []
  81. });
  82. notesStore.on("add", function (store, records, idx) {
  83. console.log("Notes added: ");
  84. console.log(records);
  85. var noteNodes = records.map(noteToNode);
  86. var notesFolders = Ext.ComponentMgr.get("notesFolders");
  87. var notesNode = notesFolders.root;
  88. notesNode.appendChild(noteNodes);
  89. });
  90. function noteToNode(record) {
  91. var title = record.data.id;
  92. var node = new Ext.tree.TreeNode({
  93. text: title,
  94. leaf: true,
  95. noteId: record.data.id
  96. });
  97. return node;
  98. }
  99. function runNotesApp () {
  100. Ext.QuickTips.init();
  101. var viewport = Ext.getCmp('viewport');
  102. var win;
  103. var selectedNoteId = null;
  104. if (!win) {
  105. var noteNodes = notesStore.getRange().map(noteToNode);
  106. var notesNode = new Ext.tree.TreeNode({
  107. text: "Notes",
  108. expanded: true
  109. });
  110. notesNode.appendChild(noteNodes);
  111. function addHandler() {
  112. var newId = "/note-" + Math.random() + ".txt";
  113. var date = new Date().toString();
  114. newId = prompt("ID for new note:", newId);
  115. // this should be the effect of a dispatch.
  116. fs.upload(newId, "text/plain", "").then(function () {
  117. notesStore.loadData([
  118. [newId, date, date]
  119. ], true);
  120. var note = notesStore.getById(newId);
  121. //notesNode.appendChild(noteToNode(note));
  122. });
  123. }
  124. var foldersComp = {
  125. xtype: "treepanel",
  126. id: "notesFolders",
  127. tbar: [{
  128. text: 'Add',
  129. handler: addHandler
  130. }],
  131. root: notesNode,
  132. listeners: {
  133. click: function (node, evt) {
  134. var id = node.attributes.noteId;
  135. var editor = Ext.ComponentMgr.get("noteEditor");
  136. selectedNoteId = id; // includes deselect, eg. folder.
  137. if (!id) {
  138. editor.setValue("");
  139. return;
  140. }
  141. console.log("note click: " + id);
  142. var note = notesStore.getById(id);
  143. //var noteHtml = note.data.body;
  144. fs.download( note.data.id ).catch(function (err) {
  145. console.log("error: " + err.httpStatus);
  146. }).then(function (noteHtml) {
  147. var editor = Ext.ComponentMgr.get("noteEditor");
  148. editor.setValue(noteHtml);
  149. });
  150. }
  151. }
  152. };
  153. var foldersConfig = {
  154. region: "west",
  155. split: true,
  156. width: 160,
  157. layout: "fit",
  158. border: false,
  159. items: [foldersComp]
  160. };
  161. var noteConfig = {
  162. region: "center",
  163. split: true,
  164. border: false,
  165. layout: "border",
  166. items: [{
  167. region: "center",
  168. xtype: "htmleditor",
  169. html: "Note.",
  170. id: "noteEditor",
  171. border: false
  172. }, {
  173. region: "south",
  174. border: false,
  175. html: "Note stats."
  176. }]
  177. };
  178. var searchConfig = {
  179. xtype: "textfield",
  180. width: 150,
  181. emptyText: "Search..."
  182. };
  183. win = new Ext.Panel({
  184. tbar: [{
  185. xtype: "tbspacer"
  186. }, searchConfig],
  187. id: "notes-win",
  188. header: false,
  189. title: "Notes",
  190. layout: "border",
  191. bodyBorder: false,
  192. autoShow: true,
  193. //border: false,
  194. //height: 280,
  195. //width: 460,
  196. items: [foldersConfig, noteConfig],
  197. //bbar: []
  198. });
  199. }
  200. viewport.add(win);
  201. viewport.doLayout();
  202. //win.show();
  203. //win.maximize();
  204. syncNotesFromDir();
  205. var editor = Ext.ComponentMgr.get("noteEditor");
  206. editor.on("sync", debounce(function (e, html) {
  207. console.log("Editor sync. Selected = " + selectedNoteId);
  208. if (!selectedNoteId) {
  209. return;
  210. }
  211. var note = notesStore.getById(selectedNoteId);
  212. fs.upload(note.data.id, "text/plain", html);
  213. //note.beginEdit();
  214. //note.set("body", html);
  215. //note.endEdit();
  216. }, 750));
  217. }