1 /* 2 * Copyright © 2009 Apple Inc. All rights reserved. 3 */ 4 5 // XXX: this should be set dynamically as needed 6 const ARCHIVES_AND_RESTORES = false; 7 8 var bookletController = new TKObject(); 9 10 bookletController.init = function () { 11 // stop current playback 12 window.iTunes.stop(); 13 14 // set up audio loop 15 this.startAudioLoop(); 16 17 // determine play position if this a movie 18 if (appData.feature.XID){ 19 appData.feature.trackObj = this.getTrack(appData.feature); 20 if (appData.feature.trackObj && appData.feature.trackObj.bookmark != 0){ 21 this.dispatchDisplayUpdates(); 22 } 23 } 24 25 // add event listeners 26 window.addEventListener("play", this, false); 27 window.addEventListener("pause", this, false); 28 window.addEventListener("videoclosed", this, false); 29 window.addEventListener("unload", this, false); 30 31 // check if we have an archive to restore from 32 this.restoreFromJSON(); 33 34 // create our navigation controller and add the home controller as the root controller 35 this.navigation = new TKNavigationController({ 36 id : 'navigation', 37 rootController : homeController, 38 delegate : this 39 }); 40 }; 41 42 bookletController.archiveToJSON = function () { 43 if (!ARCHIVES_AND_RESTORES) { 44 return; 45 } 46 // 47 var archive = { 48 controllers: {} 49 }; 50 // first, get the navigation stack of controllers 51 var controllers_stack_IDs = []; 52 for (var i = 0; i < this.navigation.controllers.length; i++) { 53 controllers_stack_IDs.push(this.navigation.controllers[i].id); 54 } 55 archive.navigationStack = controllers_stack_IDs; 56 // then archive all known controllers that aren't the navigation stack 57 for (var i in TKController.controllers) { 58 if (i == 'navigation') { 59 continue; 60 } 61 archive.controllers[i] = TKController.controllers[i].archive(); 62 } 63 // archive is complete, now save it to disk 64 window.localStorage.archive = JSON.stringify(archive); 65 }; 66 67 bookletController.restoreFromJSON = function () { 68 if (!ARCHIVES_AND_RESTORES) { 69 return; 70 } 71 // check if we have an archive to restore from 72 var json_archive = window.localStorage.archive; 73 if (json_archive === undefined) { 74 return; 75 } 76 // parse the JSON archive into a JS object that we track at all times 77 this.archive = JSON.parse(json_archive); 78 }; 79 80 bookletController.handleEvent = function(event) { 81 switch (event.type) { 82 case "play": 83 // video has started to play - stop the audio loop 84 this.stopAudioLoop(); 85 //this.dispatchDisplayUpdates(); 86 break; 87 case "pause": 88 // video has paused 89 //this.dispatchDisplayUpdates(); 90 break; 91 case "videoclosed": 92 // video has stopped - restart audio loop 93 this.startAudioLoop(); 94 this.dispatchDisplayUpdates(); 95 96 // stub to check what chapter we are on for sticky chapters 97 debug(iTunes.currentChapter); 98 break; 99 case "unload": 100 // archive our state 101 this.archiveToJSON(); 102 break; 103 default: 104 debug("Unknown event type in bookletController : " + event.type); 105 } 106 }; 107 108 bookletController.startAudioLoop = function() { 109 110 // if the loop exists already, we're being asked to resume 111 // check if we're set to do so, and if not, exit without starting playback 112 if (this.audioLoop && appData.audioLoop && !appData.audioLoop.loop) { 113 this.audioLoop.pause(); 114 this.audioLoop.volume = 0; 115 return; 116 } 117 118 // create the loop if it doesn't exist yet 119 if (appData.audioLoop && !this.audioLoop) { 120 this.audioLoop = new Audio(); 121 this.audioLoop.src = appData.audioLoop.src; 122 // make sure this background audio is never displayed 123 this.audioLoop.style.display = "none"; 124 // add it to the document so that iTunes will notice it is there 125 document.body.appendChild(this.audioLoop); 126 this.audioLoop.volume = 0; 127 } 128 129 if (this.audioLoop) { 130 this.audioLoop.loop = appData.audioLoop.loop; 131 this.audioLoop.volume = Math.min(1, window.iTunes.volume); 132 this.audioLoop.play(); 133 } 134 }; 135 136 bookletController.stopAudioLoop = function() { 137 if (this.audioLoop) { 138 this.audioLoop.pause(); 139 this.audioLoop.loop = false; 140 this.audioLoop.currentTime = 0; // reset to beginning 141 } 142 }; 143 144 bookletController.playbackHasStarted = function (){ 145 return (this.trackBookmark(appData.feature) == 0) ? false : true; 146 } 147 148 bookletController.play = function (trackObj) { 149 var track = this.getTrack(trackObj); 150 151 if (track != null){ 152 iTunes.stop(); 153 track.play(); 154 } 155 }; 156 157 bookletController.playFeature = function (){ 158 bookletController.play(appData.feature); 159 }; 160 161 bookletController.resumeFeature = function(){ 162 bookletController.playFeature(); 163 }; 164 165 bookletController.playChapter = function (index){ 166 iTunes.stop(); 167 if (appData.feature.trackObj != null){ 168 appData.feature.trackObj.play({startChapterIndex : index}); 169 } 170 }; 171 172 bookletController.getChapter = function (){ 173 if (appData.feature.trackObj && appData.feature.trackObj.chapters){ 174 if ((iTunes.currentChapter == 0 && appData.feature.trackObj.bookmark != 0) || IS_APPLE_TV){ 175 var estimatedChapter = Math.floor((appData.feature.trackObj.bookmark / appData.feature.trackObj.duration) * appData.feature.trackObj.chapters.length); 176 var actualChapter = -1; 177 178 if ((appData.feature.trackObj.chapters[estimatedChapter].startOffsetTime / 1000) == appData.feature.trackObj.bookmark){ 179 } else if ((appData.feature.trackObj.chapters[estimatedChapter].startOffsetTime / 1000) < appData.feature.trackObj.bookmark){ 180 while (estimatedChapter < appData.feature.trackObj.chapters.length && (appData.feature.trackObj.chapters[estimatedChapter].startOffsetTime / 1000) < appData.feature.trackObj.bookmark){ 181 estimatedChapter++; 182 } 183 } else if ((appData.feature.trackObj.chapters[estimatedChapter].startOffsetTime / 1000) > appData.feature.trackObj.bookmark){ 184 while (estimatedChapter >= 0 && (appData.feature.trackObj.chapters[estimatedChapter].startOffsetTime / 1000) > appData.feature.trackObj.bookmark){ 185 estimatedChapter--; 186 } 187 } 188 actualChapter = estimatedChapter; 189 debug("GET CHAPTER: estimating: " + actualChapter); 190 return actualChapter; 191 } else { 192 debug("GET CHAPTER: itunes query: " + iTunes.currentChapter); 193 return iTunes.currentChapter; 194 } 195 } else { 196 return -1; 197 } 198 } 199 200 bookletController.buildPlaylist = function (tracks){ 201 var tracklistObj = iTunes.createTempPlaylist(); 202 var tracklist = []; 203 204 for (var i = 0; i < tracks.length; i++){ 205 var track = this.getTrack(tracks[i]); 206 if (track != null){ 207 tracklist.push(track); 208 } 209 } 210 211 tracklistObj.addTracks(tracklist); 212 debug("added " + tracklist.length + " of " + tracks.length + " tracks successfully."); 213 return tracklistObj; 214 }; 215 216 bookletController.buildNonLibraryPlaylist = function (tracks){ 217 var tracklistObj = iTunes.createTempPlaylist(); 218 var tracklist = []; 219 220 for (var i = 0; i < tracks.length; i++){ 221 var track = {}; 222 track.url = "videos/" + tracks[i].src; 223 track.title = tracks[i].string; 224 track.artist = appData.feature.artist; 225 track.album = appData.feature.title; 226 debug("adding: " + track.title + " (" + track.url + ")"); 227 tracklist.push(track); 228 } 229 230 debug("pushing to tracklistOb"); 231 tracklistObj.addURLs(tracklist); 232 return tracklistObj; 233 }; 234 235 bookletController.trackDuration = function (trackObj){ 236 var track = this.getTrack(trackObj); 237 if (track != null){ 238 debug("querying duration"); 239 return track.durationAsString; 240 } else { 241 return "0:00"; 242 } 243 }; 244 245 bookletController.trackNumber = function (trackObj){ 246 var track = this.getTrack(trackObj); 247 if (track != null){ 248 debug("querying track number"); 249 return track.trackNumber; 250 } else { 251 return "0"; 252 } 253 }; 254 255 bookletController.trackBookmark = function (trackObj){ 256 var track = this.getTrack(trackObj); 257 if (track != null){ 258 debug("querying bookmark"); 259 return track.bookmark; 260 } else { 261 return "0"; 262 } 263 }; 264 265 bookletController.getTrack = function (trackObj){ 266 if (trackObj.XID){ 267 debug("searching by XID: " + trackObj.XID); 268 var iTunesTrack = window.iTunes.findTracksByXID(trackObj.XID); 269 if (iTunesTrack.length > 0){ 270 debug("found by XID"); 271 return iTunesTrack[0]; 272 } else { 273 debug("XID not found in library"); 274 return null; 275 } 276 } else { 277 debug("no XID"); 278 return null; 279 } 280 }; 281 282 bookletController.playNonLibraryContent = function (trackObj){ 283 debug("videos/" + trackObj.src); 284 debug({title : trackObj.string, artist : appData.feature.artist, album : appData.feature.title}); 285 window.iTunes.play("videos/" + trackObj.src, {title : trackObj.string, artist : appData.feature.artist, album : appData.feature.title}); 286 }; 287 288 bookletController.childControllers = []; 289 290 bookletController.registerForDisplayUpdates = function (childController) { 291 if (this.childControllers.indexOf(childController) == -1) { 292 this.childControllers.push(childController); 293 } 294 }; 295 296 bookletController.dispatchDisplayUpdates = function () { 297 for (var i=0; i < this.childControllers.length; i++) { 298 if (TKUtils.objectIsFunction(this.childControllers[i].updateDisplay)) { 299 this.childControllers[i].updateDisplay(); 300 } 301 } 302 }; 303 304 /* ================= iTunes Emulation ======================== */ 305 306 var iTunesEmulator = { 307 volume : 1, 308 platform : 'Emulator', 309 version : '-1' 310 }; 311 312 iTunesEmulator.play = function (mediaLocation) { 313 debug("iTunesEmulator - play: " + mediaLocation); 314 }; 315 316 iTunesEmulator.stop = function () { 317 debug("iTunesEmulator - stop"); 318 }; 319 320 iTunesEmulator.findTracksByStoreID = function (storeID) { 321 debug("iTunesEmulator - findTracksByStoreID: " + storeID); 322 return [new ITunesTrackEmulator()]; 323 }; 324 325 iTunesEmulator.findTracksByXID = function (xID) { 326 debug("iTunesEmulator - findTracksByXID: " + xID); 327 return [new ITunesTrackEmulator()]; 328 }; 329 330 iTunesEmulator.createTempPlaylist = function () { 331 return { 332 tracks: [], 333 addTracks: function () {} 334 }; 335 }; 336 337 function ITunesTrackEmulator() { 338 } 339 340 ITunesTrackEmulator.prototype.play = function (params) { 341 debug("iTunesTrackEmulator - play: " + params); 342 // fake the play event to the window 343 var event = document.createEvent("HTMLEvents"); 344 event.initEvent("play", false, false); 345 window.dispatchEvent(event); 346 setTimeout(function() { 347 debug("iTunesTrackEmulator - coming back from playback"); 348 event = document.createEvent("HTMLEvents"); 349 event.initEvent("videoclosed", false, false); 350 window.dispatchEvent(event); 351 }, 5000); 352 }; 353 354 /* ================= Initialisation ======================== */ 355 356 window.addEventListener('load', function () { 357 // override select start 358 document.onselectstart = function() { return false; }; 359 // check for iTunes object, create a dummy if it doesn't exist 360 if (!window.iTunes) { 361 window.iTunes = iTunesEmulator; 362 } 363 // init the booklet controller 364 bookletController.init(); 365 }, false); 366