bookreader 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitmodules +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bookreader.gemspec +42 -0
- data/lib/bookreader/engine.rb +6 -0
- data/lib/bookreader/version.rb +3 -0
- data/lib/bookreader.rb +8 -0
- data/vendor/assets/images/BRicons.png +0 -0
- data/vendor/assets/images/BRicons.svg +94 -0
- data/vendor/assets/images/BRicons_ia.png +0 -0
- data/vendor/assets/images/back_pages.png +0 -0
- data/vendor/assets/images/book_bottom_icon.png +0 -0
- data/vendor/assets/images/book_down_icon.png +0 -0
- data/vendor/assets/images/book_left_icon.png +0 -0
- data/vendor/assets/images/book_leftmost_icon.png +0 -0
- data/vendor/assets/images/book_right_icon.png +0 -0
- data/vendor/assets/images/book_rightmost_icon.png +0 -0
- data/vendor/assets/images/book_top_icon.png +0 -0
- data/vendor/assets/images/book_up_icon.png +0 -0
- data/vendor/assets/images/books_graphic.svg +177 -0
- data/vendor/assets/images/booksplit.png +0 -0
- data/vendor/assets/images/control_pause_icon.png +0 -0
- data/vendor/assets/images/control_play_icon.png +0 -0
- data/vendor/assets/images/embed_icon.png +0 -0
- data/vendor/assets/images/icon-home-ia.png +0 -0
- data/vendor/assets/images/icon_OL-logo-xs.png +0 -0
- data/vendor/assets/images/icon_alert-xs.png +0 -0
- data/vendor/assets/images/icon_book.svg +12 -0
- data/vendor/assets/images/icon_bookmark.svg +12 -0
- data/vendor/assets/images/icon_close-pop.png +0 -0
- data/vendor/assets/images/icon_download.png +0 -0
- data/vendor/assets/images/icon_gear.svg +17 -0
- data/vendor/assets/images/icon_hamburger.svg +20 -0
- data/vendor/assets/images/icon_home.png +0 -0
- data/vendor/assets/images/icon_home.svg +21 -0
- data/vendor/assets/images/icon_home_ia.png +0 -0
- data/vendor/assets/images/icon_indicator.png +0 -0
- data/vendor/assets/images/icon_info.svg +12 -0
- data/vendor/assets/images/icon_one_page.svg +16 -0
- data/vendor/assets/images/icon_return.png +0 -0
- data/vendor/assets/images/icon_search_button.svg +14 -0
- data/vendor/assets/images/icon_search_button_blue.svg +18 -0
- data/vendor/assets/images/icon_share.svg +17 -0
- data/vendor/assets/images/icon_speaker.svg +18 -0
- data/vendor/assets/images/icon_speaker_open.svg +26 -0
- data/vendor/assets/images/icon_thumbnails.svg +19 -0
- data/vendor/assets/images/icon_two_pages.svg +17 -0
- data/vendor/assets/images/icon_zoomer.png +0 -0
- data/vendor/assets/images/left_edges.png +0 -0
- data/vendor/assets/images/loading.gif +0 -0
- data/vendor/assets/images/logo_icon.png +0 -0
- data/vendor/assets/images/marker_chap-off.png +0 -0
- data/vendor/assets/images/marker_chap-off.svg +11 -0
- data/vendor/assets/images/marker_chap-off_ia.png +0 -0
- data/vendor/assets/images/marker_chap-on.png +0 -0
- data/vendor/assets/images/marker_chap-on.svg +11 -0
- data/vendor/assets/images/marker_srch-off.png +0 -0
- data/vendor/assets/images/marker_srch-off.svg +11 -0
- data/vendor/assets/images/marker_srch-on.png +0 -0
- data/vendor/assets/images/marker_srch-on.svg +11 -0
- data/vendor/assets/images/marker_srchchap-off.png +0 -0
- data/vendor/assets/images/marker_srchchap-on.png +0 -0
- data/vendor/assets/images/nav_control-dn.png +0 -0
- data/vendor/assets/images/nav_control-dn_ia.png +0 -0
- data/vendor/assets/images/nav_control-up.png +0 -0
- data/vendor/assets/images/nav_control-up_ia.png +0 -0
- data/vendor/assets/images/nav_control.png +0 -0
- data/vendor/assets/images/one_page_mode_icon.png +0 -0
- data/vendor/assets/images/paper-badge.png +0 -0
- data/vendor/assets/images/print_icon.png +0 -0
- data/vendor/assets/images/progressbar.gif +0 -0
- data/vendor/assets/images/right_edges.png +0 -0
- data/vendor/assets/images/slider.png +0 -0
- data/vendor/assets/images/slider_ia.png +0 -0
- data/vendor/assets/images/thumbnail_mode_icon.png +0 -0
- data/vendor/assets/images/transparent.png +0 -0
- data/vendor/assets/images/two_page_mode_icon.png +0 -0
- data/vendor/assets/images/zoom_in_icon.png +0 -0
- data/vendor/assets/images/zoom_out_icon.png +0 -0
- data/vendor/assets/javascripts/BookReader.js +4849 -0
- data/vendor/assets/javascripts/BookReaderJSAdvanced.js +115 -0
- data/vendor/assets/javascripts/BookReaderJSSimple.js +56 -0
- data/vendor/assets/javascripts/IIIFBookReader.js +207 -0
- data/vendor/assets/javascripts/demo-iiif.js +26 -0
- data/vendor/assets/javascripts/dragscrollable-br.js +261 -0
- data/vendor/assets/javascripts/excanvas.compiled.js +35 -0
- data/vendor/assets/javascripts/jquery-1.10.1.js +9807 -0
- data/vendor/assets/javascripts/jquery-ui-1.12.0.min.js +18686 -0
- data/vendor/assets/javascripts/jquery.browser.min.js +14 -0
- data/vendor/assets/javascripts/jquery.bt.min.js +8 -0
- data/vendor/assets/javascripts/jquery.colorbox-min.js +6 -0
- data/vendor/assets/javascripts/jquery.ui.touch-punch.min.js +11 -0
- data/vendor/assets/javascripts/mmenu/dist/addons/navbars/jquery.mmenu.navbars.min.js +43 -0
- data/vendor/assets/javascripts/mmenu/dist/addons/offcanvas/jquery.mmenu.offcanvas.min.js +7 -0
- data/vendor/assets/javascripts/mmenu/dist/addons/screenreader/jquery.mmenu.screenreader.min.js +7 -0
- data/vendor/assets/javascripts/mmenu/dist/addons/searchfield/jquery.mmenu.searchfield.min.js +7 -0
- data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.all.min.js +151 -0
- data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.all.min.umd.js +162 -0
- data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.min.js +25 -0
- data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.min.umd.js +36 -0
- data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.oncanvas.min.js +13 -0
- data/vendor/assets/javascripts/mmenu/jquery.mmenu.searchfield.js +510 -0
- data/vendor/assets/javascripts/plugins/plugin.archive_analytics.js +67 -0
- data/vendor/assets/javascripts/plugins/plugin.chapters.js +197 -0
- data/vendor/assets/javascripts/plugins/plugin.iframe.js +73 -0
- data/vendor/assets/javascripts/plugins/plugin.mobile_nav.js +197 -0
- data/vendor/assets/javascripts/plugins/plugin.print.js +70 -0
- data/vendor/assets/javascripts/plugins/plugin.resume.js +73 -0
- data/vendor/assets/javascripts/plugins/plugin.search.js +523 -0
- data/vendor/assets/javascripts/plugins/plugin.themes.js +49 -0
- data/vendor/assets/javascripts/plugins/plugin.tts.js +523 -0
- data/vendor/assets/javascripts/plugins/plugin.url.js +168 -0
- data/vendor/assets/javascripts/soundmanager/license.txt +29 -0
- data/vendor/assets/javascripts/soundmanager/script/soundmanager2-jsmin.js +113 -0
- data/vendor/assets/javascripts/soundmanager/script/soundmanager2-nodebug-jsmin.js +83 -0
- data/vendor/assets/javascripts/soundmanager/script/soundmanager2-nodebug.js +2765 -0
- data/vendor/assets/javascripts/soundmanager/script/soundmanager2.js +6325 -0
- data/vendor/assets/javascripts/soundmanager/swf/soundmanager2.swf +0 -0
- data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_debug.swf +0 -0
- data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash9.swf +0 -0
- data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash9_debug.swf +0 -0
- data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash_xdomain.zip +0 -0
- data/vendor/assets/stylesheets/BookReader.css +1887 -0
- data/vendor/assets/stylesheets/BookReaderDemo.css +18 -0
- data/vendor/assets/stylesheets/BookReaderEmbed.css +0 -0
- data/vendor/assets/stylesheets/demo.css +0 -0
- data/vendor/assets/stylesheets/dist/addons/navbars/jquery.mmenu.navbars.css +29 -0
- data/vendor/assets/stylesheets/dist/addons/offcanvas/jquery.mmenu.offcanvas.css +14 -0
- data/vendor/assets/stylesheets/dist/addons/screenreader/jquery.mmenu.screenreader.css +1 -0
- data/vendor/assets/stylesheets/dist/addons/searchfield/jquery.mmenu.searchfield.css +16 -0
- data/vendor/assets/stylesheets/dist/css/jquery.mmenu.all.css +413 -0
- data/vendor/assets/stylesheets/dist/css/jquery.mmenu.css +80 -0
- data/vendor/assets/stylesheets/dist/css/jquery.mmenu.oncanvas.css +66 -0
- metadata +226 -0
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin for Text to Speech in BookReader
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Default options for TTS
|
|
6
|
+
jQuery.extend(BookReader.defaultOptions, {
|
|
7
|
+
server: 'ia600609.us.archive.org',
|
|
8
|
+
bookPath: '',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
// Extend the constructor to add TTS properties
|
|
12
|
+
BookReader.prototype.setup = (function (super_) {
|
|
13
|
+
return function (options) {
|
|
14
|
+
super_.call(this, options);
|
|
15
|
+
|
|
16
|
+
// Text-to-Speech properties
|
|
17
|
+
this.ttsPlaying = false;
|
|
18
|
+
this.ttsIndex = null; //leaf index
|
|
19
|
+
this.ttsPosition = -1; //chunk (paragraph) number
|
|
20
|
+
this.ttsBuffering = false;
|
|
21
|
+
this.ttsPoller = null;
|
|
22
|
+
this.ttsFormat = null;
|
|
23
|
+
|
|
24
|
+
this.server = options.server;
|
|
25
|
+
this.bookPath = options.bookPath;
|
|
26
|
+
|
|
27
|
+
this.isSoundManagerSupported = false;
|
|
28
|
+
|
|
29
|
+
if (typeof(soundManager) !== 'undefined') {
|
|
30
|
+
this.isSoundManagerSupported = soundManager.supported();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
})(BookReader.prototype.setup);
|
|
34
|
+
|
|
35
|
+
BookReader.prototype.init = (function(super_) {
|
|
36
|
+
return function() {
|
|
37
|
+
// Bind to events
|
|
38
|
+
this.bind(BookReader.eventNames.PostInit, function(e, br) {
|
|
39
|
+
jIcons = br.$('.BRicon.read').click(function(e) {
|
|
40
|
+
br.ttsToggle();
|
|
41
|
+
return false;
|
|
42
|
+
});
|
|
43
|
+
// Setup sound manager for read-aloud
|
|
44
|
+
if (br.isSoundManagerSupported)
|
|
45
|
+
br.setupSoundManager();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
this.bind(BookReader.eventNames.stop, function(e, br) {
|
|
49
|
+
this.ttsStop();
|
|
50
|
+
}.bind(this));
|
|
51
|
+
|
|
52
|
+
super_.call(this);
|
|
53
|
+
};
|
|
54
|
+
})(BookReader.prototype.init);
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
// Extend buildMobileDrawerElement
|
|
58
|
+
BookReader.prototype.buildMobileDrawerElement = (function (super_) {
|
|
59
|
+
return function () {
|
|
60
|
+
var $el = super_.call(this);
|
|
61
|
+
if (this.isSoundManagerSupported) {
|
|
62
|
+
$el.find('.BRmobileMenu__moreInfoRow').after($(
|
|
63
|
+
" <li>"
|
|
64
|
+
+" <span>"
|
|
65
|
+
+" <span class=\"DrawerIconWrapper \"><img class=\"DrawerIcon\" src=\""+this.imagesBaseURL+"icon_speaker_open.svg\" alt=\"info-speaker\"/></span>"
|
|
66
|
+
+" Read Aloud"
|
|
67
|
+
+" </span>"
|
|
68
|
+
+" <div>"
|
|
69
|
+
+" <span class='larger'>Press to toggle read aloud</span> <br/>"
|
|
70
|
+
+" <button class='BRicon read'></button>"
|
|
71
|
+
+" </div>"
|
|
72
|
+
+" </li>"
|
|
73
|
+
));
|
|
74
|
+
};
|
|
75
|
+
return $el;
|
|
76
|
+
};
|
|
77
|
+
})(BookReader.prototype.buildMobileDrawerElement);
|
|
78
|
+
|
|
79
|
+
// Extend initNavbar
|
|
80
|
+
BookReader.prototype.initNavbar = (function (super_) {
|
|
81
|
+
return function () {
|
|
82
|
+
var $el = super_.call(this);
|
|
83
|
+
var readIcon = '';
|
|
84
|
+
if (this.isSoundManagerSupported) {
|
|
85
|
+
$("<button class='BRicon read js-tooltip'></button>").insertAfter($el.find('.BRpage .BRicon.thumb'));
|
|
86
|
+
}
|
|
87
|
+
return $el;
|
|
88
|
+
};
|
|
89
|
+
})(BookReader.prototype.initNavbar);
|
|
90
|
+
|
|
91
|
+
// ttsToggle()
|
|
92
|
+
//______________________________________________________________________________
|
|
93
|
+
BookReader.prototype.ttsToggle = function () {
|
|
94
|
+
this.autoStop();
|
|
95
|
+
if (false == this.ttsPlaying) {
|
|
96
|
+
this.ttsPlaying = true;
|
|
97
|
+
this.showProgressPopup('Loading audio...');
|
|
98
|
+
this.ttsStart();
|
|
99
|
+
} else {
|
|
100
|
+
this.ttsStop();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// ttsStart(
|
|
105
|
+
//______________________________________________________________________________
|
|
106
|
+
BookReader.prototype.ttsStart = function () {
|
|
107
|
+
if (soundManager.debugMode) console.log('starting readAloud');
|
|
108
|
+
if (this.constModeThumb == this.mode)
|
|
109
|
+
this.switchMode(this.constMode1up);
|
|
110
|
+
|
|
111
|
+
this.$('.BRicon.read').addClass('unread');
|
|
112
|
+
|
|
113
|
+
this.ttsIndex = this.currentIndex();
|
|
114
|
+
this.ttsFormat = 'mp3';
|
|
115
|
+
if ($.browser.mozilla) {
|
|
116
|
+
this.ttsFormat = 'ogg';
|
|
117
|
+
}
|
|
118
|
+
this.ttsGetText(this.ttsIndex, this.ttsStartCB);
|
|
119
|
+
if (navigator.userAgent.match(/mobile/i)) {
|
|
120
|
+
// HACK for iOS. Security restrictions require playback to be triggered
|
|
121
|
+
// by a user click/touch. This intention gets lost in the ajax callback
|
|
122
|
+
// above, but for some reason, if we start the audio here, it works
|
|
123
|
+
soundManager.createSound({url: this.getSoundUrl(' ')}).play();
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// ttsStop()
|
|
128
|
+
//______________________________________________________________________________
|
|
129
|
+
BookReader.prototype.ttsStop = function () {
|
|
130
|
+
if (false == this.ttsPlaying) return;
|
|
131
|
+
this.$('.BRicon.read').removeClass('unread');
|
|
132
|
+
|
|
133
|
+
if (soundManager.debugMode) console.log('stopping readaloud');
|
|
134
|
+
soundManager.stopAll();
|
|
135
|
+
soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
|
|
136
|
+
this.ttsRemoveHilites();
|
|
137
|
+
this.removeProgressPopup();
|
|
138
|
+
|
|
139
|
+
this.ttsPlaying = false;
|
|
140
|
+
this.ttsIndex = null; //leaf index
|
|
141
|
+
this.ttsPosition = -1; //chunk (paragraph) number
|
|
142
|
+
this.ttsBuffering = false;
|
|
143
|
+
this.ttsPoller = null;
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
// ttsGetText()
|
|
147
|
+
//______________________________________________________________________________
|
|
148
|
+
BookReader.prototype.ttsGetText = function(index, callback) {
|
|
149
|
+
var url = 'https://'+this.server+'/BookReader/BookReaderGetTextWrapper.php?path='+this.bookPath+'_djvu.xml&page='+index;
|
|
150
|
+
var self = this;
|
|
151
|
+
this.ttsAjax = $.ajax({
|
|
152
|
+
url: url,
|
|
153
|
+
dataType:'jsonp',
|
|
154
|
+
success: function(data) {
|
|
155
|
+
callback.call(self, data);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
BookReader.prototype.getSoundUrl = function(dataString) {
|
|
161
|
+
return 'https://'+this.server+'/BookReader/BookReaderGetTTS.php?string='
|
|
162
|
+
+ dataString
|
|
163
|
+
+ '&format=.'+this.ttsFormat;
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// ttsStartCB(): text-to-speech callback
|
|
167
|
+
//______________________________________________________________________________
|
|
168
|
+
BookReader.prototype.ttsStartCB = function(data) {
|
|
169
|
+
if (soundManager.debugMode) console.log('ttsStartCB got data: ' + data);
|
|
170
|
+
this.ttsChunks = data;
|
|
171
|
+
this.ttsHilites = [];
|
|
172
|
+
|
|
173
|
+
//deal with the page being blank
|
|
174
|
+
if (0 == data.length) {
|
|
175
|
+
if (soundManager.debugMode) console.log('first page is blank!');
|
|
176
|
+
if(this.ttsAdvance(true)) {
|
|
177
|
+
this.ttsGetText(this.ttsIndex, this.ttsStartCB);
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.showProgressPopup('Loading audio...');
|
|
183
|
+
|
|
184
|
+
///// Many soundManger2 callbacks are broken when using HTML5 audio.
|
|
185
|
+
///// whileloading: broken on safari, worked in FF4, but broken on FireFox 5
|
|
186
|
+
///// onload: fires on safari, but *after* the sound starts playing, and does not fire in FF or IE9
|
|
187
|
+
///// onbufferchange: fires in FF5 using HTML5 audio, but not in safari using flash audio
|
|
188
|
+
///// whileplaying: fires everywhere
|
|
189
|
+
|
|
190
|
+
var dataString = data[0][0];
|
|
191
|
+
dataString = encodeURIComponent(dataString);
|
|
192
|
+
|
|
193
|
+
//the .ogg is to trick SoundManager2 to use the HTML5 audio player;
|
|
194
|
+
var soundUrl = this.getSoundUrl(dataString);
|
|
195
|
+
|
|
196
|
+
this.ttsPosition = -1;
|
|
197
|
+
var snd = soundManager.createSound({
|
|
198
|
+
id: 'chunk'+this.ttsIndex+'-0',
|
|
199
|
+
url: soundUrl,
|
|
200
|
+
onload: function(){
|
|
201
|
+
this.br.removeProgressPopup();
|
|
202
|
+
}, //fires in safari...
|
|
203
|
+
onbufferchange: function(){
|
|
204
|
+
if (false == this.isBuffering) {
|
|
205
|
+
this.br.removeProgressPopup();
|
|
206
|
+
}
|
|
207
|
+
}, //fires in FF and IE9
|
|
208
|
+
onready: function() {
|
|
209
|
+
this.br.removeProgressPopup();
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
snd.br = this;
|
|
213
|
+
snd.load();
|
|
214
|
+
|
|
215
|
+
this.ttsNextChunk();
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
// ttsNextPageCB
|
|
220
|
+
//______________________________________________________________________________
|
|
221
|
+
BookReader.prototype.ttsNextPageCB = function (data) {
|
|
222
|
+
this.ttsNextChunks = data;
|
|
223
|
+
if (soundManager.debugMode) console.log('preloaded next chunks.. data is ' + data);
|
|
224
|
+
|
|
225
|
+
if (true == this.ttsBuffering) {
|
|
226
|
+
if (soundManager.debugMode) console.log('ttsNextPageCB: ttsBuffering is true');
|
|
227
|
+
this.ttsBuffering = false;
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
// ttsLoadChunk
|
|
232
|
+
//______________________________________________________________________________
|
|
233
|
+
BookReader.prototype.ttsLoadChunk = function (page, pos, string) {
|
|
234
|
+
var snd = soundManager.createSound({
|
|
235
|
+
id: 'chunk'+page+'-'+pos,
|
|
236
|
+
url: 'https://'+this.server+'/BookReader/BookReaderGetTTS.php?string=' + encodeURIComponent(string) + '&format=.'+this.ttsFormat //the .ogg is to trick SoundManager2 to use the HTML5 audio player
|
|
237
|
+
});
|
|
238
|
+
snd.br = this;
|
|
239
|
+
snd.load()
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
// ttsNextChunk()
|
|
244
|
+
//______________________________________________________________________________
|
|
245
|
+
// This function into two parts: ttsNextChunk gets run before page flip animation
|
|
246
|
+
// and ttsNextChunkPhase2 get run after page flip animation.
|
|
247
|
+
// If a page flip is necessary, ttsAdvance() will return false so Phase2 isn't
|
|
248
|
+
// called. Instead, this.animationFinishedCallback is set, so that Phase2
|
|
249
|
+
// continues after animation is finished.
|
|
250
|
+
|
|
251
|
+
BookReader.prototype.ttsNextChunk = function () {
|
|
252
|
+
if (soundManager.debugMode) console.log('nextchunk pos=' + this.ttsPosition);
|
|
253
|
+
|
|
254
|
+
if (-1 != this.ttsPosition) {
|
|
255
|
+
soundManager.destroySound('chunk'+this.ttsIndex+'-'+this.ttsPosition);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
this.ttsRemoveHilites(); //remove old hilights
|
|
259
|
+
|
|
260
|
+
var moreToPlay = this.ttsAdvance();
|
|
261
|
+
|
|
262
|
+
if (moreToPlay) {
|
|
263
|
+
this.ttsNextChunkPhase2();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
//This function is called again when ttsPlay() has finished playback.
|
|
267
|
+
//If the next chunk of text has not yet finished loading, ttsPlay()
|
|
268
|
+
//will start polling until the next chunk is ready.
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
// ttsNextChunkPhase2()
|
|
272
|
+
//______________________________________________________________________________
|
|
273
|
+
// page flip animation has now completed
|
|
274
|
+
BookReader.prototype.ttsNextChunkPhase2 = function () {
|
|
275
|
+
if (null == this.ttsChunks) {
|
|
276
|
+
alert('error: ttsChunks is null?'); //TODO
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (0 == this.ttsChunks.length) {
|
|
281
|
+
if (soundManager.debugMode) console.log('ttsNextChunk2: ttsChunks.length is zero.. hacking...');
|
|
282
|
+
this.ttsStartCB(this.ttsChunks);
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (soundManager.debugMode) console.log('next chunk is ' + this.ttsPosition);
|
|
287
|
+
|
|
288
|
+
//prefetch next page of text
|
|
289
|
+
if (0 == this.ttsPosition) {
|
|
290
|
+
if (this.ttsIndex<(this.getNumLeafs()-1)) {
|
|
291
|
+
this.ttsGetText(this.ttsIndex+1, this.ttsNextPageCB);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
this.ttsPrefetchAudio();
|
|
296
|
+
|
|
297
|
+
this.ttsPlay();
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
// ttsAdvance()
|
|
301
|
+
//______________________________________________________________________________
|
|
302
|
+
// 1. advance ttsPosition
|
|
303
|
+
// 2. if necessary, advance ttsIndex, and copy ttsNextChunks to ttsChunks
|
|
304
|
+
// 3. if necessary, flip to current page, or scroll so chunk is visible
|
|
305
|
+
// 4. do something smart is ttsNextChunks has not yet finished preloading (TODO)
|
|
306
|
+
// 5. stop playing at end of book
|
|
307
|
+
|
|
308
|
+
BookReader.prototype.ttsAdvance = function (starting) {
|
|
309
|
+
this.ttsPosition++;
|
|
310
|
+
|
|
311
|
+
if (this.ttsPosition >= this.ttsChunks.length) {
|
|
312
|
+
|
|
313
|
+
if (this.ttsIndex == (this.getNumLeafs()-1)) {
|
|
314
|
+
if (soundManager.debugMode) console.log('tts stop');
|
|
315
|
+
return false;
|
|
316
|
+
} else {
|
|
317
|
+
if ((null != this.ttsNextChunks) || (starting)) {
|
|
318
|
+
if (soundManager.debugMode) console.log('moving to next page!');
|
|
319
|
+
this.ttsIndex++;
|
|
320
|
+
this.ttsPosition = 0;
|
|
321
|
+
this.ttsChunks = this.ttsNextChunks;
|
|
322
|
+
this.ttsNextChunks = null;
|
|
323
|
+
|
|
324
|
+
//A page flip might be necessary. This code is confusing since
|
|
325
|
+
//ttsNextChunks might be null if we are starting on a blank page.
|
|
326
|
+
if (this.constMode2up == this.mode) {
|
|
327
|
+
if ((this.ttsIndex != this.twoPage.currentIndexL) && (this.ttsIndex != this.twoPage.currentIndexR)) {
|
|
328
|
+
if (!starting) {
|
|
329
|
+
this.animationFinishedCallback = this.ttsNextChunkPhase2;
|
|
330
|
+
this.next();
|
|
331
|
+
return false;
|
|
332
|
+
} else {
|
|
333
|
+
this.next();
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} else {
|
|
341
|
+
if (soundManager.debugMode) console.log('ttsAdvance: ttsNextChunks is null');
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return true;
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// ttsPrefetchAudio()
|
|
351
|
+
//______________________________________________________________________________
|
|
352
|
+
BookReader.prototype.ttsPrefetchAudio = function () {
|
|
353
|
+
|
|
354
|
+
if(false != this.ttsBuffering) {
|
|
355
|
+
alert('TTS Error: prefetch() called while content still buffering!');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
//preload next chunk
|
|
360
|
+
var nextPos = this.ttsPosition+1;
|
|
361
|
+
if (nextPos < this.ttsChunks.length) {
|
|
362
|
+
this.ttsLoadChunk(this.ttsIndex, nextPos, this.ttsChunks[nextPos][0]);
|
|
363
|
+
} else {
|
|
364
|
+
//for a short page, preload might nt have yet returned..
|
|
365
|
+
if (soundManager.debugMode) console.log('preloading chunk 0 from next page, index='+(this.ttsIndex+1));
|
|
366
|
+
if (null != this.ttsNextChunks) {
|
|
367
|
+
if (0 != this.ttsNextChunks.length) {
|
|
368
|
+
this.ttsLoadChunk(this.ttsIndex+1, 0, this.ttsNextChunks[0][0]);
|
|
369
|
+
} else {
|
|
370
|
+
if (soundManager.debugMode) console.log('prefetchAudio(): ttsNextChunks is zero length!');
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
if (soundManager.debugMode) console.log('ttsNextChunks is null, not preloading next page');
|
|
374
|
+
this.ttsBuffering = true;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
};
|
|
379
|
+
|
|
380
|
+
// ttsPlay()
|
|
381
|
+
//______________________________________________________________________________
|
|
382
|
+
BookReader.prototype.ttsPlay = function () {
|
|
383
|
+
|
|
384
|
+
var chunk = this.ttsChunks[this.ttsPosition];
|
|
385
|
+
if (soundManager.debugMode) {
|
|
386
|
+
console.log('ttsPlay position = ' + this.ttsPosition);
|
|
387
|
+
console.log('chunk = ' + chunk);
|
|
388
|
+
console.log(this.ttsChunks);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
//add new hilights
|
|
392
|
+
if (this.constMode2up == this.mode) {
|
|
393
|
+
this.ttsHilite2UP(chunk);
|
|
394
|
+
} else {
|
|
395
|
+
this.ttsHilite1UP(chunk);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
this.ttsScrollToChunk(chunk);
|
|
399
|
+
|
|
400
|
+
//play current chunk
|
|
401
|
+
if (false == this.ttsBuffering) {
|
|
402
|
+
soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsNextChunk();}});
|
|
403
|
+
} else {
|
|
404
|
+
soundManager.play('chunk'+this.ttsIndex+'-'+this.ttsPosition,{onfinish:function(){br.ttsStartPolling();}});
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
// scrollToChunk()
|
|
409
|
+
//______________________________________________________________________________
|
|
410
|
+
BookReader.prototype.ttsScrollToChunk = function(chunk) {
|
|
411
|
+
if (this.constMode1up != this.mode) return;
|
|
412
|
+
|
|
413
|
+
var leafTop = 0;
|
|
414
|
+
var h;
|
|
415
|
+
var i;
|
|
416
|
+
for (i=0; i<this.ttsIndex; i++) {
|
|
417
|
+
h = parseInt(this._getPageHeight(i)/this.reduce);
|
|
418
|
+
leafTop += h + this.padding;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
var chunkTop = chunk[1][3]; //coords are in l,b,r,t order
|
|
422
|
+
var chunkBot = chunk[chunk.length-1][1];
|
|
423
|
+
|
|
424
|
+
var topOfFirstChunk = leafTop + chunkTop/this.reduce;
|
|
425
|
+
var botOfLastChunk = leafTop + chunkBot/this.reduce;
|
|
426
|
+
|
|
427
|
+
if (soundManager.debugMode) console.log('leafTop = ' + leafTop + ' topOfFirstChunk = ' + topOfFirstChunk + ' botOfLastChunk = ' + botOfLastChunk);
|
|
428
|
+
|
|
429
|
+
var containerTop = this.refs.$brContainer.prop('scrollTop');
|
|
430
|
+
var containerBot = containerTop + this.refs.$brContainer.height();
|
|
431
|
+
if (soundManager.debugMode) console.log('containerTop = ' + containerTop + ' containerBot = ' + containerBot);
|
|
432
|
+
|
|
433
|
+
if ((topOfFirstChunk < containerTop) || (botOfLastChunk > containerBot)) {
|
|
434
|
+
this.refs.$brContainer.stop(true).animate({scrollTop: topOfFirstChunk},'fast');
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
// ttsHilite1UP()
|
|
439
|
+
//______________________________________________________________________________
|
|
440
|
+
BookReader.prototype.ttsHilite1UP = function(chunk) {
|
|
441
|
+
var i;
|
|
442
|
+
for (i=1; i<chunk.length; i++) {
|
|
443
|
+
//each rect is an array of l,b,r,t coords (djvu.xml ordering...)
|
|
444
|
+
var l = chunk[i][0];
|
|
445
|
+
var b = chunk[i][1];
|
|
446
|
+
var r = chunk[i][2];
|
|
447
|
+
var t = chunk[i][3];
|
|
448
|
+
|
|
449
|
+
var div = document.createElement('div');
|
|
450
|
+
this.ttsHilites.push(div);
|
|
451
|
+
$(div).prop('className', 'BookReaderSearchHilite').appendTo(
|
|
452
|
+
this.$('.pagediv'+this.ttsIndex)
|
|
453
|
+
);
|
|
454
|
+
|
|
455
|
+
$(div).css({
|
|
456
|
+
width: (r-l)/this.reduce + 'px',
|
|
457
|
+
height: (b-t)/this.reduce + 'px',
|
|
458
|
+
left: l/this.reduce + 'px',
|
|
459
|
+
top: t/this.reduce +'px'
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
// ttsHilite2UP()
|
|
466
|
+
//______________________________________________________________________________
|
|
467
|
+
BookReader.prototype.ttsHilite2UP = function (chunk) {
|
|
468
|
+
var i;
|
|
469
|
+
for (i=1; i<chunk.length; i++) {
|
|
470
|
+
//each rect is an array of l,b,r,t coords (djvu.xml ordering...)
|
|
471
|
+
var l = chunk[i][0];
|
|
472
|
+
var b = chunk[i][1];
|
|
473
|
+
var r = chunk[i][2];
|
|
474
|
+
var t = chunk[i][3];
|
|
475
|
+
|
|
476
|
+
var div = document.createElement('div');
|
|
477
|
+
this.ttsHilites.push(div);
|
|
478
|
+
$(div).prop('className', 'BookReaderSearchHilite').css('zIndex', 3).appendTo(this.refs.$brTwoPageView);
|
|
479
|
+
this.setHilightCss2UP(div, this.ttsIndex, l, r, t, b);
|
|
480
|
+
}
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// ttsRemoveHilites()
|
|
484
|
+
//______________________________________________________________________________
|
|
485
|
+
BookReader.prototype.ttsRemoveHilites = function (chunk) {
|
|
486
|
+
$(this.ttsHilites).remove();
|
|
487
|
+
this.ttsHilites = [];
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// ttsStartPolling()
|
|
491
|
+
//______________________________________________________________________________
|
|
492
|
+
// Play of the current chunk has ended, but the next chunk has not yet been loaded.
|
|
493
|
+
// We need to wait for the text for the next page to be loaded, so we can
|
|
494
|
+
// load the next audio chunk
|
|
495
|
+
BookReader.prototype.ttsStartPolling = function () {
|
|
496
|
+
if (soundManager.debugMode) console.log('Starting the TTS poller...');
|
|
497
|
+
var self = this;
|
|
498
|
+
this.ttsPoller=setInterval(function(){
|
|
499
|
+
if (self.ttsBuffering) {return;}
|
|
500
|
+
|
|
501
|
+
if (soundManager.debugMode) console.log('TTS buffering finished!');
|
|
502
|
+
clearInterval(self.ttsPoller);
|
|
503
|
+
self.ttsPoller = null;
|
|
504
|
+
self.ttsPrefetchAudio();
|
|
505
|
+
self.ttsNextChunk();
|
|
506
|
+
},500);
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// setupSoundManager()
|
|
510
|
+
//______________________________________________________________________________
|
|
511
|
+
BookReader.prototype.setupSoundManager = function() {
|
|
512
|
+
soundManager.setup({
|
|
513
|
+
debugMode: false,
|
|
514
|
+
// Note, there's a bug in Chrome regarding range requests.
|
|
515
|
+
// Flash is used as a workaround.
|
|
516
|
+
// See https://bugs.chromium.org/p/chromium/issues/detail?id=505707
|
|
517
|
+
preferFlash: true,
|
|
518
|
+
url: '/bookreader/BookReader/soundmanager/swf',
|
|
519
|
+
useHTML5Audio: true,
|
|
520
|
+
//flash 8 version of swf is buggy when calling play() on a sound that is still loading
|
|
521
|
+
flashVersion: 9
|
|
522
|
+
});
|
|
523
|
+
};
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin for URL management in BookReader
|
|
3
|
+
* Note read more about the url "fragment" here:
|
|
4
|
+
* https://openlibrary.org/dev/docs/bookurls
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
jQuery.extend(BookReader.defaultOptions, {
|
|
8
|
+
enableUrlPlugin: true,
|
|
9
|
+
bookId: "",
|
|
10
|
+
// Defaults can be a urlFragment string
|
|
11
|
+
defaults: null,
|
|
12
|
+
updateWindowTitle: false,
|
|
13
|
+
|
|
14
|
+
// 'history' | 'hash',
|
|
15
|
+
urlMode: 'hash',
|
|
16
|
+
|
|
17
|
+
// When using 'history' mode, this part of the URL is kept constant
|
|
18
|
+
// Example: /details/plato/
|
|
19
|
+
urlHistoryBasePath: '/',
|
|
20
|
+
|
|
21
|
+
// Only these params will be reflected onto the URL
|
|
22
|
+
urlTrackedParams: ['page', 'search', 'mode', 'region', 'highlight'],
|
|
23
|
+
|
|
24
|
+
// If true, don't update the URL when page == n0 (eg "/page/n0")
|
|
25
|
+
urlTrackIndex0: false,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
BookReader.prototype.setup = (function(super_) {
|
|
29
|
+
return function(options) {
|
|
30
|
+
super_.call(this, options);
|
|
31
|
+
|
|
32
|
+
this.bookId = options.bookId;
|
|
33
|
+
this.defaults = options.defaults;
|
|
34
|
+
|
|
35
|
+
this.locationPollId = null;
|
|
36
|
+
this.oldLocationHash = null;
|
|
37
|
+
this.oldUserHash = null;
|
|
38
|
+
};
|
|
39
|
+
})(BookReader.prototype.setup);
|
|
40
|
+
|
|
41
|
+
BookReader.prototype.init = (function(super_) {
|
|
42
|
+
return function() {
|
|
43
|
+
|
|
44
|
+
if (this.options.enableUrlPlugin) {
|
|
45
|
+
this.bind(BookReader.eventNames.PostInit, function(e, br) {
|
|
46
|
+
if (br.options.updateWindowTitle) {
|
|
47
|
+
document.title = br.shortTitle(50);
|
|
48
|
+
}
|
|
49
|
+
if (br.options.urlMode === 'hash') {
|
|
50
|
+
br.urlStartLocationPolling();
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
this.bind(BookReader.eventNames.fragmentChange,
|
|
55
|
+
this.urlUpdateFragment.bind(this)
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
super_.call(this);
|
|
59
|
+
};
|
|
60
|
+
})(BookReader.prototype.init);
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns a shortened version of the title with the maximum number of characters
|
|
64
|
+
* @param {string} maximumCharacters
|
|
65
|
+
*/
|
|
66
|
+
BookReader.prototype.shortTitle = function(maximumCharacters) {
|
|
67
|
+
if (this.bookTitle.length < maximumCharacters) {
|
|
68
|
+
return this.bookTitle;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
var title = this.bookTitle.substr(0, maximumCharacters - 3);
|
|
72
|
+
title += "...";
|
|
73
|
+
return title;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Starts polling of window.location to see hash fragment changes
|
|
78
|
+
*/
|
|
79
|
+
BookReader.prototype.urlStartLocationPolling = function() {
|
|
80
|
+
var self = this;
|
|
81
|
+
this.oldLocationHash = this.urlReadFragment();
|
|
82
|
+
|
|
83
|
+
if (this.locationPollId) {
|
|
84
|
+
clearInterval(this.locationPollID);
|
|
85
|
+
this.locationPollId = null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
this.locationPollId = setInterval(function() {
|
|
89
|
+
var newFragment = self.urlReadFragment();
|
|
90
|
+
if (newFragment != self.oldLocationHash && newFragment != self.oldUserHash) {
|
|
91
|
+
self.trigger(BookReader.eventNames.stop);
|
|
92
|
+
|
|
93
|
+
// Queue change if animating
|
|
94
|
+
if (self.animating) {
|
|
95
|
+
self.autoStop();
|
|
96
|
+
self.animationFinishedCallback = function() {
|
|
97
|
+
self.updateFromParams(self.paramsFromFragment(newFragment));
|
|
98
|
+
};
|
|
99
|
+
} else {
|
|
100
|
+
// update immediately
|
|
101
|
+
self.updateFromParams(self.paramsFromFragment(newFragment));
|
|
102
|
+
}
|
|
103
|
+
self.oldUserHash = newFragment;
|
|
104
|
+
}
|
|
105
|
+
}, 500);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Update URL from the current parameters.
|
|
110
|
+
* Call this instead of manually using window.location.replace
|
|
111
|
+
*/
|
|
112
|
+
BookReader.prototype.urlUpdateFragment = function() {
|
|
113
|
+
var allParams = this.paramsFromCurrent();
|
|
114
|
+
var params = {};
|
|
115
|
+
|
|
116
|
+
if (!this.options.urlTrackIndex0
|
|
117
|
+
&& 'undefined' !== typeof(allParams.index)
|
|
118
|
+
&& allParams.index === 0) {
|
|
119
|
+
delete allParams.index;
|
|
120
|
+
delete allParams.page;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
for (var i = 0; i < this.options.urlTrackedParams.length; i++) {
|
|
124
|
+
param = this.options.urlTrackedParams[i];
|
|
125
|
+
if (param in allParams) {
|
|
126
|
+
params[param] = allParams[param];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
var newFragment = this.fragmentFromParams(params);
|
|
131
|
+
var currFragment = this.urlReadFragment();
|
|
132
|
+
|
|
133
|
+
if (currFragment === newFragment) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (this.options.urlMode === 'history') {
|
|
138
|
+
if (window.history && window.history.replaceState) {
|
|
139
|
+
var baseWithoutSlash = this.options.urlHistoryBasePath.replace(/\/+$/, '');
|
|
140
|
+
var newFragmentWithSlash = newFragment === '' ? '' : '/' + newFragment;
|
|
141
|
+
|
|
142
|
+
window.history.replaceState(
|
|
143
|
+
{},
|
|
144
|
+
null,
|
|
145
|
+
baseWithoutSlash
|
|
146
|
+
+ newFragmentWithSlash
|
|
147
|
+
+ window.location.search
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
window.location.replace('#' + newFragment);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.oldLocationHash = newFragment;
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Will read either the hash or URL and return the bookreader fragment
|
|
159
|
+
* @return {string}
|
|
160
|
+
*/
|
|
161
|
+
BookReader.prototype.urlReadFragment = function() {
|
|
162
|
+
if (this.options.urlMode === 'history') {
|
|
163
|
+
return window.location.pathname.substr(this.options.urlHistoryBasePath.length);
|
|
164
|
+
} else {
|
|
165
|
+
return window.location.hash.substr(1);
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|