GVoice DND

Shows do not disturb on main Google Voice page

当前为 2015-07-29 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name GVoice DND
  3. // @namespace GVoice DND
  4. // @version 0.5
  5. // @description Shows do not disturb on main Google Voice page
  6. // @match https://*.google.com/voice*
  7. // @copyright 2013 Boris Joffe
  8. // @grant GM_xmlhttpRequest
  9. // ==/UserScript==
  10.  
  11. /*
  12. Copyright (C) 2013 Boris Joffe
  13.  
  14. This program is free software: you can redistribute it and/or modify
  15. it under the terms of the GNU General Public License as published by
  16. the Free Software Foundation, either version 3 of the License, or
  17. (at your option) any later version.
  18.  
  19. This program is distributed in the hope that it will be useful,
  20. but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. GNU General Public License for more details.
  23.  
  24. You should have received a copy of the GNU General Public License
  25. along with this program. If not, see <http://www.gnu.org/licenses/>.
  26. */
  27.  
  28. // Global Strings
  29. const ENABLE_DND = "Enable DND";
  30. const TOGGLE_TO_ENABLED = "Success! - Enabled Do Not Disturb";
  31.  
  32. // Global URLs
  33. //const ENABLE_DND_URL = 'https://www.google.com/voice/settings/editGeneralSettings/';
  34. const ENABLE_DND_URL = 'https://www.google.com/voice/b/0/service/post'; // new URL
  35.  
  36. // Search and Regexp strings
  37. const FIND_IF_DND_DISABLED_1 = '<span id="gc-dnd-static" style="display: none;">"Do Not Disturb" is enabled.'; // For DND with no end
  38. const FIND_IF_DND_DISABLED_2 = '<span id="gc-dnd-until" style="display: none;">"Do Not Disturb" is enabled'; // For DND until
  39.  
  40. // Other Global const
  41. const TOGGLE_TIMEOUT = 300;
  42. const DEFAULT_DND_MINUTES = 20;
  43. const OPACITY_PERCENT = 0.65;
  44.  
  45. // Colors
  46. const FATALERR_COLOR = '#f00';
  47. const ERR_COLOR = '#f31';
  48. const WARN_COLOR = '#ffaa00';
  49. const INFO_COLOR = '#999';
  50. const BUTTON_BG_COLOR = '#f0f0ff';
  51.  
  52. // Globar DOM vars
  53. var dndDiv;
  54. var debugDiv;
  55. var dndEnable, dndExp;
  56.  
  57. // URL Params
  58. var _rnr_se;
  59.  
  60. // Global boolean vars
  61. var DBG = false;
  62. var ERR_VAL = -1;
  63.  
  64. window.addEventListener('load',
  65. function() {
  66.  
  67. main();
  68. function main() {
  69. if (DBG) addDebugPane();
  70. // Initialization
  71. //dbgInfol(document.getElementsByName('_rnr_se')[0].value);
  72. _rnr_se = name0('_rnr_se').value;
  73. dbgInfol('_rnr_se: ' + _rnr_se);
  74. makeDNDdiv(isDNDenabled());
  75. document.body.appendChild(dndDiv);
  76. if (DBG && debugDiv != null) document.body.appendChild(debugDiv);
  77. }
  78. // Return true if DND is enabled; otherwise return false
  79. // TODO - fix console error below as a result of elIDstyle
  80. // Error: uncaught exception: [Exception... "Operation is not supported" code: "9" nsresult: "0x80530009 (NS_ERROR_DOM_NOT_SUPPORTED_ERR)" location: "resource://greasemonkey/runScript.js Line: 118"]
  81. function isDNDenabled() {
  82. const FIND_STATIC_DND = 'gc-dnd-display';
  83. var s = elIDstyle(FIND_STATIC_DND)
  84. if (s == null || s == ERR_VAL) {
  85. dbgErrl('isDNDenabled/elIDstyle - s is: ' + s);
  86. return ERR_VAL;
  87. }
  88. s = s.display;
  89. if (s == ERR_VAL) {
  90. dbgErrl('isDNDenabled/elIDstyle returned err');
  91. return ERR_VAL;
  92. }
  93. //dbgInfol('display: ' + s);
  94. return s.indexOf('none') != -1;
  95. }
  96. // Create DND Div
  97. function makeDNDdiv(isEnabled) {
  98. if (DBG && isEnabled) dbgInfol("dnd enabled but making div anyway");
  99. dndDiv = document.createElement('div');
  100. dndDiv.id = "dndDiv";
  101. dndDiv.style.position = 'fixed';
  102. dndDiv.style.top = "23%";
  103. dndDiv.style.right = "10%";
  104. //dndDiv.style.border = "1px solid blue";
  105. dndDiv.style.padding = "7px";
  106. dndDiv.style.backgroundColor = "#002"; //"#f0f0ff";
  107. dndDiv.style.opacity = OPACITY_PERCENT;
  108. dndDiv.style.zIndex="100"; // put it in front of other elements
  109. dndDiv.style.minWidth = "200px";
  110. dndEnable = document.createElement('button');
  111. dndEnable.style.padding = '1px';
  112. dndEnable.style.border = '1px solid blue';
  113. dndEnable.style.margin = '5px';
  114. dndEnable.style.backgroundColor = BUTTON_BG_COLOR;
  115. dndEnable.textContent = ENABLE_DND;
  116. // Expiration Time Field - in unixtime
  117. dndExp = document.createElement('input');
  118. dndExp.id = "dndExp";
  119. dndExp.type = "text";
  120. dndExp.size = 13;
  121. dndExp.style.display = "none";
  122. //dndExp.style.visible = "none";
  123. if (DBG) dndExp.style.display = "inline";
  124. // How long to set DND for
  125. dndExpDate = document.createElement('input');
  126. dndExpDate.id = "dndExpDate";
  127. dndExpDate.type = "text";
  128. dndExpDate.size = 4;
  129. var expValue = new Date( dndExp.value );
  130. dndExpDate.value = DEFAULT_DND_MINUTES;
  131. // Units of time to set DND for
  132. dndExpUnit = document.createElement('select');
  133. dndExpUnit.id = "dndExpUnit";
  134. minOpt = document.createElement('option');
  135. minOpt.value = minOpt.text = 'Min';
  136. hrOpt = document.createElement('option');
  137. hrOpt.value = hrOpt.text = 'Hr';
  138. dndExpUnit.add(minOpt);
  139. dndExpUnit.add(hrOpt);
  140. dndExpUnit.selectedIndex = 0;
  141. //dndExpUnit.size = 2;
  142. dndFutureDate = document.createElement('span');
  143. dndFutureDate.id = "dndFutureDate";
  144. dndFutureDate.innerHTML = "";
  145. dndFutureDate.style.color = "#fc0";
  146. dndFutureDate.style.fontSize = 8;
  147. //dndFutureDate.style.margin = "2px";
  148. //dndFutureDate.style.padding = "2px";
  149. dndDiv.appendChild(dndExpDate);
  150. dndDiv.appendChild(dndExpUnit);
  151. dndDiv.appendChild(dndEnable);
  152. if (DBG) dndDiv.appendChild( document.createElement('br') );
  153. dndDiv.appendChild(dndExp);
  154. dndDiv.appendChild( document.createElement('br') );
  155. dndDiv.appendChild(dndFutureDate);
  156. // Create event listener toggle
  157. dndEnable.addEventListener('click', enableDND, true);
  158. dndExpDate.addEventListener('change', expChange, true);
  159. dndExpDate.addEventListener('click', expChange, true);
  160. dndExpUnit.addEventListener('change', expChange, true);
  161. // When Do Not Distrub expiration date changes
  162. // Update seconds
  163. function expChange() {
  164. var addMin = addHr = 0;
  165. var futureDate = new Date();
  166. if ( dndExpUnit.selectedIndex == 0 ) { // minutes selected
  167. addMin = dndExpDate.value;
  168. addMin = addMin * 60 * 1000; // convert to UNIX ms
  169. futureDate.setTime( futureDate.getTime() + addMin );
  170. } else if ( dndExpUnit.selectedIndex == 1 ) { // hours selected
  171. addHr = dndExpDate.value;
  172. addHr = addHr * 60 * 60 * 1000; // convert to UNIX ms
  173. futureDate.setTime( futureDate.getTime() + addHr );
  174. } else {
  175. dbgErrl('expChange evt: could not get selectedIndex');
  176. return ERR_VAL;
  177. }
  178. dndExp.value = futureDate.getTime(); // update unix time
  179. dbgInfol('futureDate: ' + futureDate);
  180. dndFutureDate.innerHTML = futureDate;
  181. }
  182. // send AJAX POST request to enable DND for unspecified amt of time
  183. function enableDND() {
  184. dbgInfol('enabling');
  185. oldReq = "doNotDisturb=1&_rnr_se=";
  186. actualReq1 = 'sid=3&mid=5&req=%5Bnull%2C%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cfalse%2C';
  187. actualReq2 = '%5D%5D&_rnr_se=';
  188. expChange();
  189. myFullReq = actualReq1 + dndExp.value + actualReq2 + encodeURIComponent(_rnr_se);
  190. GM_xmlhttpRequest({
  191. method: "POST",
  192. url: ENABLE_DND_URL,
  193. data: myFullReq,
  194. headers: {
  195. "Content-Type": "application/x-www-form-urlencoded"
  196. },
  197. onload: function(response) {
  198. //dndEnable.innerHTML = TOGGLE_TO_ENABLED;
  199. dndFutureDate.innerHTML = TOGGLE_TO_ENABLED;
  200. //dbgInfol( 'Sending request:\n\n' + myFullReq);
  201. setTimeout(window.location.reload(true), TOGGLE_TIMEOUT); // is this useless like the dev console says??? - gv does a 2nd get request to update the page
  202. },
  203. onerror: function(response) {
  204. if (DBG) alert('Err: \n\n' + response);
  205. }
  206. });
  207. }
  208. }
  209. // Helper Functions
  210. function el(elStr) {
  211. x = document.getElementById(elStr); // get el by ID
  212. if (x == null) {
  213. dbgErrl("el: no el by id: " + elStr);
  214. return ERR_VAL;
  215. } else
  216. return x;
  217. }
  218. function elStyle(el) {
  219. x = window.getComputedStyle(el, null); // get computed style of el
  220. if (x == null) {
  221. dbgErrl('elStyle: no el style for: ' + el);
  222. return ERR_VAL;
  223. } else
  224. return x;
  225. }
  226. function elIDstyle(elStr) {
  227. x = el(elStr);
  228. if (x==ERR_VAL) {
  229. dbgErrl('elIDStyle/el - el by ID returned error');
  230. return ERR_VAL;
  231. }
  232. x = window.getComputedStyle( x, null ); // get computed style from el ID str
  233. //dbgInfol("computed style:\n"+x.content);
  234. if (x == null) {
  235. dbgErrl('elIDStyle: could not get el style from elbyId: ' + elStr);
  236. return ERR_VAL;
  237. } else
  238. return x;
  239. }
  240. function name0(elStr) {
  241. x = document.getElementsByName(elStr); // get first element of a certain name
  242. if (x == null) {
  243. dbgErrl("name0: no el by name: " + elStr);
  244. return ERR_VAL;
  245. } else if (x[0]==null) {
  246. dbgErrl('name0: el exists but el[0] doesnt');
  247. return ERR_VAL;
  248. } else if (x[0].value==null) {
  249. dbgWarnl('name0: el[0].value is null');
  250. return x[0];
  251. } else
  252. return x[0];
  253. }
  254. // ========== DEBUGGING SECTION =========
  255. // Adds a fixed div at the top right with debug info
  256. function addDebugPane() {
  257. debugDiv = document.createElement('div');
  258. debugDiv.id = "debugDiv";
  259.  
  260. //Some CSS stuff
  261. debugDiv.style.position = "fixed";
  262. debugDiv.style.top = "38%";
  263. debugDiv.style.right = "5%";
  264. debugDiv.style.padding = "5px";
  265. debugDiv.style.border = "2px solid red";
  266. debugDiv.style.backgroundColor = "white";
  267. //debugDiv.style.minWidth = "20%";
  268. debugDiv.style.zIndex="100"; // put it in front of other elements
  269. debugDiv.innerHTML = "<b><u>Debug Info</u></b><br>";
  270. }
  271. // Add info to debug div
  272. function dbgAdd(htmlText) {
  273. if (DBG && debugDiv != null)
  274. debugDiv.innerHTML += htmlText;
  275. else if (DBG) {
  276. var tmpDiv = document.createElement('div');
  277. tmpDiv.innerHTML = htmlText;
  278. alert('DBG info:\n\t' + tmpDiv.textContent + ' \n\n\n(Note: debugDiv is null)');
  279. }
  280. }
  281. function dbgAddl(htmlText) { dbgAdd(htmlText + "<br>"); }
  282. function dbgFatal(htmlText) { dbgAdd('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
  283. function dbgFatall(htmlText) { dbgAddl('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
  284. function dbgErr(htmlText) { dbgAdd('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
  285. function dbgErrl(htmlText) { dbgAddl('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
  286. function dbgWarn(htmlText) { dbgAdd('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
  287. function dbgWarnl(htmlText) { dbgAddl('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
  288. function dbgInfo(htmlText) { dbgAdd('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }
  289. function dbgInfol(htmlText) { dbgAddl('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }
  290. }, true);