add BookReaderGetTTS.php
[bookreader.git] / BookReader / BookReader.js
index 4e1d830..191617d 100644 (file)
@@ -110,6 +110,7 @@ function BookReader() {
     this.ttsPosition    = -1;    //chunk (paragraph) number
     this.ttsBuffering   = false;
     this.ttsPoller      = null;
+    this.ttsFormat      = null;
     
     return this;
 };
@@ -1262,6 +1263,7 @@ BookReader.prototype.switchMode = function(mode) {
     }
 
     this.autoStop();
+    this.ttsStop();
     this.removeSearchHilites();
 
     this.mode = mode;
@@ -1543,12 +1545,14 @@ BookReader.prototype.prepareTwoPagePopUp = function() {
 
     $(this.leafEdgeL).bind('click', this, function(e) { 
         e.data.autoStop();
+        e.data.ttsStop();
         var jumpIndex = e.data.jumpIndexForLeftEdgePageX(e.pageX);
         e.data.jumpToIndex(jumpIndex);
     });
 
     $(this.leafEdgeR).bind('click', this, function(e) { 
         e.data.autoStop();
+        e.data.ttsStop();
         var jumpIndex = e.data.jumpIndexForRightEdgePageX(e.pageX);
         e.data.jumpToIndex(jumpIndex);    
     });
@@ -1880,8 +1884,6 @@ BookReader.prototype.leftmost = function() {
 // next()
 //______________________________________________________________________________
 BookReader.prototype.next = function() {
-    this.ttsStop();
-    
     if (2 == this.mode) {
         this.autoStop();
         this.flipFwdToIndex(null);
@@ -1895,8 +1897,6 @@ BookReader.prototype.next = function() {
 // prev()
 //______________________________________________________________________________
 BookReader.prototype.prev = function() {
-    this.ttsStop();
-    
     if (2 == this.mode) {
         this.autoStop();
         this.flipBackToIndex(null);
@@ -2287,6 +2287,7 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
         { self: this },
         function(e) {
+            e.data.self.ttsStop();
             e.data.self.left();
             e.preventDefault();
         }
@@ -2295,6 +2296,7 @@ BookReader.prototype.setMouseHandlers2UP = function() {
     this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
         { self: this },
         function(e) {
+            e.data.self.ttsStop();
             e.data.self.right();
             e.preventDefault();
         }
@@ -2948,6 +2950,7 @@ BookReader.prototype.showEmbedCode = function() {
         return;
     }
     this.autoStop();
+    this.ttsStop();
     this.embedPopup = document.createElement("div");
     $(this.embedPopup).css({
         position: 'absolute',
@@ -3135,7 +3138,7 @@ BookReader.prototype.initToolbar = function(mode, ui) {
 
     $("#BookReader").append("<div id='BRtoolbar'>"
         + "<span id='BRtoolbarbuttons' style='float: right'>"
-        +   "<button class='BRicon rollover read_aloud' onclick='br.ttsStart(); return false;'/>"
+        +   "<button class='BRicon rollover read_aloud' onclick='br.ttsToggle(); return false;'/>"
         +   "<button class='BRicon print rollover' /> <button class='BRicon rollover embed' />"
         +   "<form class='BRpageform' action='javascript:' onsubmit='br.jumpToPage(this.elements[0].value)'> <span class='label'>Page:<input id='BRpagenum' type='text' size='3' onfocus='br.autoStop();'></input></span></form>"
         +   "<div class='BRtoolbarmode2' style='display: none'><button class='BRicon rollover book_leftmost' /><button class='BRicon rollover book_left' /><button class='BRicon rollover book_right' /><button class='BRicon rollover book_rightmost' /></div>"
@@ -3655,6 +3658,8 @@ BookReader.prototype.startLocationPolling = function() {
             if (newHash != self.oldUserHash) { // Only process new user hash once
                 //console.log('url change detected ' + self.oldLocationHash + " -> " + newHash);
                 
+                self.ttsStop();
+                
                 // Queue change if animating
                 if (self.animating) {
                     self.autoStop();
@@ -3797,28 +3802,49 @@ BookReader.util = {
 }
 
 
-// ttsStart()
+// ttsToggle()
 //______________________________________________________________________________
-BookReader.prototype.ttsStart = function () {
+BookReader.prototype.ttsToggle = function () {
     if (false == this.ttsPlaying) {        
-        console.log('starting readAloud');
-        this.ttsPlaying = true;
-        this.ttsIndex = this.currentIndex();
-        this.ttsGetText(this.ttsIndex, 'ttsStartCB');
+        if(soundManager.supported()) {
+            this.ttsStart();            
+        } else {               
+            soundManager.onready(function(oStatus) {
+              if (oStatus.success) {                
+                this.ttsStart();
+              } else {
+                alert('Could not load soundManger2, possibly due to FlashBlock. Audio playback is disabled');
+              }
+            }, this);        
+        }
     } else {
         this.ttsStop();
     }
 }
 
+// ttsStart()
+//______________________________________________________________________________
+BookReader.prototype.ttsStart = function () {
+    if (soundManager.debugMode) console.log('starting readAloud');
+    this.ttsPlaying = true;
+    this.ttsIndex = this.currentIndex();
+    this.ttsFormat = 'mp3';
+    if ($.browser.mozilla) {
+        this.ttsFormat = 'ogg';
+    }
+    this.ttsGetText(this.ttsIndex, 'ttsStartCB');    
+}
+
 // ttsStop()
 //______________________________________________________________________________
 BookReader.prototype.ttsStop = function () {
     if (false == this.ttsPlaying) return;
     
-    console.log('stopping readaloud');
+    if (soundManager.debugMode) console.log('stopping readaloud');
     soundManager.stopAll();
     soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
     this.ttsRemoveHilites();
+    this.ttsRemovePopup();
 
     this.ttsPlaying     = false;
     this.ttsIndex       = null;  //leaf index
@@ -3830,38 +3856,37 @@ BookReader.prototype.ttsStop = function () {
 // ttsGetText()
 //______________________________________________________________________________
 BookReader.prototype.ttsGetText = function(index, callback) {
-    var url = 'http://'+this.server+'/BookReader/BookReaderGetTextWrapper.php?path='+this.bookPath+'_djvu.xml&page='+index;
+    var url = 'http://'+this.server+'/BookReader/BookReaderGetTextWrapper.php?path='+this.bookPath+'_djvu.xml&page='+index;    
     this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:callback});
 }
 
 // ttsStartCB(): text-to-speech callback
 //______________________________________________________________________________
 BookReader.prototype.ttsStartCB = function (data) {
-    console.log('ttsStartCB got data:');
-    console.log(data);
+    if (soundManager.debugMode)  console.log('ttsStartCB got data: ' + data);
     this.ttsChunks = data;
     this.ttsHilites = [];
     
     //deal with the page being blank
     if (0 == data.length) {
-        console.log('first page is blank!');
+        if (soundManager.debugMode) console.log('first page is blank!');
         if(this.ttsAdvance(true)) {
             this.ttsGetText(this.ttsIndex, 'ttsStartCB');            
         }
         return;
-    } else {
-        console.log('length = ' + data.length);
-        console.log((0 == data.length));
     }
     
     this.ttsShowPopup();
     
+    ///// whileloading: broken on safari
+    ///// onload fires on safari, but *after* the sound starts playing..
     this.ttsPosition = -1;    
     var snd = soundManager.createSound({
      id: 'chunk'+this.ttsIndex+'-0',
-     //url: 'http://home.us.archive.org/~rkumar/arctic.ogg',     
-     url: 'http://home.us.archive.org/~rkumar/getOgg.php?string=' + escape(data[0][0]) + '&f=.ogg', //the .ogg is to trick SoundManager2 to use the HTML5 audio player
-     whileloading: function(){if (this.bytesLoaded == this.bytesTotal) {$(br.popup).remove(); br.popup=null;}} //onload never fires...
+     //url: 'http://home.us.archive.org/~rkumar/arctic.ogg',
+     url: 'http://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + escape(data[0][0]) + '&format=.'+this.ttsFormat, //the .ogg is to trick SoundManager2 to use the HTML5 audio player
+     whileloading: function(){if (this.bytesLoaded == this.bytesTotal) this.br.ttsRemovePopup();}, //onload never fires in FF...
+     onload: function(){this.br.ttsRemovePopup();} //whileloading never fires in safari...
     });    
     snd.br = this;
     snd.load();
@@ -3872,35 +3897,36 @@ BookReader.prototype.ttsStartCB = function (data) {
 // ttsShowPopup
 //______________________________________________________________________________
 BookReader.prototype.ttsShowPopup = function() {
+    if (soundManager.debugMode) console.log('ttsShowPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
+    
     this.popup = document.createElement("div");
     $(this.popup).css({
-        position: 'absolute',
-        top:      '20%',
-        left:     ($('#BRcontainer').attr('clientWidth')-600)/2 + 'px',
-        width:    '600px',
-        padding:  "20px",
-        border:   "3px double #999999",
-        zIndex:   3,
-        backgroundColor: "#f00",
-        color:           "#fff",
-        fontSize:       '1.875em'        
-    }).appendTo('#BookReader');
+        top:      $('#BRtoolbar').height() + 'px',
+        left:     $('.read_aloud').position().left + 'px',
+        width:    $('#BRtoolbar').width()-$('.read_aloud').position().left + 'px',
+        height:   '20px',
+    }).attr('className', 'BRttsPopUp').appendTo('#BookReader');
 
-    htmlStr =  '<p style="text-align:center;"><b>Ever wanted to wait while audio loads?</b><br/>';
-    htmlStr += "It's OK. We all do.</p>";
+    htmlStr =  '&nbsp;';
 
     this.popup.innerHTML = htmlStr;
 }
 
+// ttsRemovePopup
+//______________________________________________________________________________
+BookReader.prototype.ttsRemovePopup = function() {
+    $(this.popup).remove(); 
+    this.popup=null;
+}
+
 // ttsNextPageCB
 //______________________________________________________________________________
 BookReader.prototype.ttsNextPageCB = function (data) {
     this.ttsNextChunks = data;
-    console.log('preloaded next chunks.. data is');
-    console.log(data);
+    if (soundManager.debugMode) console.log('preloaded next chunks.. data is ' + data);
     
     if (true == this.ttsBuffering) {
-        console.log('ttsNextPageCB: ttsBuffering is true');
+        if (soundManager.debugMode) console.log('ttsNextPageCB: ttsBuffering is true');
         this.ttsBuffering = false;
     }
 }
@@ -3910,7 +3936,7 @@ BookReader.prototype.ttsNextPageCB = function (data) {
 BookReader.prototype.ttsLoadChunk = function (page, pos, string) {
     var snd = soundManager.createSound({
      id: 'chunk'+page+'-'+pos,
-     url: 'http://home.us.archive.org/~rkumar/getOgg.php?string=' + escape(string) + '&f=.ogg' //the .ogg is to trick SoundManager2 to use the HTML5 audio player
+     url: 'http://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + escape(string) + '&format=.'+this.ttsFormat //the .ogg is to trick SoundManager2 to use the HTML5 audio player
     });
     snd.br = this;
     snd.load()
@@ -3926,8 +3952,7 @@ BookReader.prototype.ttsLoadChunk = function (page, pos, string) {
 // continues after animation is finished.
 
 BookReader.prototype.ttsNextChunk = function () {
-    console.log(this);
-    console.log(this.ttsPosition);
+    if (soundManager.debugMode) console.log('nextchunk pos=' + this.ttsPosition);
     
     if (-1 != this.ttsPosition) {
         soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
@@ -3950,14 +3975,18 @@ BookReader.prototype.ttsNextChunk = function () {
 //______________________________________________________________________________
 // page flip animation has now completed
 BookReader.prototype.ttsNextChunkPhase2 = function () {
+    if (null == this.ttsChunks) {
+        alert('error: ttsChunks is null?'); //TODO
+        return;
+    }
+    
     if (0 == this.ttsChunks.length) {
-        console.log('ttsNextChunk2: ttsChunks.length is zero.. hacking...');
+        if (soundManager.debugMode) console.log('ttsNextChunk2: ttsChunks.length is zero.. hacking...');
         this.ttsStartCB(this.ttsChunks);
         return;
     }
     
-    console.log('next chunk is ');
-    console.log(this.ttsPosition);    
+    if (soundManager.debugMode) console.log('next chunk is ' + this.ttsPosition);  
 
     //prefetch next page of text
     if (0 == this.ttsPosition) {
@@ -3985,64 +4014,39 @@ BookReader.prototype.ttsAdvance = function (starting) {
     if (this.ttsPosition >= this.ttsChunks.length) {
         
         if (this.ttsIndex == (this.numLeafs-1)) {
-            console.log('tts stop');
+            if (soundManager.debugMode) console.log('tts stop');
             return false;
         } else {
             if ((null != this.ttsNextChunks) || (starting)) {
-                console.log('moving to next page!');
+                if (soundManager.debugMode) console.log('moving to next page!');
                 this.ttsIndex++;
                 this.ttsPosition = 0;
                 this.ttsChunks = this.ttsNextChunks;
                 this.ttsNextChunks = null;
+
+                //A page flip might be necessary. This code is confusing since
+                //ttsNextChunks might be null if we are starting on a blank page.
                 if (2 == this.mode) {
                     if ((this.ttsIndex != this.twoPage.currentIndexL) && (this.ttsIndex != this.twoPage.currentIndexR)) {
-                        this.animationFinishedCallback = this.ttsNextChunkPhase2;
-                        this.next();
-                        return false;
+                        if (!starting) {
+                            this.animationFinishedCallback = this.ttsNextChunkPhase2;
+                            this.next();
+                            return false;
+                        } else {
+                            this.next();
+                            return true;
+                        }
                     } else {
                         return true;
                     }
                 }
             } else {
-                console.log('ttsAdvance: ttsNextChunks is null');
+                if (soundManager.debugMode) console.log('ttsAdvance: ttsNextChunks is null');
                 return false; 
             }
         }
     }
-    
-    if ((this.constMode1up == this.mode) && (null != this.ttsChunks) && (0 != this.ttsChunks.length)) {
-        var leafTop = 0;
-        var h;
-        var i;
-        for (i=0; i<this.ttsIndex; i++) {
-            h = parseInt(this._getPageHeight(i)/this.reduce); 
-            leafTop += h + this.padding;
-        }
-        
-        var chunk = this.ttsChunks[this.ttsPosition];
-        console.log('chunk=');
-        console.log(chunk);
-        var chunkTop = chunk[1][3]; //coords are in l,b,r,t order
-        var chunkBot = chunk[chunk.length-1][1];
         
-        var topOfFirstChunk = leafTop + chunkTop/this.reduce;
-        var botOfLastChunk  = leafTop + chunkBot/this.reduce;
-        
-        console.log('leafTop = ' + leafTop + ' topOfFirstChunk = ' + topOfFirstChunk + ' botOfLastChunk = ' + botOfLastChunk);
-
-        var containerTop = $('#BRcontainer').attr('scrollTop');
-        var containerBot = containerTop + $('#BRcontainer').height();
-        console.log('containerTop = ' + containerTop + ' containerBot = ' + containerBot);
-
-        if ((topOfFirstChunk < containerTop) || (botOfLastChunk > containerBot)) {
-            console.log('jumping to leafTop!');
-
-            //jumpToIndex scrolls so that chunkTop is centered.. we want chunkTop at the top
-            //this.jumpToIndex(this.ttsIndex, null, chunkTop);
-            $('#BRcontainer').animate({scrollTop: topOfFirstChunk,},'fast');            
-        }
-    }
-    
     return true;
 }
 
@@ -4053,9 +4057,7 @@ BookReader.prototype.ttsPrefetchAudio = function () {
     if(false != this.ttsBuffering) {
         alert('TTS Error: prefetch() called while content still buffering!');
         return;
-    }
-    
-
+    }    
 
     //preload next chunk
     var nextPos = this.ttsPosition+1;
@@ -4063,16 +4065,16 @@ BookReader.prototype.ttsPrefetchAudio = function () {
         this.ttsLoadChunk(this.ttsIndex, nextPos, this.ttsChunks[nextPos][0]);
     } else {
         //for a short page, preload might nt have yet returned..
-        console.log('preloading chunk 0 from next page, index='+(this.ttsIndex+1))
+        if (soundManager.debugMode) console.log('preloading chunk 0 from next page, index='+(this.ttsIndex+1));
         if (null != this.ttsNextChunks) {
             console.log(this.ttsNextChunks);
             if (0 != this.ttsNextChunks.length) {
                 this.ttsLoadChunk(this.ttsIndex+1, 0, this.ttsNextChunks[0][0]);        
             } else {
-                console.log('prefetchAudio(): ttsNextChunks is zero length!');
+                if (soundManager.debugMode) console.log('prefetchAudio(): ttsNextChunks is zero length!');
             }
         } else {
-            console.log('ttsNextChunks is null, not preloading next page');
+            if (soundManager.debugMode) console.log('ttsNextChunks is null, not preloading next page');
             this.ttsBuffering = true;
         }
     }
@@ -4084,26 +4086,61 @@ BookReader.prototype.ttsPrefetchAudio = function () {
 BookReader.prototype.ttsPlay = function () {
         
     var chunk = this.ttsChunks[this.ttsPosition];
-    console.log('position = ' + this.ttsPosition);
-    console.log('chunk = ' + chunk);
-    console.log(this.ttsChunks);
-
+    if (soundManager.debugMode) {
+        console.log('ttsPlay position = ' + this.ttsPosition);
+        console.log('chunk = ' + chunk);
+        console.log(this.ttsChunks);
+    }
+    
     //add new hilights
     if (2 == this.mode) {
         this.ttsHilite2UP(chunk);
     } else {
         this.ttsHilite1UP(chunk);
     }
+    
+    this.ttsScrollToChunk(chunk);
         
     //play current chunk
-    if (false == this.ttsBuffering) {
+    if (false == this.ttsBuffering) {        
         soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsNextChunk();}});
     } else {
-        console.log('playing current chunk, but next chunk is not buffered yet!');
         soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsStartPolling();}});
     }
 }
 
+// scrollToChunk()
+//______________________________________________________________________________
+BookReader.prototype.ttsScrollToChunk = function(chunk) {
+    if (this.constMode1up != this.mode) return;
+
+    var leafTop = 0;
+    var h;
+    var i;
+    for (i=0; i<this.ttsIndex; i++) {
+        h = parseInt(this._getPageHeight(i)/this.reduce); 
+        leafTop += h + this.padding;
+    }
+    
+    var chunkTop = chunk[1][3]; //coords are in l,b,r,t order
+    var chunkBot = chunk[chunk.length-1][1];
+    
+    var topOfFirstChunk = leafTop + chunkTop/this.reduce;
+    var botOfLastChunk  = leafTop + chunkBot/this.reduce;
+    
+    if (soundManager.debugMode) console.log('leafTop = ' + leafTop + ' topOfFirstChunk = ' + topOfFirstChunk + ' botOfLastChunk = ' + botOfLastChunk);
+
+    var containerTop = $('#BRcontainer').attr('scrollTop');
+    var containerBot = containerTop + $('#BRcontainer').height();
+    if (soundManager.debugMode) console.log('containerTop = ' + containerTop + ' containerBot = ' + containerBot);
+
+    if ((topOfFirstChunk < containerTop) || (botOfLastChunk > containerBot)) {
+        //jumpToIndex scrolls so that chunkTop is centered.. we want chunkTop at the top
+        //this.jumpToIndex(this.ttsIndex, null, chunkTop);
+        $('#BRcontainer').animate({scrollTop: topOfFirstChunk},'fast');            
+    }    
+}
+
 // ttsHilite1UP()
 //______________________________________________________________________________
 BookReader.prototype.ttsHilite1UP = function(chunk) {
@@ -4160,12 +4197,12 @@ BookReader.prototype.ttsRemoveHilites = function (chunk) {
 // We need to wait for the text for the next page to be loaded, so we can
 // load the next audio chunk
 BookReader.prototype.ttsStartPolling = function () {
-    console.log('Starting the TTS poller...');
+    if (soundManager.debugMode) console.log('Starting the TTS poller...');
     var self = this;
     this.ttsPoller=setInterval(function(){
         if (self.ttsBuffering) {return;}
         
-        console.log('TTS buffering finished!');
+        if (soundManager.debugMode) console.log('TTS buffering finished!');
         clearInterval(self.ttsPoller);
         self.ttsPoller = null;
         self.ttsPrefetchAudio();