1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2007, 2008, 2009, 2010, 2012, 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) 2007, 2008, 2009, 2010, 2012, 2013, 2014, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Creates a sound plugin control. 26 * @constructor 27 * @class 28 * This class represents a widget that plays sounds. It uses a plugin such as Quick Time 29 * or Windows Media to play the sounds and to display player controls. Do not invoke the 30 * constructor directly. Instead use the create() method, which will choose the right 31 * concrete class based on available plugins. 32 * 33 * @param {hash} params a hash of parameters 34 * @param {DwtControl} params.parent the parent widget 35 * @param {number} params.width the width of player 36 * @param {number} params.height the height of player 37 * @param {number} params.volume volume on a scale of 0 - {@link DwtSoundPlugin.MAX_VOLUME} 38 * @param {string} params.url {String} the sound url 39 * @param {boolean} [params.offscreen] {Boolean} if <code>true</code>, the player is initially offscreen. Use an appropriate position style 40 * if you set this to <code>true</code>. (This reduces flicker, and a tendency for the QT player 41 * to float in the wrong place when it's first created) 42 * @param {string} [params.className] the CSS class 43 * @param {constant} [params.posStyle=DwtControl.STATIC_STYLE] the positioning style (see {@link DwtControl}) 44 * 45 * @extends DwtControl 46 */ 47 DwtSoundPlugin = function(params) { 48 if (arguments.length == 0) return; 49 params.className = params.className || "DwtSoundPlugin"; 50 DwtControl.call(this, {parent:params.parent, className:params.className, posStyle:params.posStyle}); 51 this._width = params.width || 200; 52 this._height = params.height || 18; 53 if (params.offscreen) { 54 this.setLocation(Dwt.LOC_NOWHERE, Dwt.LOC_NOWHERE); 55 } 56 }; 57 58 DwtSoundPlugin.prototype = new DwtControl; 59 DwtSoundPlugin.prototype.constructor = DwtSoundPlugin; 60 61 /** 62 * Defines the "max" volume. 63 */ 64 DwtSoundPlugin.MAX_VOLUME = 256; 65 66 // Status codes. 67 DwtSoundPlugin.WAITING = 1; 68 DwtSoundPlugin.LOADING = 2; 69 DwtSoundPlugin.PLAYABLE = 3; 70 DwtSoundPlugin.ERROR = 4; 71 72 /** 73 * Factory method. Creates an appropriate sound player for whatever plugins are or are not installed. 74 * 75 * @param {hash} params a hash of parameters 76 * @param {DwtControl} params.parent the parent widget 77 * @param {number} params.width the width of player 78 * @param {number} params.height the height of player 79 * @param {number} params.volume volume on a scale of 0 - {@link DwtSoundPlugin.MAX_VOLUME} 80 * @param {string} params.url {String} the sound url 81 * @param {boolean} [params.offscreen] {Boolean} if <code>true</code>, the player is initially offscreen. Use an appropriate position style 82 * if you set this to <code>true</code>. (This reduces flicker, and a tendency for the QT player 83 * to float in the wrong place when it's first created) 84 * @param {string} [params.className] the CSS class 85 * @param {constant} [params.posStyle=DwtControl.STATIC_STYLE] the positioning style (see {@link DwtControl}) 86 */ 87 DwtSoundPlugin.create = 88 function(params) { 89 var pluginClass = this._getPluginClass(); 90 DBG.println("DwtSoundPlugin.create class= " + pluginClass.prototype.toString() + " url=" + params.url); 91 return new pluginClass(params); 92 }; 93 94 /** 95 * Checks if the plugin is missing. 96 * 97 * @return {boolean} <code>true</code> if plugin is missing 98 */ 99 DwtSoundPlugin.isPluginMissing = 100 function() { 101 var pluginClass = this._getPluginClass(); 102 return pluginClass._pluginMissing; 103 }; 104 105 /** 106 * Checks if scripting is broken. 107 * 108 * @return {boolean} <code>true</code> if scripting is broken 109 */ 110 DwtSoundPlugin.isScriptingBroken = 111 function() { 112 var pluginClass = this._getPluginClass(); 113 return pluginClass._isScriptingBroken; 114 }; 115 116 DwtSoundPlugin._getPluginClass = 117 function() { 118 if (!DwtSoundPlugin._pluginClass) { 119 if (AjxEnv.isIE) { 120 var version; 121 try { 122 version = AjxPluginDetector.getQuickTimeVersion(); 123 } catch (e) { 124 } 125 126 //Use Quicktime for IE 8, as IE8 windows media player does not work with httpOnly cookie attribute 127 //TODO: Currently if Quick time is not installed, users will not get any prompt to install it. 128 if (AjxEnv.isIE8 && version) { 129 DwtSoundPlugin._pluginClass = DwtQTSoundPlugin; 130 } else if (AjxPluginDetector.detectWindowsMedia()) { 131 //IE8 windows media player does not work with httpOnly cookie attribute 132 DwtSoundPlugin._pluginClass = DwtWMSoundPlugin; 133 } 134 135 } 136 else if (AjxEnv.isSafari5up && !AjxEnv.isChrome && !AjxEnv.isWindows) { 137 //safari quicktime does not work with httpOnly cookie attribute 138 DwtSoundPlugin._pluginClass = DwtHtml5SoundPlugin; 139 } 140 else { 141 var version = AjxPluginDetector.getQuickTimeVersion(); 142 if (version) { 143 DBG.println("DwtSoundPlugin: QuickTime version=" + version); 144 if (DwtQTSoundPlugin.checkVersion(version) && DwtQTSoundPlugin.checkScripting()) { 145 DwtSoundPlugin._pluginClass = DwtQTSoundPlugin; 146 } else { 147 DwtSoundPlugin._pluginClass = DwtQTBrokenSoundPlugin; 148 } 149 } else { 150 if (window.DBG && !DBG.isDisabled()) { 151 DBG.println("DwtSoundPlugin: unable to get QuickTime version. Checking if QuickTime is installed at all..."); 152 AjxPluginDetector.detectQuickTime(); // Called only for logging purposes. 153 } 154 } 155 } 156 if (!DwtSoundPlugin._pluginClass) { 157 DwtSoundPlugin._pluginClass = DwtMissingSoundPlugin; 158 } 159 DBG.println("DwtSoundPlugin: plugin class = " + DwtSoundPlugin._pluginClass.prototype.toString()); 160 } 161 return DwtSoundPlugin._pluginClass; 162 }; 163 164 // "Abstract" methods. 165 /** 166 * Plays the sound. 167 * 168 */ 169 DwtSoundPlugin.prototype.play = 170 function() { 171 }; 172 173 /** 174 * Pauses the sound. 175 * 176 */ 177 DwtSoundPlugin.prototype.pause = 178 function() { 179 }; 180 181 /** 182 * Rewinds the sound. 183 * 184 */ 185 DwtSoundPlugin.prototype.rewind = 186 function() { 187 }; 188 189 /** 190 * Sets the current time in milliseconds. 191 * 192 * @param {number} time the time (in milliseconds) 193 */ 194 DwtSoundPlugin.prototype.setTime = 195 function(time) { 196 }; 197 198 /** 199 * Sets the volume. 200 * 201 * @param {number} volume the volume 202 * 203 * @see DwtSoundPlugin.MAX_VOLUME 204 */ 205 DwtSoundPlugin.prototype.setVolume = 206 function(volume) { 207 }; 208 209 /* 210 * Fills in the event with the following status information: 211 * - status, a constant representing the loaded state of the sound 212 * - duration, the length of the sound 213 * - time, the current time of the sound 214 * Returns true to continue monitoring status 215 */ 216 DwtSoundPlugin.prototype._resetEvent = 217 function(event) { 218 return false; 219 }; 220 221 DwtSoundPlugin.prototype.dispose = 222 function() { 223 DwtControl.prototype.dispose.call(this); 224 this._ignoreStatus(); 225 }; 226 227 /** 228 * Adds a change listener to monitor the status of the sound being played. 229 * The listener will be passed an event object with the following fields: 230 * <ul> 231 * <li>status, a constant representing the loaded state of the sound</li> 232 * <li>duration, the length of the sound</li> 233 * <li>time, the current time of the sound</li> 234 * </ul> 235 * 236 * @param {AjxListener} listener the listener 237 */ 238 DwtSoundPlugin.prototype.addChangeListener = 239 function(listener) { 240 this.addListener(DwtEvent.ONCHANGE, listener); 241 this._monitorStatus(); 242 }; 243 244 DwtSoundPlugin.prototype._monitorStatus = 245 function() { 246 if (this.isListenerRegistered(DwtEvent.ONCHANGE)) { 247 if (!this._statusAction) { 248 this._statusAction = new AjxTimedAction(this, this._checkStatus); 249 } 250 this._statusActionId = AjxTimedAction.scheduleAction(this._statusAction, 250); 251 } 252 }; 253 254 DwtSoundPlugin.prototype._ignoreStatus = 255 function() { 256 if (this._statusActionId) { 257 AjxTimedAction.cancelAction(this._statusActionId); 258 } 259 }; 260 261 DwtSoundPlugin.prototype._checkStatus = 262 function() { 263 this._statusActionId = 0; 264 if (!this._changeEvent) { 265 this._changeEvent = new DwtEvent(true); 266 this._changeEvent.dwtObj = this; 267 } 268 var keepChecking = this._resetEvent(this._changeEvent); 269 this.notifyListeners(DwtEvent.ONCHANGE, this._changeEvent); 270 if (keepChecking) { 271 this._monitorStatus(); 272 } 273 }; 274 275 DwtSoundPlugin.prototype._createQTHtml = 276 function(params) { 277 // Adjust volume because the html parameter is in [0 - 100], while the 278 // javascript method takes [0 - 256]. 279 var volume = params.volume * 100 / 256; 280 var html = [ 281 "<embed classid='clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B' ", 282 "id='", this._playerId, 283 "' width='", this._width, 284 "' height='", this._height, 285 "' src='", params.url, 286 "' volume='", volume, 287 "' enablejavascript='true' type='audio/wav'/>" 288 ]; 289 this.getHtmlElement().innerHTML = html.join(""); 290 }; 291 292 /** 293 * Sound player that goes through the QuickTime (QT) plugin. 294 * @class 295 * Some useful references when dealing with quick time: 296 * <ul> 297 * <li>Quick Time script reference 298 * <a href="http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_JavaScript/bQTScripting_JavaScri_Document/chapter_1000_section_5.html" target="_blank">http://developer.apple.com/documentation/QuickTime/Conceptual/QTScripting_JavaScript/bQTScripting_JavaScri_Document/chapter_1000_section_5.html</a> 299 * </li> 300 * <li>Quick Time embed tag attributes tutorial 301 * <a href="http://www.apple.com/quicktime/tutorials/embed2.html" target="_blank">http://www.apple.com/quicktime/tutorials/embed2.html</a> 302 * </li> 303 * </ul> 304 * 305 * @param {hash} params a hash of parameters 306 * 307 * @extends DwtSoundPlugin 308 * 309 * @private 310 */ 311 DwtQTSoundPlugin = function(params) { 312 if (arguments.length == 0) return; 313 params.className = params.className || "DwtSoundPlugin"; 314 DwtSoundPlugin.call(this, params); 315 316 this._playerId = Dwt.getNextId(); 317 this._createHtml(params); 318 }; 319 320 DwtQTSoundPlugin.prototype = new DwtSoundPlugin; 321 DwtQTSoundPlugin.prototype.constructor = DwtQTSoundPlugin; 322 323 DwtQTSoundPlugin.prototype.toString = 324 function() { 325 return "DwtQTSoundPlugin"; 326 }; 327 328 /** 329 * Checks the QuickTime version. 330 * 331 * @param {array} version the version as an array (for example: 7.1.6 is [7, 1, 6] ) 332 * @return {boolean} <code>true</code> if version is OK 333 */ 334 DwtQTSoundPlugin.checkVersion = 335 function(version) { 336 if (AjxEnv.isFirefox) { 337 // Quicktime 7.1.6 introduced a nasty bug in Firefox that can't be worked around by 338 // the checkScripting() routine below. I'm going to disable all QT versions that 339 // are greater than 7.1.6. We should change this check when QT is fixed. More info: 340 // http://lists.apple.com/archives/quicktime-users/2007/May/msg00016.html 341 var badVersion = "716"; 342 var currentVersion = ""; 343 for(var i = 0; i < version.length; i++) { 344 currentVersion += version[i]; 345 } 346 return currentVersion != badVersion; 347 } 348 return true; 349 }; 350 351 /** 352 * Checks scripting. 353 * 354 * @return {boolean} <code>true</code> if scripting is OK 355 */ 356 DwtQTSoundPlugin.checkScripting = 357 function() { 358 var success = false; 359 var shell = DwtControl.fromElementId(window._dwtShellId); 360 var args = { 361 parent: shell, 362 width: 200, 363 height: 16, 364 offscreen: true, 365 posStyle: DwtControl.RELATIVE_STYLE, 366 url: "/public/sounds/im/alert.wav", // Not a valid url. 367 volume: 0 368 }; 369 var test = new DwtQTSoundPlugin(args); 370 try { 371 var element = test._getPlayer(); 372 success = element.GetQuickTimeVersion && element.GetQuickTimeVersion(); 373 if (!success) { 374 DBG.println("The QuickTime plugin in this browser does not support JavaScript."); 375 } 376 } catch (e) { 377 DBG.println("An exception was thrown while checking QuickTime: " + e.toString()); 378 } finally { 379 test.dispose(); 380 } 381 return success; 382 }; 383 384 DwtQTSoundPlugin.prototype.play = 385 function() { 386 var player = this._getPlayer(); 387 try { 388 player.Play(); 389 }catch(e) { 390 } 391 this._monitorStatus(); 392 }; 393 394 DwtQTSoundPlugin.prototype.pause = 395 function() { 396 try { 397 var player = this._getPlayer(); 398 player.Stop(); 399 } catch (e) { 400 // Annoying: QT gets all messed up if you stop it before it's loaded. 401 // I could try and do more here...check the status, if it's waiting then 402 // set some flag and then if I somehow knew when the sound loaded, I 403 // could prevent it from playing. 404 DBG.println("Failed to stop QuickTime player."); 405 } 406 }; 407 408 DwtQTSoundPlugin.prototype.rewind = 409 function() { 410 try { 411 var player = this._getPlayer(); 412 player.Rewind(); 413 } catch (e) { 414 // Grrr. Same problem here as described in pause(); 415 DBG.println("Failed to rewind QuickTime player."); 416 } 417 }; 418 419 DwtQTSoundPlugin.prototype.setTime = 420 function(time) { 421 var player = this._getPlayer(); 422 try { 423 var scale = 1000 / player.GetTimeScale(); // Converts to milliseconds. 424 player.SetTime(time / scale); 425 } catch (e) { 426 // Grrr. Same problem here as described in pause(); 427 DBG.println("Failed to rewind QuickTime player."); 428 } 429 }; 430 431 DwtQTSoundPlugin.prototype.setVolume = 432 function(volume) { 433 var player = this._getPlayer(); 434 player.SetVolume(volume); 435 }; 436 437 DwtQTSoundPlugin.prototype._resetEvent = 438 function(event) { 439 var keepChecking = true; 440 var player = this._getPlayer(); 441 event.finished = false; 442 var valid = false; 443 if (player) { 444 var status = player.GetPluginStatus(); 445 switch (status) { 446 case "Waiting": 447 event.status = DwtSoundPlugin.LOADING; 448 break; 449 case "Loading": 450 event.status = DwtSoundPlugin.LOADING; 451 break; 452 case "Playable": 453 case "Complete": 454 valid = true; 455 event.status = DwtSoundPlugin.PLAYABLE; 456 break; 457 default : 458 event.status = DwtSoundPlugin.ERROR; 459 event.errorDetail = status; 460 keepChecking = false; 461 break; 462 } 463 } 464 if (valid) { 465 var scale = 1000 / player.GetTimeScale(); // Converts to milliseconds. 466 event.time = player.GetTime() * scale; 467 event.duration = player.GetDuration() * scale; 468 } else { 469 event.status = DwtSoundPlugin.WAITING; 470 event.time = 0; 471 event.duration = 100; 472 } 473 if (event.status == DwtSoundPlugin.PLAYABLE && event.time == event.duration) { 474 event.time = 0; 475 event.finished = true; 476 keepChecking = false; 477 } 478 return keepChecking; 479 }; 480 481 DwtQTSoundPlugin.prototype._createHtml = 482 function(params) { 483 this._createQTHtml(params); 484 }; 485 486 DwtQTSoundPlugin.prototype._getPlayer = 487 function() { 488 return document.getElementById(this._playerId); 489 }; 490 491 ////////////////////////////////////////////////////////////////////////////// 492 // Sound player that uses the QuickTime (QT) plugin, but does not 493 // make script calls to the plugin. This handles the bad quicktime 494 // installs all over the place. 495 // 496 ////////////////////////////////////////////////////////////////////////////// 497 DwtQTBrokenSoundPlugin = function(params) { 498 if (arguments.length == 0) return; 499 params.className = params.className || "DwtSoundPlugin"; 500 DwtSoundPlugin.call(this, params); 501 502 this._playerId = Dwt.getNextId(); 503 this._createHtml(params); 504 }; 505 506 DwtQTBrokenSoundPlugin.prototype = new DwtSoundPlugin; 507 DwtQTBrokenSoundPlugin.prototype.constructor = DwtQTBrokenSoundPlugin; 508 509 DwtQTBrokenSoundPlugin.prototype.toString = 510 function() { 511 return "DwtQTBrokenSoundPlugin"; 512 }; 513 514 DwtQTBrokenSoundPlugin._isScriptingBroken = true; 515 516 DwtQTBrokenSoundPlugin.prototype._resetEvent = 517 function(event) { 518 // Make up some fake event data 519 event.time = 0; 520 event.duration = 100; 521 event.status = DwtSoundPlugin.PLAYABLE; 522 event.finished = true; // Allows messages to be marked as read. 523 return false; // Stop checking status. 524 }; 525 526 DwtQTBrokenSoundPlugin.prototype._createHtml = 527 function(params) { 528 this._createQTHtml(params); 529 }; 530 531 /** 532 * Sound player that goes through the HTML5 audio tag 533 * @class 534 * This class provides and HTML5 audio widget 535 * 536 * @param {hash} params a hash of parameters 537 * 538 * @extends DwtSoundPlugin 539 * 540 * @private 541 */ 542 DwtHtml5SoundPlugin = function(params) { 543 if (arguments.length == 0) return; 544 params.className = params.className || "DwtSoundPlugin"; 545 DwtSoundPlugin.call(this, params); 546 547 this._playerId = Dwt.getNextId(); 548 this._retryLoadAudio = 0; 549 this._createHtml(params); 550 }; 551 552 DwtHtml5SoundPlugin.prototype = new DwtSoundPlugin; 553 DwtHtml5SoundPlugin.prototype.constructor = DwtHtml5SoundPlugin; 554 555 DwtHtml5SoundPlugin.prototype.toString = 556 function() { 557 return "DwtHtml5SoundPlugin"; 558 }; 559 560 DwtHtml5SoundPlugin.prototype._createHtml = 561 function(params) { 562 if (AjxEnv.isSafari) { 563 AjxRpc.invoke(null, params.url, { 'X-Zimbra-Encoding': 'x-base64' }, 564 this._setSource.bind(this), AjxRpcRequest.HTTP_GET); 565 } else { 566 this.getHtmlElement().innerHTML = this._getAudioHtml(params.url); 567 } 568 }; 569 570 DwtHtml5SoundPlugin.prototype._getAudioHtml = 571 function(source) { 572 var html = [ 573 "<audio autoplay='yes' ", 574 "id='", this._playerId, 575 "' preload ", 576 "><source src='", source, 577 "' type='", ZmVoiceApp.audioType, "' />", 578 "</audio>" 579 ]; 580 return html.join(""); 581 }; 582 583 DwtHtml5SoundPlugin.prototype._setSource = 584 function(response) { 585 if(response.success){ 586 this.getHtmlElement().innerHTML = this._getAudioHtml('data:' + ZmVoiceApp.audioType +';base64,' + response.text); 587 } 588 }; 589 590 DwtHtml5SoundPlugin.prototype.play = 591 function() { 592 var player = this._getPlayer(); 593 if (player && player.readyState){ 594 try { 595 this._monitorStatus(); 596 player.play(); 597 } catch (ex){ 598 DBG.println("Exception in DwtHtml5SoundPlugin.prototype.play: "+ ex); 599 } 600 } 601 }; 602 603 DwtHtml5SoundPlugin.prototype.pause = 604 function() { 605 var player = this._getPlayer(); 606 if (player && player.readyState){ 607 try { 608 player.pause(); 609 } catch (ex){ 610 DBG.println("Exception in DwtHtml5SoundPlugin.prototype.pause: "+ ex); 611 } 612 } 613 }; 614 615 DwtHtml5SoundPlugin.prototype._getPlayer = 616 function() { 617 return document.getElementById(this._playerId); 618 }; 619 620 621 DwtHtml5SoundPlugin.prototype.rewind = 622 function() { 623 this.setTime(0); 624 }; 625 626 DwtHtml5SoundPlugin.prototype.setTime = 627 function(time) { 628 if (isNaN(time)){ 629 time = 0; 630 } 631 time = (time > 0) ? (time / 1000) : 0; 632 var player = this._getPlayer(); 633 try { 634 if (player && player.readyState && player.currentTime != time ){ 635 player.currentTime = time; 636 if (player.controls){ 637 player.controls.currentPosition = time; 638 } 639 } 640 } catch(ex){ 641 DBG.println("Exception in DwtHtml5SoundPlugin.prototype.setTime: "+ ex); 642 } 643 }; 644 645 DwtHtml5SoundPlugin.prototype._resetEvent = 646 function(event) { 647 var keepChecking = true; 648 var player = this._getPlayer(); 649 event.finished = false; 650 /* 651 use HTML5 canPlay event instead 652 var valid = false; 653 if (player) { 654 var status = player.duration; 655 switch (status) { 656 case "NaN": 657 event.status = DwtSoundPlugin.LOADING; 658 break; 659 default : 660 event.status = DwtSoundPlugin.PLAYABLE; 661 valid = true; 662 keepChecking = false; 663 break; 664 } 665 } */ 666 /*if (valid) { 667 var scale = 1000; // Converts to milliseconds. 668 event.time = player.currentTime * scale; 669 event.duration = player.duration * scale; 670 } */ 671 if(!valid) { 672 event.status = DwtSoundPlugin.WAITING; 673 event.time = 0; 674 event.duration = 100; 675 } 676 if (event.status == DwtSoundPlugin.PLAYABLE && event.time == event.duration) { 677 event.time = 0; 678 event.finished = true; 679 keepChecking = false; 680 } 681 return keepChecking; 682 }; 683 684 /** 685 * Adds a change listener to monitor the status of the sound being played. 686 * Handles the HTML5 event timeupdate 687 * The listener will be passed an event object with the following fields: 688 * <ul> 689 * <li>status, a constant representing the loaded state of the sound</li> 690 * <li>duration, the length of the sound</li> 691 * <li>time, the current time of the sound</li> 692 * </ul> 693 * 694 * @param {AjxListener} listener the listener 695 */ 696 DwtHtml5SoundPlugin.prototype.addChangeListener = 697 function(listener) { 698 var player = this._getPlayer(); 699 if (!player){ 700 if (this._retryLoadAudio < 10){ 701 setTimeout(this.addChangeListener.bind(this,listener), 1000); 702 } 703 this._retryLoadAudio++; 704 return; 705 } 706 var obj = this; 707 player.addEventListener("timeupdate", function(e) { 708 listener.handleEvent({time: player.currentTime * 1000, duration: player.duration * 1000, status: DwtSoundPlugin.PLAYABLE});}, false); 709 player.addEventListener("ended", function(e) { 710 player.pause(); 711 obj.setTime(0); 712 listener.obj.setFinished(); 713 }); 714 player.addEventListener("canplay", function(event) { 715 var scale = 1000; // Converts to milliseconds. 716 event.time = player.currentTime * scale; 717 event.duration = player.duration * scale; 718 player.play(); 719 }); 720 this._monitorStatus(); 721 }; 722 ////////////////////////////////////////////////////////////////////////////// 723 // Sound player that goes through the Windows Media (WM) plugin. 724 // 725 // Some useful references when dealing with wmp: 726 // Adding Windows Media to Web Pages - Adding Scripting 727 // http://msdn2.microsoft.com/en-us/library/ms983653.aspx#adding_scripting__yhbx 728 // Parameters supported by Windows Media Player 729 // http://www.mioplanet.com/rsc/embed_mediaplayer.htm 730 // WM Object Model Reference: 731 // http://msdn2.microsoft.com/en-us/library/bb249259.aspx 732 ////////////////////////////////////////////////////////////////////////////// 733 DwtWMSoundPlugin = function(params) { 734 if (arguments.length == 0) return; 735 params.className = params.className || "DwtSoundPlugin"; 736 DwtSoundPlugin.call(this, params); 737 738 this._playerId = Dwt.getNextId(); 739 this._createHtml(params); 740 }; 741 742 DwtWMSoundPlugin.prototype = new DwtSoundPlugin; 743 DwtWMSoundPlugin.prototype.constructor = DwtWMSoundPlugin; 744 745 DwtWMSoundPlugin.prototype.toString = 746 function() { 747 return "DwtWMSoundPlugin"; 748 }; 749 750 DwtWMSoundPlugin.prototype.play = 751 function() { 752 var player = this._getPlayer(); 753 player.controls.play(); 754 this._monitorStatus(); 755 }; 756 757 DwtWMSoundPlugin.prototype.pause = 758 function() { 759 var player = this._getPlayer(); 760 player.controls.pause(); 761 }; 762 763 DwtWMSoundPlugin.prototype.rewind = 764 function() { 765 this.setTime(0); 766 }; 767 768 DwtWMSoundPlugin.prototype.setTime = 769 function(time) { 770 var player = this._getPlayer(); 771 player.controls.currentPosition = time / 1000; 772 }; 773 774 DwtWMSoundPlugin.prototype.setVolume = 775 function(volume) { 776 var volume = volume * 100 / 256; 777 var player = this._getPlayer(); 778 player.settings.volume = volume; 779 }; 780 781 DwtWMSoundPlugin.prototype._resetEvent = 782 function(event) { 783 var keepChecking = true; 784 var player = this._getPlayer(); 785 var error = player.currentMedia.error; 786 if (error) { 787 event.status = DwtSoundPlugin.ERROR; 788 event.errorDetail = error.errorDescription; 789 keepChecking = false; 790 } else if (!player.controls.isAvailable("currentPosition")) { // if (!is loaded) 791 // Whatever....fake data. 792 event.status = DwtSoundPlugin.LOADING; 793 event.time = 0; 794 event.duration = 100; 795 } else { 796 event.status = DwtSoundPlugin.PLAYABLE; 797 event.time = player.controls.currentPosition * 1000; 798 event.duration = player.currentMedia.duration * 1000 || event.time + 100; // Make sure max > min in slider 799 if (!event.time) { 800 event.finished = true; 801 keepChecking = false; 802 player.close(); 803 } 804 } 805 return keepChecking; 806 }; 807 808 //TODO: Take out all the AjxEnv stuff in here, unless we find a way to use WMP in Firefox. 809 DwtWMSoundPlugin.prototype._createHtml = 810 function(params) { 811 var volume = params.volume * 100 / 256; 812 813 var html = []; 814 var i = 0; 815 if (AjxEnv.isIE) { 816 html[i++] = "<object classid='CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6' id='"; 817 html[i++] = this._playerId; 818 html[i++] = "'>"; 819 } else { 820 html[i++] = "<embed classid='CLSID:6BF52A52-394A-11d3-B153-00C04F79FAA6' id='"; 821 html[i++] = this._playerId; 822 html[i++] = "' "; 823 } 824 var pluginArgs = { 825 width: this._width, 826 height: this._height, 827 url: params.url, 828 volume: volume, 829 enablejavascript: "true" }; 830 for (var name in pluginArgs) { 831 if (AjxEnv.isIE) { 832 html[i++] = "<param name='"; 833 html[i++] = name; 834 html[i++] = "' value='"; 835 html[i++] = pluginArgs[name]; 836 html[i++] = "'/>"; 837 } else { 838 html[i++] = name; 839 html[i++] = "='"; 840 html[i++] = pluginArgs[name]; 841 html[i++] = "' "; 842 } 843 } 844 if (AjxEnv.isIE) { 845 html[i++] = "</object>"; 846 } else { 847 html[i++] = " type='application/x-mplayer2'/>"; 848 } 849 850 this.getHtmlElement().innerHTML = html.join(""); 851 DBG.printRaw(html.join("")); 852 }; 853 854 DwtWMSoundPlugin.prototype._getPlayer = 855 function() { 856 return document.getElementById(this._playerId); 857 }; 858 859 ////////////////////////////////////////////////////////////////////////////// 860 // Sound player for browsers without a known sound plugin. 861 ////////////////////////////////////////////////////////////////////////////// 862 DwtMissingSoundPlugin = function(params) { 863 if (arguments.length == 0) return; 864 params.className = params.className || "DwtSoundPlugin"; 865 DwtSoundPlugin.call(this, params); 866 867 var args = { }; 868 this.getHtmlElement().innerHTML = AjxTemplate.expand("dwt.Widgets#DwtMissingSoundPlayer", args); 869 870 this._setMouseEventHdlrs(); 871 }; 872 873 DwtMissingSoundPlugin.prototype = new DwtSoundPlugin; 874 DwtMissingSoundPlugin.prototype.constructor = DwtMissingSoundPlugin; 875 876 DwtMissingSoundPlugin.prototype.toString = 877 function() { 878 return "DwtMissingSoundPlugin"; 879 }; 880 881 DwtMissingSoundPlugin._pluginMissing = true; 882 883 DwtMissingSoundPlugin.prototype.addHelpListener = 884 function(listener) { 885 this.addListener(DwtEvent.ONMOUSEDOWN, listener); 886 }; 887