1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2008, 2009, 2010, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Alerts of an event by playing a sound. 26 * @private 27 */ 28 ZmSoundAlert = function() { 29 this.html5AudioEnabled = ZmSoundAlert.isHtml5AudioEnabled(); 30 this.enabled = this.html5AudioEnabled || AjxPluginDetector.detectQuickTime() || AjxPluginDetector.detectWindowsMedia(); 31 if (this.enabled) { 32 var element = this._element = document.createElement("DIV"); 33 element.style.position = 'relative'; 34 element.style.top = '-1000px'; 35 element.style.left = '-1000px'; 36 document.body.appendChild(this._element); 37 } else { 38 DBG.println("No QuickTime or Windows Media plugin detected. Sound alerts are disabled.") 39 } 40 }; 41 42 //this tests for wav file support. we might want to refactor it for various file types if needed later 43 ZmSoundAlert.isHtml5AudioEnabled = 44 function() { 45 46 try { 47 var audio = new Audio(""); 48 if (!audio.canPlayType) { 49 return false; 50 } 51 } 52 catch (e) { 53 return false; 54 } 55 56 // canPlayType(type) returns: "", "no", "maybe" or "probably" 57 var canPlayWav = audio.canPlayType("audio/wav"); 58 return (canPlayWav !== "no" && canPlayWav !== ""); 59 60 } 61 62 ZmSoundAlert.prototype.toString = 63 function() { 64 return "ZmSoundAlert"; 65 }; 66 67 ZmSoundAlert.getInstance = 68 function() { 69 return ZmSoundAlert.INSTANCE = ZmSoundAlert.INSTANCE || new ZmSoundAlert(); 70 }; 71 72 ZmSoundAlert.prototype.start = 73 function(soundFile) { 74 if (!this.enabled) { 75 return; 76 } 77 78 var time = new Date().getTime(); 79 if (this._lastTime && ((time - this._lastTime) < 5000)) { 80 return; 81 } 82 this._lastTime = time; 83 84 soundFile = soundFile || "/public/sounds/im/alert.wav"; 85 var url = appContextPath + soundFile; 86 87 var html; 88 if (this.html5AudioEnabled) { 89 html = '<audio src="' + url + '" autoplay="yes"></audio>'; 90 } 91 else { 92 var embedId = Dwt.getNextId(); 93 var htmlArr = [ 94 "<object CLASSID='CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6' type='audio/wav'>", 95 "<param name='url' value='", url, "'>", 96 "<param name='autostart' value='true'>", 97 "<param name='controller' value='true'>", 98 "<embed id='", embedId, "' src='", url, "' controller='false' autostart='true' type='audio/wav'/>", 99 "</object>" 100 ]; 101 html = htmlArr.join(""); 102 } 103 this._element.innerHTML = html; 104 105 if (!this.html5AudioEnabled && AjxEnv.isFirefox && AjxEnv.isWindows) { 106 // The quicktime plugin steals focus and breaks our keyboard nav. 107 // The best workaround I've found for this is to blur the embed 108 // element, and that only works after the sound plays. 109 // 110 // Unfortunately it seems that on a slow connection this prevents 111 // the sound from playing. I'm hoping this is less bad than killing 112 // keyboard focus. 113 // 114 // Mozilla bug: https://bugzilla.mozilla.org/show_bug.cgi?id=78414 115 if (this._blurActionId) { 116 AjxTimedAction.cancelAction(this._blurActionId); 117 this._blurActionId = null; 118 } 119 this._blurEmbedTimer(embedId, 0); 120 } 121 }; 122 123 ZmSoundAlert.prototype._blurEmbedTimer = 124 function(embedId, tries) { 125 var action = new AjxTimedAction(this, this._blurEmbed, [embedId, tries]); 126 this._blurActionId = AjxTimedAction.scheduleAction(action, 500); 127 }; 128 129 ZmSoundAlert.prototype._blurEmbed = 130 function(embedId, tries) { 131 this._blurActionId = null; 132 133 // Take focus from the embed. 134 var embedEl = document.getElementById(embedId); 135 if (embedEl && embedEl.blur) { 136 embedEl.blur(); 137 } 138 139 // Force focus to the keyboard manager's focus obj. 140 var focusObj = appCtxt.getKeyboardMgr().getFocusObj(); 141 if (focusObj && focusObj.focus) { 142 focusObj.focus(); 143 } 144 145 // Repeat hack. 146 if (tries < 2) { 147 this._blurEmbedTimer(embedId, tries + 1); 148 } 149 }; 150