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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitmodules +3 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +6 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +43 -0
  9. data/Rakefile +10 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/bookreader.gemspec +42 -0
  13. data/lib/bookreader/engine.rb +6 -0
  14. data/lib/bookreader/version.rb +3 -0
  15. data/lib/bookreader.rb +8 -0
  16. data/vendor/assets/images/BRicons.png +0 -0
  17. data/vendor/assets/images/BRicons.svg +94 -0
  18. data/vendor/assets/images/BRicons_ia.png +0 -0
  19. data/vendor/assets/images/back_pages.png +0 -0
  20. data/vendor/assets/images/book_bottom_icon.png +0 -0
  21. data/vendor/assets/images/book_down_icon.png +0 -0
  22. data/vendor/assets/images/book_left_icon.png +0 -0
  23. data/vendor/assets/images/book_leftmost_icon.png +0 -0
  24. data/vendor/assets/images/book_right_icon.png +0 -0
  25. data/vendor/assets/images/book_rightmost_icon.png +0 -0
  26. data/vendor/assets/images/book_top_icon.png +0 -0
  27. data/vendor/assets/images/book_up_icon.png +0 -0
  28. data/vendor/assets/images/books_graphic.svg +177 -0
  29. data/vendor/assets/images/booksplit.png +0 -0
  30. data/vendor/assets/images/control_pause_icon.png +0 -0
  31. data/vendor/assets/images/control_play_icon.png +0 -0
  32. data/vendor/assets/images/embed_icon.png +0 -0
  33. data/vendor/assets/images/icon-home-ia.png +0 -0
  34. data/vendor/assets/images/icon_OL-logo-xs.png +0 -0
  35. data/vendor/assets/images/icon_alert-xs.png +0 -0
  36. data/vendor/assets/images/icon_book.svg +12 -0
  37. data/vendor/assets/images/icon_bookmark.svg +12 -0
  38. data/vendor/assets/images/icon_close-pop.png +0 -0
  39. data/vendor/assets/images/icon_download.png +0 -0
  40. data/vendor/assets/images/icon_gear.svg +17 -0
  41. data/vendor/assets/images/icon_hamburger.svg +20 -0
  42. data/vendor/assets/images/icon_home.png +0 -0
  43. data/vendor/assets/images/icon_home.svg +21 -0
  44. data/vendor/assets/images/icon_home_ia.png +0 -0
  45. data/vendor/assets/images/icon_indicator.png +0 -0
  46. data/vendor/assets/images/icon_info.svg +12 -0
  47. data/vendor/assets/images/icon_one_page.svg +16 -0
  48. data/vendor/assets/images/icon_return.png +0 -0
  49. data/vendor/assets/images/icon_search_button.svg +14 -0
  50. data/vendor/assets/images/icon_search_button_blue.svg +18 -0
  51. data/vendor/assets/images/icon_share.svg +17 -0
  52. data/vendor/assets/images/icon_speaker.svg +18 -0
  53. data/vendor/assets/images/icon_speaker_open.svg +26 -0
  54. data/vendor/assets/images/icon_thumbnails.svg +19 -0
  55. data/vendor/assets/images/icon_two_pages.svg +17 -0
  56. data/vendor/assets/images/icon_zoomer.png +0 -0
  57. data/vendor/assets/images/left_edges.png +0 -0
  58. data/vendor/assets/images/loading.gif +0 -0
  59. data/vendor/assets/images/logo_icon.png +0 -0
  60. data/vendor/assets/images/marker_chap-off.png +0 -0
  61. data/vendor/assets/images/marker_chap-off.svg +11 -0
  62. data/vendor/assets/images/marker_chap-off_ia.png +0 -0
  63. data/vendor/assets/images/marker_chap-on.png +0 -0
  64. data/vendor/assets/images/marker_chap-on.svg +11 -0
  65. data/vendor/assets/images/marker_srch-off.png +0 -0
  66. data/vendor/assets/images/marker_srch-off.svg +11 -0
  67. data/vendor/assets/images/marker_srch-on.png +0 -0
  68. data/vendor/assets/images/marker_srch-on.svg +11 -0
  69. data/vendor/assets/images/marker_srchchap-off.png +0 -0
  70. data/vendor/assets/images/marker_srchchap-on.png +0 -0
  71. data/vendor/assets/images/nav_control-dn.png +0 -0
  72. data/vendor/assets/images/nav_control-dn_ia.png +0 -0
  73. data/vendor/assets/images/nav_control-up.png +0 -0
  74. data/vendor/assets/images/nav_control-up_ia.png +0 -0
  75. data/vendor/assets/images/nav_control.png +0 -0
  76. data/vendor/assets/images/one_page_mode_icon.png +0 -0
  77. data/vendor/assets/images/paper-badge.png +0 -0
  78. data/vendor/assets/images/print_icon.png +0 -0
  79. data/vendor/assets/images/progressbar.gif +0 -0
  80. data/vendor/assets/images/right_edges.png +0 -0
  81. data/vendor/assets/images/slider.png +0 -0
  82. data/vendor/assets/images/slider_ia.png +0 -0
  83. data/vendor/assets/images/thumbnail_mode_icon.png +0 -0
  84. data/vendor/assets/images/transparent.png +0 -0
  85. data/vendor/assets/images/two_page_mode_icon.png +0 -0
  86. data/vendor/assets/images/zoom_in_icon.png +0 -0
  87. data/vendor/assets/images/zoom_out_icon.png +0 -0
  88. data/vendor/assets/javascripts/BookReader.js +4849 -0
  89. data/vendor/assets/javascripts/BookReaderJSAdvanced.js +115 -0
  90. data/vendor/assets/javascripts/BookReaderJSSimple.js +56 -0
  91. data/vendor/assets/javascripts/IIIFBookReader.js +207 -0
  92. data/vendor/assets/javascripts/demo-iiif.js +26 -0
  93. data/vendor/assets/javascripts/dragscrollable-br.js +261 -0
  94. data/vendor/assets/javascripts/excanvas.compiled.js +35 -0
  95. data/vendor/assets/javascripts/jquery-1.10.1.js +9807 -0
  96. data/vendor/assets/javascripts/jquery-ui-1.12.0.min.js +18686 -0
  97. data/vendor/assets/javascripts/jquery.browser.min.js +14 -0
  98. data/vendor/assets/javascripts/jquery.bt.min.js +8 -0
  99. data/vendor/assets/javascripts/jquery.colorbox-min.js +6 -0
  100. data/vendor/assets/javascripts/jquery.ui.touch-punch.min.js +11 -0
  101. data/vendor/assets/javascripts/mmenu/dist/addons/navbars/jquery.mmenu.navbars.min.js +43 -0
  102. data/vendor/assets/javascripts/mmenu/dist/addons/offcanvas/jquery.mmenu.offcanvas.min.js +7 -0
  103. data/vendor/assets/javascripts/mmenu/dist/addons/screenreader/jquery.mmenu.screenreader.min.js +7 -0
  104. data/vendor/assets/javascripts/mmenu/dist/addons/searchfield/jquery.mmenu.searchfield.min.js +7 -0
  105. data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.all.min.js +151 -0
  106. data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.all.min.umd.js +162 -0
  107. data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.min.js +25 -0
  108. data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.min.umd.js +36 -0
  109. data/vendor/assets/javascripts/mmenu/dist/js/jquery.mmenu.oncanvas.min.js +13 -0
  110. data/vendor/assets/javascripts/mmenu/jquery.mmenu.searchfield.js +510 -0
  111. data/vendor/assets/javascripts/plugins/plugin.archive_analytics.js +67 -0
  112. data/vendor/assets/javascripts/plugins/plugin.chapters.js +197 -0
  113. data/vendor/assets/javascripts/plugins/plugin.iframe.js +73 -0
  114. data/vendor/assets/javascripts/plugins/plugin.mobile_nav.js +197 -0
  115. data/vendor/assets/javascripts/plugins/plugin.print.js +70 -0
  116. data/vendor/assets/javascripts/plugins/plugin.resume.js +73 -0
  117. data/vendor/assets/javascripts/plugins/plugin.search.js +523 -0
  118. data/vendor/assets/javascripts/plugins/plugin.themes.js +49 -0
  119. data/vendor/assets/javascripts/plugins/plugin.tts.js +523 -0
  120. data/vendor/assets/javascripts/plugins/plugin.url.js +168 -0
  121. data/vendor/assets/javascripts/soundmanager/license.txt +29 -0
  122. data/vendor/assets/javascripts/soundmanager/script/soundmanager2-jsmin.js +113 -0
  123. data/vendor/assets/javascripts/soundmanager/script/soundmanager2-nodebug-jsmin.js +83 -0
  124. data/vendor/assets/javascripts/soundmanager/script/soundmanager2-nodebug.js +2765 -0
  125. data/vendor/assets/javascripts/soundmanager/script/soundmanager2.js +6325 -0
  126. data/vendor/assets/javascripts/soundmanager/swf/soundmanager2.swf +0 -0
  127. data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_debug.swf +0 -0
  128. data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash9.swf +0 -0
  129. data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash9_debug.swf +0 -0
  130. data/vendor/assets/javascripts/soundmanager/swf/soundmanager2_flash_xdomain.zip +0 -0
  131. data/vendor/assets/stylesheets/BookReader.css +1887 -0
  132. data/vendor/assets/stylesheets/BookReaderDemo.css +18 -0
  133. data/vendor/assets/stylesheets/BookReaderEmbed.css +0 -0
  134. data/vendor/assets/stylesheets/demo.css +0 -0
  135. data/vendor/assets/stylesheets/dist/addons/navbars/jquery.mmenu.navbars.css +29 -0
  136. data/vendor/assets/stylesheets/dist/addons/offcanvas/jquery.mmenu.offcanvas.css +14 -0
  137. data/vendor/assets/stylesheets/dist/addons/screenreader/jquery.mmenu.screenreader.css +1 -0
  138. data/vendor/assets/stylesheets/dist/addons/searchfield/jquery.mmenu.searchfield.css +16 -0
  139. data/vendor/assets/stylesheets/dist/css/jquery.mmenu.all.css +413 -0
  140. data/vendor/assets/stylesheets/dist/css/jquery.mmenu.css +80 -0
  141. data/vendor/assets/stylesheets/dist/css/jquery.mmenu.oncanvas.css +66 -0
  142. 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
+