GVoice DND

Show do not disturb button on main Google Voice page

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         GVoice DND
// @namespace    http://boris.joff3.com
// @version      1.0
// @description  Show do not disturb button on main Google Voice page
// @match        https://*.google.com/voice*
// @copyright    2012, 2013, 2015 Boris Joffe
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/*
    Copyright (C) 2012, 2013, 2015 Boris Joffe

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// Global Strings
const ENABLE_DND = "Enable DND";
const TOGGLE_TO_ENABLED = "Success! - Enabled Do Not Disturb";

// Global URLs
//const ENABLE_DND_URL = 'https://www.google.com/voice/settings/editGeneralSettings/';
const ENABLE_DND_URL = 'https://www.google.com/voice/b/0/service/post'; // new URL

// Search and Regexp strings
//const FIND_IF_DND_DISABLED_1 = '<span id="gc-dnd-static" style="display: none;">"Do Not Disturb" is enabled.';		// For DND with no end
//const FIND_IF_DND_DISABLED_2 = '<span id="gc-dnd-until" style="display: none;">"Do Not Disturb" is enabled';	// For DND until

// Other Global const
const TOGGLE_TIMEOUT = 300;
const DEFAULT_DND_MINUTES = 20;
const OPACITY_PERCENT = 0.65;

// Colors
//const FATALERR_COLOR = '#f00';
const ERR_COLOR = '#f31';
const WARN_COLOR = '#ffaa00';
const INFO_COLOR = '#999';
const BUTTON_BG_COLOR = '#f0f0ff';

// Globar DOM vars
var dndDiv;
var debugDiv;
var dndEnable, dndExp;

// URL Params
var _rnr_se;

// Global boolean vars
var DBG = false;
var ERR_VAL = -1;

window.addEventListener('load',
function() {

	main();

	function main() {
		if (DBG) addDebugPane();

		// Initialization
		//dbgInfol(document.getElementsByName('_rnr_se')[0].value);
		_rnr_se = name0('_rnr_se').value;
		dbgInfol('_rnr_se: ' + _rnr_se);

		makeDNDdiv(isDNDenabled());

		document.body.appendChild(dndDiv);
		if (DBG && debugDiv != null) document.body.appendChild(debugDiv);
	}

	// Return true if DND is enabled; otherwise return false
	// TODO - fix console error below as a result of elIDstyle
	// 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"]
	function isDNDenabled() {
		const FIND_STATIC_DND = 'gc-dnd-display';
		var s = elIDstyle(FIND_STATIC_DND);

		if (s == null || s == ERR_VAL) {
		  dbgErrl('isDNDenabled/elIDstyle - s is: ' + s);
		  return ERR_VAL;
		}
		s = s.display;
		if (s == ERR_VAL) {
		  dbgErrl('isDNDenabled/elIDstyle returned err');
		  return ERR_VAL;
		}
		//dbgInfol('display: ' + s);
		return s.indexOf('none') != -1;
	}


	// Create DND Div
	function makeDNDdiv(isEnabled) {
		if (DBG && isEnabled) dbgInfol("dnd enabled but making div anyway");

		dndDiv = document.createElement('div');
		dndDiv.id = "dndDiv";
		dndDiv.style.position = 'fixed';
		dndDiv.style.top = "23%";
		dndDiv.style.right = "10%";
		//dndDiv.style.border = "1px solid blue";
		dndDiv.style.padding = "7px";
		dndDiv.style.backgroundColor = "#002"; //"#f0f0ff";
		dndDiv.style.opacity = OPACITY_PERCENT;
		dndDiv.style.zIndex="100";	// put it in front of other elements
		dndDiv.style.minWidth = "200px";

		dndEnable = document.createElement('button');
		dndEnable.style.padding = '1px';
		dndEnable.style.border = '1px solid blue';
		dndEnable.style.margin = '5px';
		dndEnable.style.backgroundColor = BUTTON_BG_COLOR;
		dndEnable.textContent = ENABLE_DND;

		// Expiration Time Field - in unixtime
		dndExp = document.createElement('input');
		dndExp.id = "dndExp";
		dndExp.type = "text";
		dndExp.size = 13;
		dndExp.style.display = "none";
		//dndExp.style.visible = "none";
		if (DBG) dndExp.style.display = "inline";

		// How long to set DND for
		var dndExpDate = document.createElement('input');
		dndExpDate.id = "dndExpDate";
		dndExpDate.type = "text";
		dndExpDate.size = 4;

		//var expValue = new Date( dndExp.value );
		dndExpDate.value = DEFAULT_DND_MINUTES;

		// Units of time to set DND for
		var dndExpUnit = document.createElement('select');
		dndExpUnit.id = "dndExpUnit";
		var minOpt = document.createElement('option');
		minOpt.value = minOpt.text = 'Min';
		var hrOpt = document.createElement('option');
		hrOpt.value = hrOpt.text = 'Hr';
		dndExpUnit.add(minOpt);
		dndExpUnit.add(hrOpt);
		dndExpUnit.selectedIndex = 0;
		//dndExpUnit.size = 2;

		var dndFutureDate = document.createElement('span');
		dndFutureDate.id = "dndFutureDate";
		dndFutureDate.innerHTML = "";
		dndFutureDate.style.color = "#fc0";
		dndFutureDate.style.fontSize = 8;
		//dndFutureDate.style.margin = "2px";
		//dndFutureDate.style.padding = "2px";

		dndDiv.appendChild(dndExpDate);
		dndDiv.appendChild(dndExpUnit);
		dndDiv.appendChild(dndEnable);
		if (DBG) dndDiv.appendChild( document.createElement('br') );
		dndDiv.appendChild(dndExp);
		dndDiv.appendChild( document.createElement('br') );
		dndDiv.appendChild(dndFutureDate);


	   // Create event listener toggle
		dndEnable.addEventListener('click', enableDND, true);
		dndExpDate.addEventListener('change', expChange, true);
		dndExpDate.addEventListener('click', expChange, true);
		dndExpUnit.addEventListener('change', expChange, true);

		// When Do Not Distrub expiration date changes
		// Update seconds
		function expChange() {
		   var addMin = 0;
		   var addHr = 0;
		   var futureDate = new Date();

		   if ( dndExpUnit.selectedIndex == 0 ) {  // minutes selected
			   addMin = dndExpDate.value;
			   addMin = addMin * 60 * 1000;    // convert to UNIX ms
			   futureDate.setTime( futureDate.getTime() + addMin );
		   } else if ( dndExpUnit.selectedIndex == 1 ) { // hours selected
			   addHr = dndExpDate.value;
			   addHr = addHr * 60 * 60 * 1000;  // convert to UNIX ms
			   futureDate.setTime( futureDate.getTime() + addHr );
		   } else {
			   dbgErrl('expChange evt: could not get selectedIndex');
			   return ERR_VAL;
		   }

		   dndExp.value = futureDate.getTime();    // update unix time
		   dbgInfol('futureDate: ' + futureDate);
		   dndFutureDate.innerHTML = futureDate;

		}

		// send AJAX POST request to enable DND for unspecified amt of time
		function enableDND() {
			dbgInfol('enabling');
			var actualReq1, actualReq2;
			//var oldReq = "doNotDisturb=1&_rnr_se=";

			actualReq1 = 'sid=3&mid=5&req=%5Bnull%2C%5Bnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cnull%2Cfalse%2C';
			actualReq2 = '%5D%5D&_rnr_se=';

			expChange();
			var myFullReq = actualReq1 + dndExp.value + actualReq2 + encodeURIComponent(_rnr_se);

			GM_xmlhttpRequest({
			  method: "POST",
			  url: ENABLE_DND_URL,
			  data: myFullReq,
			  headers: {
				"Content-Type": "application/x-www-form-urlencoded"
			  },
			  onload: function(/*response*/) {
				//dndEnable.innerHTML = TOGGLE_TO_ENABLED;
				dndFutureDate.innerHTML = TOGGLE_TO_ENABLED;
				//dbgInfol( 'Sending request:\n\n' +  myFullReq);
				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
			  },
			  onerror: function(response) {
				  if (DBG) alert('Err: \n\n' + response);
			  }
			});
		}
	}

	// Helper Functions
	function el(elStr) {
		var x = document.getElementById(elStr);    // get el by ID
		if (x == null) {
		   dbgErrl("el: no el by id: " + elStr);
		   return ERR_VAL;
		} else
		   return x;
	}
	/*function elStyle(el) {
	   var x = window.getComputedStyle(el, null);	// get computed style of el
	   if (x == null) {
		   dbgErrl('elStyle: no el style for: ' + el);
		   return ERR_VAL;
	   } else
		   return x;
	}*/

	function elIDstyle(elStr) {
	   var x = el(elStr);
	   if (x==ERR_VAL) {
		   dbgErrl('elIDStyle/el - el by ID returned error');
		   return ERR_VAL;
	   }
	   x = window.getComputedStyle( x, null ); 	// get computed style from el ID str

	   //dbgInfol("computed style:\n"+x.content);

	   if (x == null) {
		   dbgErrl('elIDStyle: could not get el style from elbyId: ' + elStr);
		   return ERR_VAL;
	   } else
		   return x;
	}

	function name0(elStr) {
	   var x = document.getElementsByName(elStr); // get first element of a certain name
	   if (x == null) {
		   dbgErrl("name0: no el by name: " + elStr);
		   return ERR_VAL;
	   } else if (x[0]==null) {
		   dbgErrl('name0: el exists but el[0] doesnt');
		   return ERR_VAL;
	   } else if (x[0].value==null) {
		   dbgWarnl('name0: el[0].value is null');
		   return x[0];
	   } else
		   return x[0];
	}

	// ========== DEBUGGING SECTION =========
	// Adds a fixed div at the top right with debug info
	function addDebugPane() {
		debugDiv = document.createElement('div');
		debugDiv.id = "debugDiv";

		//Some CSS stuff
		debugDiv.style.position = "fixed";
		debugDiv.style.top = "38%";
		debugDiv.style.right = "5%";
		debugDiv.style.padding = "5px";
		debugDiv.style.border = "2px solid red";
		debugDiv.style.backgroundColor = "white";
		//debugDiv.style.minWidth = "20%";
		debugDiv.style.zIndex="100";	// put it in front of other elements

		debugDiv.innerHTML = "<b><u>Debug Info</u></b><br>";
	}

	// Add info to debug div
	function dbgAdd(htmlText) {
		if (DBG && debugDiv != null)
			debugDiv.innerHTML += htmlText;
		else if (DBG) {
			var tmpDiv = document.createElement('div');
			tmpDiv.innerHTML = htmlText;
			alert('DBG info:\n\t' + tmpDiv.textContent + ' \n\n\n(Note: debugDiv is null)');
		}
	}
	function dbgAddl(htmlText) { dbgAdd(htmlText + "<br>"); }
	//function dbgFatal(htmlText) { dbgAdd('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
	//function dbgFatall(htmlText) { dbgAddl('<b style="color:' + FATALERR_COLOR + ';">Fatal Err:</b> ' + htmlText);}
	//function dbgErr(htmlText) { dbgAdd('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
	function dbgErrl(htmlText) { dbgAddl('<b style="color:' + ERR_COLOR + ';">Err:</b> ' + htmlText); }
	//function dbgWarn(htmlText) { dbgAdd('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
	function dbgWarnl(htmlText) { dbgAddl('<i style="color:' + WARN_COLOR + ';">Warning:</i> ' + htmlText); }
	//function dbgInfo(htmlText) { dbgAdd('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }
	function dbgInfol(htmlText) { dbgAddl('<i style="color:' + INFO_COLOR + ';">Info:</i> ' + htmlText); }

}, true);