stickyNotes

Press "shift + left key" to add sticky notes in any webpage wherever you like

  1. // ==UserScript==
  2. // @name stickyNotes
  3. // @namespace sticky notes
  4. // @version v1.70
  5. // @author zhus@live.cn
  6. // @require https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js
  7. // @description Press "shift + left key" to add sticky notes in any webpage wherever you like
  8. // @include /^https?://*/
  9. // @exclude /^https?://www.baidu.com/*/
  10. // @exclude /^https?://www\.google\./
  11. // @exclude /^https?://\.bing\./
  12. // @encoding utf-8
  13. // @connect 192.168.196.9
  14. // @connect 192.168.196.6
  15. // @grant GM_xmlhttpRequest
  16. // @grant GM_notification
  17. // @grant GM_registerMenuCommand
  18. // @license GPL license
  19. // ==/UserScript==
  20. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  21.  
  22. (function ($) {
  23.  
  24. $.fn.draggable = function (options) {
  25. var settings = $.extend({
  26. handle: undefined,
  27. msg: {},
  28. callfunction: function () { }
  29. }, options);
  30. var _eleFunc = function () {
  31. var x0, y0,
  32. ele = $(this),
  33. handle;
  34. handle = (settings.handle === undefined ? ele : ele.find(settings.handle).eq(0) === undefined ? ele : ele.find(settings.handle).eq(0));
  35. ele.css({
  36. position: "absolute"
  37. }); //make sure that the "postion" is "absolute"
  38. handle.bind('mousedown', function (e0) {
  39. handle.css({
  40. cursor: "move"
  41. }); //set the appearance of cursor
  42. x0 = ele.offset().left - e0.pageX; //*1
  43. y0 = ele.offset().top - e0.pageY; //*1
  44. $(document).bind('mousemove', function (e1) { //bind the mousemove event, caution:this event must be bind to "document"
  45. ele.css({
  46. left: x0 + e1.pageX,
  47. top: y0 + e1.pageY
  48. }); //this expression and the expression of *1 equal to "ele.origin_offset+mouse.current_offset-mouse.origin_offset"
  49. });
  50. $(document).one('mouseup', settings.msg, function (e) { //when the mouse up,unbind the mousemove event,bind only once
  51. settings.callfunction(e); //callback function
  52. $(document).unbind('mousemove');
  53. handle.css({
  54. cursor: "auto"
  55. });
  56. });
  57. });
  58. };
  59. return this.each(_eleFunc);
  60. };
  61. })(jQuery);
  62. /////////////////////////////////////////////////////////////////////////////////////////////////////////////
  63.  
  64. var pNote = (function ($) {
  65. const server = 'http://192.168.196.9:6080/'; //写上自己的服务器地址。
  66. var day = new Date();
  67. var mark = parseInt(String(day.getMonth()) + String(day.getDate()) + String(day.getHours()));
  68. console.log('pnote', mark);
  69.  
  70. function showMsg(message) {
  71. GM_notification({
  72. title: location.host,
  73. text: message,
  74. timeout: 2000,
  75.  
  76. })
  77. };
  78.  
  79. function isJson(str) {
  80. if (typeof str == 'string') {
  81. try {
  82. var obj = JSON.parse(str);
  83. if (typeof obj == 'object' && obj) {
  84. return true;
  85. } else {
  86. return false;
  87. }
  88.  
  89. } catch (e) {
  90. console.log('error:' + str + '!!!' + e);
  91. return false;
  92. }
  93. };
  94. showMsg('It is not a string!');
  95. };
  96.  
  97. function getPost(flag, data = '') {
  98. return new Promise(res => {
  99. console.log('start load');
  100. GM_xmlhttpRequest({
  101. method: flag,
  102. headers: { 'Content-Type': 'application/json', 'host': location.hostname },
  103. data: data,
  104. url: server,
  105. timeout: 50000,
  106. onload: (r) => {
  107. if (flag === 'GET') {
  108. console.log('get')
  109. if (r.response !== 'no' && isJson(r.responseText)) {
  110. console.log(r.response);
  111. localStorage.setItem('wxz-sto', r.responseText);
  112. } // get 方法只会回传 no 或者 data数据
  113. } else if (flag === 'POST') {
  114. console.log('post')
  115. if (r.response === 'ok') {
  116. showMsg('upload done');
  117. //console.log('upload done');
  118. } else {
  119. alert('upload err'); // post方法会回传 ok no 或者 err 三种
  120. }
  121. };
  122. res()
  123. },
  124. onerror: (r) =>{
  125. console.log(r)
  126. showMsg('请求失败');
  127. }
  128. })
  129. })
  130. };
  131.  
  132. function setCSS() {
  133. var css = ' <style type="text/css">\
  134. .wxz-noteDiv{\
  135. z-index:99;\
  136. box-shadow:0 0 9px rgba(0,0,0,.9);\
  137. background:#FFCC00;\
  138. width:200px;\
  139. position:absolute;\
  140. opacity:0.8;\
  141. outline:0 none;\
  142. }\
  143. .wxz-noteDiv-head{\
  144. background:#FFCC00;\
  145. outline:0 none;\
  146. text-align:center;\
  147. width:200px;\
  148. line-height: initial;\
  149. font:13px/1.5 "微软雅黑",arial,serif;\
  150. }\
  151. .wxz-noteDiv-head-title{\
  152. text-align:center;\
  153. }\
  154. .wxz-noteDiv-head-close{\
  155. cursor:pointer;\
  156. position:absolute;\
  157. right:5px;\
  158. }\
  159. .wxz-noteDiv-content{\
  160. background:#FFFF66 ;\
  161. padding:5px 9px;\
  162. font:13px/1.5 "微软雅黑",arial,serif;\
  163. word-wrap:break-word;\
  164. min-height:200px;\
  165. outline:0 none;\
  166. text-align:left;\
  167. }\
  168. </style>';
  169. $('head:first').append(css);
  170. }
  171.  
  172. async function getSTO() {
  173. //import localStorage.mysto to stotage
  174. var sto;
  175. if (!localStorage.getItem('down-note') || mark - parseInt(localStorage.getItem('down-note')) > 4 ) {
  176. console.log('syncing')
  177. await getPost('GET'); //4小时同步一次,减少读服务器。
  178. localStorage.setItem('down-note', mark);
  179. };
  180. var data = localStorage.getItem('wxz-sto');
  181. if (data !== null && data && isJson(data)) {
  182. sto = JSON.parse(localStorage.getItem('wxz-sto'));
  183. } else {
  184. sto = {};
  185. localStorage.setItem('wxz-sto', sto);
  186. }
  187. return sto;
  188. }
  189.  
  190. function upDateSTO(storage) {
  191. var data = JSON.stringify(storage)
  192. if(isJson(data)){
  193. localStorage.setItem('wxz-sto', data); //update localStorage.mysto with storage
  194. getPost('POST', data);
  195. } else {
  196. showMsg('data is not json');
  197. };
  198. }
  199.  
  200. async function addNoteToStorage(keyName, x, y, text) {
  201. try{
  202. let storage = await getSTO(); //just call for add notes not for update or delete
  203. var path = {},
  204. temp = {}
  205. temp.keyName = keyName;
  206. temp.x = x;
  207. temp.y = y;
  208. temp.text = text;
  209. if (storage[location.pathname] !== undefined) {
  210. path = storage[location.pathname];
  211. }
  212. path[temp.keyName] = temp; //save notes to path
  213. storage[location.pathname] = path;
  214. upDateSTO(storage); //update local storage
  215. path = null;
  216. temp = null;
  217. storage = null;
  218. }catch(e){
  219. showMsg(e)
  220. }
  221. };
  222.  
  223.  
  224. async function removeNoteFromStorage(keyName) {
  225. try{
  226. let storage = await getSTO();
  227. var path = {}
  228. path = storage[location.pathname];
  229. if (path !== undefined) {
  230. delete path[keyName];
  231. if (Object.keys(path).length === 0) {
  232. delete storage[location.pathname];
  233. } else {
  234. storage[location.pathname] = path;
  235. }
  236. //update the localStorage.pathname
  237. upDateSTO(storage);
  238. }
  239. path = null;
  240. storage = null;
  241. }
  242. catch(e){
  243. showMsg(e)
  244. }
  245. };
  246.  
  247. function save(keyName) {
  248. var
  249. x, y, text,
  250. selector = "div[keyName='" + keyName + "']";
  251. x = $(selector).css('left');
  252. y = $(selector).css('top');
  253. text = $(selector).find('.wxz-noteDiv-content').html();
  254. addNoteToStorage(keyName, x, y, text);
  255. $(selector).find('.wxz-noteDiv-head-flag').text('');
  256. }
  257.  
  258. function del(keyName) {
  259. if (confirm("Do you like to delete the note?")) {
  260. var selector = "div[keyName='" + keyName + "']";
  261. $(selector).remove();
  262. removeNoteFromStorage(keyName);
  263. }
  264. }
  265.  
  266. function show(keyName, x, y, text) {
  267. var
  268. html = '<div class="wxz-noteDiv" >\
  269. <div class="wxz-noteDiv-head">\
  270. <nobr class="wxz-noteDiv-head-flag">*</nobr><nobr class="wxz-noteDiv-head-title"></nobr><nobr class="wxz-noteDiv-head-close">X</nobr>\
  271. </div>\
  272. <div class="wxz-noteDiv-content" contenteditable="true"></div>\
  273. </div>',
  274. thisNote,
  275. tempTime = new Date(parseInt(keyName, 10));
  276. thisNote = $(html);
  277. thisNote.appendTo('body:first');
  278. thisNote.attr('keyName', keyName);
  279. thisNote.find('.wxz-noteDiv-head-title').html(tempTime.toDateString());
  280. //set the coordinate
  281. thisNote.css({
  282. position: 'absolute',
  283. top: y,
  284. left: x
  285. });
  286. //write text to content
  287. thisNote.find('.wxz-noteDiv-content').html(text);
  288. // draggable;
  289. thisNote.draggable({
  290. handle: '.wxz-noteDiv-head',
  291. msg: {
  292. msg: keyName
  293. },
  294. callfunction: function (e) {
  295. save(e.data.msg);
  296. }
  297. }
  298. );
  299. //bind click event
  300. thisNote.find('.wxz-noteDiv-head-close').bind('click', {
  301. msg: keyName
  302. }, function (e) {
  303. del(e.data.msg);
  304. });
  305. //save when it lost focus
  306. thisNote.focusout({
  307. msg: keyName
  308. }, function (e) {
  309. save(e.data.msg);
  310. });
  311. thisNote.focusin(function () {
  312. thisNote.find('.wxz-noteDiv-head-flag').text('*');
  313. });
  314.  
  315. }
  316.  
  317. async function loadNotes() {
  318. try {
  319. let storage = await getSTO();
  320. var noteList;
  321. if (storage[location.pathname] !== undefined) {
  322. noteList = storage[location.pathname];
  323. $.each(noteList, function (i, e) {
  324. show(e.keyName, e.x, e.y, e.text);
  325. });
  326. $('.wxz-noteDiv-head-flag').text('');
  327. }
  328. console.log('load notes successfully');
  329. }catch(e){
  330. showMsg(e);
  331. }
  332. }
  333.  
  334. // function showNotes() {
  335. // $("wxz-noteDiv").css({
  336. // 'display': 'inline'
  337. // });
  338. // }
  339.  
  340. // function closeNotes() {
  341. // $("wxz-noteDiv").css({
  342. // 'display': 'none'
  343. // });
  344. // }
  345.  
  346. return {
  347. init: function () {
  348. setCSS();
  349. loadNotes();
  350. $("body").mousedown(function (e) {
  351. if (e.shiftKey) {
  352. var
  353. x = e.pageX,
  354. y = e.pageY,
  355. keyName = (new Date()).getTime();
  356. // keyName = e.timeStamp;//a bug in firefox since 2004
  357. e.preventDefault();
  358. show(keyName, x, y, '');
  359. console.log('new note');
  360. }
  361. });
  362. console.log('initialized successfully');
  363. }
  364.  
  365. };
  366.  
  367. })(jQuery);
  368.  
  369. pNote.init();