| 1 | package com.longtailvideo.jwplayer.player { |
|---|
| 2 | import com.longtailvideo.jwplayer.events.MediaEvent; |
|---|
| 3 | import com.longtailvideo.jwplayer.events.PlayerEvent; |
|---|
| 4 | import com.longtailvideo.jwplayer.events.PlayerStateEvent; |
|---|
| 5 | import com.longtailvideo.jwplayer.events.PlaylistEvent; |
|---|
| 6 | import com.longtailvideo.jwplayer.events.ViewEvent; |
|---|
| 7 | import com.longtailvideo.jwplayer.model.Playlist; |
|---|
| 8 | import com.longtailvideo.jwplayer.utils.JavascriptSerialization; |
|---|
| 9 | import com.longtailvideo.jwplayer.utils.Logger; |
|---|
| 10 | import com.longtailvideo.jwplayer.utils.Strings; |
|---|
| 11 | |
|---|
| 12 | import flash.events.Event; |
|---|
| 13 | import flash.events.TimerEvent; |
|---|
| 14 | import flash.external.ExternalInterface; |
|---|
| 15 | import flash.utils.Timer; |
|---|
| 16 | |
|---|
| 17 | public class JavascriptAPI { |
|---|
| 18 | protected var _player:IPlayer; |
|---|
| 19 | protected var _playerBuffer:Number = 0; |
|---|
| 20 | protected var _playerPosition:Number = 0; |
|---|
| 21 | |
|---|
| 22 | protected var _listeners:Object; |
|---|
| 23 | |
|---|
| 24 | public function JavascriptAPI(player:IPlayer) { |
|---|
| 25 | _listeners = {}; |
|---|
| 26 | |
|---|
| 27 | _player = player; |
|---|
| 28 | _player.addEventListener(PlayerEvent.JWPLAYER_READY, playerReady); |
|---|
| 29 | |
|---|
| 30 | setupPlayerListeners(); |
|---|
| 31 | setupJSListeners(); |
|---|
| 32 | } |
|---|
| 33 | |
|---|
| 34 | /** Delay the response to PlayerReady to allow the external interface to initialize in some browsers **/ |
|---|
| 35 | private function playerReady(evt:PlayerEvent):void { |
|---|
| 36 | var timer:Timer = new Timer(50, 1); |
|---|
| 37 | timer.addEventListener(TimerEvent.TIMER_COMPLETE, function(timerEvent:TimerEvent):void { |
|---|
| 38 | var callbacks:String = _player.config.playerready ? _player.config.playerready + "," + "playerReady" : "playerReady"; |
|---|
| 39 | if (ExternalInterface.available) { |
|---|
| 40 | for each (var callback:String in callbacks.replace(/\s/,"").split(",")) { |
|---|
| 41 | try { |
|---|
| 42 | ExternalInterface.call(callback,{ |
|---|
| 43 | id:evt.id, |
|---|
| 44 | client:evt.client, |
|---|
| 45 | version:evt.version |
|---|
| 46 | }); |
|---|
| 47 | } catch (e:Error) {} |
|---|
| 48 | } |
|---|
| 49 | } |
|---|
| 50 | }); |
|---|
| 51 | timer.start(); |
|---|
| 52 | } |
|---|
| 53 | |
|---|
| 54 | protected function setupPlayerListeners():void { |
|---|
| 55 | _player.addEventListener(PlaylistEvent.JWPLAYER_PLAYLIST_ITEM, resetPosition); |
|---|
| 56 | _player.addEventListener(MediaEvent.JWPLAYER_MEDIA_TIME, updatePosition); |
|---|
| 57 | _player.addEventListener(MediaEvent.JWPLAYER_MEDIA_BUFFER, updateBuffer); |
|---|
| 58 | } |
|---|
| 59 | |
|---|
| 60 | protected function resetPosition(evt:PlaylistEvent):void { |
|---|
| 61 | _playerPosition = 0; |
|---|
| 62 | _playerBuffer = 0; |
|---|
| 63 | } |
|---|
| 64 | |
|---|
| 65 | protected function updatePosition(evt:MediaEvent):void { |
|---|
| 66 | _playerPosition = evt.position; |
|---|
| 67 | } |
|---|
| 68 | |
|---|
| 69 | protected function updateBuffer(evt:MediaEvent):void { |
|---|
| 70 | _playerBuffer = evt.bufferPercent; |
|---|
| 71 | } |
|---|
| 72 | |
|---|
| 73 | protected function setupJSListeners():void { |
|---|
| 74 | try { |
|---|
| 75 | // Event handlers |
|---|
| 76 | ExternalInterface.addCallback("addEventListener", js_addEventListener); |
|---|
| 77 | ExternalInterface.addCallback("removeEventListener", js_removeEventListener); |
|---|
| 78 | |
|---|
| 79 | // Getters |
|---|
| 80 | ExternalInterface.addCallback("getBandwidth", js_getBandwidth); |
|---|
| 81 | ExternalInterface.addCallback("getBuffer", js_getBuffer); |
|---|
| 82 | ExternalInterface.addCallback("getDuration", js_getDuration); |
|---|
| 83 | ExternalInterface.addCallback("getFullscreen", js_getFullscreen); |
|---|
| 84 | ExternalInterface.addCallback("getHeight", js_getHeight); |
|---|
| 85 | ExternalInterface.addCallback("getLevel", js_getLevel); |
|---|
| 86 | ExternalInterface.addCallback("getLockState", js_getLockState); |
|---|
| 87 | ExternalInterface.addCallback("getMute", js_getMute); |
|---|
| 88 | // ExternalInterface.addCallback("getPlaylist", js_getPlaylist); // This is handled by the compatibility API |
|---|
| 89 | ExternalInterface.addCallback("getPosition", js_getPosition); |
|---|
| 90 | ExternalInterface.addCallback("getState", js_getState); |
|---|
| 91 | ExternalInterface.addCallback("getWidth", js_getWidth); |
|---|
| 92 | ExternalInterface.addCallback("getVersion", js_getVersion); |
|---|
| 93 | ExternalInterface.addCallback("getVolume", js_getVolume); |
|---|
| 94 | |
|---|
| 95 | // Player API Calls |
|---|
| 96 | ExternalInterface.addCallback("play", js_play); |
|---|
| 97 | ExternalInterface.addCallback("pause", js_pause); |
|---|
| 98 | ExternalInterface.addCallback("stop", js_stop); |
|---|
| 99 | ExternalInterface.addCallback("seek", js_seek); |
|---|
| 100 | ExternalInterface.addCallback("load", js_load); |
|---|
| 101 | ExternalInterface.addCallback("playlistItem", js_playlistItem); |
|---|
| 102 | ExternalInterface.addCallback("playlistNext", js_playlistNext); |
|---|
| 103 | ExternalInterface.addCallback("playlistPrev", js_playlistPrev); |
|---|
| 104 | ExternalInterface.addCallback("mute", js_mute); |
|---|
| 105 | ExternalInterface.addCallback("volume", js_volume); |
|---|
| 106 | |
|---|
| 107 | |
|---|
| 108 | } catch(e:Error) { |
|---|
| 109 | Logger.log("Could not initialize JavaScript API: " + e.message); |
|---|
| 110 | } |
|---|
| 111 | |
|---|
| 112 | } |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | /*********************************************** |
|---|
| 116 | ** EVENT LISTENERS ** |
|---|
| 117 | ***********************************************/ |
|---|
| 118 | |
|---|
| 119 | private function js_addEventListener(eventType:String, callback:String):void { |
|---|
| 120 | if (!_listeners[eventType]) { |
|---|
| 121 | _listeners[eventType] = []; |
|---|
| 122 | _player.addEventListener(eventType, listenerCallback); |
|---|
| 123 | } |
|---|
| 124 | (_listeners[eventType] as Array).push(callback); |
|---|
| 125 | } |
|---|
| 126 | |
|---|
| 127 | private function js_removeEventListener(eventType:String, callback:String):void { |
|---|
| 128 | var callbacks:Array = _listeners[eventType]; |
|---|
| 129 | if (callbacks) { |
|---|
| 130 | var callIndex:Number = callbacks.indexOf(callback); |
|---|
| 131 | if (callIndex > -1) { |
|---|
| 132 | callbacks.splice(callIndex, 1); |
|---|
| 133 | } |
|---|
| 134 | } |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | |
|---|
| 138 | |
|---|
| 139 | private function listenerCallback(evt:PlayerEvent):void { |
|---|
| 140 | var args:Object; |
|---|
| 141 | |
|---|
| 142 | if (evt is MediaEvent) |
|---|
| 143 | args = listnerCallbackMedia(evt as MediaEvent); |
|---|
| 144 | else if (evt is PlaylistEvent) |
|---|
| 145 | args = listenerCallbackState(evt as PlayerStateEvent); |
|---|
| 146 | else if (evt is ViewEvent && (evt as ViewEvent).data != null) |
|---|
| 147 | args['data'] = (evt as ViewEvent).data; |
|---|
| 148 | |
|---|
| 149 | var callbacks:Array = _listeners[evt.type] as Array; |
|---|
| 150 | |
|---|
| 151 | if (callbacks) { |
|---|
| 152 | for each (var call:String in callbacks) { |
|---|
| 153 | ExternalInterface.call(call, args); |
|---|
| 154 | } |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | } |
|---|
| 158 | |
|---|
| 159 | private function merge(obj1:Object, obj2:Object):Object { |
|---|
| 160 | var newObj:Object = {}; |
|---|
| 161 | |
|---|
| 162 | for (var key:String in obj1) { |
|---|
| 163 | newObj[key] = obj1[key]; |
|---|
| 164 | } |
|---|
| 165 | |
|---|
| 166 | for (key in obj2) { |
|---|
| 167 | newObj[key] = obj2[key]; |
|---|
| 168 | } |
|---|
| 169 | |
|---|
| 170 | return newObj; |
|---|
| 171 | } |
|---|
| 172 | |
|---|
| 173 | private function listnerCallbackMedia(evt:MediaEvent):Object { |
|---|
| 174 | switch(evt.type) { |
|---|
| 175 | case MediaEvent.JWPLAYER_MEDIA_BUFFER: |
|---|
| 176 | return { |
|---|
| 177 | bufferPercent: evt.bufferPercent, |
|---|
| 178 | offset: evt.offset, |
|---|
| 179 | duration: evt.duration, |
|---|
| 180 | position: evt.position |
|---|
| 181 | }; |
|---|
| 182 | break; |
|---|
| 183 | case MediaEvent.JWPLAYER_MEDIA_TIME: |
|---|
| 184 | return { |
|---|
| 185 | offset: evt.offset, |
|---|
| 186 | duration: evt.duration, |
|---|
| 187 | position: evt.position |
|---|
| 188 | }; |
|---|
| 189 | break; |
|---|
| 190 | case MediaEvent.JWPLAYER_MEDIA_VOLUME: |
|---|
| 191 | return { |
|---|
| 192 | volume: evt.volume |
|---|
| 193 | }; |
|---|
| 194 | break; |
|---|
| 195 | case MediaEvent.JWPLAYER_MEDIA_MUTE: |
|---|
| 196 | return { |
|---|
| 197 | mute: evt.mute |
|---|
| 198 | }; |
|---|
| 199 | break; |
|---|
| 200 | case MediaEvent.JWPLAYER_MEDIA_ERROR: |
|---|
| 201 | return { |
|---|
| 202 | message: evt.mute |
|---|
| 203 | }; |
|---|
| 204 | break; |
|---|
| 205 | case MediaEvent.JWPLAYER_MEDIA_META: |
|---|
| 206 | Logger.log("got metadata: " + Strings.print_r(evt.metadata)); |
|---|
| 207 | |
|---|
| 208 | return {metadata: evt.metadata}; |
|---|
| 209 | break; |
|---|
| 210 | } |
|---|
| 211 | return {}; |
|---|
| 212 | } |
|---|
| 213 | |
|---|
| 214 | |
|---|
| 215 | private function listenerCallbackState(evt:PlayerStateEvent):Object { |
|---|
| 216 | if (evt.type == PlayerStateEvent.JWPLAYER_PLAYER_STATE) { |
|---|
| 217 | return { newstate: evt.newstate, oldstate: evt.oldstate }; |
|---|
| 218 | } else return {}; |
|---|
| 219 | } |
|---|
| 220 | |
|---|
| 221 | |
|---|
| 222 | |
|---|
| 223 | /*********************************************** |
|---|
| 224 | ** GETTERS ** |
|---|
| 225 | ***********************************************/ |
|---|
| 226 | |
|---|
| 227 | private function js_getBandwidth():Number { |
|---|
| 228 | return _player.config.bandwidth; |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | private function js_getBuffer():Number { |
|---|
| 232 | return _playerBuffer; |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | private function js_getDuration():Number { |
|---|
| 236 | return _player.playlist.currentItem.duration; |
|---|
| 237 | } |
|---|
| 238 | |
|---|
| 239 | private function js_getFullscreen():Boolean { |
|---|
| 240 | return _player.config.fullscreen; |
|---|
| 241 | } |
|---|
| 242 | |
|---|
| 243 | private function js_getHeight():Number { |
|---|
| 244 | return _player.config.height; |
|---|
| 245 | } |
|---|
| 246 | |
|---|
| 247 | private function js_getLevel():Number { |
|---|
| 248 | return _player.playlist.currentItem.currentLevel; |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | private function js_getLockState():Boolean { |
|---|
| 252 | return _player.locked; |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | private function js_getMute():Boolean { |
|---|
| 256 | return _player.config.mute; |
|---|
| 257 | } |
|---|
| 258 | |
|---|
| 259 | private function js_getPlaylist():Array { |
|---|
| 260 | return JavascriptSerialization.playlistToArray(_player.playlist); |
|---|
| 261 | } |
|---|
| 262 | |
|---|
| 263 | private function js_getPosition():Number { |
|---|
| 264 | return _playerPosition; |
|---|
| 265 | } |
|---|
| 266 | |
|---|
| 267 | private function js_getState():String { |
|---|
| 268 | return _player.state; |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | private function js_getWidth():Number { |
|---|
| 272 | return _player.config.width; |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | private function js_getVersion():String { |
|---|
| 276 | return _player.version; |
|---|
| 277 | } |
|---|
| 278 | |
|---|
| 279 | private function js_getVolume():Number { |
|---|
| 280 | return _player.config.volume; |
|---|
| 281 | } |
|---|
| 282 | |
|---|
| 283 | /*********************************************** |
|---|
| 284 | ** PLAYBACK ** |
|---|
| 285 | ***********************************************/ |
|---|
| 286 | |
|---|
| 287 | private function js_play(playstate:*=null):void { |
|---|
| 288 | if (playstate != null && playstate != "") { |
|---|
| 289 | if ((playstate is Boolean && playstate === true) || String(playstate).toLowerCase() == "true") { |
|---|
| 290 | _player.play(); |
|---|
| 291 | } else { |
|---|
| 292 | Logger.log("Pausing player: " + typeof playstate); |
|---|
| 293 | _player.pause(); |
|---|
| 294 | } |
|---|
| 295 | } else { |
|---|
| 296 | playToggle(); |
|---|
| 297 | } |
|---|
| 298 | } |
|---|
| 299 | |
|---|
| 300 | |
|---|
| 301 | private function js_pause():void { |
|---|
| 302 | playToggle(); |
|---|
| 303 | } |
|---|
| 304 | |
|---|
| 305 | private function playToggle():void { |
|---|
| 306 | if (_player.state == PlayerState.IDLE || _player.state == PlayerState.PAUSED) { |
|---|
| 307 | _player.play(); |
|---|
| 308 | } else { |
|---|
| 309 | _player.pause(); |
|---|
| 310 | } |
|---|
| 311 | } |
|---|
| 312 | |
|---|
| 313 | private function js_stop():void { |
|---|
| 314 | _player.stop(); |
|---|
| 315 | } |
|---|
| 316 | |
|---|
| 317 | private function js_seek(position:Number=0):void { |
|---|
| 318 | _player.seek(position); |
|---|
| 319 | } |
|---|
| 320 | |
|---|
| 321 | private function js_load(toLoad:*):void { |
|---|
| 322 | _player.load(toLoad); |
|---|
| 323 | } |
|---|
| 324 | |
|---|
| 325 | private function js_playlistItem(item:Number):void { |
|---|
| 326 | _player.playlistItem(item); |
|---|
| 327 | } |
|---|
| 328 | |
|---|
| 329 | private function js_playlistNext():void { |
|---|
| 330 | _player.playlistNext(); |
|---|
| 331 | } |
|---|
| 332 | |
|---|
| 333 | private function js_playlistPrev():void { |
|---|
| 334 | _player.playlistPrev(); |
|---|
| 335 | } |
|---|
| 336 | |
|---|
| 337 | private function js_mute(mutestate:*=null):void { |
|---|
| 338 | if (mutestate is Boolean) { |
|---|
| 339 | if (Boolean(mutestate)) { |
|---|
| 340 | _player.mute(true); |
|---|
| 341 | } else { |
|---|
| 342 | _player.mute(false); |
|---|
| 343 | } |
|---|
| 344 | } else { |
|---|
| 345 | _player.mute(!_player.config.mute); |
|---|
| 346 | } |
|---|
| 347 | } |
|---|
| 348 | |
|---|
| 349 | private function js_volume(volume:Number):void { |
|---|
| 350 | _player.volume(volume); |
|---|
| 351 | } |
|---|
| 352 | |
|---|
| 353 | |
|---|
| 354 | } |
|---|
| 355 | |
|---|
| 356 | } |
|---|