GVoice DND

Show do not disturb button on main Google Voice page

  1. // ==UserScript==
  2. // @name GVoice DND
  3. // @namespace http://boris.joff3.com
  4. // @version 1.0
  5. // @description Show do not disturb button on main Google Voice page
  6. // @match https://*.google.com/voice*
  7. // @copyright 2012, 2013, 2015 Boris Joffe
  8. // @grant GM_xmlhttpRequest
  9. // ==/UserScript==
  10.  
  11. /*
  12. Copyright (C) 2012, 2013, 2015 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.  
  69. function main() {
  70. if (DBG) addDebugPane();
  71.  
  72. // Initialization
  73. //dbgInfol(document.getElementsByName('_rnr_se')[0].value);
  74. _rnr_se = name0('_rnr_se').value;
  75. dbgInfol('_rnr_se: ' + _rnr_se);
  76.  
  77. makeDNDdiv(isDNDenabled());
  78.  
  79. document.body.appendChild(dndDiv);
  80. if (DBG && debugDiv != null) document.body.appendChild(debugDiv);
  81. }
  82.  
  83. // Return true if DND is enabled; otherwise return false
  84. // TODO - fix console error below as a result of elIDstyle
  85. // 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"]
  86. function isDNDenabled() {
  87. const FIND_STATIC_DND = 'gc-dnd-display';
  88. var s = elIDstyle(FIND_STATIC_DND);
  89.  
  90. if (s == null || s == ERR_VAL) {
  91. dbgErrl('isDNDenabled/elIDstyle - s is: ' + s);
  92. return ERR_VAL;
  93. }
  94. s = s.display;
  95. if (s == ERR_VAL) {
  96. dbgErrl('isDNDenabled/elIDstyle returned err');
  97. return ERR_VAL;
  98. }
  99. //dbgInfol('display: ' + s);
  100. return s.indexOf('none') != -1;
  101. }
  102.  
  103.  
  104. // Create DND Div
  105. function makeDNDdiv(isEnabled) {
  106. if (DBG && isEnabled) dbgInfol("dnd enabled but making div anyway");
  107.  
  108. dndDiv = document.createElement('div');
  109. dndDiv.id = "dndDiv";
  110. dndDiv.style.position = 'fixed';
  111. dndDiv.style.top = "23%";
  112. dndDiv.style.right = "10%";
  113. //dndDiv.style.border = "1px solid blue";
  114. dndDiv.style.padding = "7px";
  115. dndDiv.style.backgroundColor = "#002"; //"#f0f0ff";
  116. dndDiv.style.opacity = OPACITY_PERCENT;
  117. dndDiv.style.zIndex="100"; // put it in front of other elements
  118. dndDiv.style.minWidth = "200px";
  119.  
  120. dndEnable = document.createElement('button');
  121. dndEnable.style.padding = '1px';
  122. dndEnable.style.border = '1px solid blue';
  123. dndEnable.style.margin = '5px';
  124. dndEnable.style.backgroundColor = BUTTON_BG_COLOR;
  125. dndEnable.textContent = ENABLE_DND;
  126.  
  127. // Expiration Time Field - in unixtime
  128. dndExp = document.createElement('input');
  129. dndExp.id = "dndExp";
  130. dndExp.type = "text";
  131. dndExp.size = 13;
  132. dndExp.style.display = "none";
  133. //dndExp.style.visible = "none";
  134. if (DBG) dndExp.style.display = "inline";
  135.  
  136. // How long to set DND for
  137. var dndExpDate = document.createElement('input');
  138. dndExpDate.id = "dndExpDate";
  139. dndExpDate.type = "text";
  140. dndExpDate.size = 4;
  141.  
  142. //var expValue = new Date( dndExp.value );
  143. dndExpDate.value = DEFAULT_DND_MINUTES;
  144.  
  145. // Units of time to set DND for
  146. var dndExpUnit = document.createElement('select');
  147. dndExpUnit.id = "dndExpUnit";
  148. var minOpt = document.createElement('option');
  149. minOpt.value = minOpt.text = 'Min';
  150. var hrOpt = document.createElement('option');
  151. hrOpt.value = hrOpt.text = 'Hr';
  152. dndExpUnit.add(minOpt);
  153. dndExpUnit.add(hrOpt);
  154. dndExpUnit.selectedIndex = 0;
  155. //dndExpUnit.size = 2;
  156.  
  157. var dndFutureDate = document.createElement('span');
  158. dndFutureDate.id = "dndFutureDate";
  159. dndFutureDate.innerHTML = "";
  160. dndFutureDate.style.color = "#fc0";
  161. dndFutureDate.style.fontSize = 8;
  162. //dndFutureDate.style.margin = "2px";
  163. //dndFutureDate.style.padding = "2px";
  164.  
  165. dndDiv.appendChild(dndExpDate);
  166. dndDiv.appendChild(dndExpUnit);
  167. dndDiv.appendChild(dndEnable);
  168. if (DBG) dndDiv.appendChild( document.createElement('br') );
  169. dndDiv.appendChild(dndExp);
  170. dndDiv.appendChild( document.createElement('br') );
  171. dndDiv.appendChild(dndFutureDate);
  172.  
  173.  
  174. // Create event listener toggle
  175. dndEnable.addEventListener('click', enableDND, true);
  176. dndExpDate.addEventListener('change', expChange, true);
  177. dndExpDate.addEventListener('click', expChange, true);
  178. dndExpUnit.addEventListener('change', expChange, true);
  179.  
  180. // When Do Not Distrub expiration date changes
  181. // Update seconds
  182. function expChange() {
  183. var addMin = 0;
  184. var addHr = 0;
  185. var futureDate = new Date();
  186.  
  187. if ( dndExpUnit.selectedIndex == 0 ) { // minutes selected
  188. addMin = dndExpDate.value;
  189. addMin = addMin * 60 * 1000; // convert to UNIX ms
  190. futureDate.setTime( futureDate.getTime() + addMin );
  191. } else if ( dndExpUnit.selectedIndex == 1 ) { // hours selected
  192. addHr = dndExpDate.value;
  193. addHr = addHr * 60 * 60 * 1000; // convert to UNIX ms
  194. futureDate.setTime( futureDate.getTime() + addHr );
  195. } else {
  196. dbgErrl('expChange evt: could not get selectedIndex');
  197. return ERR_VAL;
  198. }
  199.  
  200. dndExp.value = futureDate.getTime(); // update unix time
  201. dbgInfol('futureDate: ' + futureDate);
  202. dndFutureDate.innerHTML = futureDate;
  203.  
  204. }
  205.  
  206. // send AJAX POST request to enable DND for unspecified amt of time
  207. function enableDND() {
  208. dbgInfol('enabling');
  209. var actualReq1, actualReq2;
  210. //var oldReq = "doNotDisturb=1&_rnr_se=";
  211.  
  212. actualReq1 = 'sid=3&mid=5&req=%5Bnull%2C%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cfalse%2C';
  213. actualReq2 = '%5D%5D&_rnr_se=';
  214.  
  215. expChange();
  216. var myFullReq = actualReq1 + dndExp.value + actualReq2 + encodeURIComponent(_rnr_se);
  217.  
  218. GM_xmlhttpRequest({
  219. method: "POST",
  220. url: ENABLE_DND_URL,
  221. data: myFullReq,
  222. headers: {
  223. "Content-Type": "application/x-www-form-urlencoded"
  224. },
  225. onload: function(/*response*/) {
  226. //dndEnable.innerHTML = TOGGLE_TO_ENABLED;
  227. dndFutureDate.innerHTML = TOGGLE_TO_ENABLED;
  228. //dbgInfol( 'Sending request:\n\n' + myFullReq);
  229. 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
  230. },
  231. onerror: function(response) {
  232. if (DBG) alert('Err: \n\n' + response);
  233. }
  234. });
  235. }
  236. }
  237.  
  238. // Helper Functions
  239. function el(elStr) {
  240. var x = document.getElementById(elStr); // get el by ID
  241. if (x == null) {
  242. dbgErrl("el: no el by id: " + elStr);
  243. return ERR_VAL;
  244. } else
  245. return x;
  246. }
  247. /*function elStyle(el) {
  248. var x = window.getComputedStyle(el, null); // get computed style of el
  249. if (x == null) {
  250. dbgErrl('elStyle: no el style for: ' + el);
  251. return ERR_VAL;
  252. } else
  253. return x;
  254. }*/
  255.  
  256. function elIDstyle(elStr) {
  257. var x = el(elStr);
  258. if (x==ERR_VAL) {
  259. dbgErrl('elIDStyle/el - el by ID returned error');
  260. return ERR_VAL;
  261. }
  262. x = window.getComputedStyle( x, null ); // get computed style from el ID str
  263.  
  264. //dbgInfol("computed style:\n"+x.content);
  265.  
  266. if (x == null) {
  267. dbgErrl('elIDStyle: could not get el style from elbyId: ' + elStr);
  268. return ERR_VAL;
  269. } else
  270. return x;
  271. }
  272.  
  273. function name0(elStr) {
  274. var x = document.getElementsByName(elStr); // get first element of a certain name
  275. if (x == null) {
  276. dbgErrl("name0: no el by name: " + elStr);
  277. return ERR_VAL;
  278. } else if (x[0]==null) {
  279. dbgErrl('name0: el exists but el[0] doesnt');
  280. return ERR_VAL;
  281. } else if (x[0].value==null) {
  282. dbgWarnl('name0: el[0].value is null');
  283. return x[0];
  284. } else
  285. return x[0];
  286. }
  287.  
  288. // ========== DEBUGGING SECTION =========
  289. // Adds a fixed div at the top right with debug info
  290. function addDebugPane() {
  291. debugDiv = document.createElement('div');
  292. debugDiv.id = "debugDiv";
  293.  
  294. //Some CSS stuff
  295. debugDiv.style.position = "fixed";
  296. debugDiv.style.top = "38%";
  297. debugDiv.style.right = "5%";
  298. debugDiv.style.padding = "5px";
  299. debugDiv.style.border = "2px solid red";
  300. debugDiv.style.backgroundColor = "white";
  301. //debugDiv.style.minWidth = "20%";
  302. debugDiv.style.zIndex="100"; // put it in front of other elements
  303.  
  304. debugDiv.innerHTML = "<b><u>Debug Info</u></b><br>";
  305. }
  306.  
  307. // Add info to debug div
  308. function dbgAdd(htmlText) {
  309. if (DBG && debugDiv != null)
  310. debugDiv.innerHTML += htmlText;
  311. else if (DBG) {
  312. var tmpDiv = document.createElement('div');
  313. tmpDiv.innerHTML = htmlText;
  314. alert('DBG info:\n\t' + tmpDiv.textContent + ' \n\n\n(Note: debugDiv is null)');
  315. }
  316. }
  317. function dbgAddl(htmlText) { dbgAdd(htmlText + "<br>"); }
  318. //function dbgFatal(htmlText) { dbgAdd('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
  319. //function dbgFatall(htmlText) { dbgAddl('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
  320. //function dbgErr(htmlText) { dbgAdd('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
  321. function dbgErrl(htmlText) { dbgAddl('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
  322. //function dbgWarn(htmlText) { dbgAdd('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
  323. function dbgWarnl(htmlText) { dbgAddl('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
  324. //function dbgInfo(htmlText) { dbgAdd('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }
  325. function dbgInfol(htmlText) { dbgAddl('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }
  326.  
  327. }, true);