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