Merge branch 'newui' of git@github.com:openlibrary/bookreader into newui
authorrajbot <raj@archive.org>
Mon, 18 Oct 2010 20:29:40 +0000 (20:29 +0000)
committerrajbot <raj@archive.org>
Mon, 18 Oct 2010 20:29:40 +0000 (20:29 +0000)
1  2 
BookReader/BookReader.js

diff --combined BookReader/BookReader.js
@@@ -74,7 -74,7 +74,7 @@@ function BookReader() 
      this.printPopup = null;
      
      this.searchTerm = '';
 -    this.searchResults = {};
 +    this.searchResults = null;
      
      this.firstIndex = null;
      
@@@ -2635,40 -2635,92 +2635,40 @@@ BookReader.prototype.getPageWidth2UP = 
  // search()
  //______________________________________________________________________________
  BookReader.prototype.search = function(term) {
 -    term = term.replace(/\//g, ' '); // strip slashes
 +    //console.log('search called with term=' + term);
 +    var url = 'http://'+this.server.replace(/:.+/, ''); //remove the port and userdir
 +    url    += '/~edward/inside_jsonp.php?item_id='+this.bookId;
 +    url    += '&doc='+this.subPrefix;   //TODO: test with subitem
 +    url    += '&path='+this.bookPath.replace(new RegExp('/'+this.subPrefix+'$'), ''); //remove subPrefix from end of path
 +    url    += '&q='+escape(term);
 +    //console.log('search url='+url);
 +    
 +    term = term.replace(/\//g, ' '); // strip slashes, since this goes in the url
      this.searchTerm = term;
 -    $('#BookReaderSearchScript').remove();
 -    var script  = document.createElement("script");
 -    script.setAttribute('id', 'BookReaderSearchScript');
 -    script.setAttribute("type", "text/javascript");
 -    script.setAttribute("src", 'http://'+this.server+'/BookReader/flipbook_search_br.php?url='+escape(this.bookPath + '_djvu.xml')+'&term='+term+'&format=XML&callback=br.BRSearchCallback');
 -    document.getElementsByTagName('head')[0].appendChild(script);
 -    $('#BookReaderSearchBox').val(term);
 -    $('#BookReaderSearchResults').html('Searching...');
 +    
 +    this.removeSearchResults();
 +    this.showProgressPopup();
 +    this.ttsAjax = $.ajax({url:url, dataType:'jsonp', jsonpCallback:'BRSearchCallback'});    
  }
  
  // BRSearchCallback()
  //______________________________________________________________________________
 -BookReader.prototype.BRSearchCallback = function(txt) {
 -    //alert(txt);
 -    if (jQuery.browser.msie) {
 -        var dom=new ActiveXObject("Microsoft.XMLDOM");
 -        dom.async="false";
 -        dom.loadXML(txt);    
 -    } else {
 -        var parser = new DOMParser();
 -        var dom = parser.parseFromString(txt, "text/xml");    
 -    }
 -    
 -    $('#BookReaderSearchResults').empty();    
 -    $('#BookReaderSearchResults').append('<ul>');
 -    
 -    for (var key in this.searchResults) {
 -        if (null != this.searchResults[key].div) {
 -            $(this.searchResults[key].div).remove();
 -        }
 -        delete this.searchResults[key];
 +// Unfortunately, we can't pass 'br.searchCallback' to our search service,
 +// because it can't handle the '.'
 +function BRSearchCallback(results) {    
 +    //console.log('got ' + results.matches.length + ' results');
 +    br.removeSearchResults();
 +    br.searchResults = results; 
 +    //console.log(br.searchResults);
 +    var i;    
 +    for (i=0; i<results.matches.length; i++) {        
 +        br.addSearchResult(results.matches[i].text, br.leafNumToIndex(results.matches[i].par[0].page));
      }
 -    
 -    var pages = dom.getElementsByTagName('PAGE');
 -    
 -    if (0 == pages.length) {
 -        // $$$ it would be nice to echo the (sanitized) search result here
 -        $('#BookReaderSearchResults').append('<li>No search results found</li>');
 -    } else {    
 -        for (var i = 0; i < pages.length; i++){
 -            //console.log(pages[i].getAttribute('file').substr(1) +'-'+ parseInt(pages[i].getAttribute('file').substr(1), 10));
 -    
 -            
 -            var re = new RegExp (/_(\d{4})\.djvu/);
 -            var reMatch = re.exec(pages[i].getAttribute('file'));
 -            var index = parseInt(reMatch[1], 10);
 -            //var index = parseInt(pages[i].getAttribute('file').substr(1), 10);
 -            
 -            var children = pages[i].childNodes;
 -            var context = '';
 -            for (var j=0; j<children.length; j++) {
 -                //console.log(j + ' - ' + children[j].nodeName);
 -                //console.log(children[j].firstChild.nodeValue);
 -                if ('CONTEXT' == children[j].nodeName) {
 -                    context += children[j].firstChild.nodeValue;
 -                } else if ('WORD' == children[j].nodeName) {
 -                    context += '<b>'+children[j].firstChild.nodeValue+'</b>';
 -                    
 -                    var index = this.leafNumToIndex(index);
 -                    if (null != index) {
 -                        //coordinates are [left, bottom, right, top, [baseline]]
 -                        //we'll skip baseline for now...
 -                        var coords = children[j].getAttribute('coords').split(',',4);
 -                        if (4 == coords.length) {
 -                            this.searchResults[index] = {'l':parseInt(coords[0]), 'b':parseInt(coords[1]), 'r':parseInt(coords[2]), 't':parseInt(coords[3]), 'div':null};
 -                        }
 -                    }
 -                }
 -            }
 -            var pageName = this.getPageName(index);
 -            var middleX = (this.searchResults[index].l + this.searchResults[index].r) >> 1;
 -            var middleY = (this.searchResults[index].t + this.searchResults[index].b) >> 1;
 -            //TODO: remove hardcoded instance name
 -            $('#BookReaderSearchResults').append('<li><b><a href="javascript:br.jumpToIndex('+index+','+middleX+','+middleY+');">' + pageName + '</a></b> - ' + context + '</li>');
 -        }
 -    }
 -    $('#BookReaderSearchResults').append('</ul>');
 -
 -    // $$$ update again for case of loading search URL in new browser window (search box may not have been ready yet)
 -    $('#BookReaderSearchBox').val(this.searchTerm);
 -
 -    this.updateSearchHilites();
 +    br.updateSearchHilites();
 +    br.removeProgressPopup();
  }
  
 +
  // updateSearchHilites()
  //______________________________________________________________________________
  BookReader.prototype.updateSearchHilites = function() {
  // showSearchHilites1UP()
  //______________________________________________________________________________
  BookReader.prototype.updateSearchHilites1UP = function() {
 -
 -    for (var key in this.searchResults) {
 -        
 -        if (jQuery.inArray(parseInt(key), this.displayedIndices) >= 0) {
 -            var result = this.searchResults[key];
 -            if (null == result.div) {
 -                result.div = document.createElement('div');
 -                $(result.div).attr('className', 'BookReaderSearchHilite').appendTo('#pagediv'+key);
 -                //console.log('appending ' + key);
 -            }    
 -            $(result.div).css({
 -                width:  (result.r-result.l)/this.reduce + 'px',
 -                height: (result.b-result.t)/this.reduce + 'px',
 -                left:   (result.l)/this.reduce + 'px',
 -                top:    (result.t)/this.reduce +'px'
 -            });
 -
 -        } else {
 -            //console.log(key + ' not displayed');
 -            this.searchResults[key].div=null;
 +    var results = this.searchResults;
 +    if (null == results) return;
 +    var i, j;
 +    for (i=0; i<results.matches.length; i++) {
 +        //console.log(results.matches[i].par[0]);
 +        for (j=0; j<results.matches[i].par[0].boxes.length; j++) {
 +            var box = results.matches[i].par[0].boxes[j];
 +            var pageIndex = this.leafNumToIndex(box.page);
 +            if (jQuery.inArray(pageIndex, this.displayedIndices) >= 0) {
 +                if (null == box.div) {
 +                    //create a div for the search highlight, and stash it in the box object
 +                    box.div = document.createElement('div');
 +                    $(box.div).attr('className', 'BookReaderSearchHilite').appendTo('#pagediv'+pageIndex);
 +                }
 +                $(box.div).css({
 +                    width:  (box.r-box.l)/this.reduce + 'px',
 +                    height: (box.b-box.t)/this.reduce + 'px',
 +                    left:   (box.l)/this.reduce + 'px',
 +                    top:    (box.t)/this.reduce +'px'
 +                });                
 +            } else {
 +                if (null != box.div) {
 +                    //console.log('removing search highlight div');
 +                    $(box.div).remove();
 +                    box.div=null;
 +                }                
 +            }
          }
      }
 +    
  }
  
 +
  // twoPageGutter()
  //______________________________________________________________________________
  // Returns the position of the gutter (line between the page images)
@@@ -2842,36 -2885,31 +2842,36 @@@ BookReader.prototype.twoPagePlaceFlipAr
      });
  }
      
 -// showSearchHilites2UP()
 +// showSearchHilites2UPNew()
  //______________________________________________________________________________
  BookReader.prototype.updateSearchHilites2UP = function() {
 -
 -    for (var key in this.searchResults) {
 -        key = parseInt(key, 10);
 -        if (jQuery.inArray(key, this.displayedIndices) >= 0) {
 -            var result = this.searchResults[key];
 -            if (null == result.div) {
 -                result.div = document.createElement('div');
 -                $(result.div).attr('className', 'BookReaderSearchHilite').css('zIndex', 3).appendTo('#BRtwopageview');
 -                //console.log('appending ' + key);
 -            }
 -
 -            this.setHilightCss2UP(result.div, key, result.l, result.r, result.t, result.b);
 -
 -        } else {
 -            //console.log(key + ' not displayed');
 -            if (null != this.searchResults[key].div) {
 -                //console.log('removing ' + key);
 -                $(this.searchResults[key].div).remove();
 +    //console.log('updateSearchHilites2UP results = ' + this.searchResults); 
 +    var results = this.searchResults;
 +    if (null == results) return;
 +    var i, j;
 +    for (i=0; i<results.matches.length; i++) {
 +        //console.log(results.matches[i].par[0]);
 +        for (j=0; j<results.matches[i].par[0].boxes.length; j++) {
 +            var box = results.matches[i].par[0].boxes[j];
 +            var pageIndex = this.leafNumToIndex(box.page);
 +            if (jQuery.inArray(pageIndex, this.displayedIndices) >= 0) {
 +                if (null == box.div) {
 +                    //create a div for the search highlight, and stash it in the box object
 +                    box.div = document.createElement('div');
 +                    $(box.div).attr('className', 'BookReaderSearchHilite').css('zIndex', 3).appendTo('#BRtwopageview');
 +                    //console.log('appending new div');
 +                }
 +                this.setHilightCss2UP(box.div, pageIndex, box.l, box.r, box.t, box.b);
 +            } else {
 +                if (null != box.div) {
 +                    //console.log('removing search highlight div');
 +                    $(box.div).remove();
 +                    box.div=null;
 +                }                
              }
 -            this.searchResults[key].div=null;
          }
      }
 +    
  }
  
  // setHilightCss2UP()
@@@ -2906,21 -2944,14 +2906,21 @@@ BookReader.prototype.setHilightCss2UP 
  // removeSearchHilites()
  //______________________________________________________________________________
  BookReader.prototype.removeSearchHilites = function() {
 -    for (var key in this.searchResults) {
 -        if (null != this.searchResults[key].div) {
 -            $(this.searchResults[key].div).remove();
 -            this.searchResults[key].div=null;
 -        }        
 -    }
 +    var results = this.searchResults;
 +    if (null == results) return;
 +    var i, j;
 +    for (i=0; i<results.matches.length; i++) {
 +        for (j=0; j<results.matches[i].par[0].boxes.length; j++) {
 +            var box = results.matches[i].par[0].boxes[j];
 +            if (null != box.div) {
 +                $(box.div).remove();
 +                box.div=null;                
 +            }
 +        }
 +    }    
  }
  
 +
  // printPage
  //______________________________________________________________________________
  BookReader.prototype.printPage = function() {
@@@ -3393,8 -3424,7 +3393,8 @@@ BookReader.prototype.updateNavIndex = f
      $('#BRpager').data('swallowchange', true).slider('value', index);
  }
  
 -BookReader.prototype.addSearchResult = function(queryString, pageNumber, pageIndex) {
 +BookReader.prototype.addSearchResult = function(queryString, pageIndex) {
 +    var pageNumber = this.getPageNum(pageIndex);
      var uiStringSearch = "Search result"; // i18n
      var uiStringPage = "Page"; // i18n
      
          pageDisplayString = uiStringPage + ' ' + pageNumber;
      }
      
 +    var re = new RegExp('{{{(.+?)}}}', 'g');    
 +    queryString = queryString.replace(re, '<a href="#" onclick="br.jumpToIndex('+pageIndex+'); return false;">$1</a>')
 +
      $('<div class="search" style="left:' + percentThrough + ';" title="' + uiStringSearch + '"><div class="query">'
          + queryString + '<span>' + uiStringPage + ' ' + pageNumber + '</span></div>')
      .data({'self': this, 'pageIndex': pageIndex })
  }
  
  BookReader.prototype.removeSearchResults = function() {
 +    this.removeSearchHilites(); //be sure to set all box.divs to null
      $('#BRnavpos .search').remove();
  }
  
@@@ -3577,20 -3603,15 +3577,20 @@@ BookReader.prototype.addChapterFromEntr
  BookReader.prototype.initToolbar = function(mode, ui) {
  
      // $$$mang should be contained within the BookReader div instead of body
 +    var readIcon = ''
 +    if (!navigator.userAgent.match(/mobile/i)) {
 +        readIcon = "<button class='BRicon read modal'></button>";
 +    }
 +
      $("body").append(
            "<div id='BRtoolbar'>"
          +   "<span id='BRtoolbarbuttons'>"
          /* XXXmang integrate search */
 -        +     "<form method='get' id='booksearch'><input type='search' id='textSrch' name='textSrch' val='' placeholder='Search inside'/><button type='submit' id='btnSrch' name='btnSrch'>GO</button></form>"
 +        +     "<form action='javascript:' id='booksearch'><input type='search' id='textSrch' name='textSrch' val='' placeholder='Search inside'/><button type='submit' id='btnSrch' name='btnSrch'>GO</button></form>"
          // XXXmang icons incorrect or handlers wrong
          +     "<button class='BRicon info'></button>"
          +     "<button class='BRicon share'></button>"
 -        +     "<button class='BRicon read modal'></button>"
 +        +     readIcon
          +     "<button class='BRicon full'></button>"
          +   "</span>"
          +   "<span><a class='logo' href='" + this.logoURL + "'></a></span>"
@@@ -3797,11 -3818,6 +3797,11 @@@ BookReader.prototype.bindToolbarNavHand
          self.zoom(-1);
          return false;
      });
 +    
 +    $('#booksearch').bind('submit', function() {
 +        self.search($('#textSrch').val());
 +    });
 +    
  }
  
  // updateToolbarZoom(reduce)
@@@ -4250,31 -4266,17 +4250,31 @@@ BookReader.prototype.canSwitchToMode = 
  // searchHighlightVisible
  //________
  // Returns true if a search highlight is currently being displayed
 -BookReader.prototype.searchHighlightVisible = function() {
 +BookReader.prototype.searchHighlightVisible = function() {    
 +    var results = this.searchResults;
 +    if (null == results) return false;    
 +    
      if (this.constMode2up == this.mode) {
 -        if (this.searchResults[this.twoPage.currentIndexL]
 -                || this.searchResults[this.twoPage.currentIndexR]) {
 -            return true;
 -        }
 -    } else { // 1up
 -        if (this.searchResults[this.currentIndex()]) {
 -            return true;
 +        var visiblePages = Array(this.twoPage.currentIndexL, this.twoPage.currentIndexR);
 +    } else if (this.constMode1up == this.mode) {
 +        var visiblePages = Array();
 +        visiblePages[0] = this.currentIndex();
 +    } else {
 +        return false;
 +    }
 +    
 +    var i, j;
 +    for (i=0; i<results.matches.length; i++) {
 +        //console.log(results.matches[i].par[0]);
 +        for (j=0; j<results.matches[i].par[0].boxes.length; j++) {
 +            var box = results.matches[i].par[0].boxes[j];
 +            var pageIndex = this.leafNumToIndex(box.page);
 +            if (jQuery.inArray(pageIndex, visiblePages) >= 0) {
 +                return true;
 +            }
          }
      }
 +    
      return false;
  }
  
@@@ -4337,6 -4339,7 +4337,7 @@@ BookReader.prototype.gotOpenLibraryReco
      // $$$ could refactor this so that 'this' is available
      if (olObject) {
          if (olObject['table_of_contents']) {
+             // XXX check here that TOC is valid
              self.updateTOC(olObject['table_of_contents']);
          }
      }
@@@ -4396,9 -4399,7 +4397,9 @@@ BookReader.util = 
  // ttsToggle()
  //______________________________________________________________________________
  BookReader.prototype.ttsToggle = function () {
 -    if (false == this.ttsPlaying) {        
 +    if (false == this.ttsPlaying) {
 +        this.ttsPlaying = true;
 +        this.showProgressPopup();    
          if(soundManager.supported()) {
              this.ttsStart();            
          } else {               
                if (oStatus.success) {                
                  this.ttsStart();
                } else {
 -                alert('Could not load soundManger2, possibly due to FlashBlock. Audio playback is disabled');
 +                alert('Could not load soundManager2, possibly due to FlashBlock. Audio playback is disabled');
                }
              }, this);        
          }
@@@ -4421,7 -4422,7 +4422,7 @@@ BookReader.prototype.ttsStart = functio
      if (soundManager.debugMode) console.log('starting readAloud');
      if (this.constModeThumb == this.mode) this.switchMode(this.constMode1up);
      
 -    this.ttsPlaying = true;
 +    //this.ttsPlaying = true; //set this in ttsToggle()
      this.ttsIndex = this.currentIndex();
      this.ttsFormat = 'mp3';
      if ($.browser.mozilla) {
@@@ -4439,7 -4440,7 +4440,7 @@@ BookReader.prototype.ttsStop = functio
      soundManager.stopAll();
      soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
      this.ttsRemoveHilites();
 -    this.ttsRemovePopup();
 +    this.removeProgressPopup();
  
      this.ttsPlaying     = false;
      this.ttsIndex       = null;  //leaf index
@@@ -4471,7 -4472,7 +4472,7 @@@ BookReader.prototype.ttsStartCB = funct
          return;
      }
      
 -    this.ttsShowPopup();
 +    this.showProgressPopup();
      
      ///// whileloading: broken on safari
      ///// onload fires on safari, but *after* the sound starts playing..
       id: 'chunk'+this.ttsIndex+'-0',
       //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...
 +     whileloading: function(){if (this.bytesLoaded == this.bytesTotal) this.br.removeProgressPopup();}, //onload never fires in FF...
 +     onload: function(){this.br.removeProgressPopup();} //whileloading never fires in safari...
      });    
      snd.br = this;
      snd.load();
      this.ttsNextChunk();
  }
  
 -// ttsShowPopup
 +// showProgressPopup
  //______________________________________________________________________________
 -BookReader.prototype.ttsShowPopup = function() {
 -    if (soundManager.debugMode) console.log('ttsShowPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
 +BookReader.prototype.showProgressPopup = function() {
 +    if (soundManager.debugMode) console.log('showProgressPopup index='+this.ttsIndex+' pos='+this.ttsPosition);
 +    if (this.popup) return;
      
      this.popup = document.createElement("div");
      $(this.popup).css({
          left:     $('#BookReader').width()-220 + 'px',
          width:    '220px',
          height:   '20px',
 -    }).attr('className', 'BRttsPopUp').appendTo('#BookReader');
 +    }).attr('className', 'BRprogresspopup').appendTo('#BookReader');
  
      htmlStr =  '&nbsp;';
  
      this.popup.innerHTML = htmlStr;
  }
  
 -// ttsRemovePopup
 +// removeProgressPopup
  //______________________________________________________________________________
 -BookReader.prototype.ttsRemovePopup = function() {
 +BookReader.prototype.removeProgressPopup = function() {
      $(this.popup).remove(); 
      this.popup=null;
  }