this.printPopup = null;
this.searchTerm = '';
- this.searchResults = {};
+ this.searchResults = null;
this.firstIndex = null;
// 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)
});
}
-// 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()
// 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() {
$('#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();
}
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>"
self.zoom(-1);
return false;
});
+
+ $('#booksearch').bind('submit', function() {
+ self.search($('#textSrch').val());
+ });
+
}
// updateToolbarZoom(reduce)
// 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;
}
// $$$ 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']);
}
}
// 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);
}
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) {
soundManager.stopAll();
soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
this.ttsRemoveHilites();
- this.ttsRemovePopup();
+ this.removeProgressPopup();
this.ttsPlaying = false;
this.ttsIndex = null; //leaf index
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 = ' ';
this.popup.innerHTML = htmlStr;
}
-// ttsRemovePopup
+// removeProgressPopup
//______________________________________________________________________________
-BookReader.prototype.ttsRemovePopup = function() {
+BookReader.prototype.removeProgressPopup = function() {
$(this.popup).remove();
this.popup=null;
}