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,4849 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright(c)2008-2016 Internet Archive. Software license AGPL version 3.
|
|
3
|
+
|
|
4
|
+
This file is part of BookReader.
|
|
5
|
+
|
|
6
|
+
BookReader is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
BookReader is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with BookReader. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
|
|
19
|
+
The BookReader source is hosted at http://github.com/internetarchive/bookreader/
|
|
20
|
+
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
window.BookReader = (function ($) {
|
|
24
|
+
|
|
25
|
+
// BookReader()
|
|
26
|
+
//_________________________________________________________________________
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* BookReader
|
|
30
|
+
* @param {Object} options
|
|
31
|
+
* TODO document all options properties
|
|
32
|
+
* @constructor
|
|
33
|
+
*/
|
|
34
|
+
function BookReader(options) {
|
|
35
|
+
options = options || {};
|
|
36
|
+
options = jQuery.extend({}, BookReader.defaultOptions, options, BookReader.optionOverrides);
|
|
37
|
+
this.setup(options);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
BookReader.version = "4.0.0-beta";
|
|
41
|
+
|
|
42
|
+
// Mode constants
|
|
43
|
+
BookReader.constMode1up = 1;
|
|
44
|
+
BookReader.constMode2up = 2;
|
|
45
|
+
BookReader.constModeThumb = 3;
|
|
46
|
+
|
|
47
|
+
// Names of events that can be triggered via BookReader.prototype.trigger()
|
|
48
|
+
BookReader.eventNames = {
|
|
49
|
+
// Indicates that the fragment (a serialization of the reader state)
|
|
50
|
+
// has changed.
|
|
51
|
+
fragmentChange: 'fragmentChange',
|
|
52
|
+
PostInit: 'PostInit',
|
|
53
|
+
stop: 'stop',
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
BookReader.defaultOptions = {
|
|
57
|
+
// A string, such as "mode/1up"
|
|
58
|
+
defaults: null,
|
|
59
|
+
|
|
60
|
+
// Padding in 1up
|
|
61
|
+
padding: 10,
|
|
62
|
+
// UI mode
|
|
63
|
+
ui: 'full', // full, embed, responsive
|
|
64
|
+
|
|
65
|
+
// Controls whether nav/toolbar will autohide
|
|
66
|
+
uiAutoHide: false,
|
|
67
|
+
|
|
68
|
+
// thumbnail mode
|
|
69
|
+
// number of rows to pre-cache out a view
|
|
70
|
+
thumbRowBuffer: 2,
|
|
71
|
+
thumbColumns: 6,
|
|
72
|
+
// number of thumbnails to load at once
|
|
73
|
+
thumbMaxLoading: 4,
|
|
74
|
+
// spacing between thumbnails
|
|
75
|
+
thumbPadding: 10,
|
|
76
|
+
// speed for flip animation
|
|
77
|
+
flipSpeed: 'fast',
|
|
78
|
+
|
|
79
|
+
showToolbar: true,
|
|
80
|
+
showLogo: true,
|
|
81
|
+
// Where the logo links to
|
|
82
|
+
logoURL: 'https://archive.org',
|
|
83
|
+
|
|
84
|
+
// Base URL for UI images - should be overriden (before init) by
|
|
85
|
+
// custom implementations.
|
|
86
|
+
// $$$ This is the same directory as the images referenced by relative
|
|
87
|
+
// path in the CSS. Would be better to automagically find that path.
|
|
88
|
+
imagesBaseURL: '/BookReader/images/',
|
|
89
|
+
|
|
90
|
+
// Zoom levels
|
|
91
|
+
// $$$ provide finer grained zooming, {reduce: 8, autofit: null}, {reduce: 16, autofit: null}
|
|
92
|
+
/* The autofit code ensures that fit to width and fit to height will be available */
|
|
93
|
+
reductionFactors: [
|
|
94
|
+
{reduce: 0.5, autofit: null},
|
|
95
|
+
{reduce: 1, autofit: null},
|
|
96
|
+
{reduce: 2, autofit: null},
|
|
97
|
+
{reduce: 3, autofit: null},
|
|
98
|
+
{reduce: 4, autofit: null},
|
|
99
|
+
{reduce: 6, autofit: null}
|
|
100
|
+
],
|
|
101
|
+
|
|
102
|
+
// Object to hold parameters related to 1up mode
|
|
103
|
+
onePage: {
|
|
104
|
+
autofit: 'auto', // valid values are height, width, auto, none
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
// Object to hold parameters related to 2up mode
|
|
108
|
+
twoPage: {
|
|
109
|
+
coverInternalPadding: 0, // Width of cover
|
|
110
|
+
coverExternalPadding: 0, // Padding outside of cover
|
|
111
|
+
bookSpineDivWidth: 64, // Width of book spine $$$ consider sizing based on book length
|
|
112
|
+
autofit: 'auto'
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
onePageMinBreakpoint: 800,
|
|
116
|
+
|
|
117
|
+
bookTitle: '',
|
|
118
|
+
bookUrl: null,
|
|
119
|
+
bookUrlText: null,
|
|
120
|
+
bookUrlTitle: null,
|
|
121
|
+
enableBookTitleLink: true,
|
|
122
|
+
|
|
123
|
+
// Fields used to populate the info window
|
|
124
|
+
metadata: [],
|
|
125
|
+
thumbnail: null,
|
|
126
|
+
bookUrlMoreInfo: null,
|
|
127
|
+
|
|
128
|
+
// Experimental Controls (eg b/w)
|
|
129
|
+
enableExperimentalControls: false,
|
|
130
|
+
|
|
131
|
+
// CSS selectors
|
|
132
|
+
// Where BookReader mounts to
|
|
133
|
+
el: '#BookReader',
|
|
134
|
+
|
|
135
|
+
// Page progression. Choices: 'lr', 'rl'
|
|
136
|
+
pageProgression: 'lr',
|
|
137
|
+
|
|
138
|
+
// Should image downloads be blocked
|
|
139
|
+
protected: false,
|
|
140
|
+
|
|
141
|
+
// Data is a simple way to populate the bookreader
|
|
142
|
+
// Example:
|
|
143
|
+
// [
|
|
144
|
+
// // Each child is a spread
|
|
145
|
+
// [
|
|
146
|
+
// {
|
|
147
|
+
// width: 123,
|
|
148
|
+
// height: 123,
|
|
149
|
+
// // Optional: If not provided, include a getPageURI
|
|
150
|
+
// uri: 'https://archive.org/image.jpg',
|
|
151
|
+
// // Optional: Shown instead of leaf number if present.
|
|
152
|
+
// pageNum: 1
|
|
153
|
+
// },
|
|
154
|
+
// {width: 123, height: 123, uri: 'https://archive.org/image2.jpg', pageNum: 2},
|
|
155
|
+
// ]
|
|
156
|
+
// ],
|
|
157
|
+
//
|
|
158
|
+
// Note if URI is omitted, a custom getPageURI can be provided. This allows the page
|
|
159
|
+
// URI to the result of a function, which allows for thigns such as dynamic
|
|
160
|
+
// page scaling.
|
|
161
|
+
data: [],
|
|
162
|
+
|
|
163
|
+
// Advanced methods for page rendering
|
|
164
|
+
getNumLeafs: null,
|
|
165
|
+
getPageWidth: null,
|
|
166
|
+
getPageHeight: null,
|
|
167
|
+
getPageURI: null,
|
|
168
|
+
|
|
169
|
+
// Return which side, left or right, that a given page should be displayed on
|
|
170
|
+
getPageSide: null,
|
|
171
|
+
|
|
172
|
+
// This function returns the left and right indices for the user-visible
|
|
173
|
+
// spread that contains the given index. The return values may be
|
|
174
|
+
// null if there is no facing page or the index is invalid.
|
|
175
|
+
getSpreadIndices: null,
|
|
176
|
+
|
|
177
|
+
getPageNum: null,
|
|
178
|
+
leafNumToIndex: null,
|
|
179
|
+
|
|
180
|
+
// Optional: if present, and embed code will be shown in the share dialog
|
|
181
|
+
getEmbedCode: null,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* This is here, just in case you need to absolutely override an option.
|
|
186
|
+
*/
|
|
187
|
+
BookReader.optionOverrides = {};
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Setup
|
|
191
|
+
* It is separate from the constructor, so plugins can extend.
|
|
192
|
+
* @param {Object} options
|
|
193
|
+
*/
|
|
194
|
+
BookReader.prototype.setup = function(options) {
|
|
195
|
+
// Store the options used to setup bookreader
|
|
196
|
+
this.options = options;
|
|
197
|
+
|
|
198
|
+
// @deprecated: Instance constants. Use Class constants instead
|
|
199
|
+
this.constMode1up = BookReader.constMode1up;
|
|
200
|
+
this.constMode2up = BookReader.constMode2up;
|
|
201
|
+
this.constModeThumb = BookReader.constModeThumb;
|
|
202
|
+
|
|
203
|
+
// Private properties below. Configuration should be done with options.
|
|
204
|
+
this.reduce = 4;
|
|
205
|
+
this.defaults = options.defaults;
|
|
206
|
+
this.padding = options.padding;
|
|
207
|
+
this.mode = this.constMode1up;
|
|
208
|
+
this.prevReadMode = null;
|
|
209
|
+
this.ui = options.ui;
|
|
210
|
+
this.uiAutoHide = options.uiAutoHide;
|
|
211
|
+
|
|
212
|
+
this.thumbWidth = 100; // will be overridden during prepareThumbnailView
|
|
213
|
+
this.thumbRowBuffer = options.thumbRowBuffer;
|
|
214
|
+
this.thumbColumns = options.thumbColumns;
|
|
215
|
+
this.thumbMaxLoading = options.thumbMaxLoading;
|
|
216
|
+
this.thumbPadding = options.thumbPadding;
|
|
217
|
+
this.displayedRows = [];
|
|
218
|
+
|
|
219
|
+
this.displayedIndices = [];
|
|
220
|
+
this.imgs = {};
|
|
221
|
+
this.prefetchedImgs = {}; //an object with numeric keys cooresponding to page index
|
|
222
|
+
|
|
223
|
+
this.animating = false;
|
|
224
|
+
this.auto = false;
|
|
225
|
+
this.autoTimer = null;
|
|
226
|
+
this.flipSpeed = options.flipSpeed;
|
|
227
|
+
this.twoPagePopUp = null;
|
|
228
|
+
this.leafEdgeTmp = null;
|
|
229
|
+
this.firstIndex = null;
|
|
230
|
+
this.lastDisplayableIndex2up = null;
|
|
231
|
+
this.isFullscreenActive = false;
|
|
232
|
+
|
|
233
|
+
this.showToolbar = options.showToolbar;
|
|
234
|
+
this.showLogo = options.showLogo;
|
|
235
|
+
this.logoURL = options.logoURL;
|
|
236
|
+
this.imagesBaseURL = options.imagesBaseURL;
|
|
237
|
+
|
|
238
|
+
this.reductionFactors = options.reductionFactors;
|
|
239
|
+
this.onePage = options.onePage;
|
|
240
|
+
this.twoPage = options.twoPage;
|
|
241
|
+
this.onePageMinBreakpoint = options.onePageMinBreakpoint;
|
|
242
|
+
|
|
243
|
+
this.bookTitle = options.bookTitle;
|
|
244
|
+
this.bookUrl = options.bookUrl;
|
|
245
|
+
this.bookUrlText = options.bookUrlText;
|
|
246
|
+
this.bookUrlTitle = options.bookUrlTitle;
|
|
247
|
+
this.titleLeaf = options.titleLeaf;
|
|
248
|
+
|
|
249
|
+
this.metadata = options.metadata;
|
|
250
|
+
this.thumbnail = options.thumbnail;
|
|
251
|
+
this.bookUrlMoreInfo = options.bookUrlMoreInfo;
|
|
252
|
+
|
|
253
|
+
this.enableExperimentalControls = options.enableExperimentalControls;
|
|
254
|
+
this.el = options.el;
|
|
255
|
+
|
|
256
|
+
this.pageProgression = options.pageProgression;
|
|
257
|
+
this.protected = options.protected;
|
|
258
|
+
this.getEmbedCode = options.getEmbedCode;
|
|
259
|
+
|
|
260
|
+
// Assign the data methods
|
|
261
|
+
this.data = options.data;
|
|
262
|
+
this.getNumLeafs = options.getNumLeafs || BookReader.prototype.getNumLeafs;
|
|
263
|
+
this.getPageWidth = options.getPageWidth || BookReader.prototype.getPageWidth;
|
|
264
|
+
this.getPageHeight = options.getPageHeight || BookReader.prototype.getPageHeight;
|
|
265
|
+
this.getPageURI = options.getPageURI || BookReader.prototype.getPageURI;
|
|
266
|
+
this.getPageSide = options.getPageSide || BookReader.prototype.getPageSide;
|
|
267
|
+
this.getPageNum = options.getPageNum || BookReader.prototype.getPageNum;
|
|
268
|
+
this.getSpreadIndices = options.getSpreadIndices || BookReader.prototype.getSpreadIndices;
|
|
269
|
+
this.leafNumToIndex = options.leafNumToIndex || BookReader.prototype.leafNumToIndex;
|
|
270
|
+
this.refs = {};
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// Library functions
|
|
274
|
+
// At top of file so they can be used below
|
|
275
|
+
BookReader.util = {
|
|
276
|
+
disableSelect: function(jObject) {
|
|
277
|
+
// Bind mouse handlers
|
|
278
|
+
// Disable mouse click to avoid selected/highlighted page images - bug 354239
|
|
279
|
+
jObject.bind('mousedown', function(e) {
|
|
280
|
+
// $$$ check here for right-click and don't disable. Also use jQuery style
|
|
281
|
+
// for stopping propagation. See https://bugs.edge.launchpad.net/gnubook/+bug/362626
|
|
282
|
+
return false;
|
|
283
|
+
});
|
|
284
|
+
// Special hack for IE7
|
|
285
|
+
jObject[0].onselectstart = function(e) { return false; };
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
clamp: function(value, min, max) {
|
|
289
|
+
return Math.min(Math.max(value, min), max);
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// Given value and maximum, calculate a percentage suitable for CSS
|
|
293
|
+
cssPercentage: function(value, max) {
|
|
294
|
+
return (((value + 0.0) / max) * 100) + '%';
|
|
295
|
+
},
|
|
296
|
+
|
|
297
|
+
notInArray: function(value, array) {
|
|
298
|
+
// inArray returns -1 or undefined if value not in array
|
|
299
|
+
return ! (jQuery.inArray(value, array) >= 0);
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
getIFrameDocument: function(iframe) {
|
|
303
|
+
// Adapted from http://xkr.us/articles/dom/iframe-document/
|
|
304
|
+
var outer = (iframe.contentWindow || iframe.contentDocument);
|
|
305
|
+
return (outer.document || outer);
|
|
306
|
+
},
|
|
307
|
+
|
|
308
|
+
escapeHTML: function (str) {
|
|
309
|
+
return(
|
|
310
|
+
str.replace(/&/g,'&').
|
|
311
|
+
replace(/>/g,'>').
|
|
312
|
+
replace(/</g,'<').
|
|
313
|
+
replace(/"/g,'"')
|
|
314
|
+
);
|
|
315
|
+
},
|
|
316
|
+
|
|
317
|
+
decodeURIComponentPlus: function(value) {
|
|
318
|
+
// Decodes a URI component and converts '+' to ' '
|
|
319
|
+
return decodeURIComponent(value).replace(/\+/g, ' ');
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
encodeURIComponentPlus: function(value) {
|
|
323
|
+
// Encodes a URI component and converts ' ' to '+'
|
|
324
|
+
return encodeURIComponent(value).replace(/%20/g, '+');
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Returns a function, that, as long as it continues to be invoked, will not
|
|
329
|
+
* be triggered. The function will be called after it stops being called for
|
|
330
|
+
* N milliseconds. If `immediate` is passed, trigger the function on the
|
|
331
|
+
* leading edge, instead of the trailing.
|
|
332
|
+
* @see https://davidwalsh.name/javascript-debounce-function
|
|
333
|
+
*/
|
|
334
|
+
debounce: function(func, wait, immediate) {
|
|
335
|
+
var timeout;
|
|
336
|
+
return function() {
|
|
337
|
+
var context = this, args = arguments;
|
|
338
|
+
var later = function() {
|
|
339
|
+
timeout = null;
|
|
340
|
+
if (!immediate) func.apply(context, args);
|
|
341
|
+
};
|
|
342
|
+
var callNow = immediate && !timeout;
|
|
343
|
+
clearTimeout(timeout);
|
|
344
|
+
timeout = setTimeout(later, wait);
|
|
345
|
+
if (callNow) func.apply(context, args);
|
|
346
|
+
};
|
|
347
|
+
},
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Throttle function
|
|
351
|
+
* @see https://remysharp.com/2010/07/21/throttling-function-calls
|
|
352
|
+
*/
|
|
353
|
+
throttle: function(fn, threshhold, delay) {
|
|
354
|
+
threshhold || (threshhold = 250);
|
|
355
|
+
var last,
|
|
356
|
+
deferTimer;
|
|
357
|
+
if (delay) last = +new Date;
|
|
358
|
+
return function () {
|
|
359
|
+
var context = this;
|
|
360
|
+
var now = +new Date,
|
|
361
|
+
args = arguments;
|
|
362
|
+
if (last && now < last + threshhold) {
|
|
363
|
+
// hold on to it
|
|
364
|
+
clearTimeout(deferTimer);
|
|
365
|
+
deferTimer = setTimeout(function () {
|
|
366
|
+
last = now;
|
|
367
|
+
fn.apply(context, args);
|
|
368
|
+
}, threshhold);
|
|
369
|
+
} else {
|
|
370
|
+
last = now;
|
|
371
|
+
fn.apply(context, args);
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Helper to merge in params in to a params object.
|
|
379
|
+
* It normalizes "page" into the "index" field to disambiguate and prevent concflicts
|
|
380
|
+
* @private
|
|
381
|
+
*/
|
|
382
|
+
BookReader.prototype.extendParams = function(params, newParams) {
|
|
383
|
+
var modifiedNewParams = $.extend({}, newParams);
|
|
384
|
+
if ('undefined' != typeof(modifiedNewParams.page)) {
|
|
385
|
+
var pageIndex = this.parsePageString(modifiedNewParams.page);
|
|
386
|
+
if (!isNaN(pageIndex))
|
|
387
|
+
modifiedNewParams.index = pageIndex;
|
|
388
|
+
delete modifiedNewParams.page;
|
|
389
|
+
}
|
|
390
|
+
$.extend(params, modifiedNewParams);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Parses params from from various initialization contexts (url, cookie, options)
|
|
395
|
+
* @private
|
|
396
|
+
* @return {object} the parased params
|
|
397
|
+
*/
|
|
398
|
+
BookReader.prototype.initParams = function() {
|
|
399
|
+
var params = {};
|
|
400
|
+
|
|
401
|
+
// This is ordered from lowest to highest priority
|
|
402
|
+
|
|
403
|
+
// If we have a title leaf, use that as the default instead of index 0,
|
|
404
|
+
// but only use as default if book has a few pages
|
|
405
|
+
if ('undefined' != typeof(this.titleLeaf) && this.getNumLeafs() > 2) {
|
|
406
|
+
params.index = this.leafNumToIndex(this.titleLeaf);
|
|
407
|
+
} else {
|
|
408
|
+
params.index = 0;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// this.defaults is a string passed in the url format. eg "page/1/mode/1up"
|
|
412
|
+
if (this.defaults) {
|
|
413
|
+
this.extendParams(params, this.paramsFromFragment(this.defaults));
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Check for Resume plugin
|
|
417
|
+
if (this.options.enablePageResume) {
|
|
418
|
+
// Check cookies
|
|
419
|
+
var val = this.getResumeValue();
|
|
420
|
+
if (val !== null) {
|
|
421
|
+
params.index = val;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (this.options.enableUrlPlugin) {
|
|
426
|
+
// params explicitly set in URL take precedence over all other methods
|
|
427
|
+
var urlParams = this.paramsFromFragment(this.urlReadFragment());
|
|
428
|
+
if (urlParams.mode) {
|
|
429
|
+
this.prevReadMode = urlParams.mode;
|
|
430
|
+
}
|
|
431
|
+
this.extendParams(params, urlParams);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
return params;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// init()
|
|
438
|
+
//______________________________________________________________________________
|
|
439
|
+
BookReader.prototype.init = function() {
|
|
440
|
+
this.pageScale = this.reduce; // preserve current reduce
|
|
441
|
+
|
|
442
|
+
var params = this.initParams();
|
|
443
|
+
|
|
444
|
+
// params.index takes precedence over params.page
|
|
445
|
+
if (params.index) {
|
|
446
|
+
this.firstIndex = params.index;
|
|
447
|
+
} else {
|
|
448
|
+
this.firstIndex = 0;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Use params or browser width to set view mode
|
|
452
|
+
var windowWidth = $(window).width();
|
|
453
|
+
var nextMode;
|
|
454
|
+
if ('undefined' != typeof(params.mode)) {
|
|
455
|
+
nextMode = params.mode;
|
|
456
|
+
} else if (this.ui == 'full'
|
|
457
|
+
&& this.enableMobileNav
|
|
458
|
+
&& this.isFullscreenActive
|
|
459
|
+
&& windowWidth <= this.onePageMinBreakpoint
|
|
460
|
+
) {
|
|
461
|
+
// In full mode, we set the default based on width
|
|
462
|
+
nextMode = this.constMode1up;
|
|
463
|
+
} else {
|
|
464
|
+
nextMode = this.constMode2up;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (this.canSwitchToMode(nextMode)) {
|
|
468
|
+
this.mode = nextMode;
|
|
469
|
+
} else {
|
|
470
|
+
this.mode = this.constMode1up;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
//-------------------------------------------------------------------------
|
|
474
|
+
// Setup Navbars and other UI
|
|
475
|
+
|
|
476
|
+
this.isTouchDevice = !!('ontouchstart' in window) || !!('msmaxtouchpoints' in window.navigator);
|
|
477
|
+
|
|
478
|
+
// Calculate Max page num (used for pagination display)
|
|
479
|
+
this.maxPageNum = 0;
|
|
480
|
+
var pageNumVal;
|
|
481
|
+
for (var i = 0; i < this.getNumLeafs(); i++) {
|
|
482
|
+
pageNumVal = this.getPageNum(i);
|
|
483
|
+
if (!isNaN(pageNumVal) && pageNumVal > this.maxPageNum) {
|
|
484
|
+
this.maxPageNum = pageNumVal;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
this.refs.$br = $(this.el);
|
|
489
|
+
this.refs.$br.empty().removeClass().addClass("ui-" + this.ui).addClass('BookReader');
|
|
490
|
+
|
|
491
|
+
this.refs.$brContainer = $("<div class='BRcontainer' dir='ltr'></div>");
|
|
492
|
+
this.refs.$br.append(this.refs.$brContainer);
|
|
493
|
+
|
|
494
|
+
// We init the nav bar after the params processing so that the nav slider
|
|
495
|
+
// knows where it should start (doesn't jump after init)
|
|
496
|
+
if (this.ui == "embed") {
|
|
497
|
+
this.initEmbedNavbar();
|
|
498
|
+
} else {
|
|
499
|
+
if (this.showToolbar) {
|
|
500
|
+
this.initToolbar(this.mode, this.ui); // Build inside of toolbar div
|
|
501
|
+
}
|
|
502
|
+
this.initNavbar();
|
|
503
|
+
}
|
|
504
|
+
this.resizeBRcontainer();
|
|
505
|
+
|
|
506
|
+
// Set strings in the UI
|
|
507
|
+
this.initUIStrings();
|
|
508
|
+
|
|
509
|
+
// $$$ refactor this so it's enough to set the first index and call preparePageView
|
|
510
|
+
// (get rid of mode-specific logic at this point)
|
|
511
|
+
if (this.constMode1up == this.mode) {
|
|
512
|
+
this.prepareOnePageView();
|
|
513
|
+
} else if (this.constModeThumb == this.mode) {
|
|
514
|
+
this.prepareThumbnailView();
|
|
515
|
+
} else {
|
|
516
|
+
this.displayedIndices = [this.firstIndex];
|
|
517
|
+
this.prepareTwoPageView();
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Enact other parts of initial params
|
|
521
|
+
this.updateFromParams(params);
|
|
522
|
+
|
|
523
|
+
// Add a class if this is a touch enabled device
|
|
524
|
+
if (this.isTouchDevice) {
|
|
525
|
+
this.refs.$br.addClass("touch");
|
|
526
|
+
} else {
|
|
527
|
+
this.refs.$br.addClass("no-touch");
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// Add class to body for mode. Responsiveness is disabled in embed.
|
|
531
|
+
this.refs.$br.addClass("br-ui-" + this.ui);
|
|
532
|
+
|
|
533
|
+
//-------------------------------------------------------------------------
|
|
534
|
+
// Bind to events
|
|
535
|
+
|
|
536
|
+
if (!this.isTouchDevice) this.setupTooltips();
|
|
537
|
+
this.bindNavigationHandlers();
|
|
538
|
+
this.setupKeyListeners();
|
|
539
|
+
|
|
540
|
+
this.lastScroll = (new Date().getTime());
|
|
541
|
+
this.refs.$brContainer.bind('scroll', this, function(e) {
|
|
542
|
+
// Note, this scroll event fires for both user, and js generated calls
|
|
543
|
+
// It is functioning in some cases as the primary triggerer for rendering
|
|
544
|
+
e.data.lastScroll = (new Date().getTime());
|
|
545
|
+
if (e.data.constMode2up != e.data.mode) {
|
|
546
|
+
e.data.drawLeafsThrottled();
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
$(window).bind('resize', this, function(e) {
|
|
551
|
+
e.data.resize();
|
|
552
|
+
});
|
|
553
|
+
$(window).bind("orientationchange", this, function(e) {
|
|
554
|
+
e.data.resize();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
if (this.protected) {
|
|
558
|
+
$(document).on('contextmenu dragstart', '.BRpagediv1up', function(e) {
|
|
559
|
+
return false;
|
|
560
|
+
});
|
|
561
|
+
$(document).on('contextmenu dragstart', '.BRpageimage', function(e) {
|
|
562
|
+
return false;
|
|
563
|
+
});
|
|
564
|
+
$(document).on('contextmenu dragstart', '.BRpagedivthumb', function(e) {
|
|
565
|
+
return false;
|
|
566
|
+
});
|
|
567
|
+
this.$('.BRicon.share').hide();
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
this.$('.BRpagediv1up').bind('mousedown', this, function(e) {
|
|
571
|
+
// $$$ the purpose of this is to disable selection of the image (makes it turn blue)
|
|
572
|
+
// but this also interferes with right-click. See https://bugs.edge.launchpad.net/gnubook/+bug/362626
|
|
573
|
+
return false;
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
this.bind(BookReader.eventNames.stop, function(e, br) {
|
|
577
|
+
br.autoStop();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
this.trigger(BookReader.eventNames.PostInit);
|
|
581
|
+
|
|
582
|
+
this.init.initComplete = true;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
BookReader.prototype.trigger = function(name, props) {
|
|
586
|
+
$(document).trigger('BookReader:' + name, this, props);
|
|
587
|
+
};
|
|
588
|
+
|
|
589
|
+
BookReader.prototype.bind = function(name, callback) {
|
|
590
|
+
$(document).bind('BookReader:' + name, callback);
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
BookReader.prototype.unbind = function(name, callback) {
|
|
594
|
+
$(document).bind('BookReader:' + name, callback);
|
|
595
|
+
};
|
|
596
|
+
|
|
597
|
+
// resize
|
|
598
|
+
// Resizes the bookreader
|
|
599
|
+
//______________________________________________________________________________
|
|
600
|
+
BookReader.prototype.resize = function() {
|
|
601
|
+
if (!this.init.initComplete) return;
|
|
602
|
+
|
|
603
|
+
this.resizeBRcontainer();
|
|
604
|
+
|
|
605
|
+
if (this.constMode1up == this.mode) {
|
|
606
|
+
if (this.onePage.autofit != 'none') {
|
|
607
|
+
this.resizePageView();
|
|
608
|
+
this.centerPageView();
|
|
609
|
+
if (this.enableSearch) this.updateSearchHilites(); //deletes hilights but does not call remove()
|
|
610
|
+
} else {
|
|
611
|
+
this.centerPageView();
|
|
612
|
+
this.displayedIndices = [];
|
|
613
|
+
if (this.enableSearch) this.updateSearchHilites(); //deletes hilights but does not call remove()
|
|
614
|
+
this.drawLeafsThrottled();
|
|
615
|
+
}
|
|
616
|
+
} else if (this.constModeThumb == this.mode){
|
|
617
|
+
this.prepareThumbnailView();
|
|
618
|
+
} else {
|
|
619
|
+
// We only need to prepare again in autofit (size of spread changes)
|
|
620
|
+
if (this.twoPage.autofit) {
|
|
621
|
+
this.prepareTwoPageView();
|
|
622
|
+
} else {
|
|
623
|
+
// Re-center if the scrollbars have disappeared
|
|
624
|
+
var center = this.twoPageGetViewCenter();
|
|
625
|
+
var doRecenter = false;
|
|
626
|
+
if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
|
|
627
|
+
center.percentageX = 0.5;
|
|
628
|
+
doRecenter = true;
|
|
629
|
+
}
|
|
630
|
+
if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
|
|
631
|
+
center.percentageY = 0.5;
|
|
632
|
+
doRecenter = true;
|
|
633
|
+
}
|
|
634
|
+
if (doRecenter) {
|
|
635
|
+
this.twoPageCenterView(center.percentageX, center.percentageY);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
BookReader.prototype.setupKeyListeners = function() {
|
|
642
|
+
var self = this;
|
|
643
|
+
|
|
644
|
+
var KEY_PGUP = 33;
|
|
645
|
+
var KEY_PGDOWN = 34;
|
|
646
|
+
var KEY_END = 35;
|
|
647
|
+
var KEY_HOME = 36;
|
|
648
|
+
|
|
649
|
+
var KEY_LEFT = 37;
|
|
650
|
+
var KEY_UP = 38;
|
|
651
|
+
var KEY_RIGHT = 39;
|
|
652
|
+
var KEY_DOWN = 40;
|
|
653
|
+
|
|
654
|
+
// We use document here instead of window to avoid a bug in jQuery on IE7
|
|
655
|
+
$(document).keydown(function(e) {
|
|
656
|
+
|
|
657
|
+
// Keyboard navigation
|
|
658
|
+
if (!self.keyboardNavigationIsDisabled(e)) {
|
|
659
|
+
switch(e.keyCode) {
|
|
660
|
+
case KEY_PGUP:
|
|
661
|
+
case KEY_UP:
|
|
662
|
+
// In 1up mode page scrolling is handled by browser
|
|
663
|
+
if (self.constMode2up == self.mode) {
|
|
664
|
+
e.preventDefault();
|
|
665
|
+
self.prev();
|
|
666
|
+
}
|
|
667
|
+
break;
|
|
668
|
+
case KEY_DOWN:
|
|
669
|
+
case KEY_PGDOWN:
|
|
670
|
+
if (self.constMode2up == self.mode) {
|
|
671
|
+
e.preventDefault();
|
|
672
|
+
self.next();
|
|
673
|
+
}
|
|
674
|
+
break;
|
|
675
|
+
case KEY_END:
|
|
676
|
+
e.preventDefault();
|
|
677
|
+
self.last();
|
|
678
|
+
break;
|
|
679
|
+
case KEY_HOME:
|
|
680
|
+
e.preventDefault();
|
|
681
|
+
self.first();
|
|
682
|
+
break;
|
|
683
|
+
case KEY_LEFT:
|
|
684
|
+
if (self.constModeThumb != self.mode) {
|
|
685
|
+
e.preventDefault();
|
|
686
|
+
self.left();
|
|
687
|
+
}
|
|
688
|
+
break;
|
|
689
|
+
case KEY_RIGHT:
|
|
690
|
+
if (self.constModeThumb != self.mode) {
|
|
691
|
+
e.preventDefault();
|
|
692
|
+
self.right();
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
// setupTooltips()
|
|
701
|
+
//______________________________________________________________________________
|
|
702
|
+
BookReader.prototype.setupTooltips = function() {
|
|
703
|
+
this.$('.js-tooltip').each(function(idx, el) {
|
|
704
|
+
var options = {
|
|
705
|
+
positions: ['top', 'bottom'],
|
|
706
|
+
shrinkToFit: true,
|
|
707
|
+
spikeGirth: 0,
|
|
708
|
+
spikeLength: 8,
|
|
709
|
+
fill: 'transparent',
|
|
710
|
+
cornerRadius: 0,
|
|
711
|
+
strokeWidth: 0,
|
|
712
|
+
cssStyles: {},
|
|
713
|
+
};
|
|
714
|
+
var $el = $(el);
|
|
715
|
+
if ($el.parents('.BRtoolbar').length) {
|
|
716
|
+
options.positions = ['bottom'];
|
|
717
|
+
options.spikeLength = 12;
|
|
718
|
+
} else if ($el.parents('.BRnav').length) {
|
|
719
|
+
options.positions = ['top'];
|
|
720
|
+
}
|
|
721
|
+
$el.bt(options);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
// drawLeafs()
|
|
727
|
+
//______________________________________________________________________________
|
|
728
|
+
BookReader.prototype.drawLeafs = function() {
|
|
729
|
+
if (this.constMode1up == this.mode) {
|
|
730
|
+
this.drawLeafsOnePage();
|
|
731
|
+
} else if (this.constModeThumb == this.mode) {
|
|
732
|
+
this.drawLeafsThumbnail();
|
|
733
|
+
} else {
|
|
734
|
+
this.drawLeafsTwoPage();
|
|
735
|
+
}
|
|
736
|
+
};
|
|
737
|
+
|
|
738
|
+
// bindGestures(jElement)
|
|
739
|
+
//______________________________________________________________________________
|
|
740
|
+
BookReader.prototype.bindGestures = function(jElement) {
|
|
741
|
+
// TODO support gesture change is only iOS. Support android.
|
|
742
|
+
// HACK(2017-01-20) - Momentum scrolling is causing the scroll position
|
|
743
|
+
// to jump after zooming in on mobile device. I am able to reproduce
|
|
744
|
+
// when you move the book with one finger and then add another
|
|
745
|
+
// finger to pinch. Gestures are aware of scroll state.
|
|
746
|
+
|
|
747
|
+
var self = this;
|
|
748
|
+
var numTouches = 1;
|
|
749
|
+
|
|
750
|
+
jElement.unbind('touchmove').bind('touchmove', function(e) {
|
|
751
|
+
if (e.originalEvent.cancelable) numTouches = e.originalEvent.touches.length;
|
|
752
|
+
e.stopPropagation();
|
|
753
|
+
});
|
|
754
|
+
|
|
755
|
+
jElement.unbind('gesturechange').bind('gesturechange', function(e) {
|
|
756
|
+
e.preventDefault();
|
|
757
|
+
// These are two very important fixes to adjust for the scroll position
|
|
758
|
+
// issues described below
|
|
759
|
+
if (!(numTouches !== 2 || (new Date().getTime()) - self.lastScroll < 500)) {
|
|
760
|
+
if (e.originalEvent.scale > 1.5) {
|
|
761
|
+
self.zoom(1);
|
|
762
|
+
} else if (e.originalEvent.scale < 0.6) {
|
|
763
|
+
self.zoom(-1);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
});
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
BookReader.prototype.setClickHandler2UP = function( element, data, handler) {
|
|
770
|
+
$(element).unbind('click').bind('click', data, function(e) {
|
|
771
|
+
handler(e);
|
|
772
|
+
});
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
// drawLeafsOnePage()
|
|
776
|
+
//______________________________________________________________________________
|
|
777
|
+
BookReader.prototype.drawLeafsOnePage = function() {
|
|
778
|
+
var containerHeight = this.refs.$brContainer.height();
|
|
779
|
+
var scrollTop = this.refs.$brContainer.prop('scrollTop');
|
|
780
|
+
var scrollBottom = scrollTop + containerHeight;
|
|
781
|
+
var viewWidth = this.refs.$brContainer.prop('scrollWidth');
|
|
782
|
+
|
|
783
|
+
var indicesToDisplay = [];
|
|
784
|
+
|
|
785
|
+
var i;
|
|
786
|
+
var leafTop = 0;
|
|
787
|
+
var leafBottom = 0;
|
|
788
|
+
for (i=0; i<this.getNumLeafs(); i++) {
|
|
789
|
+
var height = parseInt(this._getPageHeight(i)/this.reduce);
|
|
790
|
+
|
|
791
|
+
leafBottom += height;
|
|
792
|
+
var topInView = (leafTop >= scrollTop) && (leafTop <= scrollBottom);
|
|
793
|
+
var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
|
|
794
|
+
var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom);
|
|
795
|
+
if (topInView || bottomInView || middleInView) {
|
|
796
|
+
indicesToDisplay.push(i);
|
|
797
|
+
}
|
|
798
|
+
leafTop += height +10;
|
|
799
|
+
leafBottom += 10;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Based of the pages displayed in the view we set the current index
|
|
803
|
+
// $$$ we should consider the page in the center of the view to be the current one
|
|
804
|
+
var firstIndexToDraw = indicesToDisplay[0];
|
|
805
|
+
this.firstIndex = firstIndexToDraw;
|
|
806
|
+
|
|
807
|
+
// Notify of new fragment, but only if we're currently displaying a leaf
|
|
808
|
+
// Hack that fixes #365790
|
|
809
|
+
if (this.displayedIndices.length > 0) {
|
|
810
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if ((0 != firstIndexToDraw) && (1 < this.reduce)) {
|
|
814
|
+
firstIndexToDraw--;
|
|
815
|
+
indicesToDisplay.unshift(firstIndexToDraw);
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
var lastIndexToDraw = indicesToDisplay[indicesToDisplay.length-1];
|
|
819
|
+
if ( ((this.getNumLeafs()-1) != lastIndexToDraw) && (1 < this.reduce) ) {
|
|
820
|
+
indicesToDisplay.push(lastIndexToDraw+1);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
var BRpageViewEl = this.refs.$brPageViewEl.get(0);
|
|
824
|
+
|
|
825
|
+
leafTop = 0;
|
|
826
|
+
var i;
|
|
827
|
+
for (i=0; i<firstIndexToDraw; i++) {
|
|
828
|
+
leafTop += parseInt(this._getPageHeight(i)/this.reduce) +10;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
for (i=0; i<indicesToDisplay.length; i++) {
|
|
832
|
+
var index = indicesToDisplay[i];
|
|
833
|
+
var height = parseInt(this._getPageHeight(index)/this.reduce);
|
|
834
|
+
|
|
835
|
+
if (BookReader.util.notInArray(indicesToDisplay[i], this.displayedIndices)) {
|
|
836
|
+
var width = parseInt(this._getPageWidth(index)/this.reduce);
|
|
837
|
+
var div = document.createElement('div');
|
|
838
|
+
div.className = 'BRpagediv1up pagediv' + index;
|
|
839
|
+
div.style.position = "absolute";
|
|
840
|
+
div.style.top = leafTop + 'px';
|
|
841
|
+
var left = (viewWidth-width)>>1;
|
|
842
|
+
if (left<0) left = 0;
|
|
843
|
+
div.style.left = left + 'px';
|
|
844
|
+
div.style.width = width + 'px';
|
|
845
|
+
div.style.height = height + 'px';
|
|
846
|
+
|
|
847
|
+
BRpageViewEl.appendChild(div);
|
|
848
|
+
|
|
849
|
+
var img = document.createElement('img');
|
|
850
|
+
img.src = this._getPageURI(index, this.reduce, 0);
|
|
851
|
+
img.className = 'BRnoselect BRonePageImage';
|
|
852
|
+
img.style.width = width + 'px';
|
|
853
|
+
img.style.height = height + 'px';
|
|
854
|
+
div.appendChild(img);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
leafTop += height +10;
|
|
858
|
+
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
for (i=0; i<this.displayedIndices.length; i++) {
|
|
862
|
+
if (BookReader.util.notInArray(this.displayedIndices[i], indicesToDisplay)) {
|
|
863
|
+
var index = this.displayedIndices[i];
|
|
864
|
+
this.$('.pagediv'+index).remove();
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
this.displayedIndices = indicesToDisplay.slice();
|
|
869
|
+
if (this.enableSearch) this.updateSearchHilites();
|
|
870
|
+
|
|
871
|
+
this.updateToolbarZoom(this.reduce);
|
|
872
|
+
|
|
873
|
+
// Update the slider
|
|
874
|
+
this.updateNavIndexThrottled();
|
|
875
|
+
};
|
|
876
|
+
|
|
877
|
+
// drawLeafsThumbnail()
|
|
878
|
+
//______________________________________________________________________________
|
|
879
|
+
// If seekIndex is defined, the view will be drawn with that page visible (without any
|
|
880
|
+
// animated scrolling)
|
|
881
|
+
BookReader.prototype.drawLeafsThumbnail = function( seekIndex ) {
|
|
882
|
+
var viewWidth = this.refs.$brContainer.prop('scrollWidth') - 20; // width minus buffer
|
|
883
|
+
|
|
884
|
+
var i;
|
|
885
|
+
var leafWidth;
|
|
886
|
+
var leafHeight;
|
|
887
|
+
var rightPos = 0;
|
|
888
|
+
var bottomPos = 0;
|
|
889
|
+
var maxRight = 0;
|
|
890
|
+
var currentRow = 0;
|
|
891
|
+
var leafIndex = 0;
|
|
892
|
+
var leafMap = [];
|
|
893
|
+
|
|
894
|
+
var self = this;
|
|
895
|
+
|
|
896
|
+
// Will be set to top of requested seek index, if set
|
|
897
|
+
var seekTop;
|
|
898
|
+
|
|
899
|
+
// Calculate the position of every thumbnail. $$$ cache instead of calculating on every draw
|
|
900
|
+
for (i=0; i<this.getNumLeafs(); i++) {
|
|
901
|
+
leafWidth = this.thumbWidth;
|
|
902
|
+
if (rightPos + (leafWidth + this.thumbPadding) > viewWidth){
|
|
903
|
+
currentRow++;
|
|
904
|
+
rightPos = 0;
|
|
905
|
+
leafIndex = 0;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (leafMap[currentRow]===undefined) { leafMap[currentRow] = {}; }
|
|
909
|
+
if (leafMap[currentRow].leafs===undefined) {
|
|
910
|
+
leafMap[currentRow].leafs = [];
|
|
911
|
+
leafMap[currentRow].height = 0;
|
|
912
|
+
leafMap[currentRow].top = 0;
|
|
913
|
+
}
|
|
914
|
+
leafMap[currentRow].leafs[leafIndex] = {};
|
|
915
|
+
leafMap[currentRow].leafs[leafIndex].num = i;
|
|
916
|
+
leafMap[currentRow].leafs[leafIndex].left = rightPos;
|
|
917
|
+
|
|
918
|
+
leafHeight = parseInt((this.getPageHeight(leafMap[currentRow].leafs[leafIndex].num)*this.thumbWidth)/this.getPageWidth(leafMap[currentRow].leafs[leafIndex].num), 10);
|
|
919
|
+
if (leafHeight > leafMap[currentRow].height) {
|
|
920
|
+
leafMap[currentRow].height = leafHeight;
|
|
921
|
+
}
|
|
922
|
+
if (leafIndex===0) { bottomPos += this.thumbPadding + leafMap[currentRow].height; }
|
|
923
|
+
rightPos += leafWidth + this.thumbPadding;
|
|
924
|
+
if (rightPos > maxRight) { maxRight = rightPos; }
|
|
925
|
+
leafIndex++;
|
|
926
|
+
|
|
927
|
+
if (i == seekIndex) {
|
|
928
|
+
seekTop = bottomPos - this.thumbPadding - leafMap[currentRow].height;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// reset the bottom position based on thumbnails
|
|
933
|
+
this.refs.$brPageViewEl.height(bottomPos);
|
|
934
|
+
|
|
935
|
+
var pageViewBuffer = Math.floor((this.refs.$brContainer.prop('scrollWidth') - maxRight) / 2) - 14;
|
|
936
|
+
|
|
937
|
+
// If seekTop is defined, seeking was requested and target found
|
|
938
|
+
if (typeof(seekTop) != 'undefined') {
|
|
939
|
+
this.refs.$brContainer.scrollTop( seekTop );
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
var scrollTop = this.refs.$brContainer.prop('scrollTop');
|
|
943
|
+
var scrollBottom = scrollTop + this.refs.$brContainer.height();
|
|
944
|
+
|
|
945
|
+
var leafTop = 0;
|
|
946
|
+
var leafBottom = 0;
|
|
947
|
+
var rowsToDisplay = [];
|
|
948
|
+
|
|
949
|
+
// Visible leafs with least/greatest index
|
|
950
|
+
var leastVisible = this.getNumLeafs() - 1;
|
|
951
|
+
var mostVisible = 0;
|
|
952
|
+
|
|
953
|
+
// Determine the thumbnails in view
|
|
954
|
+
for (i=0; i<leafMap.length; i++) {
|
|
955
|
+
leafBottom += this.thumbPadding + leafMap[i].height;
|
|
956
|
+
var topInView = (leafTop >= scrollTop) && (leafTop <= scrollBottom);
|
|
957
|
+
var bottomInView = (leafBottom >= scrollTop) && (leafBottom <= scrollBottom);
|
|
958
|
+
var middleInView = (leafTop <=scrollTop) && (leafBottom>=scrollBottom);
|
|
959
|
+
if (topInView || bottomInView || middleInView) {
|
|
960
|
+
rowsToDisplay.push(i);
|
|
961
|
+
if (leafMap[i].leafs[0].num < leastVisible) {
|
|
962
|
+
leastVisible = leafMap[i].leafs[0].num;
|
|
963
|
+
}
|
|
964
|
+
if (leafMap[i].leafs[leafMap[i].leafs.length - 1].num > mostVisible) {
|
|
965
|
+
mostVisible = leafMap[i].leafs[leafMap[i].leafs.length - 1].num;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
if (leafTop > leafMap[i].top) { leafMap[i].top = leafTop; }
|
|
969
|
+
leafTop = leafBottom;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// create a buffer of preloaded rows before and after the visible rows
|
|
973
|
+
var firstRow = rowsToDisplay[0];
|
|
974
|
+
var lastRow = rowsToDisplay[rowsToDisplay.length-1];
|
|
975
|
+
for (i=1; i<this.thumbRowBuffer+1; i++) {
|
|
976
|
+
if (lastRow+i < leafMap.length) { rowsToDisplay.push(lastRow+i); }
|
|
977
|
+
}
|
|
978
|
+
for (i=1; i<this.thumbRowBuffer+1; i++) {
|
|
979
|
+
if (firstRow-i >= 0) { rowsToDisplay.push(firstRow-i); }
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
// Create the thumbnail divs and images (lazy loaded)
|
|
983
|
+
var j;
|
|
984
|
+
var row;
|
|
985
|
+
var left;
|
|
986
|
+
var index;
|
|
987
|
+
var div;
|
|
988
|
+
var link;
|
|
989
|
+
var img;
|
|
990
|
+
var page;
|
|
991
|
+
for (i=0; i<rowsToDisplay.length; i++) {
|
|
992
|
+
if (BookReader.util.notInArray(rowsToDisplay[i], this.displayedRows)) {
|
|
993
|
+
row = rowsToDisplay[i];
|
|
994
|
+
|
|
995
|
+
for (j=0; j<leafMap[row].leafs.length; j++) {
|
|
996
|
+
index = j;
|
|
997
|
+
leaf = leafMap[row].leafs[j].num;
|
|
998
|
+
|
|
999
|
+
leafWidth = this.thumbWidth;
|
|
1000
|
+
leafHeight = parseInt((this.getPageHeight(leaf)*this.thumbWidth)/this.getPageWidth(leaf), 10);
|
|
1001
|
+
leafTop = leafMap[row].top;
|
|
1002
|
+
left = leafMap[row].leafs[index].left + pageViewBuffer;
|
|
1003
|
+
if ('rl' == this.pageProgression){
|
|
1004
|
+
left = viewWidth - leafWidth - left;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
div = document.createElement("div");
|
|
1008
|
+
div.style.position = "absolute";
|
|
1009
|
+
div.className = 'BRpagedivthumb pagediv' + leaf;
|
|
1010
|
+
|
|
1011
|
+
left += this.thumbPadding;
|
|
1012
|
+
div.style.top = leafTop + 'px';
|
|
1013
|
+
div.style.left = left + 'px';
|
|
1014
|
+
div.style.width = leafWidth + 'px';
|
|
1015
|
+
div.style.height = leafHeight + 'px';
|
|
1016
|
+
|
|
1017
|
+
// link back to page
|
|
1018
|
+
link = document.createElement("a");
|
|
1019
|
+
$(link).data('leaf', leaf);
|
|
1020
|
+
link.addEventListener('mouseup', function(event) {
|
|
1021
|
+
self.firstIndex = $(this).data('leaf');
|
|
1022
|
+
if (self.prevReadMode === self.constMode1up
|
|
1023
|
+
|| self.prevReadMode === self.constMode2up) {
|
|
1024
|
+
self.switchMode(self.prevReadMode);
|
|
1025
|
+
} else {
|
|
1026
|
+
self.switchMode(self.constMode1up);
|
|
1027
|
+
}
|
|
1028
|
+
event.preventDefault();
|
|
1029
|
+
event.stopPropagation();
|
|
1030
|
+
}, true);
|
|
1031
|
+
$(div).append(link);
|
|
1032
|
+
|
|
1033
|
+
this.refs.$brPageViewEl.append(div);
|
|
1034
|
+
|
|
1035
|
+
img = document.createElement("img");
|
|
1036
|
+
var thumbReduce = Math.floor(this.getPageWidth(leaf) / this.thumbWidth);
|
|
1037
|
+
|
|
1038
|
+
$(img).attr('src', this.imagesBaseURL + 'transparent.png')
|
|
1039
|
+
.css({'width': leafWidth+'px', 'height': leafHeight+'px' })
|
|
1040
|
+
.addClass('BRlazyload')
|
|
1041
|
+
// Store the URL of the image that will replace this one
|
|
1042
|
+
.data('srcURL', this._getPageURI(leaf, thumbReduce));
|
|
1043
|
+
$(link).append(img);
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// Remove thumbnails that are not to be displayed
|
|
1049
|
+
var k;
|
|
1050
|
+
for (i=0; i<this.displayedRows.length; i++) {
|
|
1051
|
+
if (BookReader.util.notInArray(this.displayedRows[i], rowsToDisplay)) {
|
|
1052
|
+
row = this.displayedRows[i];
|
|
1053
|
+
for (k=0; k<leafMap[row].leafs.length; k++) {
|
|
1054
|
+
index = leafMap[row].leafs[k].num;
|
|
1055
|
+
this.$('.pagediv'+index).remove();
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// Update which page is considered current to make sure a visible page is the current one
|
|
1061
|
+
var currentIndex = this.currentIndex();
|
|
1062
|
+
if (currentIndex < leastVisible) {
|
|
1063
|
+
this.setCurrentIndex(leastVisible);
|
|
1064
|
+
} else if (currentIndex > mostVisible) {
|
|
1065
|
+
this.setCurrentIndex(mostVisible);
|
|
1066
|
+
}
|
|
1067
|
+
this.updateNavIndexThrottled();
|
|
1068
|
+
|
|
1069
|
+
this.displayedRows = rowsToDisplay.slice();
|
|
1070
|
+
|
|
1071
|
+
// Notify of new fragment, but only if we're currently displaying a leaf
|
|
1072
|
+
// Hack that fixes #365790
|
|
1073
|
+
if (this.displayedRows.length > 0) {
|
|
1074
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// remove previous highlights
|
|
1078
|
+
this.$('.BRpagedivthumb_highlight').removeClass('BRpagedivthumb_highlight');
|
|
1079
|
+
|
|
1080
|
+
// highlight current page
|
|
1081
|
+
this.$('.pagediv'+this.currentIndex()).addClass('BRpagedivthumb_highlight');
|
|
1082
|
+
|
|
1083
|
+
this.lazyLoadThumbnails();
|
|
1084
|
+
|
|
1085
|
+
this.updateToolbarZoom(this.reduce);
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
BookReader.prototype.lazyLoadThumbnails = function() {
|
|
1089
|
+
// We check the complete property since load may not be fired if loading from the cache
|
|
1090
|
+
this.$('.BRlazyloading').filter('[complete=true]').removeClass('BRlazyloading');
|
|
1091
|
+
|
|
1092
|
+
var loading = this.$('.BRlazyloading').length;
|
|
1093
|
+
var toLoad = this.thumbMaxLoading - loading;
|
|
1094
|
+
|
|
1095
|
+
var self = this;
|
|
1096
|
+
|
|
1097
|
+
if (toLoad > 0) {
|
|
1098
|
+
// $$$ TODO load those near top (but not beyond) page view first
|
|
1099
|
+
this.refs.$brPageViewEl.find('img.BRlazyload').filter(':lt(' + toLoad + ')').each( function() {
|
|
1100
|
+
self.lazyLoadImage(this);
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
1103
|
+
};
|
|
1104
|
+
|
|
1105
|
+
BookReader.prototype.lazyLoadImage = function (dummyImage) {
|
|
1106
|
+
var img = new Image();
|
|
1107
|
+
var self = this;
|
|
1108
|
+
|
|
1109
|
+
$(img)
|
|
1110
|
+
.addClass('BRlazyloading')
|
|
1111
|
+
.one('load', function() {
|
|
1112
|
+
$(this).removeClass('BRlazyloading');
|
|
1113
|
+
|
|
1114
|
+
// $$$ Calling lazyLoadThumbnails here was causing stack overflow on IE so
|
|
1115
|
+
// we call the function after a slight delay. Also the img.complete property
|
|
1116
|
+
// is not yet set in IE8 inside this onload handler
|
|
1117
|
+
setTimeout(function() { self.lazyLoadThumbnails(); }, 100);
|
|
1118
|
+
})
|
|
1119
|
+
.one('error', function() {
|
|
1120
|
+
// Remove class so we no longer count as loading
|
|
1121
|
+
$(this).removeClass('BRlazyloading');
|
|
1122
|
+
})
|
|
1123
|
+
|
|
1124
|
+
//the width set with .attr is ignored by Internet Explorer, causing it to show the image at its original size
|
|
1125
|
+
//but with this one line of css, even IE shows the image at the proper size
|
|
1126
|
+
.css({
|
|
1127
|
+
'width': $(dummyImage).width()+'px',
|
|
1128
|
+
'height': $(dummyImage).height()+'px'
|
|
1129
|
+
})
|
|
1130
|
+
.attr({
|
|
1131
|
+
'width': $(dummyImage).width(),
|
|
1132
|
+
'height': $(dummyImage).height(),
|
|
1133
|
+
'src': $(dummyImage).data('srcURL')
|
|
1134
|
+
});
|
|
1135
|
+
|
|
1136
|
+
// replace with the new img
|
|
1137
|
+
$(dummyImage).before(img).remove();
|
|
1138
|
+
|
|
1139
|
+
img = null; // tidy up closure
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
// drawLeafsTwoPage()
|
|
1144
|
+
//______________________________________________________________________________
|
|
1145
|
+
BookReader.prototype.drawLeafsTwoPage = function() {
|
|
1146
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
1147
|
+
var scrollTop = $twoPageViewEl.prop('scrollTop');
|
|
1148
|
+
var scrollBottom = scrollTop + $twoPageViewEl.height();
|
|
1149
|
+
|
|
1150
|
+
// $$$ we should use calculated values in this.twoPage (recalc if necessary)
|
|
1151
|
+
|
|
1152
|
+
var indexL = this.twoPage.currentIndexL;
|
|
1153
|
+
|
|
1154
|
+
var heightL = this._getPageHeight(indexL);
|
|
1155
|
+
var widthL = this._getPageWidth(indexL);
|
|
1156
|
+
|
|
1157
|
+
var leafEdgeWidthL = this.leafEdgeWidth(indexL);
|
|
1158
|
+
var leafEdgeWidthR = this.twoPage.edgeWidth - leafEdgeWidthL;
|
|
1159
|
+
var bookCoverDivWidth = this.twoPage.bookCoverDivWidth;
|
|
1160
|
+
|
|
1161
|
+
var middle = this.twoPage.middle; // $$$ getter instead?
|
|
1162
|
+
var top = this.twoPageTop();
|
|
1163
|
+
var bookCoverDivLeft = this.twoPage.bookCoverDivLeft;
|
|
1164
|
+
|
|
1165
|
+
this.twoPage.scaledWL = this.getPageWidth2UP(indexL);
|
|
1166
|
+
this.twoPage.gutter = this.twoPageGutter();
|
|
1167
|
+
|
|
1168
|
+
this.prefetchImg(indexL);
|
|
1169
|
+
$(this.prefetchedImgs[indexL]).css({
|
|
1170
|
+
position: 'absolute',
|
|
1171
|
+
left: this.twoPage.gutter-this.twoPage.scaledWL+'px',
|
|
1172
|
+
right: '',
|
|
1173
|
+
top: top+'px',
|
|
1174
|
+
height: this.twoPage.height +'px', // $$$ height forced the same for both pages
|
|
1175
|
+
width: this.twoPage.scaledWL + 'px',
|
|
1176
|
+
zIndex: 2
|
|
1177
|
+
}).appendTo($twoPageViewEl);
|
|
1178
|
+
|
|
1179
|
+
var indexR = this.twoPage.currentIndexR;
|
|
1180
|
+
var heightR = this._getPageHeight(indexR);
|
|
1181
|
+
var widthR = this._getPageWidth(indexR);
|
|
1182
|
+
|
|
1183
|
+
// $$$ should use getwidth2up?
|
|
1184
|
+
//var scaledWR = this.twoPage.height*widthR/heightR;
|
|
1185
|
+
this.twoPage.scaledWR = this.getPageWidth2UP(indexR);
|
|
1186
|
+
this.prefetchImg(indexR);
|
|
1187
|
+
$(this.prefetchedImgs[indexR]).css({
|
|
1188
|
+
position: 'absolute',
|
|
1189
|
+
left: this.twoPage.gutter+'px',
|
|
1190
|
+
right: '',
|
|
1191
|
+
top: top+'px',
|
|
1192
|
+
height: this.twoPage.height + 'px', // $$$ height forced the same for both pages
|
|
1193
|
+
width: this.twoPage.scaledWR + 'px',
|
|
1194
|
+
zIndex: 2
|
|
1195
|
+
}).appendTo($twoPageViewEl);
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
this.displayedIndices = [this.twoPage.currentIndexL, this.twoPage.currentIndexR];
|
|
1199
|
+
this.setMouseHandlers2UP();
|
|
1200
|
+
this.twoPageSetCursor();
|
|
1201
|
+
|
|
1202
|
+
this.updatePageNumBox2UP();
|
|
1203
|
+
this.updateToolbarZoom(this.reduce);
|
|
1204
|
+
};
|
|
1205
|
+
|
|
1206
|
+
// updatePageNumBox2UP
|
|
1207
|
+
//______________________________________________________________________________
|
|
1208
|
+
BookReader.prototype.updatePageNumBox2UP = function() {
|
|
1209
|
+
// TODO see if this function is still needed
|
|
1210
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
|
1211
|
+
};
|
|
1212
|
+
|
|
1213
|
+
// drawLeafsThrottled()
|
|
1214
|
+
// A throttled version of drawLeafs
|
|
1215
|
+
//______________________________________________________________________________
|
|
1216
|
+
BookReader.prototype.drawLeafsThrottled = BookReader.util.throttle(
|
|
1217
|
+
BookReader.prototype.drawLeafs,
|
|
1218
|
+
250 // 250 ms gives quick feedback, but doesn't eat cpu
|
|
1219
|
+
);
|
|
1220
|
+
|
|
1221
|
+
|
|
1222
|
+
// zoom(direction)
|
|
1223
|
+
//
|
|
1224
|
+
// Pass 1 to zoom in, anything else to zoom out
|
|
1225
|
+
//______________________________________________________________________________
|
|
1226
|
+
BookReader.prototype.zoom = function(direction) {
|
|
1227
|
+
switch (this.mode) {
|
|
1228
|
+
case this.constMode1up:
|
|
1229
|
+
if (direction == 1) {
|
|
1230
|
+
// XXX other cases
|
|
1231
|
+
this.zoom1up('in');
|
|
1232
|
+
} else {
|
|
1233
|
+
this.zoom1up('out');
|
|
1234
|
+
}
|
|
1235
|
+
break
|
|
1236
|
+
case this.constMode2up:
|
|
1237
|
+
if (direction == 1) {
|
|
1238
|
+
// XXX other cases
|
|
1239
|
+
this.zoom2up('in');
|
|
1240
|
+
} else {
|
|
1241
|
+
this.zoom2up('out');
|
|
1242
|
+
}
|
|
1243
|
+
break
|
|
1244
|
+
case this.constModeThumb:
|
|
1245
|
+
// XXX update zoomThumb for named directions
|
|
1246
|
+
this.zoomThumb(direction);
|
|
1247
|
+
break
|
|
1248
|
+
}
|
|
1249
|
+
return;
|
|
1250
|
+
};
|
|
1251
|
+
|
|
1252
|
+
// zoom1up(dir)
|
|
1253
|
+
//______________________________________________________________________________
|
|
1254
|
+
BookReader.prototype.zoom1up = function(direction) {
|
|
1255
|
+
|
|
1256
|
+
if (this.constMode2up == this.mode) { //can only zoom in 1-page mode
|
|
1257
|
+
this.switchMode(this.constMode1up);
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
var reduceFactor = this.nextReduce(this.reduce, direction, this.onePage.reductionFactors);
|
|
1262
|
+
|
|
1263
|
+
if (this.reduce == reduceFactor.reduce) {
|
|
1264
|
+
// Already at this level
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
|
|
1268
|
+
this.reduce = reduceFactor.reduce; // $$$ incorporate into function
|
|
1269
|
+
this.onePage.autofit = reduceFactor.autofit;
|
|
1270
|
+
|
|
1271
|
+
this.pageScale = this.reduce; // preserve current reduce
|
|
1272
|
+
|
|
1273
|
+
this.resizePageView();
|
|
1274
|
+
this.updateToolbarZoom(this.reduce);
|
|
1275
|
+
|
|
1276
|
+
// Recalculate search hilites
|
|
1277
|
+
if (this.enableSearch) this.removeSearchHilites();
|
|
1278
|
+
if (this.enableSearch) this.updateSearchHilites();
|
|
1279
|
+
|
|
1280
|
+
};
|
|
1281
|
+
|
|
1282
|
+
// Resizes the inner container to fit within the visible space to prevent
|
|
1283
|
+
// the top toolbar and bottom navbar from clipping the visible book
|
|
1284
|
+
BookReader.prototype.resizeBRcontainer = function() {
|
|
1285
|
+
this.refs.$brContainer.css({
|
|
1286
|
+
top: this.getToolBarHeight(),
|
|
1287
|
+
bottom: this.getNavHeight(),
|
|
1288
|
+
});
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
// resizePageView()
|
|
1292
|
+
//______________________________________________________________________________
|
|
1293
|
+
BookReader.prototype.resizePageView = function() {
|
|
1294
|
+
// $$$ This code assumes 1up mode
|
|
1295
|
+
// e.g. does not preserve position in thumbnail mode
|
|
1296
|
+
// See http://bugs.launchpad.net/bookreader/+bug/552972
|
|
1297
|
+
switch (this.mode) {
|
|
1298
|
+
case this.constMode1up:
|
|
1299
|
+
this.resizePageView1up(); // $$$ necessary in non-1up mode?
|
|
1300
|
+
break;
|
|
1301
|
+
case this.constMode2up:
|
|
1302
|
+
break;
|
|
1303
|
+
case this.constModeThumb:
|
|
1304
|
+
this.prepareThumbnailView( this.currentIndex() );
|
|
1305
|
+
break;
|
|
1306
|
+
default:
|
|
1307
|
+
alert('Resize not implemented for this mode');
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
|
|
1311
|
+
// Resize the current one page view
|
|
1312
|
+
// Note this calls drawLeafs
|
|
1313
|
+
BookReader.prototype.resizePageView1up = function() {
|
|
1314
|
+
var i;
|
|
1315
|
+
var viewHeight = 0;
|
|
1316
|
+
var viewWidth = this.refs.$brContainer.prop('clientWidth');
|
|
1317
|
+
var oldScrollTop = this.refs.$brContainer.prop('scrollTop');
|
|
1318
|
+
var oldPageViewHeight = this.refs.$brPageViewEl.height();
|
|
1319
|
+
var oldPageViewWidth = this.refs.$brPageViewEl.width();
|
|
1320
|
+
|
|
1321
|
+
// May have come here after preparing the view, in which case the scrollTop and view height are not set
|
|
1322
|
+
|
|
1323
|
+
var scrollRatio = 0;
|
|
1324
|
+
if (oldScrollTop > 0) {
|
|
1325
|
+
// We have scrolled - implies view has been set up
|
|
1326
|
+
var oldCenterY = this.centerY1up();
|
|
1327
|
+
var oldCenterX = this.centerX1up();
|
|
1328
|
+
scrollRatio = oldCenterY / oldPageViewHeight;
|
|
1329
|
+
} else {
|
|
1330
|
+
// Have not scrolled, e.g. because in new container
|
|
1331
|
+
|
|
1332
|
+
// We set the scroll ratio so that the current index will still be considered the
|
|
1333
|
+
// current index in drawLeafsOnePage after we create the new view container
|
|
1334
|
+
|
|
1335
|
+
// Make sure this will count as current page after resize
|
|
1336
|
+
var fudgeFactor = (this.getPageHeight(this.currentIndex()) / this.reduce) * 0.6;
|
|
1337
|
+
var oldLeafTop = this.onePageGetPageTop(this.currentIndex()) + fudgeFactor;
|
|
1338
|
+
var oldViewDimensions = this.onePageCalculateViewDimensions(this.reduce, this.padding);
|
|
1339
|
+
scrollRatio = oldLeafTop / oldViewDimensions.height;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
// Recalculate 1up reduction factors
|
|
1343
|
+
this.onePageCalculateReductionFactors();
|
|
1344
|
+
// Update current reduce (if in autofit)
|
|
1345
|
+
if (this.onePage.autofit) {
|
|
1346
|
+
var reductionFactor = this.nextReduce(this.reduce, this.onePage.autofit, this.onePage.reductionFactors);
|
|
1347
|
+
this.reduce = reductionFactor.reduce;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
var viewDimensions = this.onePageCalculateViewDimensions(this.reduce, this.padding);
|
|
1351
|
+
|
|
1352
|
+
this.refs.$brPageViewEl.height(viewDimensions.height);
|
|
1353
|
+
this.refs.$brPageViewEl.width(viewDimensions.width);
|
|
1354
|
+
|
|
1355
|
+
|
|
1356
|
+
var newCenterY = scrollRatio*viewDimensions.height;
|
|
1357
|
+
var newTop = Math.max(0, Math.floor( newCenterY - this.refs.$brContainer.height()/2 ));
|
|
1358
|
+
this.refs.$brContainer.prop('scrollTop', newTop);
|
|
1359
|
+
|
|
1360
|
+
// We use clientWidth here to avoid miscalculating due to scroll bar
|
|
1361
|
+
var newCenterX = oldCenterX * (viewWidth / oldPageViewWidth);
|
|
1362
|
+
var newLeft = newCenterX - this.refs.$brContainer.prop('clientWidth') / 2;
|
|
1363
|
+
newLeft = Math.max(newLeft, 0);
|
|
1364
|
+
this.refs.$brContainer.prop('scrollLeft', newLeft);
|
|
1365
|
+
|
|
1366
|
+
this.refs.$brPageViewEl.empty();
|
|
1367
|
+
this.displayedIndices = [];
|
|
1368
|
+
this.drawLeafs();
|
|
1369
|
+
|
|
1370
|
+
if (this.enableSearch) {
|
|
1371
|
+
this.removeSearchHilites();
|
|
1372
|
+
this.updateSearchHilites();
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// Calculate the dimensions for a one page view with images at the given reduce and padding
|
|
1377
|
+
BookReader.prototype.onePageCalculateViewDimensions = function(reduce, padding) {
|
|
1378
|
+
var viewWidth = 0;
|
|
1379
|
+
var viewHeight = 0;
|
|
1380
|
+
for (i=0; i<this.getNumLeafs(); i++) {
|
|
1381
|
+
viewHeight += parseInt(this._getPageHeight(i)/reduce) + padding;
|
|
1382
|
+
var width = parseInt(this._getPageWidth(i)/reduce);
|
|
1383
|
+
if (width>viewWidth) viewWidth=width;
|
|
1384
|
+
}
|
|
1385
|
+
return { width: viewWidth, height: viewHeight }
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
// centerX1up()
|
|
1389
|
+
//______________________________________________________________________________
|
|
1390
|
+
// Returns the current offset of the viewport center in scaled document coordinates.
|
|
1391
|
+
BookReader.prototype.centerX1up = function() {
|
|
1392
|
+
var centerX;
|
|
1393
|
+
if (this.refs.$brPageViewEl.width() < this.refs.$brContainer.prop('clientWidth')) { // fully shown
|
|
1394
|
+
centerX = this.refs.$brPageViewEl.width();
|
|
1395
|
+
} else {
|
|
1396
|
+
centerX = this.refs.$brContainer.prop('scrollLeft') + this.refs.$brContainer.prop('clientWidth') / 2;
|
|
1397
|
+
}
|
|
1398
|
+
centerX = Math.floor(centerX);
|
|
1399
|
+
return centerX;
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
// centerY1up()
|
|
1403
|
+
//______________________________________________________________________________
|
|
1404
|
+
// Returns the current offset of the viewport center in scaled document coordinates.
|
|
1405
|
+
BookReader.prototype.centerY1up = function() {
|
|
1406
|
+
var centerY = this.refs.$brContainer.prop('scrollTop') + this.refs.$brContainer.height() / 2;
|
|
1407
|
+
return Math.floor(centerY);
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// centerPageView()
|
|
1411
|
+
//______________________________________________________________________________
|
|
1412
|
+
BookReader.prototype.centerPageView = function() {
|
|
1413
|
+
var scrollWidth = this.refs.$brContainer.prop('scrollWidth');
|
|
1414
|
+
var clientWidth = this.refs.$brContainer.prop('clientWidth');
|
|
1415
|
+
if (scrollWidth > clientWidth) {
|
|
1416
|
+
this.refs.$brContainer.prop('scrollLeft', (scrollWidth-clientWidth)/2);
|
|
1417
|
+
}
|
|
1418
|
+
};
|
|
1419
|
+
|
|
1420
|
+
// zoom2up(direction)
|
|
1421
|
+
//______________________________________________________________________________
|
|
1422
|
+
BookReader.prototype.zoom2up = function(direction) {
|
|
1423
|
+
|
|
1424
|
+
// Hard stop autoplay
|
|
1425
|
+
this.stopFlipAnimations();
|
|
1426
|
+
|
|
1427
|
+
// Recalculate autofit factors
|
|
1428
|
+
this.twoPageCalculateReductionFactors();
|
|
1429
|
+
|
|
1430
|
+
// Get new zoom state
|
|
1431
|
+
var reductionFactor = this.nextReduce(this.reduce, direction, this.twoPage.reductionFactors);
|
|
1432
|
+
if ((this.reduce == reductionFactor.reduce) && (this.twoPage.autofit == reductionFactor.autofit)) {
|
|
1433
|
+
// Same zoom
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
this.twoPage.autofit = reductionFactor.autofit;
|
|
1437
|
+
this.reduce = reductionFactor.reduce;
|
|
1438
|
+
this.pageScale = this.reduce; // preserve current reduce
|
|
1439
|
+
|
|
1440
|
+
// Preserve view center position
|
|
1441
|
+
var oldCenter = this.twoPageGetViewCenter();
|
|
1442
|
+
|
|
1443
|
+
// If zooming in, reload imgs. DOM elements will be removed by prepareTwoPageView
|
|
1444
|
+
// $$$ An improvement would be to use the low res image until the larger one is loaded.
|
|
1445
|
+
if (1 == direction) {
|
|
1446
|
+
for (var img in this.prefetchedImgs) {
|
|
1447
|
+
delete this.prefetchedImgs[img];
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
// Prepare view with new center to minimize visual glitches
|
|
1452
|
+
this.prepareTwoPageView(oldCenter.percentageX, oldCenter.percentageY);
|
|
1453
|
+
};
|
|
1454
|
+
|
|
1455
|
+
BookReader.prototype.zoomThumb = function(direction) {
|
|
1456
|
+
var oldColumns = this.thumbColumns;
|
|
1457
|
+
switch (direction) {
|
|
1458
|
+
case -1:
|
|
1459
|
+
this.thumbColumns += 1;
|
|
1460
|
+
break;
|
|
1461
|
+
case 1:
|
|
1462
|
+
this.thumbColumns -= 1;
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// clamp
|
|
1467
|
+
if (this.thumbColumns < 2) {
|
|
1468
|
+
this.thumbColumns = 2;
|
|
1469
|
+
} else if (this.thumbColumns > 8) {
|
|
1470
|
+
this.thumbColumns = 8;
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
if (this.thumbColumns != oldColumns) {
|
|
1474
|
+
this.prepareThumbnailView();
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
|
|
1478
|
+
// Returns the width per thumbnail to display the requested number of columns
|
|
1479
|
+
// Note: #BRpageview must already exist since its width is used to calculate the
|
|
1480
|
+
// thumbnail width
|
|
1481
|
+
BookReader.prototype.getThumbnailWidth = function(thumbnailColumns) {
|
|
1482
|
+
var padding = (thumbnailColumns + 1) * this.thumbPadding;
|
|
1483
|
+
var width = (this.refs.$brPageViewEl.width() - padding) / (thumbnailColumns + 0.5); // extra 0.5 is for some space at sides
|
|
1484
|
+
return parseInt(width);
|
|
1485
|
+
};
|
|
1486
|
+
|
|
1487
|
+
// quantizeReduce(reduce)
|
|
1488
|
+
//______________________________________________________________________________
|
|
1489
|
+
// Quantizes the given reduction factor to closest power of two from set from 12.5% to 200%
|
|
1490
|
+
BookReader.prototype.quantizeReduce = function(reduce, reductionFactors) {
|
|
1491
|
+
var quantized = reductionFactors[0].reduce;
|
|
1492
|
+
var distance = Math.abs(reduce - quantized);
|
|
1493
|
+
for (var i = 1; i < reductionFactors.length; i++) {
|
|
1494
|
+
newDistance = Math.abs(reduce - reductionFactors[i].reduce);
|
|
1495
|
+
if (newDistance < distance) {
|
|
1496
|
+
distance = newDistance;
|
|
1497
|
+
quantized = reductionFactors[i].reduce;
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return quantized;
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
// reductionFactors should be array of sorted reduction factors
|
|
1505
|
+
// e.g. [ {reduce: 0.25, autofit: null}, {reduce: 0.3, autofit: 'width'}, {reduce: 1, autofit: null} ]
|
|
1506
|
+
BookReader.prototype.nextReduce = function(currentReduce, direction, reductionFactors) {
|
|
1507
|
+
// XXX add 'closest', to replace quantize function
|
|
1508
|
+
|
|
1509
|
+
if (direction === 'in') {
|
|
1510
|
+
var newReduceIndex = 0;
|
|
1511
|
+
for (var i = 1; i < reductionFactors.length; i++) {
|
|
1512
|
+
if (reductionFactors[i].reduce < currentReduce) {
|
|
1513
|
+
newReduceIndex = i;
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return reductionFactors[newReduceIndex];
|
|
1517
|
+
} else if (direction === 'out') { // zoom out
|
|
1518
|
+
var lastIndex = reductionFactors.length - 1;
|
|
1519
|
+
var newReduceIndex = lastIndex;
|
|
1520
|
+
|
|
1521
|
+
for (var i = lastIndex; i >= 0; i--) {
|
|
1522
|
+
if (reductionFactors[i].reduce > currentReduce) {
|
|
1523
|
+
newReduceIndex = i;
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
return reductionFactors[newReduceIndex];
|
|
1527
|
+
} else if (direction === 'auto') {
|
|
1528
|
+
// Auto mode chooses the least reduction
|
|
1529
|
+
var choice = null;
|
|
1530
|
+
for (var i = 0; i < reductionFactors.length; i++) {
|
|
1531
|
+
if (reductionFactors[i].autofit === 'height' || reductionFactors[i].autofit === 'width') {
|
|
1532
|
+
if (choice === null || choice.reduce < reductionFactors[i].reduce) {
|
|
1533
|
+
choice = reductionFactors[i];
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
if (choice) {
|
|
1538
|
+
return choice;
|
|
1539
|
+
}
|
|
1540
|
+
} else if (direction === 'height' || direction === 'width') {
|
|
1541
|
+
// Asked for specific autofit mode
|
|
1542
|
+
for (var i = 0; i < reductionFactors.length; i++) {
|
|
1543
|
+
if (reductionFactors[i].autofit === direction) {
|
|
1544
|
+
return reductionFactors[i];
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
alert('Could not find reduction factor for direction ' + direction);
|
|
1550
|
+
return reductionFactors[0];
|
|
1551
|
+
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1554
|
+
BookReader.prototype._reduceSort = function(a, b) {
|
|
1555
|
+
return a.reduce - b.reduce;
|
|
1556
|
+
};
|
|
1557
|
+
|
|
1558
|
+
|
|
1559
|
+
// jumpToPage()
|
|
1560
|
+
//______________________________________________________________________________
|
|
1561
|
+
// Attempts to jump to page. Returns true if page could be found, false otherwise.
|
|
1562
|
+
BookReader.prototype.jumpToPage = function(pageNum) {
|
|
1563
|
+
var pageIndex = this.parsePageString(pageNum);
|
|
1564
|
+
|
|
1565
|
+
if ('undefined' != typeof(pageIndex)) {
|
|
1566
|
+
this.jumpToIndex(pageIndex);
|
|
1567
|
+
return true;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
// Page not found
|
|
1571
|
+
return false;
|
|
1572
|
+
};
|
|
1573
|
+
|
|
1574
|
+
// jumpToIndex()
|
|
1575
|
+
//______________________________________________________________________________
|
|
1576
|
+
BookReader.prototype.jumpToIndex = function(index, pageX, pageY, noAnimate) {
|
|
1577
|
+
var self = this;
|
|
1578
|
+
var prevCurrentIndex = this.currentIndex();
|
|
1579
|
+
|
|
1580
|
+
// Not throttling is important to prevent race conditions with scroll
|
|
1581
|
+
this.updateNavIndexThrottled(index);
|
|
1582
|
+
this.trigger(BookReader.eventNames.stop);
|
|
1583
|
+
|
|
1584
|
+
if (this.constMode2up == this.mode) {
|
|
1585
|
+
// By checking against min/max we do nothing if requested index
|
|
1586
|
+
// is current
|
|
1587
|
+
if (index < Math.min(this.twoPage.currentIndexL, this.twoPage.currentIndexR)) {
|
|
1588
|
+
this.flipBackToIndex(index);
|
|
1589
|
+
} else if (index > Math.max(this.twoPage.currentIndexL, this.twoPage.currentIndexR)) {
|
|
1590
|
+
this.flipFwdToIndex(index);
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
} else if (this.constModeThumb == this.mode) {
|
|
1594
|
+
var viewWidth = this.refs.$brContainer.prop('scrollWidth') - 20; // width minus buffer
|
|
1595
|
+
var i;
|
|
1596
|
+
var leafWidth = 0;
|
|
1597
|
+
var leafHeight = 0;
|
|
1598
|
+
var rightPos = 0;
|
|
1599
|
+
var bottomPos = 0;
|
|
1600
|
+
var rowHeight = 0;
|
|
1601
|
+
var leafTop = 0;
|
|
1602
|
+
var leafIndex = 0;
|
|
1603
|
+
|
|
1604
|
+
for (i=0; i<(index+1); i++) {
|
|
1605
|
+
leafWidth = this.thumbWidth;
|
|
1606
|
+
if (rightPos + (leafWidth + this.thumbPadding) > viewWidth){
|
|
1607
|
+
rightPos = 0;
|
|
1608
|
+
rowHeight = 0;
|
|
1609
|
+
leafIndex = 0;
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
leafHeight = parseInt((this.getPageHeight(leafIndex)*this.thumbWidth)/this.getPageWidth(leafIndex), 10);
|
|
1613
|
+
if (leafHeight > rowHeight) { rowHeight = leafHeight; }
|
|
1614
|
+
if (leafIndex==0) { leafTop = bottomPos; }
|
|
1615
|
+
if (leafIndex==0) { bottomPos += this.thumbPadding + rowHeight; }
|
|
1616
|
+
rightPos += leafWidth + this.thumbPadding;
|
|
1617
|
+
leafIndex++;
|
|
1618
|
+
}
|
|
1619
|
+
this.firstIndex=index;
|
|
1620
|
+
if (this.refs.$brContainer.prop('scrollTop') == leafTop) {
|
|
1621
|
+
this.drawLeafs();
|
|
1622
|
+
} else {
|
|
1623
|
+
this.animating = true;
|
|
1624
|
+
this.refs.$brContainer.stop(true).animate({
|
|
1625
|
+
scrollTop: leafTop,
|
|
1626
|
+
}, 'fast', function() {
|
|
1627
|
+
self.animating = false;
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
} else {
|
|
1631
|
+
// 1up
|
|
1632
|
+
var leafTop = this.onePageGetPageTop(index);
|
|
1633
|
+
|
|
1634
|
+
if (pageY) {
|
|
1635
|
+
var offset = parseInt( (pageY) / this.reduce);
|
|
1636
|
+
offset -= this.refs.$brContainer.prop('clientHeight') >> 1;
|
|
1637
|
+
leafTop += offset;
|
|
1638
|
+
} else {
|
|
1639
|
+
// Show page just a little below the top
|
|
1640
|
+
leafTop -= this.padding / 2;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
if (pageX) {
|
|
1644
|
+
var offset = parseInt( (pageX) / this.reduce);
|
|
1645
|
+
offset -= this.refs.$brContainer.prop('clientWidth') >> 1;
|
|
1646
|
+
leafLeft += offset;
|
|
1647
|
+
} else {
|
|
1648
|
+
// Preserve left position
|
|
1649
|
+
leafLeft = this.refs.$brContainer.scrollLeft();
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
// Only animate for small distances
|
|
1653
|
+
if (!noAnimate && Math.abs(prevCurrentIndex - index) <= 4) {
|
|
1654
|
+
this.animating = true;
|
|
1655
|
+
this.refs.$brContainer.stop(true).animate({
|
|
1656
|
+
scrollTop: leafTop,
|
|
1657
|
+
scrollLeft: leafLeft,
|
|
1658
|
+
}, 'fast', function() {
|
|
1659
|
+
self.animating = false;
|
|
1660
|
+
});
|
|
1661
|
+
} else {
|
|
1662
|
+
this.refs.$brContainer.stop(true).prop('scrollTop', leafTop);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
};
|
|
1666
|
+
|
|
1667
|
+
// switchMode()
|
|
1668
|
+
//______________________________________________________________________________
|
|
1669
|
+
BookReader.prototype.switchMode = function(mode) {
|
|
1670
|
+
if (mode === this.mode) {
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if (!this.canSwitchToMode(mode)) {
|
|
1675
|
+
return;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
this.trigger(BookReader.eventNames.stop);
|
|
1679
|
+
if (this.enableSearch) this.removeSearchHilites();
|
|
1680
|
+
|
|
1681
|
+
if (this.mode === this.constMode1up || this.mode === this.constMode2up) {
|
|
1682
|
+
this.prevReadMode = this.mode;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
this.mode = mode;
|
|
1686
|
+
|
|
1687
|
+
// reinstate scale if moving from thumbnail view
|
|
1688
|
+
if (this.pageScale !== this.reduce) {
|
|
1689
|
+
this.reduce = this.pageScale;
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
// $$$ TODO preserve center of view when switching between mode
|
|
1693
|
+
// See https://bugs.edge.launchpad.net/gnubook/+bug/416682
|
|
1694
|
+
|
|
1695
|
+
// XXX maybe better to preserve zoom in each mode
|
|
1696
|
+
if (this.constMode1up == mode) {
|
|
1697
|
+
this.onePageCalculateReductionFactors();
|
|
1698
|
+
this.reduce = this.quantizeReduce(this.reduce, this.onePage.reductionFactors);
|
|
1699
|
+
this.prepareOnePageView();
|
|
1700
|
+
} else if (this.constModeThumb == mode) {
|
|
1701
|
+
this.reduce = this.quantizeReduce(this.reduce, this.reductionFactors);
|
|
1702
|
+
this.prepareThumbnailView();
|
|
1703
|
+
} else {
|
|
1704
|
+
// $$$ why don't we save autofit?
|
|
1705
|
+
// this.twoPage.autofit = null; // Take zoom level from other mode
|
|
1706
|
+
this.twoPageCalculateReductionFactors();
|
|
1707
|
+
this.reduce = this.quantizeReduce(this.reduce, this.twoPage.reductionFactors);
|
|
1708
|
+
this.prepareTwoPageView();
|
|
1709
|
+
this.twoPageCenterView(0.5, 0.5); // $$$ TODO preserve center
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
this.trigger(BookReader.eventNames.fragmentChange);
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
BookReader.prototype.updateBrClasses = function() {
|
|
1716
|
+
var modeToClass = {};
|
|
1717
|
+
modeToClass[this.constMode1up] = 'BRmode1up';
|
|
1718
|
+
modeToClass[this.constMode2up] = 'BRmode2up';
|
|
1719
|
+
modeToClass[this.constModeThumb] = 'BRmodeThumb';
|
|
1720
|
+
|
|
1721
|
+
this.refs.$br
|
|
1722
|
+
.removeClass('BRmode1up BRmode2up BRmodeThumb')
|
|
1723
|
+
.addClass(modeToClass[this.mode]);
|
|
1724
|
+
|
|
1725
|
+
if (this.isFullscreen()) {
|
|
1726
|
+
this.refs.$br.addClass('fullscreenActive');
|
|
1727
|
+
$(document.body).addClass('BRfullscreenActive');
|
|
1728
|
+
} else {
|
|
1729
|
+
this.refs.$br.removeClass('fullscreenActive');
|
|
1730
|
+
$(document.body).removeClass('BRfullscreenActive');
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
|
|
1734
|
+
BookReader.prototype.isFullscreen = function() {
|
|
1735
|
+
return this.isFullscreenActive;
|
|
1736
|
+
};
|
|
1737
|
+
|
|
1738
|
+
BookReader.prototype.toggleFullscreen = function() {
|
|
1739
|
+
if (this.isFullscreen()) {
|
|
1740
|
+
this.exitFullScreen();
|
|
1741
|
+
} else {
|
|
1742
|
+
this.enterFullscreen();
|
|
1743
|
+
}
|
|
1744
|
+
};
|
|
1745
|
+
|
|
1746
|
+
BookReader.prototype.enterFullscreen = function() {
|
|
1747
|
+
this.refs.$brContainer.css('opacity', 0);
|
|
1748
|
+
|
|
1749
|
+
var windowWidth = $(window).width();
|
|
1750
|
+
if (windowWidth <= this.onePageMinBreakpoint) {
|
|
1751
|
+
this.switchMode(this.constMode1up);
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
this.isFullscreenActive = true;
|
|
1755
|
+
this.updateBrClasses();
|
|
1756
|
+
|
|
1757
|
+
this.resize();
|
|
1758
|
+
this.refs.$brContainer.animate({opacity: 1}, 400, 'linear');
|
|
1759
|
+
|
|
1760
|
+
this._fullscreenCloseHandler = function (e) {
|
|
1761
|
+
if (e.keyCode === 27) this.exitFullScreen();
|
|
1762
|
+
}.bind(this);
|
|
1763
|
+
$(document).keyup(this._fullscreenCloseHandler);
|
|
1764
|
+
|
|
1765
|
+
};
|
|
1766
|
+
|
|
1767
|
+
BookReader.prototype.exitFullScreen = function() {
|
|
1768
|
+
this.refs.$brContainer.css('opacity', 0);
|
|
1769
|
+
|
|
1770
|
+
$(document).unbind('keyup', this._fullscreenCloseHandler);
|
|
1771
|
+
|
|
1772
|
+
var windowWidth = $(window).width();
|
|
1773
|
+
if (windowWidth <= this.onePageMinBreakpoint) {
|
|
1774
|
+
this.switchMode(this.constMode2up);
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
this.isFullscreenActive = false;
|
|
1778
|
+
this.updateBrClasses()
|
|
1779
|
+
|
|
1780
|
+
this.resize();
|
|
1781
|
+
this.refs.$brContainer.animate({opacity: 1}, 400, 'linear');
|
|
1782
|
+
|
|
1783
|
+
};
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
//prepareOnePageView()
|
|
1787
|
+
// This is called when we switch to one page view
|
|
1788
|
+
//______________________________________________________________________________
|
|
1789
|
+
BookReader.prototype.prepareOnePageView = function() {
|
|
1790
|
+
var startLeaf = this.currentIndex();
|
|
1791
|
+
|
|
1792
|
+
this.refs.$brContainer.empty();
|
|
1793
|
+
this.refs.$brContainer.css({
|
|
1794
|
+
overflowY: 'scroll',
|
|
1795
|
+
overflowX: 'auto'
|
|
1796
|
+
});
|
|
1797
|
+
|
|
1798
|
+
this.refs.$brPageViewEl = $("<div class='BRpageview'></div>");
|
|
1799
|
+
this.refs.$brContainer.append(this.refs.$brPageViewEl);
|
|
1800
|
+
|
|
1801
|
+
// Attaches to first child - child must be present
|
|
1802
|
+
this.refs.$brContainer.dragscrollable();
|
|
1803
|
+
this.bindGestures(this.refs.$brContainer);
|
|
1804
|
+
|
|
1805
|
+
// $$$ keep select enabled for now since disabling it breaks keyboard
|
|
1806
|
+
// nav in FF 3.6 (https://bugs.edge.launchpad.net/bookreader/+bug/544666)
|
|
1807
|
+
// BookReader.util.disableSelect(this.$('#BRpageview'));
|
|
1808
|
+
|
|
1809
|
+
this.resizePageView();
|
|
1810
|
+
this.jumpToIndex(startLeaf);
|
|
1811
|
+
this.updateBrClasses();
|
|
1812
|
+
};
|
|
1813
|
+
|
|
1814
|
+
//prepareThumbnailView()
|
|
1815
|
+
//______________________________________________________________________________
|
|
1816
|
+
BookReader.prototype.prepareThumbnailView = function() {
|
|
1817
|
+
this.refs.$brContainer.empty();
|
|
1818
|
+
this.refs.$brContainer.css({
|
|
1819
|
+
overflowY: 'scroll',
|
|
1820
|
+
overflowX: 'auto'
|
|
1821
|
+
});
|
|
1822
|
+
|
|
1823
|
+
this.refs.$brPageViewEl = $("<div class='BRpageview'></div>");
|
|
1824
|
+
this.refs.$brContainer.append(this.refs.$brPageViewEl);
|
|
1825
|
+
this.refs.$brContainer.dragscrollable({preventDefault:true});
|
|
1826
|
+
|
|
1827
|
+
this.bindGestures(this.refs.$brContainer);
|
|
1828
|
+
|
|
1829
|
+
// $$$ keep select enabled for now since disabling it breaks keyboard
|
|
1830
|
+
// nav in FF 3.6 (https://bugs.edge.launchpad.net/bookreader/+bug/544666)
|
|
1831
|
+
// BookReader.util.disableSelect(this.$('#BRpageview'));
|
|
1832
|
+
|
|
1833
|
+
this.thumbWidth = this.getThumbnailWidth(this.thumbColumns);
|
|
1834
|
+
this.reduce = this.getPageWidth(0)/this.thumbWidth;
|
|
1835
|
+
|
|
1836
|
+
this.displayedRows = [];
|
|
1837
|
+
|
|
1838
|
+
// Draw leafs with current index directly in view (no animating to the index)
|
|
1839
|
+
this.drawLeafsThumbnail( this.currentIndex() );
|
|
1840
|
+
this.updateBrClasses();
|
|
1841
|
+
};
|
|
1842
|
+
|
|
1843
|
+
// prepareTwoPageView()
|
|
1844
|
+
//______________________________________________________________________________
|
|
1845
|
+
// Some decisions about two page view:
|
|
1846
|
+
//
|
|
1847
|
+
// Both pages will be displayed at the same height, even if they were different physical/scanned
|
|
1848
|
+
// sizes. This simplifies the animation (from a design as well as technical standpoint). We
|
|
1849
|
+
// examine the page aspect ratios (in calculateSpreadSize) and use the page with the most "normal"
|
|
1850
|
+
// aspect ratio to determine the height.
|
|
1851
|
+
//
|
|
1852
|
+
// The two page view div is resized to keep the middle of the book in the middle of the div
|
|
1853
|
+
// even as the page sizes change. To e.g. keep the middle of the book in the middle of the BRcontent
|
|
1854
|
+
// div requires adjusting the offset of BRtwpageview and/or scrolling in BRcontent.
|
|
1855
|
+
BookReader.prototype.prepareTwoPageView = function(centerPercentageX, centerPercentageY) {
|
|
1856
|
+
this.refs.$brContainer.empty();
|
|
1857
|
+
this.refs.$brContainer.css('overflow', 'auto');
|
|
1858
|
+
|
|
1859
|
+
// We want to display two facing pages. We may be missing
|
|
1860
|
+
// one side of the spread because it is the first/last leaf,
|
|
1861
|
+
// foldouts, missing pages, etc
|
|
1862
|
+
|
|
1863
|
+
var targetLeaf = this.firstIndex;
|
|
1864
|
+
|
|
1865
|
+
if (targetLeaf < this.firstDisplayableIndex()) {
|
|
1866
|
+
targetLeaf = this.firstDisplayableIndex();
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
if (targetLeaf > this.lastDisplayableIndex()) {
|
|
1870
|
+
targetLeaf = this.lastDisplayableIndex();
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
var currentSpreadIndices = this.getSpreadIndices(targetLeaf);
|
|
1874
|
+
this.twoPage.currentIndexL = currentSpreadIndices[0];
|
|
1875
|
+
this.twoPage.currentIndexR = currentSpreadIndices[1];
|
|
1876
|
+
this.firstIndex = this.twoPage.currentIndexL;
|
|
1877
|
+
|
|
1878
|
+
this.calculateSpreadSize(); //sets twoPage.width, twoPage.height and others
|
|
1879
|
+
|
|
1880
|
+
this.pruneUnusedImgs();
|
|
1881
|
+
this.prefetch(); // Preload images or reload if scaling has changed
|
|
1882
|
+
|
|
1883
|
+
// Add the two page view
|
|
1884
|
+
// $$$ Can we get everything set up and then append?
|
|
1885
|
+
var $twoPageViewEl = $('<div class="BRtwopageview"></div>');
|
|
1886
|
+
this.refs.$brTwoPageView = $twoPageViewEl;
|
|
1887
|
+
this.refs.$brContainer.append($twoPageViewEl);
|
|
1888
|
+
|
|
1889
|
+
// Attaches to first child, so must come after we add the page view
|
|
1890
|
+
this.refs.$brContainer.dragscrollable({preventDefault:true});
|
|
1891
|
+
this.bindGestures(this.refs.$brContainer);
|
|
1892
|
+
|
|
1893
|
+
// $$$ calculate first then set
|
|
1894
|
+
this.refs.$brTwoPageView.css({
|
|
1895
|
+
height: this.twoPage.totalHeight + 'px',
|
|
1896
|
+
width: this.twoPage.totalWidth + 'px',
|
|
1897
|
+
position: 'absolute'
|
|
1898
|
+
});
|
|
1899
|
+
|
|
1900
|
+
// If there will not be scrollbars (e.g. when zooming out) we center the book
|
|
1901
|
+
// since otherwise the book will be stuck off-center
|
|
1902
|
+
if (this.twoPage.totalWidth < this.refs.$brContainer.prop('clientWidth')) {
|
|
1903
|
+
centerPercentageX = 0.5;
|
|
1904
|
+
}
|
|
1905
|
+
if (this.twoPage.totalHeight < this.refs.$brContainer.prop('clientHeight')) {
|
|
1906
|
+
centerPercentageY = 0.5;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
this.twoPageCenterView(centerPercentageX, centerPercentageY);
|
|
1910
|
+
|
|
1911
|
+
this.twoPage.coverDiv = document.createElement('div');
|
|
1912
|
+
$(this.twoPage.coverDiv).attr('class', 'BRbookcover').css({
|
|
1913
|
+
width: this.twoPage.bookCoverDivWidth + 'px',
|
|
1914
|
+
height: this.twoPage.bookCoverDivHeight+'px',
|
|
1915
|
+
visibility: 'visible'
|
|
1916
|
+
}).appendTo(this.refs.$brTwoPageView);
|
|
1917
|
+
|
|
1918
|
+
this.leafEdgeR = document.createElement('div');
|
|
1919
|
+
this.leafEdgeR.className = 'BRleafEdgeR';
|
|
1920
|
+
$(this.leafEdgeR).css({
|
|
1921
|
+
width: this.twoPage.leafEdgeWidthR + 'px',
|
|
1922
|
+
height: this.twoPage.height + 'px',
|
|
1923
|
+
left: this.twoPage.gutter+this.twoPage.scaledWR+'px',
|
|
1924
|
+
top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px'
|
|
1925
|
+
}).appendTo(this.refs.$brTwoPageView);
|
|
1926
|
+
|
|
1927
|
+
this.leafEdgeL = document.createElement('div');
|
|
1928
|
+
this.leafEdgeL.className = 'BRleafEdgeL';
|
|
1929
|
+
$(this.leafEdgeL).css({
|
|
1930
|
+
width: this.twoPage.leafEdgeWidthL + 'px',
|
|
1931
|
+
height: this.twoPage.height + 'px',
|
|
1932
|
+
left: this.twoPage.bookCoverDivLeft+this.twoPage.coverInternalPadding+'px',
|
|
1933
|
+
top: this.twoPage.bookCoverDivTop+this.twoPage.coverInternalPadding+'px'
|
|
1934
|
+
}).appendTo(this.refs.$brTwoPageView);
|
|
1935
|
+
|
|
1936
|
+
div = document.createElement('div');
|
|
1937
|
+
$(div).attr('class', 'BRgutter').css({
|
|
1938
|
+
width: this.twoPage.bookSpineDivWidth+'px',
|
|
1939
|
+
height: this.twoPage.bookSpineDivHeight+'px',
|
|
1940
|
+
left: (this.twoPage.gutter - this.twoPage.bookSpineDivWidth*0.5)+'px',
|
|
1941
|
+
top: this.twoPage.bookSpineDivTop+'px'
|
|
1942
|
+
}).appendTo(this.refs.$brTwoPageView);
|
|
1943
|
+
|
|
1944
|
+
this.prepareTwoPagePopUp();
|
|
1945
|
+
|
|
1946
|
+
this.displayedIndices = [];
|
|
1947
|
+
|
|
1948
|
+
this.drawLeafsTwoPage();
|
|
1949
|
+
this.updateToolbarZoom(this.reduce);
|
|
1950
|
+
|
|
1951
|
+
this.prefetch();
|
|
1952
|
+
|
|
1953
|
+
if (this.enableSearch) {
|
|
1954
|
+
this.removeSearchHilites();
|
|
1955
|
+
this.updateSearchHilites();
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
this.updateBrClasses();
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
// prepareTwoPagePopUp()
|
|
1962
|
+
//
|
|
1963
|
+
// This function prepares the "View Page n" popup that shows while the mouse is
|
|
1964
|
+
// over the left/right "stack of sheets" edges. It also binds the mouse
|
|
1965
|
+
// events for these divs.
|
|
1966
|
+
//______________________________________________________________________________
|
|
1967
|
+
BookReader.prototype.prepareTwoPagePopUp = function() {
|
|
1968
|
+
|
|
1969
|
+
this.twoPagePopUp = document.createElement('div');
|
|
1970
|
+
this.twoPagePopUp.className = 'BRtwoPagePopUp';
|
|
1971
|
+
$(this.twoPagePopUp).css({
|
|
1972
|
+
zIndex: '1000'
|
|
1973
|
+
}).appendTo(this.refs.$brContainer);
|
|
1974
|
+
$(this.twoPagePopUp).hide();
|
|
1975
|
+
|
|
1976
|
+
$(this.leafEdgeL).add(this.leafEdgeR).bind('mouseenter', this, function(e) {
|
|
1977
|
+
$(e.data.twoPagePopUp).show();
|
|
1978
|
+
});
|
|
1979
|
+
|
|
1980
|
+
$(this.leafEdgeL).add(this.leafEdgeR).bind('mouseleave', this, function(e) {
|
|
1981
|
+
$(e.data.twoPagePopUp).hide();
|
|
1982
|
+
});
|
|
1983
|
+
|
|
1984
|
+
$(this.leafEdgeL).bind('click', this, function(e) {
|
|
1985
|
+
e.data.trigger(BookReader.eventNames.stop);
|
|
1986
|
+
var jumpIndex = e.data.jumpIndexForLeftEdgePageX(e.pageX);
|
|
1987
|
+
e.data.jumpToIndex(jumpIndex);
|
|
1988
|
+
});
|
|
1989
|
+
|
|
1990
|
+
$(this.leafEdgeR).bind('click', this, function(e) {
|
|
1991
|
+
e.data.trigger(BookReader.eventNames.stop);
|
|
1992
|
+
var jumpIndex = e.data.jumpIndexForRightEdgePageX(e.pageX);
|
|
1993
|
+
e.data.jumpToIndex(jumpIndex);
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1996
|
+
$(this.leafEdgeR).bind('mousemove', this, function(e) {
|
|
1997
|
+
|
|
1998
|
+
var jumpIndex = e.data.jumpIndexForRightEdgePageX(e.pageX);
|
|
1999
|
+
$(e.data.twoPagePopUp).text('View ' + e.data.getPageName(jumpIndex));
|
|
2000
|
+
|
|
2001
|
+
// $$$ TODO: Make sure popup is positioned so that it is in view
|
|
2002
|
+
// (https://bugs.edge.launchpad.net/gnubook/+bug/327456)
|
|
2003
|
+
$(e.data.twoPagePopUp).css({
|
|
2004
|
+
left: e.pageX- e.data.refs.$brContainer.offset().left + e.data.refs.$brContainer.scrollLeft() - 120 + 'px',
|
|
2005
|
+
top: e.pageY - e.data.refs.$brContainer.offset().top + e.data.refs.$brContainer.scrollTop() + 'px'
|
|
2006
|
+
});
|
|
2007
|
+
});
|
|
2008
|
+
|
|
2009
|
+
$(this.leafEdgeL).bind('mousemove', this, function(e) {
|
|
2010
|
+
|
|
2011
|
+
var jumpIndex = e.data.jumpIndexForLeftEdgePageX(e.pageX);
|
|
2012
|
+
$(e.data.twoPagePopUp).text('View '+ e.data.getPageName(jumpIndex));
|
|
2013
|
+
|
|
2014
|
+
// $$$ TODO: Make sure popup is positioned so that it is in view
|
|
2015
|
+
// (https://bugs.edge.launchpad.net/gnubook/+bug/327456)
|
|
2016
|
+
$(e.data.twoPagePopUp).css({
|
|
2017
|
+
left: e.pageX - e.data.refs.$brContainer.offset().left + e.data.refs.$brContainer.scrollLeft() - $(e.data.twoPagePopUp).width() + 120 + 'px',
|
|
2018
|
+
top: e.pageY-e.data.refs.$brContainer.offset().top + e.data.refs.$brContainer.scrollTop() + 'px'
|
|
2019
|
+
});
|
|
2020
|
+
});
|
|
2021
|
+
};
|
|
2022
|
+
|
|
2023
|
+
// calculateSpreadSize()
|
|
2024
|
+
//______________________________________________________________________________
|
|
2025
|
+
// Calculates 2-page spread dimensions based on this.twoPage.currentIndexL and
|
|
2026
|
+
// this.twoPage.currentIndexR
|
|
2027
|
+
// This function sets this.twoPage.height, twoPage.width
|
|
2028
|
+
|
|
2029
|
+
BookReader.prototype.calculateSpreadSize = function() {
|
|
2030
|
+
|
|
2031
|
+
var firstIndex = this.twoPage.currentIndexL;
|
|
2032
|
+
var secondIndex = this.twoPage.currentIndexR;
|
|
2033
|
+
|
|
2034
|
+
// Calculate page sizes and total leaf width
|
|
2035
|
+
var spreadSize;
|
|
2036
|
+
if ( this.twoPage.autofit) {
|
|
2037
|
+
spreadSize = this.getIdealSpreadSize(firstIndex, secondIndex);
|
|
2038
|
+
} else {
|
|
2039
|
+
// set based on reduction factor
|
|
2040
|
+
spreadSize = this.getSpreadSizeFromReduce(firstIndex, secondIndex, this.reduce);
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// Both pages together
|
|
2044
|
+
this.twoPage.height = spreadSize.height;
|
|
2045
|
+
this.twoPage.width = spreadSize.width;
|
|
2046
|
+
|
|
2047
|
+
// Individual pages
|
|
2048
|
+
this.twoPage.scaledWL = this.getPageWidth2UP(firstIndex);
|
|
2049
|
+
this.twoPage.scaledWR = this.getPageWidth2UP(secondIndex);
|
|
2050
|
+
|
|
2051
|
+
// Leaf edges
|
|
2052
|
+
this.twoPage.edgeWidth = spreadSize.totalLeafEdgeWidth; // The combined width of both edges
|
|
2053
|
+
this.twoPage.leafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL);
|
|
2054
|
+
this.twoPage.leafEdgeWidthR = this.twoPage.edgeWidth - this.twoPage.leafEdgeWidthL;
|
|
2055
|
+
|
|
2056
|
+
|
|
2057
|
+
// Book cover
|
|
2058
|
+
// The width of the book cover div. The combined width of both pages, twice the width
|
|
2059
|
+
// of the book cover internal padding (2*10) and the page edges
|
|
2060
|
+
this.twoPage.bookCoverDivWidth = this.twoPageCoverWidth(this.twoPage.scaledWL + this.twoPage.scaledWR);
|
|
2061
|
+
// The height of the book cover div
|
|
2062
|
+
this.twoPage.bookCoverDivHeight = this.twoPage.height + 2 * this.twoPage.coverInternalPadding;
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
// We calculate the total width and height for the div so that we can make the book
|
|
2066
|
+
// spine centered
|
|
2067
|
+
var leftGutterOffset = this.gutterOffsetForIndex(firstIndex);
|
|
2068
|
+
var leftWidthFromCenter = this.twoPage.scaledWL - leftGutterOffset + this.twoPage.leafEdgeWidthL;
|
|
2069
|
+
var rightWidthFromCenter = this.twoPage.scaledWR + leftGutterOffset + this.twoPage.leafEdgeWidthR;
|
|
2070
|
+
var largestWidthFromCenter = Math.max( leftWidthFromCenter, rightWidthFromCenter );
|
|
2071
|
+
this.twoPage.totalWidth = 2 * (largestWidthFromCenter + this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding);
|
|
2072
|
+
this.twoPage.totalHeight = this.twoPage.height + 2 * (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding);
|
|
2073
|
+
|
|
2074
|
+
// We want to minimize the unused space in two-up mode (maximize the amount of page
|
|
2075
|
+
// shown). We give width to the leaf edges and these widths change (though the sum
|
|
2076
|
+
// of the two remains constant) as we flip through the book. With the book
|
|
2077
|
+
// cover centered and fixed in the BRcontainer div the page images will meet
|
|
2078
|
+
// at the "gutter" which is generally offset from the center.
|
|
2079
|
+
this.twoPage.middle = this.twoPage.totalWidth >> 1;
|
|
2080
|
+
this.twoPage.gutter = this.twoPage.middle + this.gutterOffsetForIndex(firstIndex);
|
|
2081
|
+
|
|
2082
|
+
// The left edge of the book cover moves depending on the width of the pages
|
|
2083
|
+
// $$$ change to getter
|
|
2084
|
+
this.twoPage.bookCoverDivLeft = this.twoPage.gutter - this.twoPage.scaledWL - this.twoPage.leafEdgeWidthL - this.twoPage.coverInternalPadding;
|
|
2085
|
+
// The top edge of the book cover stays a fixed distance from the top
|
|
2086
|
+
this.twoPage.bookCoverDivTop = this.twoPage.coverExternalPadding;
|
|
2087
|
+
|
|
2088
|
+
// Book spine
|
|
2089
|
+
this.twoPage.bookSpineDivHeight = this.twoPage.height + 2*this.twoPage.coverInternalPadding;
|
|
2090
|
+
this.twoPage.bookSpineDivLeft = this.twoPage.middle - (this.twoPage.bookSpineDivWidth >> 1);
|
|
2091
|
+
this.twoPage.bookSpineDivTop = this.twoPage.bookCoverDivTop;
|
|
2092
|
+
|
|
2093
|
+
|
|
2094
|
+
this.reduce = spreadSize.reduce; // $$$ really set this here?
|
|
2095
|
+
};
|
|
2096
|
+
|
|
2097
|
+
BookReader.prototype.getIdealSpreadSize = function(firstIndex, secondIndex) {
|
|
2098
|
+
var ideal = {};
|
|
2099
|
+
|
|
2100
|
+
// We check which page is closest to a "normal" page and use that to set the height
|
|
2101
|
+
// for both pages. This means that foldouts and other odd size pages will be displayed
|
|
2102
|
+
// smaller than the nominal zoom amount.
|
|
2103
|
+
var canon5Dratio = 1.5;
|
|
2104
|
+
|
|
2105
|
+
var first = {
|
|
2106
|
+
height: this._getPageHeight(firstIndex),
|
|
2107
|
+
width: this._getPageWidth(firstIndex)
|
|
2108
|
+
};
|
|
2109
|
+
|
|
2110
|
+
var second = {
|
|
2111
|
+
height: this._getPageHeight(secondIndex),
|
|
2112
|
+
width: this._getPageWidth(secondIndex)
|
|
2113
|
+
};
|
|
2114
|
+
|
|
2115
|
+
var firstIndexRatio = first.height / first.width;
|
|
2116
|
+
var secondIndexRatio = second.height / second.width;
|
|
2117
|
+
|
|
2118
|
+
var ratio;
|
|
2119
|
+
if (Math.abs(firstIndexRatio - canon5Dratio) < Math.abs(secondIndexRatio - canon5Dratio)) {
|
|
2120
|
+
ratio = firstIndexRatio;
|
|
2121
|
+
} else {
|
|
2122
|
+
ratio = secondIndexRatio;
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
var totalLeafEdgeWidth = parseInt(this.getNumLeafs() * 0.1);
|
|
2126
|
+
var maxLeafEdgeWidth = parseInt(this.refs.$brContainer.prop('clientWidth') * 0.1);
|
|
2127
|
+
ideal.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth);
|
|
2128
|
+
|
|
2129
|
+
var widthOutsidePages = 2 * (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding) + ideal.totalLeafEdgeWidth;
|
|
2130
|
+
var heightOutsidePages = 2* (this.twoPage.coverInternalPadding + this.twoPage.coverExternalPadding);
|
|
2131
|
+
|
|
2132
|
+
ideal.width = (this.refs.$brContainer.width() - widthOutsidePages) >> 1;
|
|
2133
|
+
ideal.width -= 10; // $$$ fudge factor
|
|
2134
|
+
ideal.height = this.refs.$brContainer.height() - heightOutsidePages;
|
|
2135
|
+
|
|
2136
|
+
ideal.height -= 15; // fudge factor
|
|
2137
|
+
|
|
2138
|
+
if (ideal.height/ratio <= ideal.width) {
|
|
2139
|
+
//use height
|
|
2140
|
+
ideal.width = parseInt(ideal.height/ratio);
|
|
2141
|
+
} else {
|
|
2142
|
+
//use width
|
|
2143
|
+
ideal.height = parseInt(ideal.width*ratio);
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// $$$ check this logic with large spreads
|
|
2147
|
+
ideal.reduce = ((first.height + second.height) / 2) / ideal.height;
|
|
2148
|
+
|
|
2149
|
+
return ideal;
|
|
2150
|
+
};
|
|
2151
|
+
|
|
2152
|
+
// getSpreadSizeFromReduce()
|
|
2153
|
+
//______________________________________________________________________________
|
|
2154
|
+
// Returns the spread size calculated from the reduction factor for the given pages
|
|
2155
|
+
BookReader.prototype.getSpreadSizeFromReduce = function(firstIndex, secondIndex, reduce) {
|
|
2156
|
+
var spreadSize = {};
|
|
2157
|
+
// $$$ Scale this based on reduce?
|
|
2158
|
+
var totalLeafEdgeWidth = parseInt(this.getNumLeafs() * 0.1);
|
|
2159
|
+
var maxLeafEdgeWidth = parseInt(this.refs.$brContainer.prop('clientWidth') * 0.1); // $$$ Assumes leaf edge width constant at all zoom levels
|
|
2160
|
+
spreadSize.totalLeafEdgeWidth = Math.min(totalLeafEdgeWidth, maxLeafEdgeWidth);
|
|
2161
|
+
|
|
2162
|
+
// $$$ Possibly incorrect -- we should make height "dominant"
|
|
2163
|
+
var nativeWidth = this._getPageWidth(firstIndex) + this._getPageWidth(secondIndex);
|
|
2164
|
+
var nativeHeight = this._getPageHeight(firstIndex) + this._getPageHeight(secondIndex);
|
|
2165
|
+
spreadSize.height = parseInt( (nativeHeight / 2) / this.reduce );
|
|
2166
|
+
spreadSize.width = parseInt( (nativeWidth / 2) / this.reduce );
|
|
2167
|
+
spreadSize.reduce = reduce;
|
|
2168
|
+
|
|
2169
|
+
return spreadSize;
|
|
2170
|
+
};
|
|
2171
|
+
|
|
2172
|
+
// twoPageGetAutofitReduce()
|
|
2173
|
+
//______________________________________________________________________________
|
|
2174
|
+
// Returns the current ideal reduction factor
|
|
2175
|
+
BookReader.prototype.twoPageGetAutofitReduce = function() {
|
|
2176
|
+
var spreadSize = this.getIdealSpreadSize(this.twoPage.currentIndexL, this.twoPage.currentIndexR);
|
|
2177
|
+
return spreadSize.reduce;
|
|
2178
|
+
};
|
|
2179
|
+
|
|
2180
|
+
// twoPageIsZoomedIn
|
|
2181
|
+
//______________________________________________________________________________
|
|
2182
|
+
// Returns true if the pages extend past the edge of the view
|
|
2183
|
+
BookReader.prototype.twoPageIsZoomedIn = function() {
|
|
2184
|
+
var autofitReduce = this.twoPageGetAutofitReduce();
|
|
2185
|
+
var isZoomedIn = false;
|
|
2186
|
+
if (this.twoPage.autofit != 'auto') {
|
|
2187
|
+
if (this.reduce < this.twoPageGetAutofitReduce()) {
|
|
2188
|
+
isZoomedIn = true;
|
|
2189
|
+
}
|
|
2190
|
+
}
|
|
2191
|
+
return isZoomedIn;
|
|
2192
|
+
};
|
|
2193
|
+
|
|
2194
|
+
BookReader.prototype.onePageGetAutofitWidth = function() {
|
|
2195
|
+
var widthPadding = 20;
|
|
2196
|
+
return (this.getMedianPageSize().width + 0.0) / (this.refs.$brContainer.prop('clientWidth') - widthPadding * 2);
|
|
2197
|
+
};
|
|
2198
|
+
|
|
2199
|
+
BookReader.prototype.onePageGetAutofitHeight = function() {
|
|
2200
|
+
var availableHeight = this.refs.$brContainer.innerHeight();
|
|
2201
|
+
return (this.getMedianPageSize().height + 0.0) / (availableHeight - this.padding * 2); // make sure a little of adjacent pages show
|
|
2202
|
+
};
|
|
2203
|
+
|
|
2204
|
+
// Returns where the top of the page with given index should be in one page view
|
|
2205
|
+
BookReader.prototype.onePageGetPageTop = function(index)
|
|
2206
|
+
{
|
|
2207
|
+
var i;
|
|
2208
|
+
var leafTop = 0;
|
|
2209
|
+
var leafLeft = 0;
|
|
2210
|
+
var h;
|
|
2211
|
+
for (i=0; i<index; i++) {
|
|
2212
|
+
h = parseInt(this._getPageHeight(i)/this.reduce);
|
|
2213
|
+
leafTop += h + this.padding;
|
|
2214
|
+
}
|
|
2215
|
+
return leafTop;
|
|
2216
|
+
};
|
|
2217
|
+
|
|
2218
|
+
BookReader.prototype.getMedianPageSize = function() {
|
|
2219
|
+
if (this._medianPageSize) {
|
|
2220
|
+
return this._medianPageSize;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
// A little expensive but we just do it once
|
|
2224
|
+
var widths = [];
|
|
2225
|
+
var heights = [];
|
|
2226
|
+
for (var i = 0; i < this.getNumLeafs(); i++) {
|
|
2227
|
+
widths.push(this.getPageWidth(i));
|
|
2228
|
+
heights.push(this.getPageHeight(i));
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
widths.sort();
|
|
2232
|
+
heights.sort();
|
|
2233
|
+
|
|
2234
|
+
this._medianPageSize = { width: widths[parseInt(widths.length / 2)], height: heights[parseInt(heights.length / 2)] };
|
|
2235
|
+
return this._medianPageSize;
|
|
2236
|
+
};
|
|
2237
|
+
|
|
2238
|
+
// Update the reduction factors for 1up mode given the available width and height. Recalculates
|
|
2239
|
+
// the autofit reduction factors.
|
|
2240
|
+
BookReader.prototype.onePageCalculateReductionFactors = function() {
|
|
2241
|
+
this.onePage.reductionFactors = this.reductionFactors.concat(
|
|
2242
|
+
[
|
|
2243
|
+
{ reduce: this.onePageGetAutofitWidth(), autofit: 'width' },
|
|
2244
|
+
{ reduce: this.onePageGetAutofitHeight(), autofit: 'height'}
|
|
2245
|
+
]);
|
|
2246
|
+
this.onePage.reductionFactors.sort(this._reduceSort);
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
BookReader.prototype.twoPageCalculateReductionFactors = function() {
|
|
2250
|
+
this.twoPage.reductionFactors = this.reductionFactors.concat(
|
|
2251
|
+
[
|
|
2252
|
+
{ reduce: this.getIdealSpreadSize( this.twoPage.currentIndexL, this.twoPage.currentIndexR ).reduce,
|
|
2253
|
+
autofit: 'auto' }
|
|
2254
|
+
]);
|
|
2255
|
+
this.twoPage.reductionFactors.sort(this._reduceSort);
|
|
2256
|
+
};
|
|
2257
|
+
|
|
2258
|
+
// twoPageSetCursor()
|
|
2259
|
+
//______________________________________________________________________________
|
|
2260
|
+
// Set the cursor for two page view
|
|
2261
|
+
BookReader.prototype.twoPageSetCursor = function() {
|
|
2262
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
2263
|
+
if ( ($twoPageViewEl.width() > this.refs.$brContainer.prop('clientWidth')) ||
|
|
2264
|
+
($twoPageViewEl.height() > this.refs.$brContainer.prop('clientHeight')) ) {
|
|
2265
|
+
if (this.prefetchedImgs[this.twoPage.currentIndexL])
|
|
2266
|
+
this.prefetchedImgs[this.twoPage.currentIndexL].style.cursor = 'move';
|
|
2267
|
+
if (this.prefetchedImgs[this.twoPage.currentIndexR])
|
|
2268
|
+
this.prefetchedImgs[this.twoPage.currentIndexR].style.cursor = 'move';
|
|
2269
|
+
} else {
|
|
2270
|
+
if (this.prefetchedImgs[this.twoPage.currentIndexL])
|
|
2271
|
+
this.prefetchedImgs[this.twoPage.currentIndexL].style.cursor = '';
|
|
2272
|
+
if (this.prefetchedImgs[this.twoPage.currentIndexR])
|
|
2273
|
+
this.prefetchedImgs[this.twoPage.currentIndexR].style.cursor = '';
|
|
2274
|
+
}
|
|
2275
|
+
};
|
|
2276
|
+
|
|
2277
|
+
// currentIndex()
|
|
2278
|
+
//______________________________________________________________________________
|
|
2279
|
+
// Returns the currently active index.
|
|
2280
|
+
BookReader.prototype.currentIndex = function() {
|
|
2281
|
+
// $$$ we should be cleaner with our idea of which index is active in 1up/2up
|
|
2282
|
+
if (this.mode == this.constMode1up || this.mode == this.constModeThumb) {
|
|
2283
|
+
return this.firstIndex; // $$$ TODO page in center of view would be better
|
|
2284
|
+
} else if (this.mode == this.constMode2up) {
|
|
2285
|
+
// Only allow indices that are actually present in book
|
|
2286
|
+
return BookReader.util.clamp(this.firstIndex, 0, this.getNumLeafs() - 1);
|
|
2287
|
+
} else {
|
|
2288
|
+
throw 'currentIndex called for unimplemented mode ' + this.mode;
|
|
2289
|
+
}
|
|
2290
|
+
};
|
|
2291
|
+
|
|
2292
|
+
// setCurrentIndex(index)
|
|
2293
|
+
//______________________________________________________________________________
|
|
2294
|
+
// Sets the idea of current index without triggering other actions such as animation.
|
|
2295
|
+
// Compare to jumpToIndex which animates to that index
|
|
2296
|
+
BookReader.prototype.setCurrentIndex = function(index) {
|
|
2297
|
+
this.firstIndex = index;
|
|
2298
|
+
};
|
|
2299
|
+
|
|
2300
|
+
|
|
2301
|
+
// right()
|
|
2302
|
+
//______________________________________________________________________________
|
|
2303
|
+
// Flip the right page over onto the left
|
|
2304
|
+
BookReader.prototype.right = function() {
|
|
2305
|
+
if ('rl' != this.pageProgression) {
|
|
2306
|
+
// LTR
|
|
2307
|
+
this.next();
|
|
2308
|
+
} else {
|
|
2309
|
+
// RTL
|
|
2310
|
+
this.prev();
|
|
2311
|
+
}
|
|
2312
|
+
};
|
|
2313
|
+
|
|
2314
|
+
// rightmost()
|
|
2315
|
+
//______________________________________________________________________________
|
|
2316
|
+
// Flip to the rightmost page
|
|
2317
|
+
BookReader.prototype.rightmost = function() {
|
|
2318
|
+
if ('rl' != this.pageProgression) {
|
|
2319
|
+
this.last();
|
|
2320
|
+
} else {
|
|
2321
|
+
this.first();
|
|
2322
|
+
}
|
|
2323
|
+
};
|
|
2324
|
+
|
|
2325
|
+
// left()
|
|
2326
|
+
//______________________________________________________________________________
|
|
2327
|
+
// Flip the left page over onto the right.
|
|
2328
|
+
BookReader.prototype.left = function() {
|
|
2329
|
+
if ('rl' != this.pageProgression) {
|
|
2330
|
+
// LTR
|
|
2331
|
+
this.prev();
|
|
2332
|
+
} else {
|
|
2333
|
+
// RTL
|
|
2334
|
+
this.next();
|
|
2335
|
+
}
|
|
2336
|
+
};
|
|
2337
|
+
|
|
2338
|
+
// leftmost()
|
|
2339
|
+
//______________________________________________________________________________
|
|
2340
|
+
// Flip to the leftmost page
|
|
2341
|
+
BookReader.prototype.leftmost = function() {
|
|
2342
|
+
if ('rl' != this.pageProgression) {
|
|
2343
|
+
this.first();
|
|
2344
|
+
} else {
|
|
2345
|
+
this.last();
|
|
2346
|
+
}
|
|
2347
|
+
};
|
|
2348
|
+
|
|
2349
|
+
// next()
|
|
2350
|
+
//______________________________________________________________________________
|
|
2351
|
+
BookReader.prototype.next = function() {
|
|
2352
|
+
if (this.constMode2up == this.mode) {
|
|
2353
|
+
this.autoStop();
|
|
2354
|
+
this.flipFwdToIndex(null);
|
|
2355
|
+
|
|
2356
|
+
} else {
|
|
2357
|
+
if (this.firstIndex < this.lastDisplayableIndex()) {
|
|
2358
|
+
this.jumpToIndex(this.firstIndex+1);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
};
|
|
2362
|
+
|
|
2363
|
+
// prev()
|
|
2364
|
+
//______________________________________________________________________________
|
|
2365
|
+
BookReader.prototype.prev = function() {
|
|
2366
|
+
if (this.constMode2up == this.mode) {
|
|
2367
|
+
this.autoStop();
|
|
2368
|
+
this.flipBackToIndex(null);
|
|
2369
|
+
} else {
|
|
2370
|
+
if (this.firstIndex >= 1) {
|
|
2371
|
+
this.jumpToIndex(this.firstIndex-1);
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
BookReader.prototype.first = function() {
|
|
2377
|
+
this.jumpToIndex(this.firstDisplayableIndex());
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
BookReader.prototype.last = function() {
|
|
2381
|
+
this.jumpToIndex(this.lastDisplayableIndex());
|
|
2382
|
+
};
|
|
2383
|
+
|
|
2384
|
+
// scrollDown()
|
|
2385
|
+
//______________________________________________________________________________
|
|
2386
|
+
// Scrolls down one screen view
|
|
2387
|
+
BookReader.prototype.scrollDown = function() {
|
|
2388
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
|
2389
|
+
if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
|
|
2390
|
+
// Whole pages are visible, scroll whole page only
|
|
2391
|
+
return this.next();
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
this.refs.$brContainer.stop(true).animate(
|
|
2395
|
+
{ scrollTop: '+=' + this._scrollAmount() + 'px'},
|
|
2396
|
+
400, 'easeInOutExpo'
|
|
2397
|
+
);
|
|
2398
|
+
return true;
|
|
2399
|
+
} else {
|
|
2400
|
+
return false;
|
|
2401
|
+
}
|
|
2402
|
+
};
|
|
2403
|
+
|
|
2404
|
+
// scrollUp()
|
|
2405
|
+
//______________________________________________________________________________
|
|
2406
|
+
// Scrolls up one screen view
|
|
2407
|
+
BookReader.prototype.scrollUp = function() {
|
|
2408
|
+
if ($.inArray(this.mode, [this.constMode1up, this.constModeThumb]) >= 0) {
|
|
2409
|
+
if ( this.mode == this.constMode1up && (this.reduce >= this.onePageGetAutofitHeight()) ) {
|
|
2410
|
+
// Whole pages are visible, scroll whole page only
|
|
2411
|
+
return this.prev();
|
|
2412
|
+
}
|
|
2413
|
+
|
|
2414
|
+
this.refs.$brContainer.stop(true).animate(
|
|
2415
|
+
{ scrollTop: '-=' + this._scrollAmount() + 'px'},
|
|
2416
|
+
400, 'easeInOutExpo'
|
|
2417
|
+
);
|
|
2418
|
+
return true;
|
|
2419
|
+
} else {
|
|
2420
|
+
return false;
|
|
2421
|
+
}
|
|
2422
|
+
};
|
|
2423
|
+
|
|
2424
|
+
// _scrollAmount()
|
|
2425
|
+
//______________________________________________________________________________
|
|
2426
|
+
// The amount to scroll vertically in integer pixels
|
|
2427
|
+
BookReader.prototype._scrollAmount = function() {
|
|
2428
|
+
if (this.constMode1up == this.mode) {
|
|
2429
|
+
// Overlap by % of page size
|
|
2430
|
+
return parseInt(this.refs.$brContainer.prop('clientHeight') - this.getPageHeight(this.currentIndex()) / this.reduce * 0.03);
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
return parseInt(0.9 * this.refs.$brContainer.prop('clientHeight'));
|
|
2434
|
+
};
|
|
2435
|
+
|
|
2436
|
+
|
|
2437
|
+
// flipBackToIndex()
|
|
2438
|
+
//______________________________________________________________________________
|
|
2439
|
+
// to flip back one spread, pass index=null
|
|
2440
|
+
BookReader.prototype.flipBackToIndex = function(index) {
|
|
2441
|
+
|
|
2442
|
+
if (this.constMode1up == this.mode) return;
|
|
2443
|
+
|
|
2444
|
+
var leftIndex = this.twoPage.currentIndexL;
|
|
2445
|
+
|
|
2446
|
+
if (this.animating) return;
|
|
2447
|
+
|
|
2448
|
+
if (null != this.leafEdgeTmp) {
|
|
2449
|
+
alert('error: leafEdgeTmp should be null!');
|
|
2450
|
+
return;
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2453
|
+
if (null == index) {
|
|
2454
|
+
index = leftIndex-2;
|
|
2455
|
+
}
|
|
2456
|
+
|
|
2457
|
+
this.updateNavIndex(index);
|
|
2458
|
+
|
|
2459
|
+
var previousIndices = this.getSpreadIndices(index);
|
|
2460
|
+
|
|
2461
|
+
if (previousIndices[0] < this.firstDisplayableIndex() || previousIndices[1] < this.firstDisplayableIndex()) {
|
|
2462
|
+
return;
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
this.animating = true;
|
|
2466
|
+
|
|
2467
|
+
if ('rl' != this.pageProgression) {
|
|
2468
|
+
// Assume LTR and we are going backward
|
|
2469
|
+
this.prepareFlipLeftToRight(previousIndices[0], previousIndices[1]);
|
|
2470
|
+
this.flipLeftToRight(previousIndices[0], previousIndices[1]);
|
|
2471
|
+
} else {
|
|
2472
|
+
// RTL and going backward
|
|
2473
|
+
var gutter = this.prepareFlipRightToLeft(previousIndices[0], previousIndices[1]);
|
|
2474
|
+
this.flipRightToLeft(previousIndices[0], previousIndices[1], gutter);
|
|
2475
|
+
}
|
|
2476
|
+
};
|
|
2477
|
+
|
|
2478
|
+
// flipLeftToRight()
|
|
2479
|
+
//______________________________________________________________________________
|
|
2480
|
+
// Flips the page on the left towards the page on the right
|
|
2481
|
+
BookReader.prototype.flipLeftToRight = function(newIndexL, newIndexR) {
|
|
2482
|
+
|
|
2483
|
+
var leftLeaf = this.twoPage.currentIndexL;
|
|
2484
|
+
|
|
2485
|
+
var oldLeafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL);
|
|
2486
|
+
var newLeafEdgeWidthL = this.leafEdgeWidth(newIndexL);
|
|
2487
|
+
var leafEdgeTmpW = oldLeafEdgeWidthL - newLeafEdgeWidthL;
|
|
2488
|
+
|
|
2489
|
+
var currWidthL = this.getPageWidth2UP(leftLeaf);
|
|
2490
|
+
var newWidthL = this.getPageWidth2UP(newIndexL);
|
|
2491
|
+
var newWidthR = this.getPageWidth2UP(newIndexR);
|
|
2492
|
+
|
|
2493
|
+
var top = this.twoPageTop();
|
|
2494
|
+
var gutter = this.twoPage.middle + this.gutterOffsetForIndex(newIndexL);
|
|
2495
|
+
|
|
2496
|
+
//animation strategy:
|
|
2497
|
+
// 0. remove search highlight, if any.
|
|
2498
|
+
// 1. create a new div, called leafEdgeTmp to represent the leaf edge between the leftmost edge
|
|
2499
|
+
// of the left leaf and where the user clicked in the leaf edge.
|
|
2500
|
+
// Note that if this function was triggered by left() and not a
|
|
2501
|
+
// mouse click, the width of leafEdgeTmp is very small (zero px).
|
|
2502
|
+
// 2. animate both leafEdgeTmp to the gutter (without changing its width) and animate
|
|
2503
|
+
// leftLeaf to width=0.
|
|
2504
|
+
// 3. When step 2 is finished, animate leafEdgeTmp to right-hand side of new right leaf
|
|
2505
|
+
// (left=gutter+newWidthR) while also animating the new right leaf from width=0 to
|
|
2506
|
+
// its new full width.
|
|
2507
|
+
// 4. After step 3 is finished, do the following:
|
|
2508
|
+
// - remove leafEdgeTmp from the dom.
|
|
2509
|
+
// - resize and move the right leaf edge (leafEdgeR) to left=gutter+newWidthR
|
|
2510
|
+
// and width=twoPage.edgeWidth-newLeafEdgeWidthL.
|
|
2511
|
+
// - resize and move the left leaf edge (leafEdgeL) to left=gutter-newWidthL-newLeafEdgeWidthL
|
|
2512
|
+
// and width=newLeafEdgeWidthL.
|
|
2513
|
+
// - resize the back cover (twoPage.coverDiv) to left=gutter-newWidthL-newLeafEdgeWidthL-10
|
|
2514
|
+
// and width=newWidthL+newWidthR+twoPage.edgeWidth+20
|
|
2515
|
+
// - move new left leaf (newIndexL) forward to zindex=2 so it can receive clicks.
|
|
2516
|
+
// - remove old left and right leafs from the dom [pruneUnusedImgs()].
|
|
2517
|
+
// - prefetch new adjacent leafs.
|
|
2518
|
+
// - set up click handlers for both new left and right leafs.
|
|
2519
|
+
// - redraw the search highlight.
|
|
2520
|
+
// - update the pagenum box and the url.
|
|
2521
|
+
|
|
2522
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
2523
|
+
var leftEdgeTmpLeft = gutter - currWidthL - leafEdgeTmpW;
|
|
2524
|
+
|
|
2525
|
+
this.leafEdgeTmp = document.createElement('div');
|
|
2526
|
+
this.leafEdgeTmp.className = 'BRleafEdgeTmp';
|
|
2527
|
+
$(this.leafEdgeTmp).css({
|
|
2528
|
+
width: leafEdgeTmpW + 'px',
|
|
2529
|
+
height: this.twoPage.height + 'px',
|
|
2530
|
+
left: leftEdgeTmpLeft + 'px',
|
|
2531
|
+
top: top+'px',
|
|
2532
|
+
zIndex:1000
|
|
2533
|
+
}).appendTo($twoPageViewEl);
|
|
2534
|
+
|
|
2535
|
+
$(this.leafEdgeL).css({
|
|
2536
|
+
width: newLeafEdgeWidthL+'px',
|
|
2537
|
+
left: gutter-currWidthL-newLeafEdgeWidthL+'px'
|
|
2538
|
+
});
|
|
2539
|
+
|
|
2540
|
+
// Left gets the offset of the current left leaf from the document
|
|
2541
|
+
var left = $(this.prefetchedImgs[leftLeaf]).offset().left;
|
|
2542
|
+
// $$$ This seems very similar to the gutter. May be able to consolidate the logic.
|
|
2543
|
+
var right = $twoPageViewEl.prop('clientWidth') - left - $(this.prefetchedImgs[leftLeaf]).width() + $twoPageViewEl.offset().left - 2 + 'px';
|
|
2544
|
+
|
|
2545
|
+
// We change the left leaf to right positioning
|
|
2546
|
+
// $$$ This causes animation glitches during resize. See https://bugs.edge.launchpad.net/gnubook/+bug/328327
|
|
2547
|
+
$(this.prefetchedImgs[leftLeaf]).css({
|
|
2548
|
+
right: right,
|
|
2549
|
+
left: ''
|
|
2550
|
+
});
|
|
2551
|
+
|
|
2552
|
+
$(this.leafEdgeTmp).animate({left: gutter}, this.flipSpeed, 'easeInSine');
|
|
2553
|
+
|
|
2554
|
+
var self = this;
|
|
2555
|
+
|
|
2556
|
+
if (this.enableSearch) this.removeSearchHilites();
|
|
2557
|
+
|
|
2558
|
+
$(this.prefetchedImgs[leftLeaf]).animate({width: '0px'}, self.flipSpeed, 'easeInSine', function() {
|
|
2559
|
+
|
|
2560
|
+
$(self.leafEdgeTmp).animate({left: gutter+newWidthR+'px'}, self.flipSpeed, 'easeOutSine');
|
|
2561
|
+
|
|
2562
|
+
self.$('.BRgutter').css({left: (gutter - self.twoPage.bookSpineDivWidth*0.5)+'px'});
|
|
2563
|
+
|
|
2564
|
+
$(self.prefetchedImgs[newIndexR]).animate({width: newWidthR+'px'}, self.flipSpeed, 'easeOutSine', function() {
|
|
2565
|
+
$(self.prefetchedImgs[newIndexL]).css('zIndex', 2);
|
|
2566
|
+
|
|
2567
|
+
//jquery adds display:block to the element style, which interferes with our print css
|
|
2568
|
+
$(self.prefetchedImgs[newIndexL]).css('display', '');
|
|
2569
|
+
$(self.prefetchedImgs[newIndexR]).css('display', '');
|
|
2570
|
+
|
|
2571
|
+
$(self.leafEdgeR).css({
|
|
2572
|
+
// Moves the right leaf edge
|
|
2573
|
+
width: self.twoPage.edgeWidth-newLeafEdgeWidthL+'px',
|
|
2574
|
+
left: gutter+newWidthR+'px'
|
|
2575
|
+
});
|
|
2576
|
+
|
|
2577
|
+
$(self.leafEdgeL).css({
|
|
2578
|
+
// Moves and resizes the left leaf edge
|
|
2579
|
+
width: newLeafEdgeWidthL+'px',
|
|
2580
|
+
left: gutter-newWidthL-newLeafEdgeWidthL+'px'
|
|
2581
|
+
});
|
|
2582
|
+
|
|
2583
|
+
// Resizes the brown border div
|
|
2584
|
+
$(self.twoPage.coverDiv).css({
|
|
2585
|
+
width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px',
|
|
2586
|
+
left: gutter-newWidthL-newLeafEdgeWidthL-self.twoPage.coverInternalPadding+'px'
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
$(self.leafEdgeTmp).remove();
|
|
2590
|
+
self.leafEdgeTmp = null;
|
|
2591
|
+
|
|
2592
|
+
// $$$ TODO refactor with opposite direction flip
|
|
2593
|
+
|
|
2594
|
+
self.twoPage.currentIndexL = newIndexL;
|
|
2595
|
+
self.twoPage.currentIndexR = newIndexR;
|
|
2596
|
+
self.twoPage.scaledWL = newWidthL;
|
|
2597
|
+
self.twoPage.scaledWR = newWidthR;
|
|
2598
|
+
self.twoPage.gutter = gutter;
|
|
2599
|
+
|
|
2600
|
+
self.firstIndex = self.twoPage.currentIndexL;
|
|
2601
|
+
self.displayedIndices = [newIndexL, newIndexR];
|
|
2602
|
+
self.pruneUnusedImgs();
|
|
2603
|
+
self.prefetch();
|
|
2604
|
+
self.animating = false;
|
|
2605
|
+
|
|
2606
|
+
if (self.enableSearch) self.updateSearchHilites2UP();
|
|
2607
|
+
self.updatePageNumBox2UP();
|
|
2608
|
+
|
|
2609
|
+
self.setMouseHandlers2UP();
|
|
2610
|
+
self.twoPageSetCursor();
|
|
2611
|
+
|
|
2612
|
+
if (self.animationFinishedCallback) {
|
|
2613
|
+
self.animationFinishedCallback();
|
|
2614
|
+
self.animationFinishedCallback = null;
|
|
2615
|
+
}
|
|
2616
|
+
});
|
|
2617
|
+
});
|
|
2618
|
+
|
|
2619
|
+
};
|
|
2620
|
+
|
|
2621
|
+
// flipFwdToIndex()
|
|
2622
|
+
//______________________________________________________________________________
|
|
2623
|
+
// Whether we flip left or right is dependent on the page progression
|
|
2624
|
+
// to flip forward one spread, pass index=null
|
|
2625
|
+
BookReader.prototype.flipFwdToIndex = function(index) {
|
|
2626
|
+
|
|
2627
|
+
if (this.animating) return;
|
|
2628
|
+
|
|
2629
|
+
if (null != this.leafEdgeTmp) {
|
|
2630
|
+
alert('error: leafEdgeTmp should be null!');
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
if (null == index) {
|
|
2635
|
+
index = this.twoPage.currentIndexR+2; // $$$ assumes indices are continuous
|
|
2636
|
+
}
|
|
2637
|
+
if (index > this.lastDisplayableIndex()) return;
|
|
2638
|
+
|
|
2639
|
+
this.updateNavIndex(index);
|
|
2640
|
+
|
|
2641
|
+
this.animating = true;
|
|
2642
|
+
|
|
2643
|
+
var nextIndices = this.getSpreadIndices(index);
|
|
2644
|
+
|
|
2645
|
+
if ('rl' != this.pageProgression) {
|
|
2646
|
+
// We did not specify RTL
|
|
2647
|
+
var gutter = this.prepareFlipRightToLeft(nextIndices[0], nextIndices[1]);
|
|
2648
|
+
this.flipRightToLeft(nextIndices[0], nextIndices[1], gutter);
|
|
2649
|
+
} else {
|
|
2650
|
+
// RTL
|
|
2651
|
+
var gutter = this.prepareFlipLeftToRight(nextIndices[0], nextIndices[1]);
|
|
2652
|
+
this.flipLeftToRight(nextIndices[0], nextIndices[1]);
|
|
2653
|
+
}
|
|
2654
|
+
};
|
|
2655
|
+
|
|
2656
|
+
// flipRightToLeft(nextL, nextR, gutter)
|
|
2657
|
+
// $$$ better not to have to pass gutter in
|
|
2658
|
+
//______________________________________________________________________________
|
|
2659
|
+
// Flip from left to right and show the nextL and nextR indices on those sides
|
|
2660
|
+
BookReader.prototype.flipRightToLeft = function(newIndexL, newIndexR) {
|
|
2661
|
+
var oldLeafEdgeWidthL = this.leafEdgeWidth(this.twoPage.currentIndexL);
|
|
2662
|
+
var oldLeafEdgeWidthR = this.twoPage.edgeWidth-oldLeafEdgeWidthL;
|
|
2663
|
+
var newLeafEdgeWidthL = this.leafEdgeWidth(newIndexL);
|
|
2664
|
+
var newLeafEdgeWidthR = this.twoPage.edgeWidth-newLeafEdgeWidthL;
|
|
2665
|
+
|
|
2666
|
+
var leafEdgeTmpW = oldLeafEdgeWidthR - newLeafEdgeWidthR;
|
|
2667
|
+
|
|
2668
|
+
var top = this.twoPageTop();
|
|
2669
|
+
var scaledW = this.getPageWidth2UP(this.twoPage.currentIndexR);
|
|
2670
|
+
|
|
2671
|
+
var middle = this.twoPage.middle;
|
|
2672
|
+
var gutter = middle + this.gutterOffsetForIndex(newIndexL);
|
|
2673
|
+
|
|
2674
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
2675
|
+
|
|
2676
|
+
this.leafEdgeTmp = document.createElement('div');
|
|
2677
|
+
this.leafEdgeTmp.className = 'BRleafEdgeTmp';
|
|
2678
|
+
$(this.leafEdgeTmp).css({
|
|
2679
|
+
width: leafEdgeTmpW + 'px',
|
|
2680
|
+
height: this.twoPage.height + 'px',
|
|
2681
|
+
left: gutter+scaledW+'px',
|
|
2682
|
+
top: top+'px',
|
|
2683
|
+
zIndex:1000
|
|
2684
|
+
}).appendTo($twoPageViewEl);
|
|
2685
|
+
|
|
2686
|
+
var currWidthL = this.getPageWidth2UP(this.twoPage.currentIndexL);
|
|
2687
|
+
var currWidthR = this.getPageWidth2UP(this.twoPage.currentIndexR);
|
|
2688
|
+
var newWidthL = this.getPageWidth2UP(newIndexL);
|
|
2689
|
+
var newWidthR = this.getPageWidth2UP(newIndexR);
|
|
2690
|
+
|
|
2691
|
+
$(this.leafEdgeR).css({width: newLeafEdgeWidthR+'px', left: gutter+newWidthR+'px' });
|
|
2692
|
+
|
|
2693
|
+
var self = this; // closure-tastic!
|
|
2694
|
+
|
|
2695
|
+
var speed = this.flipSpeed;
|
|
2696
|
+
|
|
2697
|
+
if (this.enableSearch) this.removeSearchHilites();
|
|
2698
|
+
|
|
2699
|
+
$(this.leafEdgeTmp).animate({left: gutter}, speed, 'easeInSine');
|
|
2700
|
+
$(this.prefetchedImgs[this.twoPage.currentIndexR]).animate({width: '0px'}, speed, 'easeInSine', function() {
|
|
2701
|
+
self.$('BRgutter').css({left: (gutter - self.twoPage.bookSpineDivWidth*0.5)+'px'});
|
|
2702
|
+
$(self.leafEdgeTmp).animate({left: gutter-newWidthL-leafEdgeTmpW+'px'}, speed, 'easeOutSine');
|
|
2703
|
+
$(self.prefetchedImgs[newIndexL]).animate({width: newWidthL+'px'}, speed, 'easeOutSine', function() {
|
|
2704
|
+
$(self.prefetchedImgs[newIndexR]).css('zIndex', 2);
|
|
2705
|
+
|
|
2706
|
+
//jquery adds display:block to the element style, which interferes with our print css
|
|
2707
|
+
$(self.prefetchedImgs[newIndexL]).css('display', '');
|
|
2708
|
+
$(self.prefetchedImgs[newIndexR]).css('display', '');
|
|
2709
|
+
|
|
2710
|
+
$(self.leafEdgeL).css({
|
|
2711
|
+
width: newLeafEdgeWidthL+'px',
|
|
2712
|
+
left: gutter-newWidthL-newLeafEdgeWidthL+'px'
|
|
2713
|
+
});
|
|
2714
|
+
|
|
2715
|
+
// Resizes the book cover
|
|
2716
|
+
$(self.twoPage.coverDiv).css({
|
|
2717
|
+
width: self.twoPageCoverWidth(newWidthL+newWidthR)+'px',
|
|
2718
|
+
left: gutter - newWidthL - newLeafEdgeWidthL - self.twoPage.coverInternalPadding + 'px'
|
|
2719
|
+
});
|
|
2720
|
+
|
|
2721
|
+
$(self.leafEdgeTmp).remove();
|
|
2722
|
+
self.leafEdgeTmp = null;
|
|
2723
|
+
|
|
2724
|
+
self.twoPage.currentIndexL = newIndexL;
|
|
2725
|
+
self.twoPage.currentIndexR = newIndexR;
|
|
2726
|
+
self.twoPage.scaledWL = newWidthL;
|
|
2727
|
+
self.twoPage.scaledWR = newWidthR;
|
|
2728
|
+
self.twoPage.gutter = gutter;
|
|
2729
|
+
|
|
2730
|
+
self.firstIndex = self.twoPage.currentIndexL;
|
|
2731
|
+
self.displayedIndices = [newIndexL, newIndexR];
|
|
2732
|
+
self.pruneUnusedImgs();
|
|
2733
|
+
self.prefetch();
|
|
2734
|
+
self.animating = false;
|
|
2735
|
+
|
|
2736
|
+
|
|
2737
|
+
if (self.enableSearch) self.updateSearchHilites2UP();
|
|
2738
|
+
self.updatePageNumBox2UP();
|
|
2739
|
+
|
|
2740
|
+
self.setMouseHandlers2UP();
|
|
2741
|
+
self.twoPageSetCursor();
|
|
2742
|
+
|
|
2743
|
+
if (self.animationFinishedCallback) {
|
|
2744
|
+
self.animationFinishedCallback();
|
|
2745
|
+
self.animationFinishedCallback = null;
|
|
2746
|
+
}
|
|
2747
|
+
});
|
|
2748
|
+
});
|
|
2749
|
+
};
|
|
2750
|
+
|
|
2751
|
+
// setMouseHandlers2UP
|
|
2752
|
+
//______________________________________________________________________________
|
|
2753
|
+
BookReader.prototype.setMouseHandlers2UP = function() {
|
|
2754
|
+
this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexL],
|
|
2755
|
+
{ self: this },
|
|
2756
|
+
function(e) {
|
|
2757
|
+
if (e.which == 3) {
|
|
2758
|
+
// right click
|
|
2759
|
+
if (e.data.self.protected) {
|
|
2760
|
+
return false;
|
|
2761
|
+
}
|
|
2762
|
+
return true;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
if (! e.data.self.twoPageIsZoomedIn()) {
|
|
2766
|
+
e.data.self.trigger(BookReader.eventNames.stop);
|
|
2767
|
+
e.data.self.left();
|
|
2768
|
+
}
|
|
2769
|
+
e.preventDefault();
|
|
2770
|
+
}
|
|
2771
|
+
);
|
|
2772
|
+
|
|
2773
|
+
this.setClickHandler2UP( this.prefetchedImgs[this.twoPage.currentIndexR],
|
|
2774
|
+
{ self: this },
|
|
2775
|
+
function(e) {
|
|
2776
|
+
if (e.which == 3) {
|
|
2777
|
+
// right click
|
|
2778
|
+
return !e.data.self.protected;
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
if (! e.data.self.twoPageIsZoomedIn()) {
|
|
2782
|
+
e.data.self.trigger(BookReader.eventNames.stop);
|
|
2783
|
+
e.data.self.right();
|
|
2784
|
+
}
|
|
2785
|
+
e.preventDefault();
|
|
2786
|
+
}
|
|
2787
|
+
);
|
|
2788
|
+
};
|
|
2789
|
+
|
|
2790
|
+
// prefetchImg()
|
|
2791
|
+
//______________________________________________________________________________
|
|
2792
|
+
BookReader.prototype.prefetchImg = function(index) {
|
|
2793
|
+
var pageURI = this._getPageURI(index);
|
|
2794
|
+
|
|
2795
|
+
// Load image if not loaded or URI has changed (e.g. due to scaling)
|
|
2796
|
+
var loadImage = false;
|
|
2797
|
+
if (undefined == this.prefetchedImgs[index]) {
|
|
2798
|
+
loadImage = true;
|
|
2799
|
+
} else if (pageURI != this.prefetchedImgs[index].uri) {
|
|
2800
|
+
loadImage = true;
|
|
2801
|
+
}
|
|
2802
|
+
|
|
2803
|
+
if (loadImage) {
|
|
2804
|
+
var img = document.createElement("img");
|
|
2805
|
+
$(img).addClass('BRpageimage').addClass('BRnoselect');
|
|
2806
|
+
if (index < 0 || index > (this.getNumLeafs() - 1) ) {
|
|
2807
|
+
// Facing page at beginning or end, or beyond
|
|
2808
|
+
$(img).addClass('BRemptypage');
|
|
2809
|
+
}
|
|
2810
|
+
img.src = pageURI;
|
|
2811
|
+
img.uri = pageURI; // browser may rewrite src so we stash raw URI here
|
|
2812
|
+
this.prefetchedImgs[index] = img;
|
|
2813
|
+
}
|
|
2814
|
+
};
|
|
2815
|
+
|
|
2816
|
+
|
|
2817
|
+
// prepareFlipLeftToRight()
|
|
2818
|
+
//
|
|
2819
|
+
//______________________________________________________________________________
|
|
2820
|
+
//
|
|
2821
|
+
// Prepare to flip the left page towards the right. This corresponds to moving
|
|
2822
|
+
// backward when the page progression is left to right.
|
|
2823
|
+
BookReader.prototype.prepareFlipLeftToRight = function(prevL, prevR) {
|
|
2824
|
+
this.prefetchImg(prevL);
|
|
2825
|
+
this.prefetchImg(prevR);
|
|
2826
|
+
|
|
2827
|
+
var height = this._getPageHeight(prevL);
|
|
2828
|
+
var width = this._getPageWidth(prevL);
|
|
2829
|
+
var middle = this.twoPage.middle;
|
|
2830
|
+
var top = this.twoPageTop();
|
|
2831
|
+
var scaledW = this.twoPage.height*width/height; // $$$ assumes height of page is dominant
|
|
2832
|
+
|
|
2833
|
+
// The gutter is the dividing line between the left and right pages.
|
|
2834
|
+
// It is offset from the middle to create the illusion of thickness to the pages
|
|
2835
|
+
var gutter = middle + this.gutterOffsetForIndex(prevL);
|
|
2836
|
+
|
|
2837
|
+
var leftCSS = {
|
|
2838
|
+
position: 'absolute',
|
|
2839
|
+
left: gutter-scaledW+'px',
|
|
2840
|
+
right: '', // clear right property
|
|
2841
|
+
top: top+'px',
|
|
2842
|
+
height: this.twoPage.height,
|
|
2843
|
+
width: scaledW+'px',
|
|
2844
|
+
zIndex: 1
|
|
2845
|
+
};
|
|
2846
|
+
|
|
2847
|
+
$(this.prefetchedImgs[prevL]).css(leftCSS);
|
|
2848
|
+
|
|
2849
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
2850
|
+
$twoPageViewEl.append(this.prefetchedImgs[prevL]);
|
|
2851
|
+
|
|
2852
|
+
var rightCSS = {
|
|
2853
|
+
position: 'absolute',
|
|
2854
|
+
left: gutter+'px',
|
|
2855
|
+
right: '',
|
|
2856
|
+
top: top+'px',
|
|
2857
|
+
height: this.twoPage.height,
|
|
2858
|
+
width: '0',
|
|
2859
|
+
zIndex: 2
|
|
2860
|
+
};
|
|
2861
|
+
|
|
2862
|
+
$(this.prefetchedImgs[prevR]).css(rightCSS);
|
|
2863
|
+
|
|
2864
|
+
$twoPageViewEl.append(this.prefetchedImgs[prevR]);
|
|
2865
|
+
};
|
|
2866
|
+
|
|
2867
|
+
// $$$ mang we're adding an extra pixel in the middle. See https://bugs.edge.launchpad.net/gnubook/+bug/411667
|
|
2868
|
+
// prepareFlipRightToLeft()
|
|
2869
|
+
//______________________________________________________________________________
|
|
2870
|
+
BookReader.prototype.prepareFlipRightToLeft = function(nextL, nextR) {
|
|
2871
|
+
// Prefetch images
|
|
2872
|
+
this.prefetchImg(nextL);
|
|
2873
|
+
this.prefetchImg(nextR);
|
|
2874
|
+
|
|
2875
|
+
var height = this._getPageHeight(nextR);
|
|
2876
|
+
var width = this._getPageWidth(nextR);
|
|
2877
|
+
var middle = this.twoPage.middle;
|
|
2878
|
+
var top = this.twoPageTop();
|
|
2879
|
+
var scaledW = this.twoPage.height*width/height;
|
|
2880
|
+
|
|
2881
|
+
var gutter = middle + this.gutterOffsetForIndex(nextL);
|
|
2882
|
+
|
|
2883
|
+
$(this.prefetchedImgs[nextR]).css({
|
|
2884
|
+
position: 'absolute',
|
|
2885
|
+
left: gutter+'px',
|
|
2886
|
+
top: top+'px',
|
|
2887
|
+
height: this.twoPage.height,
|
|
2888
|
+
width: scaledW+'px',
|
|
2889
|
+
zIndex: 1
|
|
2890
|
+
});
|
|
2891
|
+
|
|
2892
|
+
var $twoPageViewEl = this.refs.$brTwoPageView;
|
|
2893
|
+
$twoPageViewEl.append(this.prefetchedImgs[nextR]);
|
|
2894
|
+
|
|
2895
|
+
height = this._getPageHeight(nextL);
|
|
2896
|
+
width = this._getPageWidth(nextL);
|
|
2897
|
+
scaledW = this.twoPage.height*width/height;
|
|
2898
|
+
|
|
2899
|
+
$(this.prefetchedImgs[nextL]).css({
|
|
2900
|
+
position: 'absolute',
|
|
2901
|
+
right: $twoPageViewEl.prop('clientWidth')-gutter+'px',
|
|
2902
|
+
top: top+'px',
|
|
2903
|
+
height: this.twoPage.height,
|
|
2904
|
+
width: 0+'px', // Start at 0 width, then grow to the left
|
|
2905
|
+
zIndex: 2
|
|
2906
|
+
});
|
|
2907
|
+
|
|
2908
|
+
$twoPageViewEl.append(this.prefetchedImgs[nextL]);
|
|
2909
|
+
};
|
|
2910
|
+
|
|
2911
|
+
// getNextLeafs() -- NOT RTL AWARE
|
|
2912
|
+
//______________________________________________________________________________
|
|
2913
|
+
// BookReader.prototype.getNextLeafs = function(o) {
|
|
2914
|
+
// //TODO: we might have two left or two right leafs in a row (damaged book)
|
|
2915
|
+
// //For now, assume that leafs are contiguous.
|
|
2916
|
+
//
|
|
2917
|
+
// //return [this.twoPage.currentIndexL+2, this.twoPage.currentIndexL+3];
|
|
2918
|
+
// o.L = this.twoPage.currentIndexL+2;
|
|
2919
|
+
// o.R = this.twoPage.currentIndexL+3;
|
|
2920
|
+
// }
|
|
2921
|
+
|
|
2922
|
+
// getprevLeafs() -- NOT RTL AWARE
|
|
2923
|
+
//______________________________________________________________________________
|
|
2924
|
+
// BookReader.prototype.getPrevLeafs = function(o) {
|
|
2925
|
+
// //TODO: we might have two left or two right leafs in a row (damaged book)
|
|
2926
|
+
// //For now, assume that leafs are contiguous.
|
|
2927
|
+
//
|
|
2928
|
+
// //return [this.twoPage.currentIndexL-2, this.twoPage.currentIndexL-1];
|
|
2929
|
+
// o.L = this.twoPage.currentIndexL-2;
|
|
2930
|
+
// o.R = this.twoPage.currentIndexL-1;
|
|
2931
|
+
// }
|
|
2932
|
+
|
|
2933
|
+
// pruneUnusedImgs()
|
|
2934
|
+
//______________________________________________________________________________
|
|
2935
|
+
BookReader.prototype.pruneUnusedImgs = function() {
|
|
2936
|
+
for (var key in this.prefetchedImgs) {
|
|
2937
|
+
if ((key != this.twoPage.currentIndexL) && (key != this.twoPage.currentIndexR)) {
|
|
2938
|
+
$(this.prefetchedImgs[key]).remove();
|
|
2939
|
+
}
|
|
2940
|
+
if ((key < this.twoPage.currentIndexL-4) || (key > this.twoPage.currentIndexR+4)) {
|
|
2941
|
+
delete this.prefetchedImgs[key];
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
};
|
|
2945
|
+
|
|
2946
|
+
// prefetch()
|
|
2947
|
+
//______________________________________________________________________________
|
|
2948
|
+
BookReader.prototype.prefetch = function() {
|
|
2949
|
+
// $$$ We should check here if the current indices have finished
|
|
2950
|
+
// loading (with some timeout) before loading more page images
|
|
2951
|
+
// See https://bugs.edge.launchpad.net/bookreader/+bug/511391
|
|
2952
|
+
|
|
2953
|
+
// prefetch visible pages first
|
|
2954
|
+
this.prefetchImg(this.twoPage.currentIndexL);
|
|
2955
|
+
this.prefetchImg(this.twoPage.currentIndexR);
|
|
2956
|
+
|
|
2957
|
+
var adjacentPagesToLoad = 3;
|
|
2958
|
+
|
|
2959
|
+
var lowCurrent = Math.min(this.twoPage.currentIndexL, this.twoPage.currentIndexR);
|
|
2960
|
+
var highCurrent = Math.max(this.twoPage.currentIndexL, this.twoPage.currentIndexR);
|
|
2961
|
+
|
|
2962
|
+
var start = Math.max(lowCurrent - adjacentPagesToLoad, 0);
|
|
2963
|
+
var end = Math.min(highCurrent + adjacentPagesToLoad, this.getNumLeafs() - 1);
|
|
2964
|
+
|
|
2965
|
+
// Load images spreading out from current
|
|
2966
|
+
for (var i = 1; i <= adjacentPagesToLoad; i++) {
|
|
2967
|
+
var goingDown = lowCurrent - i;
|
|
2968
|
+
if (goingDown >= start) {
|
|
2969
|
+
this.prefetchImg(goingDown);
|
|
2970
|
+
}
|
|
2971
|
+
var goingUp = highCurrent + i;
|
|
2972
|
+
if (goingUp <= end) {
|
|
2973
|
+
this.prefetchImg(goingUp);
|
|
2974
|
+
}
|
|
2975
|
+
}
|
|
2976
|
+
};
|
|
2977
|
+
|
|
2978
|
+
// getPageWidth2UP()
|
|
2979
|
+
//______________________________________________________________________________
|
|
2980
|
+
BookReader.prototype.getPageWidth2UP = function(index) {
|
|
2981
|
+
// We return the width based on the dominant height
|
|
2982
|
+
var height = this._getPageHeight(index);
|
|
2983
|
+
var width = this._getPageWidth(index);
|
|
2984
|
+
return Math.floor(this.twoPage.height*width/height); // $$$ we assume width is relative to current spread
|
|
2985
|
+
};
|
|
2986
|
+
|
|
2987
|
+
// twoPageGutter()
|
|
2988
|
+
//______________________________________________________________________________
|
|
2989
|
+
// Returns the position of the gutter (line between the page images)
|
|
2990
|
+
BookReader.prototype.twoPageGutter = function() {
|
|
2991
|
+
return this.twoPage.middle + this.gutterOffsetForIndex(this.twoPage.currentIndexL);
|
|
2992
|
+
};
|
|
2993
|
+
|
|
2994
|
+
// twoPageTop()
|
|
2995
|
+
//______________________________________________________________________________
|
|
2996
|
+
// Returns the offset for the top of the page images
|
|
2997
|
+
BookReader.prototype.twoPageTop = function() {
|
|
2998
|
+
return this.twoPage.coverExternalPadding + this.twoPage.coverInternalPadding; // $$$ + border?
|
|
2999
|
+
};
|
|
3000
|
+
|
|
3001
|
+
// twoPageCoverWidth()
|
|
3002
|
+
//______________________________________________________________________________
|
|
3003
|
+
// Returns the width of the cover div given the total page width
|
|
3004
|
+
BookReader.prototype.twoPageCoverWidth = function(totalPageWidth) {
|
|
3005
|
+
return totalPageWidth + this.twoPage.edgeWidth + 2*this.twoPage.coverInternalPadding;
|
|
3006
|
+
};
|
|
3007
|
+
|
|
3008
|
+
// twoPageGetViewCenter()
|
|
3009
|
+
//______________________________________________________________________________
|
|
3010
|
+
// Returns the percentage offset into twopageview div at the center of container div
|
|
3011
|
+
// { percentageX: float, percentageY: float }
|
|
3012
|
+
BookReader.prototype.twoPageGetViewCenter = function() {
|
|
3013
|
+
var center = {};
|
|
3014
|
+
|
|
3015
|
+
var containerOffset = this.refs.$brContainer.offset();
|
|
3016
|
+
var viewOffset = this.refs.$brTwoPageView.offset();
|
|
3017
|
+
center.percentageX = (containerOffset.left - viewOffset.left + (this.refs.$brContainer.prop('clientWidth') >> 1)) / this.twoPage.totalWidth;
|
|
3018
|
+
center.percentageY = (containerOffset.top - viewOffset.top + (this.refs.$brContainer.prop('clientHeight') >> 1)) / this.twoPage.totalHeight;
|
|
3019
|
+
|
|
3020
|
+
return center;
|
|
3021
|
+
};
|
|
3022
|
+
|
|
3023
|
+
// twoPageCenterView(percentageX, percentageY)
|
|
3024
|
+
//______________________________________________________________________________
|
|
3025
|
+
// Centers the point given by percentage from left,top of twopageview
|
|
3026
|
+
BookReader.prototype.twoPageCenterView = function(percentageX, percentageY) {
|
|
3027
|
+
if ('undefined' == typeof(percentageX)) {
|
|
3028
|
+
percentageX = 0.5;
|
|
3029
|
+
}
|
|
3030
|
+
if ('undefined' == typeof(percentageY)) {
|
|
3031
|
+
percentageY = 0.5;
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
var viewWidth = this.refs.$brTwoPageView.width();
|
|
3035
|
+
var containerClientWidth = this.refs.$brContainer.prop('clientWidth');
|
|
3036
|
+
var intoViewX = percentageX * viewWidth;
|
|
3037
|
+
|
|
3038
|
+
var viewHeight = this.refs.$brTwoPageView.height();
|
|
3039
|
+
var containerClientHeight = this.refs.$brContainer.prop('clientHeight');
|
|
3040
|
+
var intoViewY = percentageY * viewHeight;
|
|
3041
|
+
|
|
3042
|
+
if (viewWidth < containerClientWidth) {
|
|
3043
|
+
// Can fit width without scrollbars - center by adjusting offset
|
|
3044
|
+
this.refs.$brTwoPageView.css('left', (containerClientWidth >> 1) - intoViewX + 'px');
|
|
3045
|
+
} else {
|
|
3046
|
+
// Need to scroll to center
|
|
3047
|
+
this.refs.$brTwoPageView.css('left', 0);
|
|
3048
|
+
this.refs.$brContainer.scrollLeft(intoViewX - (containerClientWidth >> 1));
|
|
3049
|
+
}
|
|
3050
|
+
|
|
3051
|
+
if (viewHeight < containerClientHeight) {
|
|
3052
|
+
// Fits with scrollbars - add offset
|
|
3053
|
+
this.refs.$brTwoPageView.css('top', (containerClientHeight >> 1) - intoViewY + 'px');
|
|
3054
|
+
} else {
|
|
3055
|
+
this.refs.$brTwoPageView.css('top', 0);
|
|
3056
|
+
this.refs.$brContainer.scrollTop(intoViewY - (containerClientHeight >> 1));
|
|
3057
|
+
}
|
|
3058
|
+
};
|
|
3059
|
+
|
|
3060
|
+
// twoPageFlipAreaHeight
|
|
3061
|
+
//______________________________________________________________________________
|
|
3062
|
+
// Returns the integer height of the click-to-flip areas at the edges of the book
|
|
3063
|
+
BookReader.prototype.twoPageFlipAreaHeight = function() {
|
|
3064
|
+
return parseInt(this.twoPage.height);
|
|
3065
|
+
};
|
|
3066
|
+
|
|
3067
|
+
// twoPageFlipAreaWidth
|
|
3068
|
+
//______________________________________________________________________________
|
|
3069
|
+
// Returns the integer width of the flip areas
|
|
3070
|
+
BookReader.prototype.twoPageFlipAreaWidth = function() {
|
|
3071
|
+
var max = 100; // $$$ TODO base on view width?
|
|
3072
|
+
var min = 10;
|
|
3073
|
+
|
|
3074
|
+
var width = this.twoPage.width * 0.15;
|
|
3075
|
+
return parseInt(BookReader.util.clamp(width, min, max));
|
|
3076
|
+
};
|
|
3077
|
+
|
|
3078
|
+
// twoPageFlipAreaTop
|
|
3079
|
+
//______________________________________________________________________________
|
|
3080
|
+
// Returns integer top offset for flip areas
|
|
3081
|
+
BookReader.prototype.twoPageFlipAreaTop = function() {
|
|
3082
|
+
return parseInt(this.twoPage.bookCoverDivTop + this.twoPage.coverInternalPadding);
|
|
3083
|
+
};
|
|
3084
|
+
|
|
3085
|
+
// twoPageLeftFlipAreaLeft
|
|
3086
|
+
//______________________________________________________________________________
|
|
3087
|
+
// Left offset for left flip area
|
|
3088
|
+
BookReader.prototype.twoPageLeftFlipAreaLeft = function() {
|
|
3089
|
+
return parseInt(this.twoPage.gutter - this.twoPage.scaledWL);
|
|
3090
|
+
};
|
|
3091
|
+
|
|
3092
|
+
// twoPageRightFlipAreaLeft
|
|
3093
|
+
//______________________________________________________________________________
|
|
3094
|
+
// Left offset for right flip area
|
|
3095
|
+
BookReader.prototype.twoPageRightFlipAreaLeft = function() {
|
|
3096
|
+
return parseInt(this.twoPage.gutter + this.twoPage.scaledWR - this.twoPageFlipAreaWidth());
|
|
3097
|
+
};
|
|
3098
|
+
|
|
3099
|
+
// setHilightCss2UP()
|
|
3100
|
+
//______________________________________________________________________________
|
|
3101
|
+
//position calculation shared between search and text-to-speech functions
|
|
3102
|
+
BookReader.prototype.setHilightCss2UP = function(div, index, left, right, top, bottom) {
|
|
3103
|
+
// We calculate the reduction factor for the specific page because it can be different
|
|
3104
|
+
// for each page in the spread
|
|
3105
|
+
var height = this._getPageHeight(index);
|
|
3106
|
+
var width = this._getPageWidth(index);
|
|
3107
|
+
var reduce = this.twoPage.height/height;
|
|
3108
|
+
var scaledW = parseInt(width*reduce);
|
|
3109
|
+
|
|
3110
|
+
var gutter = this.twoPageGutter();
|
|
3111
|
+
var pageL;
|
|
3112
|
+
if ('L' == this.getPageSide(index)) {
|
|
3113
|
+
pageL = gutter-scaledW;
|
|
3114
|
+
} else {
|
|
3115
|
+
pageL = gutter;
|
|
3116
|
+
}
|
|
3117
|
+
var pageT = this.twoPageTop();
|
|
3118
|
+
|
|
3119
|
+
$(div).css({
|
|
3120
|
+
width: (right-left)*reduce + 'px',
|
|
3121
|
+
height: (bottom-top)*reduce + 'px',
|
|
3122
|
+
left: pageL+left*reduce + 'px',
|
|
3123
|
+
top: pageT+top*reduce +'px'
|
|
3124
|
+
});
|
|
3125
|
+
};
|
|
3126
|
+
|
|
3127
|
+
|
|
3128
|
+
// autoToggle()
|
|
3129
|
+
//______________________________________________________________________________
|
|
3130
|
+
BookReader.prototype.autoToggle = function() {
|
|
3131
|
+
|
|
3132
|
+
this.trigger(BookReader.eventNames.stop);
|
|
3133
|
+
|
|
3134
|
+
var bComingFrom1up = false;
|
|
3135
|
+
if (2 != this.mode) {
|
|
3136
|
+
bComingFrom1up = true;
|
|
3137
|
+
this.switchMode(this.constMode2up);
|
|
3138
|
+
}
|
|
3139
|
+
|
|
3140
|
+
// Change to autofit if book is too large
|
|
3141
|
+
if (this.reduce < this.twoPageGetAutofitReduce()) {
|
|
3142
|
+
this.zoom2up('auto');
|
|
3143
|
+
}
|
|
3144
|
+
|
|
3145
|
+
var self = this;
|
|
3146
|
+
if (null == this.autoTimer) {
|
|
3147
|
+
this.flipSpeed = 2000;
|
|
3148
|
+
|
|
3149
|
+
// $$$ Draw events currently cause layout problems when they occur during animation.
|
|
3150
|
+
// There is a specific problem when changing from 1-up immediately to autoplay in RTL so
|
|
3151
|
+
// we workaround for now by not triggering immediate animation in that case.
|
|
3152
|
+
// See https://bugs.launchpad.net/gnubook/+bug/328327
|
|
3153
|
+
if (('rl' == this.pageProgression) && bComingFrom1up) {
|
|
3154
|
+
// don't flip immediately -- wait until timer fires
|
|
3155
|
+
} else {
|
|
3156
|
+
// flip immediately
|
|
3157
|
+
this.flipFwdToIndex();
|
|
3158
|
+
}
|
|
3159
|
+
|
|
3160
|
+
this.$('.play').hide();
|
|
3161
|
+
this.$('.pause').show();
|
|
3162
|
+
this.autoTimer=setInterval(function(){
|
|
3163
|
+
if (self.animating) {return;}
|
|
3164
|
+
|
|
3165
|
+
if (Math.max(self.twoPage.currentIndexL, self.twoPage.currentIndexR) >= self.lastDisplayableIndex()) {
|
|
3166
|
+
self.flipBackToIndex(1); // $$$ really what we want?
|
|
3167
|
+
} else {
|
|
3168
|
+
self.flipFwdToIndex();
|
|
3169
|
+
}
|
|
3170
|
+
},5000);
|
|
3171
|
+
} else {
|
|
3172
|
+
this.autoStop();
|
|
3173
|
+
}
|
|
3174
|
+
};
|
|
3175
|
+
|
|
3176
|
+
// autoStop()
|
|
3177
|
+
//______________________________________________________________________________
|
|
3178
|
+
// Stop autoplay mode, allowing animations to finish
|
|
3179
|
+
BookReader.prototype.autoStop = function() {
|
|
3180
|
+
if (null != this.autoTimer) {
|
|
3181
|
+
clearInterval(this.autoTimer);
|
|
3182
|
+
this.flipSpeed = 'fast';
|
|
3183
|
+
this.$('.pause').hide();
|
|
3184
|
+
this.$('.play').show();
|
|
3185
|
+
this.autoTimer = null;
|
|
3186
|
+
}
|
|
3187
|
+
};
|
|
3188
|
+
|
|
3189
|
+
// stopFlipAnimations
|
|
3190
|
+
//______________________________________________________________________________
|
|
3191
|
+
// Immediately stop flip animations. Callbacks are triggered.
|
|
3192
|
+
BookReader.prototype.stopFlipAnimations = function() {
|
|
3193
|
+
|
|
3194
|
+
this.autoStop(); // Clear timers
|
|
3195
|
+
|
|
3196
|
+
// Stop animation, clear queue, trigger callbacks
|
|
3197
|
+
if (this.leafEdgeTmp) {
|
|
3198
|
+
$(this.leafEdgeTmp).stop(false, true);
|
|
3199
|
+
}
|
|
3200
|
+
jQuery.each(this.prefetchedImgs, function() {
|
|
3201
|
+
$(this).stop(false, true);
|
|
3202
|
+
});
|
|
3203
|
+
|
|
3204
|
+
// And again since animations also queued in callbacks
|
|
3205
|
+
if (this.leafEdgeTmp) {
|
|
3206
|
+
$(this.leafEdgeTmp).stop(false, true);
|
|
3207
|
+
}
|
|
3208
|
+
jQuery.each(this.prefetchedImgs, function() {
|
|
3209
|
+
$(this).stop(false, true);
|
|
3210
|
+
});
|
|
3211
|
+
|
|
3212
|
+
};
|
|
3213
|
+
|
|
3214
|
+
// keyboardNavigationIsDisabled(event)
|
|
3215
|
+
// - returns true if keyboard navigation should be disabled for the event
|
|
3216
|
+
//______________________________________________________________________________
|
|
3217
|
+
BookReader.prototype.keyboardNavigationIsDisabled = function(event) {
|
|
3218
|
+
return event.target.tagName == "INPUT";
|
|
3219
|
+
};
|
|
3220
|
+
|
|
3221
|
+
// gutterOffsetForIndex
|
|
3222
|
+
//______________________________________________________________________________
|
|
3223
|
+
//
|
|
3224
|
+
// Returns the gutter offset for the spread containing the given index.
|
|
3225
|
+
// This function supports RTL
|
|
3226
|
+
BookReader.prototype.gutterOffsetForIndex = function(pindex) {
|
|
3227
|
+
|
|
3228
|
+
// To find the offset of the gutter from the middle we calculate our percentage distance
|
|
3229
|
+
// through the book (0..1), remap to (-0.5..0.5) and multiply by the total page edge width
|
|
3230
|
+
var offset = parseInt(((pindex / this.getNumLeafs()) - 0.5) * this.twoPage.edgeWidth);
|
|
3231
|
+
|
|
3232
|
+
// But then again for RTL it's the opposite
|
|
3233
|
+
if ('rl' == this.pageProgression) {
|
|
3234
|
+
offset = -offset;
|
|
3235
|
+
}
|
|
3236
|
+
|
|
3237
|
+
return offset;
|
|
3238
|
+
};
|
|
3239
|
+
|
|
3240
|
+
// leafEdgeWidth
|
|
3241
|
+
//______________________________________________________________________________
|
|
3242
|
+
// Returns the width of the leaf edge div for the page with index given
|
|
3243
|
+
BookReader.prototype.leafEdgeWidth = function(pindex) {
|
|
3244
|
+
// $$$ could there be single pixel rounding errors for L vs R?
|
|
3245
|
+
if ((this.getPageSide(pindex) == 'L') && (this.pageProgression != 'rl')) {
|
|
3246
|
+
return parseInt( (pindex/this.getNumLeafs()) * this.twoPage.edgeWidth + 0.5);
|
|
3247
|
+
} else {
|
|
3248
|
+
return parseInt( (1 - pindex/this.getNumLeafs()) * this.twoPage.edgeWidth + 0.5);
|
|
3249
|
+
}
|
|
3250
|
+
};
|
|
3251
|
+
|
|
3252
|
+
// jumpIndexForLeftEdgePageX
|
|
3253
|
+
//______________________________________________________________________________
|
|
3254
|
+
// Returns the target jump leaf given a page coordinate (inside the left page edge div)
|
|
3255
|
+
BookReader.prototype.jumpIndexForLeftEdgePageX = function(pageX) {
|
|
3256
|
+
if ('rl' != this.pageProgression) {
|
|
3257
|
+
// LTR - flipping backward
|
|
3258
|
+
var jumpIndex = this.twoPage.currentIndexL - ($(this.leafEdgeL).offset().left + $(this.leafEdgeL).width() - pageX) * 10;
|
|
3259
|
+
|
|
3260
|
+
// browser may have resized the div due to font size change -- see https://bugs.launchpad.net/gnubook/+bug/333570
|
|
3261
|
+
jumpIndex = BookReader.util.clamp(Math.round(jumpIndex), this.firstDisplayableIndex(), this.twoPage.currentIndexL - 2);
|
|
3262
|
+
return jumpIndex;
|
|
3263
|
+
|
|
3264
|
+
} else {
|
|
3265
|
+
var jumpIndex = this.twoPage.currentIndexL + ($(this.leafEdgeL).offset().left + $(this.leafEdgeL).width() - pageX) * 10;
|
|
3266
|
+
jumpIndex = BookReader.util.clamp(Math.round(jumpIndex), this.twoPage.currentIndexL + 2, this.lastDisplayableIndex());
|
|
3267
|
+
return jumpIndex;
|
|
3268
|
+
}
|
|
3269
|
+
};
|
|
3270
|
+
|
|
3271
|
+
// jumpIndexForRightEdgePageX
|
|
3272
|
+
//______________________________________________________________________________
|
|
3273
|
+
// Returns the target jump leaf given a page coordinate (inside the right page edge div)
|
|
3274
|
+
BookReader.prototype.jumpIndexForRightEdgePageX = function(pageX) {
|
|
3275
|
+
if ('rl' != this.pageProgression) {
|
|
3276
|
+
// LTR
|
|
3277
|
+
var jumpIndex = this.twoPage.currentIndexR + (pageX - $(this.leafEdgeR).offset().left) * 10;
|
|
3278
|
+
jumpIndex = BookReader.util.clamp(Math.round(jumpIndex), this.twoPage.currentIndexR + 2, this.lastDisplayableIndex());
|
|
3279
|
+
return jumpIndex;
|
|
3280
|
+
} else {
|
|
3281
|
+
var jumpIndex = this.twoPage.currentIndexR - (pageX - $(this.leafEdgeR).offset().left) * 10;
|
|
3282
|
+
jumpIndex = BookReader.util.clamp(Math.round(jumpIndex), this.firstDisplayableIndex(), this.twoPage.currentIndexR - 2);
|
|
3283
|
+
return jumpIndex;
|
|
3284
|
+
}
|
|
3285
|
+
};
|
|
3286
|
+
|
|
3287
|
+
|
|
3288
|
+
// initNavbar
|
|
3289
|
+
//______________________________________________________________________________
|
|
3290
|
+
// Initialize the navigation bar.
|
|
3291
|
+
// $$$ this could also add the base elements to the DOM, so disabling the nav bar
|
|
3292
|
+
// could be as simple as not calling this function
|
|
3293
|
+
BookReader.prototype.initNavbar = function() {
|
|
3294
|
+
// Setup nav / chapter / search results bar
|
|
3295
|
+
this.refs.$BRnav = $(
|
|
3296
|
+
"<div class=\"BRnav BRnavDesktop\">"
|
|
3297
|
+
|
|
3298
|
+
+" <div class=\"BRnavCntl BRnavCntlBtm BRdn js-tooltip\" title=\"Toogle toolbars\"></div>"
|
|
3299
|
+
|
|
3300
|
+
+" <div class=\"BRnavpos\">"
|
|
3301
|
+
+" <div class=\"BRpager\"></div>"
|
|
3302
|
+
+" <div class=\"BRnavline\">"
|
|
3303
|
+
+" </div>"
|
|
3304
|
+
+" </div>"
|
|
3305
|
+
+" <div class=\"BRpage\">"
|
|
3306
|
+
|
|
3307
|
+
// Note, it's important for there to not be whitespace
|
|
3308
|
+
+ "<span class='BRcurrentpage'></span>"
|
|
3309
|
+
+ "<button class=\"BRicon book_left js-tooltip\"></button>"
|
|
3310
|
+
+ "<button class=\"BRicon book_right js-tooltip\"></button>"
|
|
3311
|
+
+ "<button class=\"BRicon onepg desktop-only js-tooltip\"></button>"
|
|
3312
|
+
+ "<button class=\"BRicon twopg desktop-only js-tooltip\"></button>"
|
|
3313
|
+
+ "<button class=\"BRicon thumb desktop-only js-tooltip\"></button>"
|
|
3314
|
+
|
|
3315
|
+
// zoomx`
|
|
3316
|
+
+ "<button class='BRicon zoom_out desktop-only js-tooltip'></button>"
|
|
3317
|
+
+ "<button class='BRicon zoom_in desktop-only js-tooltip'></button>"
|
|
3318
|
+
+ "<button class='BRicon full js-tooltip'></button>"
|
|
3319
|
+
+" </div>"
|
|
3320
|
+
|
|
3321
|
+
+"</div>"
|
|
3322
|
+
);
|
|
3323
|
+
|
|
3324
|
+
this.refs.$br.append(this.refs.$BRnav);
|
|
3325
|
+
|
|
3326
|
+
var self = this;
|
|
3327
|
+
this.$('.BRpager').slider({
|
|
3328
|
+
animate: true,
|
|
3329
|
+
min: 0,
|
|
3330
|
+
max: this.getNumLeafs() - 1,
|
|
3331
|
+
value: this.currentIndex(),
|
|
3332
|
+
range: "min"
|
|
3333
|
+
})
|
|
3334
|
+
.bind('slide', function(event, ui) {
|
|
3335
|
+
self.updateNavPageNum(ui.value);
|
|
3336
|
+
return true;
|
|
3337
|
+
})
|
|
3338
|
+
.bind('slidechange', function(event, ui) {
|
|
3339
|
+
self.updateNavPageNum(ui.value);
|
|
3340
|
+
// recursion prevention for jumpToIndex
|
|
3341
|
+
if ( $(this).data('swallowchange') ) {
|
|
3342
|
+
$(this).data('swallowchange', false);
|
|
3343
|
+
} else {
|
|
3344
|
+
self.jumpToIndex(ui.value);
|
|
3345
|
+
}
|
|
3346
|
+
return true;
|
|
3347
|
+
});
|
|
3348
|
+
|
|
3349
|
+
this.updateNavPageNum(this.currentIndex());
|
|
3350
|
+
|
|
3351
|
+
return this.refs.$BRnav;
|
|
3352
|
+
};
|
|
3353
|
+
|
|
3354
|
+
// initEmbedNavbar
|
|
3355
|
+
//______________________________________________________________________________
|
|
3356
|
+
// Initialize the navigation bar when embedded
|
|
3357
|
+
BookReader.prototype.initEmbedNavbar = function() {
|
|
3358
|
+
// IA-specific
|
|
3359
|
+
var thisLink = (window.location + '')
|
|
3360
|
+
.replace('?ui=embed','')
|
|
3361
|
+
.replace('/stream/', '/details/')
|
|
3362
|
+
.replace('#', '/')
|
|
3363
|
+
;
|
|
3364
|
+
|
|
3365
|
+
var logoHtml = '';
|
|
3366
|
+
if (this.showLogo) {
|
|
3367
|
+
logoHtml = "<a class='logo' href='" + this.logoURL + "' 'target='_blank' ></a>";
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
this.refs.$BRnav = $('<div class="BRnav BRnavEmbed">'
|
|
3371
|
+
+ logoHtml
|
|
3372
|
+
+ "<span class='BRembedreturn'>"
|
|
3373
|
+
+ "<a href='" + thisLink + "' target='_blank'>"+this.bookTitle+"</a>"
|
|
3374
|
+
+ "</span>"
|
|
3375
|
+
+ "<span class='BRtoolbarbuttons'>"
|
|
3376
|
+
+ '<button class="BRicon book_left"></button>'
|
|
3377
|
+
+ '<button class="BRicon book_right"></button>'
|
|
3378
|
+
+ '<button class="BRicon full"></button>'
|
|
3379
|
+
+ "</span>"
|
|
3380
|
+
+ '</div>');
|
|
3381
|
+
|
|
3382
|
+
this.refs.$br.append(this.refs.$BRnav);
|
|
3383
|
+
};
|
|
3384
|
+
|
|
3385
|
+
|
|
3386
|
+
BookReader.prototype.getNavPageNumString = function(index, excludePrefix) {
|
|
3387
|
+
excludePrefix = excludePrefix === undefined ? false : true;
|
|
3388
|
+
var pageNum = this.getPageNum(index);
|
|
3389
|
+
var pageStr;
|
|
3390
|
+
if (pageNum && pageNum[0] == 'n') { // funny index
|
|
3391
|
+
pageStr = index + 1 + ' / ' + this.getNumLeafs(); // Accessible index starts at 0 (alas) so we add 1 to make human
|
|
3392
|
+
} else {
|
|
3393
|
+
pageStr = pageNum + ' of ' + this.maxPageNum;
|
|
3394
|
+
if (!excludePrefix) pageStr = 'Page ' + pageStr;
|
|
3395
|
+
}
|
|
3396
|
+
return pageStr;
|
|
3397
|
+
}
|
|
3398
|
+
BookReader.prototype.updateNavPageNum = function(index) {
|
|
3399
|
+
this.$('.BRcurrentpage').text(this.getNavPageNumString(index));
|
|
3400
|
+
};
|
|
3401
|
+
|
|
3402
|
+
/*
|
|
3403
|
+
* Update the nav bar display - does not cause navigation.
|
|
3404
|
+
*/
|
|
3405
|
+
BookReader.prototype.updateNavIndex = function(index) {
|
|
3406
|
+
// We want to update the value, but normally moving the slider
|
|
3407
|
+
// triggers jumpToIndex which triggers this method
|
|
3408
|
+
index = index !== undefined ? index : this.currentIndex();
|
|
3409
|
+
this.$('.BRpager').data('swallowchange', true).slider('value', index);
|
|
3410
|
+
};
|
|
3411
|
+
|
|
3412
|
+
BookReader.prototype.updateNavIndexDebounced = BookReader.util.debounce(BookReader.prototype.updateNavIndex, 500);
|
|
3413
|
+
|
|
3414
|
+
BookReader.prototype.updateNavIndexThrottled = BookReader.util.throttle(BookReader.prototype.updateNavIndex, 500, false);
|
|
3415
|
+
|
|
3416
|
+
|
|
3417
|
+
|
|
3418
|
+
/**
|
|
3419
|
+
* This method builds the html for the toolbar. It can be decorated to extend
|
|
3420
|
+
* the toolbar.
|
|
3421
|
+
* @return {jqueryElement}
|
|
3422
|
+
*/
|
|
3423
|
+
BookReader.prototype.buildToolbarElement = function() {
|
|
3424
|
+
var logoHtml = '';
|
|
3425
|
+
if (this.showLogo) {
|
|
3426
|
+
logoHtml = "<span class='BRtoolbarSection BRtoolbarSectionLogo'>"
|
|
3427
|
+
+ "<a class='logo' href='" + this.logoURL + "'></a>"
|
|
3428
|
+
+ "</span>";
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// Add large screen navigation
|
|
3432
|
+
this.refs.$BRtoolbar = $(
|
|
3433
|
+
"<div class='BRtoolbar header'>"
|
|
3434
|
+
+ "<div class='BRtoolbarbuttons'>"
|
|
3435
|
+
+ "<div class='BRtoolbarLeft'>"
|
|
3436
|
+
+ logoHtml
|
|
3437
|
+
+ "<span class='BRtoolbarSection BRtoolbarSectionTitle'></span>"
|
|
3438
|
+
+ "</div>"
|
|
3439
|
+
|
|
3440
|
+
+ "<div class='BRtoolbarRight'>"
|
|
3441
|
+
+ "<span class='BRtoolbarSection BRtoolbarSectionInfo'>"
|
|
3442
|
+
+ "<button class='BRpill info js-tooltip'>Info</button>"
|
|
3443
|
+
+ "<button class='BRpill share js-tooltip'>Share</button>"
|
|
3444
|
+
+ "</span>"
|
|
3445
|
+
// + "<span class='BRtoolbarSection BRtoolbarSectionMenu'>"
|
|
3446
|
+
// TODO actual hamburger menu
|
|
3447
|
+
// + "<button class='BRpill BRtoolbarHamburger'>"
|
|
3448
|
+
// + "<img src='"+this.imagesBaseURL+"icon_hamburger.svg' />"
|
|
3449
|
+
// + "<div class='BRhamburgerDrawer'><ul><li>hi</li></ul></div>"
|
|
3450
|
+
// + "</button>"
|
|
3451
|
+
// + "</span>"
|
|
3452
|
+
+ "</div>" // end BRtoolbarRight
|
|
3453
|
+
+ "</div>"
|
|
3454
|
+
+ "</div>"
|
|
3455
|
+
);
|
|
3456
|
+
|
|
3457
|
+
var $titleSectionEl = this.refs.$BRtoolbar.find('.BRtoolbarSectionTitle');
|
|
3458
|
+
|
|
3459
|
+
if (this.bookUrl && this.options.enableBookTitleLink) {
|
|
3460
|
+
$titleSectionEl.append(
|
|
3461
|
+
$('<a>')
|
|
3462
|
+
.attr({'href': this.bookUrl, 'title': this.bookUrlTitle})
|
|
3463
|
+
.addClass('BRreturn')
|
|
3464
|
+
.html(this.bookUrlText || this.bookTitle)
|
|
3465
|
+
)
|
|
3466
|
+
} else if (this.bookTitle) {
|
|
3467
|
+
$titleSectionEl.append(this.bookUrlText || this.bookTitle);
|
|
3468
|
+
}
|
|
3469
|
+
|
|
3470
|
+
// var $hamburger = this.refs.$BRtoolbar.find('BRtoolbarHamburger');
|
|
3471
|
+
return this.refs.$BRtoolbar;
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3474
|
+
|
|
3475
|
+
|
|
3476
|
+
BookReader.prototype.initToolbar = function(mode, ui) {
|
|
3477
|
+
var self = this;
|
|
3478
|
+
|
|
3479
|
+
this.refs.$br.append(this.buildToolbarElement());
|
|
3480
|
+
|
|
3481
|
+
this.$('.BRnavCntl').addClass('BRup');
|
|
3482
|
+
this.$('.pause').hide();
|
|
3483
|
+
|
|
3484
|
+
this.updateToolbarZoom(this.reduce); // Pretty format
|
|
3485
|
+
|
|
3486
|
+
// We build in mode 2
|
|
3487
|
+
this.refs.$BRtoolbar.append();
|
|
3488
|
+
|
|
3489
|
+
// Hide mode buttons and autoplay if 2up is not available
|
|
3490
|
+
// $$$ if we end up with more than two modes we should show the applicable buttons
|
|
3491
|
+
if ( !this.canSwitchToMode(this.constMode2up) ) {
|
|
3492
|
+
this.$('.two_page_mode, .play, .pause').hide();
|
|
3493
|
+
}
|
|
3494
|
+
if ( !this.canSwitchToMode(this.constModeThumb) ) {
|
|
3495
|
+
this.$('.thumbnail_mode').hide();
|
|
3496
|
+
}
|
|
3497
|
+
|
|
3498
|
+
// Hide one page button if it is the only mode available
|
|
3499
|
+
if ( ! (this.canSwitchToMode(this.constMode2up) || this.canSwitchToMode(this.constModeThumb)) ) {
|
|
3500
|
+
this.$('.one_page_mode').hide();
|
|
3501
|
+
}
|
|
3502
|
+
|
|
3503
|
+
$('<div style="display: none;"></div>').append(
|
|
3504
|
+
this.blankShareDiv()
|
|
3505
|
+
).append(
|
|
3506
|
+
this.blankInfoDiv()
|
|
3507
|
+
).appendTo(this.refs.$br);
|
|
3508
|
+
|
|
3509
|
+
this.$('.BRinfo .BRfloatTitle a')
|
|
3510
|
+
.attr({'href': this.bookUrl})
|
|
3511
|
+
.text(this.bookTitle)
|
|
3512
|
+
.addClass('title');
|
|
3513
|
+
|
|
3514
|
+
// These functions can be overridden
|
|
3515
|
+
this.buildInfoDiv(this.$('.BRinfo'));
|
|
3516
|
+
this.buildShareDiv(this.$('.BRshare'));
|
|
3517
|
+
|
|
3518
|
+
|
|
3519
|
+
this.$('.share').colorbox({
|
|
3520
|
+
inline: true,
|
|
3521
|
+
opacity: "0.5",
|
|
3522
|
+
href: this.$('.BRshare'),
|
|
3523
|
+
onLoad: function() {
|
|
3524
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3525
|
+
self.$('.BRpageviewValue').val(window.location.href);
|
|
3526
|
+
}
|
|
3527
|
+
});
|
|
3528
|
+
this.$('.info').colorbox({
|
|
3529
|
+
inline: true,
|
|
3530
|
+
opacity: "0.5",
|
|
3531
|
+
href: this.$('.BRinfo'),
|
|
3532
|
+
onLoad: function() {
|
|
3533
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3534
|
+
}
|
|
3535
|
+
});
|
|
3536
|
+
};
|
|
3537
|
+
|
|
3538
|
+
BookReader.prototype.blankInfoDiv = function() {
|
|
3539
|
+
return $([
|
|
3540
|
+
'<div class="BRfloat BRinfo">',
|
|
3541
|
+
'<div class="BRfloatHead">About this book',
|
|
3542
|
+
'<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
|
|
3543
|
+
'</div>',
|
|
3544
|
+
'<div class="BRfloatBody">',
|
|
3545
|
+
'<div class="BRfloatCover">',
|
|
3546
|
+
'</div>',
|
|
3547
|
+
'<div class="BRfloatMeta">',
|
|
3548
|
+
'<div class="BRfloatTitle">',
|
|
3549
|
+
'<h2><a/></h2>',
|
|
3550
|
+
'</div>',
|
|
3551
|
+
'</div>',
|
|
3552
|
+
'</div>',
|
|
3553
|
+
'<div class="BRfloatFoot">',
|
|
3554
|
+
'<a href="https://openlibrary.org/dev/docs/bookreader">About the BookReader</a>',
|
|
3555
|
+
'</div>',
|
|
3556
|
+
'</div>'].join('\n')
|
|
3557
|
+
);
|
|
3558
|
+
};
|
|
3559
|
+
|
|
3560
|
+
BookReader.prototype.blankShareDiv = function() {
|
|
3561
|
+
return $([
|
|
3562
|
+
'<div class="BRfloat BRshare">',
|
|
3563
|
+
'<div class="BRfloatHead">',
|
|
3564
|
+
'Share',
|
|
3565
|
+
'<button class="floatShut" href="javascript:;" onclick="$.fn.colorbox.close();"><span class="shift">Close</span></a>',
|
|
3566
|
+
'</div>',
|
|
3567
|
+
'</div>'].join('\n')
|
|
3568
|
+
);
|
|
3569
|
+
};
|
|
3570
|
+
|
|
3571
|
+
|
|
3572
|
+
// updateToolbarZoom(reduce)
|
|
3573
|
+
//______________________________________________________________________________
|
|
3574
|
+
// Update the displayed zoom factor based on reduction factor
|
|
3575
|
+
BookReader.prototype.updateToolbarZoom = function(reduce) {
|
|
3576
|
+
var value;
|
|
3577
|
+
var autofit = null;
|
|
3578
|
+
|
|
3579
|
+
// $$$ TODO preserve zoom/fit for each mode
|
|
3580
|
+
if (this.mode == this.constMode2up) {
|
|
3581
|
+
autofit = this.twoPage.autofit;
|
|
3582
|
+
} else {
|
|
3583
|
+
autofit = this.onePage.autofit;
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
if (autofit) {
|
|
3587
|
+
value = autofit.slice(0,1).toUpperCase() + autofit.slice(1);
|
|
3588
|
+
} else {
|
|
3589
|
+
value = (100 / reduce).toFixed(2);
|
|
3590
|
+
// Strip trailing zeroes and decimal if all zeroes
|
|
3591
|
+
value = value.replace(/0+$/,'');
|
|
3592
|
+
value = value.replace(/\.$/,'');
|
|
3593
|
+
value += '%';
|
|
3594
|
+
}
|
|
3595
|
+
this.$('.BRzoom').text(value);
|
|
3596
|
+
};
|
|
3597
|
+
|
|
3598
|
+
// bindNavigationHandlers
|
|
3599
|
+
//______________________________________________________________________________
|
|
3600
|
+
// Bind navigation handlers
|
|
3601
|
+
BookReader.prototype.bindNavigationHandlers = function() {
|
|
3602
|
+
var self = this;
|
|
3603
|
+
|
|
3604
|
+
// Note the mobile plugin attaches itself to body, so we need to select outside
|
|
3605
|
+
var jIcons = this.$('.BRicon').add('.BRmobileMenu .BRicon');
|
|
3606
|
+
|
|
3607
|
+
jIcons.filter('.onepg').bind('click', function(e) {
|
|
3608
|
+
self.switchMode(self.constMode1up);
|
|
3609
|
+
});
|
|
3610
|
+
|
|
3611
|
+
jIcons.filter('.twopg').bind('click', function(e) {
|
|
3612
|
+
self.switchMode(self.constMode2up);
|
|
3613
|
+
});
|
|
3614
|
+
|
|
3615
|
+
jIcons.filter('.thumb').bind('click', function(e) {
|
|
3616
|
+
self.switchMode(self.constModeThumb);
|
|
3617
|
+
});
|
|
3618
|
+
|
|
3619
|
+
jIcons.filter('.fit').bind('fit', function(e) {
|
|
3620
|
+
// XXXmang implement autofit zoom
|
|
3621
|
+
});
|
|
3622
|
+
|
|
3623
|
+
jIcons.filter('.book_left').click(function(e) {
|
|
3624
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3625
|
+
self.left();
|
|
3626
|
+
return false;
|
|
3627
|
+
});
|
|
3628
|
+
|
|
3629
|
+
jIcons.filter('.book_right').click(function(e) {
|
|
3630
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3631
|
+
self.right();
|
|
3632
|
+
return false;
|
|
3633
|
+
});
|
|
3634
|
+
|
|
3635
|
+
jIcons.filter('.book_up').bind('click', function(e) {
|
|
3636
|
+
if ($.inArray(self.mode, [self.constMode1up, self.constModeThumb]) >= 0) {
|
|
3637
|
+
self.scrollUp();
|
|
3638
|
+
} else {
|
|
3639
|
+
self.prev();
|
|
3640
|
+
}
|
|
3641
|
+
return false;
|
|
3642
|
+
});
|
|
3643
|
+
|
|
3644
|
+
jIcons.filter('.book_down').bind('click', function(e) {
|
|
3645
|
+
if ($.inArray(self.mode, [self.constMode1up, self.constModeThumb]) >= 0) {
|
|
3646
|
+
self.scrollDown();
|
|
3647
|
+
} else {
|
|
3648
|
+
self.next();
|
|
3649
|
+
}
|
|
3650
|
+
return false;
|
|
3651
|
+
});
|
|
3652
|
+
|
|
3653
|
+
jIcons.filter('.play').click(function(e) {
|
|
3654
|
+
self.autoToggle();
|
|
3655
|
+
return false;
|
|
3656
|
+
});
|
|
3657
|
+
|
|
3658
|
+
jIcons.filter('.pause').click(function(e) {
|
|
3659
|
+
self.autoToggle();
|
|
3660
|
+
return false;
|
|
3661
|
+
});
|
|
3662
|
+
|
|
3663
|
+
jIcons.filter('.book_top').click(function(e) {
|
|
3664
|
+
self.first();
|
|
3665
|
+
return false;
|
|
3666
|
+
});
|
|
3667
|
+
|
|
3668
|
+
jIcons.filter('.book_bottom').click(function(e) {
|
|
3669
|
+
self.last();
|
|
3670
|
+
return false;
|
|
3671
|
+
});
|
|
3672
|
+
|
|
3673
|
+
jIcons.filter('.book_leftmost').click(function(e) {
|
|
3674
|
+
self.leftmost();
|
|
3675
|
+
return false;
|
|
3676
|
+
});
|
|
3677
|
+
|
|
3678
|
+
jIcons.filter('.book_rightmost').click(function(e) {
|
|
3679
|
+
self.rightmost();
|
|
3680
|
+
return false;
|
|
3681
|
+
});
|
|
3682
|
+
|
|
3683
|
+
jIcons.filter('.zoom_in').bind('click', function() {
|
|
3684
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3685
|
+
self.zoom(1);
|
|
3686
|
+
return false;
|
|
3687
|
+
});
|
|
3688
|
+
|
|
3689
|
+
jIcons.filter('.zoom_out').bind('click', function() {
|
|
3690
|
+
self.trigger(BookReader.eventNames.stop);
|
|
3691
|
+
self.zoom(-1);
|
|
3692
|
+
return false;
|
|
3693
|
+
});
|
|
3694
|
+
|
|
3695
|
+
jIcons.filter('.full').bind('click', function() {
|
|
3696
|
+
if (self.ui == 'embed') {
|
|
3697
|
+
var url = self.$('.BRembedreturn a').attr('href');
|
|
3698
|
+
window.open(url);
|
|
3699
|
+
} else {
|
|
3700
|
+
self.toggleFullscreen();
|
|
3701
|
+
}
|
|
3702
|
+
});
|
|
3703
|
+
|
|
3704
|
+
var $brNavCntlBtmEl = this.$('.BRnavCntlBtm');
|
|
3705
|
+
var $brNavCntlTopEl = this.$('.BRnavCntlTop');
|
|
3706
|
+
|
|
3707
|
+
this.$('.BRnavCntl').click(
|
|
3708
|
+
function(){
|
|
3709
|
+
var promises = [];
|
|
3710
|
+
// TODO don't use magic constants
|
|
3711
|
+
// TODO move this to a function
|
|
3712
|
+
if ($brNavCntlBtmEl.hasClass('BRdn')) {
|
|
3713
|
+
if (self.refs.$BRtoolbar)
|
|
3714
|
+
promises.push(self.refs.$BRtoolbar.animate(
|
|
3715
|
+
{top: self.getToolBarHeight() * -1}
|
|
3716
|
+
).promise());
|
|
3717
|
+
promises.push(self.$('.BRnav').animate({bottom: self.getNavHeight() * -1}).promise());
|
|
3718
|
+
$brNavCntlBtmEl.addClass('BRup').removeClass('BRdn');
|
|
3719
|
+
$brNavCntlTopEl.addClass('BRdn').removeClass('BRup');
|
|
3720
|
+
self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'45px'});
|
|
3721
|
+
self.$('.BRnavCntl').delay(1000).animate({opacity:.75}, 1000);
|
|
3722
|
+
} else {
|
|
3723
|
+
if (self.refs.$BRtoolbar)
|
|
3724
|
+
promises.push(self.refs.$BRtoolbar.animate({top:0}).promise());
|
|
3725
|
+
promises.push(self.$('.BRnav').animate({bottom:0}).promise());
|
|
3726
|
+
$brNavCntlBtmEl.addClass('BRdn').removeClass('BRup');
|
|
3727
|
+
$brNavCntlTopEl.addClass('BRup').removeClass('BRdn');
|
|
3728
|
+
self.$('.BRnavCntlBtm.BRnavCntl').animate({height:'30px'});
|
|
3729
|
+
self.$('.BRvavCntl').animate({opacity:1})
|
|
3730
|
+
};
|
|
3731
|
+
$.when.apply($, promises).done(function() {
|
|
3732
|
+
// Only do full resize in auto mode and need to recalc. size
|
|
3733
|
+
if (self.mode == self.constMode2up && self.twoPage.autofit != null
|
|
3734
|
+
&& self.twoPage.autofit != 'none') {
|
|
3735
|
+
self.resize();
|
|
3736
|
+
} else if (self.mode == self.constMode1up && self.onePage.autofit != null
|
|
3737
|
+
&& self.onePage.autofit != 'none') {
|
|
3738
|
+
self.resize();
|
|
3739
|
+
} else {
|
|
3740
|
+
// Don't do a full resize to avoid redrawing images
|
|
3741
|
+
self.resizeBRcontainer();
|
|
3742
|
+
}
|
|
3743
|
+
});
|
|
3744
|
+
}
|
|
3745
|
+
);
|
|
3746
|
+
$brNavCntlBtmEl.mouseover(function(){
|
|
3747
|
+
if ($(this).hasClass('BRup')) {
|
|
3748
|
+
self.$('.BRnavCntl').animate({opacity:1},250);
|
|
3749
|
+
}
|
|
3750
|
+
}).mouseleave(function(){
|
|
3751
|
+
if ($(this).hasClass('BRup')) {
|
|
3752
|
+
self.$('.BRnavCntl').animate({opacity:.75},250);
|
|
3753
|
+
}
|
|
3754
|
+
});
|
|
3755
|
+
$brNavCntlTopEl.mouseover(function(){
|
|
3756
|
+
if ($(this).hasClass('BRdn')) {
|
|
3757
|
+
self.$('.BRnavCntl').animate({opacity:1},250);
|
|
3758
|
+
}
|
|
3759
|
+
}).mouseleave(function(){
|
|
3760
|
+
if ($(this).hasClass('BRdn')) {
|
|
3761
|
+
self.$('.BRnavCntl').animate({opacity:.75},250);
|
|
3762
|
+
}
|
|
3763
|
+
});
|
|
3764
|
+
|
|
3765
|
+
this.initSwipeData();
|
|
3766
|
+
|
|
3767
|
+
$(document).off('mousemove.navigation', this.el);
|
|
3768
|
+
$(document).on(
|
|
3769
|
+
'mousemove.navigation',
|
|
3770
|
+
this.el,
|
|
3771
|
+
{ 'br': this },
|
|
3772
|
+
this.navigationMousemoveHandler
|
|
3773
|
+
);
|
|
3774
|
+
|
|
3775
|
+
$(document).off('mousedown.swipe', '.BRpageimage');
|
|
3776
|
+
$(document).on(
|
|
3777
|
+
'mousedown.swipe',
|
|
3778
|
+
'.BRpageimage',
|
|
3779
|
+
{ 'br': this },
|
|
3780
|
+
this.swipeMousedownHandler
|
|
3781
|
+
);
|
|
3782
|
+
|
|
3783
|
+
this.bindMozTouchHandlers();
|
|
3784
|
+
};
|
|
3785
|
+
|
|
3786
|
+
// unbindNavigationHandlers
|
|
3787
|
+
//______________________________________________________________________________
|
|
3788
|
+
// Unbind navigation handlers
|
|
3789
|
+
BookReader.prototype.unbindNavigationHandlers = function() {
|
|
3790
|
+
$(document).off('mousemove.navigation', this.el);
|
|
3791
|
+
};
|
|
3792
|
+
|
|
3793
|
+
// navigationMousemoveHandler
|
|
3794
|
+
//______________________________________________________________________________
|
|
3795
|
+
// Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
|
|
3796
|
+
BookReader.prototype.navigationMousemoveHandler = function(event) {
|
|
3797
|
+
// $$$ possibly not great to be calling this for every mousemove
|
|
3798
|
+
|
|
3799
|
+
if (event.data['br'].uiAutoHide) {
|
|
3800
|
+
// TODO look into these magic numbers: 75 and 76
|
|
3801
|
+
var navkey = $(document).height() - 75;
|
|
3802
|
+
if ((event.pageY < 76) || (event.pageY > navkey)) {
|
|
3803
|
+
// inside or near navigation elements
|
|
3804
|
+
event.data['br'].hideNavigation();
|
|
3805
|
+
} else {
|
|
3806
|
+
event.data['br'].showNavigation();
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
};
|
|
3810
|
+
|
|
3811
|
+
BookReader.prototype.initSwipeData = function(clientX, clientY) {
|
|
3812
|
+
/*
|
|
3813
|
+
* Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
|
|
3814
|
+
*/
|
|
3815
|
+
this._swipe = {
|
|
3816
|
+
mightBeSwiping: false,
|
|
3817
|
+
didSwipe: false,
|
|
3818
|
+
mightBeDraggin: false,
|
|
3819
|
+
didDrag: false,
|
|
3820
|
+
startTime: (new Date).getTime(),
|
|
3821
|
+
startX: clientX,
|
|
3822
|
+
startY: clientY,
|
|
3823
|
+
lastX: clientX,
|
|
3824
|
+
lastY: clientY,
|
|
3825
|
+
deltaX: 0,
|
|
3826
|
+
deltaY: 0,
|
|
3827
|
+
deltaT: 0
|
|
3828
|
+
}
|
|
3829
|
+
};
|
|
3830
|
+
|
|
3831
|
+
BookReader.prototype.swipeMousedownHandler = function(event) {
|
|
3832
|
+
var self = event.data['br'];
|
|
3833
|
+
|
|
3834
|
+
// We should be the last bubble point for the page images
|
|
3835
|
+
// Disable image drag and select, but keep right-click
|
|
3836
|
+
if (event.which == 3) {
|
|
3837
|
+
return !self.protected;
|
|
3838
|
+
}
|
|
3839
|
+
|
|
3840
|
+
$(event.target).bind('mouseout.swipe',
|
|
3841
|
+
{ 'br': self},
|
|
3842
|
+
self.swipeMouseupHandler
|
|
3843
|
+
).bind('mouseup.swipe',
|
|
3844
|
+
{ 'br': self},
|
|
3845
|
+
self.swipeMouseupHandler
|
|
3846
|
+
).bind('mousemove.swipe',
|
|
3847
|
+
{ 'br': self },
|
|
3848
|
+
self.swipeMousemoveHandler
|
|
3849
|
+
);
|
|
3850
|
+
|
|
3851
|
+
self.initSwipeData(event.clientX, event.clientY);
|
|
3852
|
+
self._swipe.mightBeSwiping = true;
|
|
3853
|
+
self._swipe.mightBeDragging = true;
|
|
3854
|
+
|
|
3855
|
+
event.preventDefault();
|
|
3856
|
+
event.returnValue = false;
|
|
3857
|
+
event.cancelBubble = true;
|
|
3858
|
+
return false;
|
|
3859
|
+
};
|
|
3860
|
+
|
|
3861
|
+
BookReader.prototype.swipeMousemoveHandler = function(event) {
|
|
3862
|
+
var self = event.data['br'];
|
|
3863
|
+
var _swipe = self._swipe;
|
|
3864
|
+
if (! _swipe.mightBeSwiping) {
|
|
3865
|
+
return;
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
// Update swipe data
|
|
3869
|
+
_swipe.deltaX = event.clientX - _swipe.startX;
|
|
3870
|
+
_swipe.deltaY = event.clientY - _swipe.startY;
|
|
3871
|
+
_swipe.deltaT = (new Date).getTime() - _swipe.startTime;
|
|
3872
|
+
|
|
3873
|
+
var absX = Math.abs(_swipe.deltaX);
|
|
3874
|
+
var absY = Math.abs(_swipe.deltaY);
|
|
3875
|
+
|
|
3876
|
+
// Minimum distance in the amount of tim to trigger the swipe
|
|
3877
|
+
var minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
|
|
3878
|
+
var maxSwipeTime = 400;
|
|
3879
|
+
|
|
3880
|
+
// Check for horizontal swipe
|
|
3881
|
+
if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
|
|
3882
|
+
_swipe.mightBeSwiping = false; // only trigger once
|
|
3883
|
+
_swipe.didSwipe = true;
|
|
3884
|
+
if (self.mode == self.constMode2up) {
|
|
3885
|
+
if (_swipe.deltaX < 0) {
|
|
3886
|
+
self.right();
|
|
3887
|
+
} else {
|
|
3888
|
+
self.left();
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
|
|
3894
|
+
if (_swipe.mightBeDragging) {
|
|
3895
|
+
// Dragging
|
|
3896
|
+
_swipe.didDrag = true;
|
|
3897
|
+
self.refs.$brContainer
|
|
3898
|
+
.scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
|
|
3899
|
+
.scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
_swipe.lastX = event.clientX;
|
|
3903
|
+
_swipe.lastY = event.clientY;
|
|
3904
|
+
|
|
3905
|
+
event.preventDefault();
|
|
3906
|
+
event.returnValue = false;
|
|
3907
|
+
event.cancelBubble = true;
|
|
3908
|
+
return false;
|
|
3909
|
+
};
|
|
3910
|
+
BookReader.prototype.swipeMouseupHandler = function(event) {
|
|
3911
|
+
var _swipe = event.data['br']._swipe;
|
|
3912
|
+
_swipe.mightBeSwiping = false;
|
|
3913
|
+
_swipe.mightBeDragging = false;
|
|
3914
|
+
|
|
3915
|
+
$(event.target).unbind('mouseout.swipe').unbind('mouseup.swipe').unbind('mousemove.swipe');
|
|
3916
|
+
|
|
3917
|
+
if (_swipe.didSwipe || _swipe.didDrag) {
|
|
3918
|
+
// Swallow event if completed swipe gesture
|
|
3919
|
+
event.preventDefault();
|
|
3920
|
+
event.returnValue = false;
|
|
3921
|
+
event.cancelBubble = true;
|
|
3922
|
+
return false;
|
|
3923
|
+
}
|
|
3924
|
+
return true;
|
|
3925
|
+
};
|
|
3926
|
+
BookReader.prototype.bindMozTouchHandlers = function() {
|
|
3927
|
+
var self = this;
|
|
3928
|
+
|
|
3929
|
+
// Currently only want touch handlers in 2up
|
|
3930
|
+
this.refs.$br.bind('MozTouchDown', function(event) {
|
|
3931
|
+
if (this.mode == self.constMode2up) {
|
|
3932
|
+
event.preventDefault();
|
|
3933
|
+
}
|
|
3934
|
+
})
|
|
3935
|
+
.bind('MozTouchMove', function(event) {
|
|
3936
|
+
if (this.mode == self.constMode2up) {
|
|
3937
|
+
event.preventDefault();
|
|
3938
|
+
}
|
|
3939
|
+
})
|
|
3940
|
+
.bind('MozTouchUp', function(event) {
|
|
3941
|
+
if (this.mode == self.constMode2up) {
|
|
3942
|
+
event.preventDefault();
|
|
3943
|
+
}
|
|
3944
|
+
});
|
|
3945
|
+
};
|
|
3946
|
+
|
|
3947
|
+
// navigationIsVisible
|
|
3948
|
+
//______________________________________________________________________________
|
|
3949
|
+
// Returns true if the navigation elements are currently visible
|
|
3950
|
+
BookReader.prototype.navigationIsVisible = function() {
|
|
3951
|
+
// $$$ doesn't account for transitioning states, nav must be fully visible to return true
|
|
3952
|
+
var toolpos = this.refs.$BRtoolbar.offset();
|
|
3953
|
+
var tooltop = toolpos.top;
|
|
3954
|
+
return tooltop == 0;
|
|
3955
|
+
};
|
|
3956
|
+
|
|
3957
|
+
// hideNavigation
|
|
3958
|
+
//______________________________________________________________________________
|
|
3959
|
+
// Hide navigation elements, if visible
|
|
3960
|
+
BookReader.prototype.hideNavigation = function() {
|
|
3961
|
+
// Check if navigation is showing
|
|
3962
|
+
if (this.navigationIsVisible()) {
|
|
3963
|
+
var toolbarHeight = this.getToolBarHeight();
|
|
3964
|
+
var navbarHeight = this.getNavHeight();
|
|
3965
|
+
this.refs.$BRtoolbar.animate({top: toolbarHeight * -1});
|
|
3966
|
+
this.refs.$BRnav.animate({bottom: navbarHeight * -1});
|
|
3967
|
+
}
|
|
3968
|
+
};
|
|
3969
|
+
|
|
3970
|
+
// showNavigation
|
|
3971
|
+
//______________________________________________________________________________
|
|
3972
|
+
// Show navigation elements
|
|
3973
|
+
BookReader.prototype.showNavigation = function() {
|
|
3974
|
+
// Check if navigation is hidden
|
|
3975
|
+
if (!this.navigationIsVisible()) {
|
|
3976
|
+
this.refs.$BRtoolbar.animate({top:0});
|
|
3977
|
+
this.refs.$BRnav.animate({bottom:0});
|
|
3978
|
+
}
|
|
3979
|
+
};
|
|
3980
|
+
|
|
3981
|
+
|
|
3982
|
+
// firstDisplayableIndex
|
|
3983
|
+
//______________________________________________________________________________
|
|
3984
|
+
// Returns the index of the first visible page, dependent on the mode.
|
|
3985
|
+
// $$$ Currently we cannot display the front/back cover in 2-up and will need to update
|
|
3986
|
+
// this function when we can as part of https://bugs.launchpad.net/gnubook/+bug/296788
|
|
3987
|
+
BookReader.prototype.firstDisplayableIndex = function() {
|
|
3988
|
+
if (this.mode != this.constMode2up) {
|
|
3989
|
+
return 0;
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
if ('rl' != this.pageProgression) {
|
|
3993
|
+
// LTR
|
|
3994
|
+
if (this.getPageSide(0) == 'L') {
|
|
3995
|
+
return 0;
|
|
3996
|
+
} else {
|
|
3997
|
+
return -1;
|
|
3998
|
+
}
|
|
3999
|
+
} else {
|
|
4000
|
+
// RTL
|
|
4001
|
+
if (this.getPageSide(0) == 'R') {
|
|
4002
|
+
return 0;
|
|
4003
|
+
} else {
|
|
4004
|
+
return -1;
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
};
|
|
4008
|
+
|
|
4009
|
+
// lastDisplayableIndex
|
|
4010
|
+
//______________________________________________________________________________
|
|
4011
|
+
// Returns the index of the last visible page, dependent on the mode.
|
|
4012
|
+
// $$$ Currently we cannot display the front/back cover in 2-up and will need to update
|
|
4013
|
+
// this function when we can as pa rt of https://bugs.launchpad.net/gnubook/+bug/296788
|
|
4014
|
+
BookReader.prototype.lastDisplayableIndex = function() {
|
|
4015
|
+
|
|
4016
|
+
var lastIndex = this.getNumLeafs() - 1;
|
|
4017
|
+
|
|
4018
|
+
if (this.mode != this.constMode2up) {
|
|
4019
|
+
return lastIndex;
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
if ('rl' != this.pageProgression) {
|
|
4023
|
+
// LTR
|
|
4024
|
+
if (this.getPageSide(lastIndex) == 'R') {
|
|
4025
|
+
return lastIndex;
|
|
4026
|
+
} else {
|
|
4027
|
+
return lastIndex + 1;
|
|
4028
|
+
}
|
|
4029
|
+
} else {
|
|
4030
|
+
// RTL
|
|
4031
|
+
if (this.getPageSide(lastIndex) == 'L') {
|
|
4032
|
+
return lastIndex;
|
|
4033
|
+
} else {
|
|
4034
|
+
return lastIndex + 1;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
};
|
|
4038
|
+
|
|
4039
|
+
// Parameter related functions
|
|
4040
|
+
|
|
4041
|
+
// updateFromParams(params)
|
|
4042
|
+
//________
|
|
4043
|
+
// Update ourselves from the params object.
|
|
4044
|
+
//
|
|
4045
|
+
BookReader.prototype.updateFromParams = function(params) {
|
|
4046
|
+
if ('undefined' != typeof(params.mode)) {
|
|
4047
|
+
this.switchMode(params.mode);
|
|
4048
|
+
}
|
|
4049
|
+
|
|
4050
|
+
// $$$ process /zoom
|
|
4051
|
+
var pageFound = false;
|
|
4052
|
+
// We only respect page if index is not set
|
|
4053
|
+
if ('undefined' != typeof(params.index)) {
|
|
4054
|
+
pageFound = true;
|
|
4055
|
+
if (params.index != this.currentIndex()) {
|
|
4056
|
+
this.jumpToIndex(params.index);
|
|
4057
|
+
}
|
|
4058
|
+
} else if ('undefined' != typeof(params.page)) {
|
|
4059
|
+
pageFound = true;
|
|
4060
|
+
// $$$ this assumes page numbers are unique
|
|
4061
|
+
if (params.page != this.getPageNum(this.currentIndex())) {
|
|
4062
|
+
this.jumpToPage(params.page);
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
|
|
4066
|
+
// process /search
|
|
4067
|
+
if (this.enableSearch && 'undefined' != typeof(params.search)) {
|
|
4068
|
+
if (this.searchTerm != params.search) {
|
|
4069
|
+
this.search(params.search, {goToFirstResult: !pageFound});
|
|
4070
|
+
this.$('.BRsearchInput').val(params.search);
|
|
4071
|
+
}
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
// $$$ process /region
|
|
4075
|
+
// $$$ process /highlight
|
|
4076
|
+
|
|
4077
|
+
// $$$ process /theme
|
|
4078
|
+
if (this.enableThemesPlugin && 'undefined' != typeof(params.theme)) {
|
|
4079
|
+
this.updateTheme(params.theme);
|
|
4080
|
+
}
|
|
4081
|
+
};
|
|
4082
|
+
|
|
4083
|
+
|
|
4084
|
+
// getPageIndex(pageNum)
|
|
4085
|
+
//________
|
|
4086
|
+
// Returns the *highest* index the given page number, or undefined
|
|
4087
|
+
BookReader.prototype.getPageIndex = function(pageNum) {
|
|
4088
|
+
var pageIndices = this.getPageIndices(pageNum);
|
|
4089
|
+
|
|
4090
|
+
if (pageIndices.length > 0) {
|
|
4091
|
+
return pageIndices[pageIndices.length - 1];
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
return undefined;
|
|
4095
|
+
};
|
|
4096
|
+
|
|
4097
|
+
// getPageIndices(pageNum)
|
|
4098
|
+
//________
|
|
4099
|
+
// Returns an array (possibly empty) of the indices with the given page number
|
|
4100
|
+
BookReader.prototype.getPageIndices = function(pageNum) {
|
|
4101
|
+
var indices = [];
|
|
4102
|
+
|
|
4103
|
+
// Check for special "nXX" page number
|
|
4104
|
+
if (pageNum.slice(0,1) == 'n') {
|
|
4105
|
+
try {
|
|
4106
|
+
var pageIntStr = pageNum.slice(1, pageNum.length);
|
|
4107
|
+
var pageIndex = parseInt(pageIntStr);
|
|
4108
|
+
indices.push(pageIndex);
|
|
4109
|
+
return indices;
|
|
4110
|
+
} catch(err) {
|
|
4111
|
+
// Do nothing... will run through page names and see if one matches
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
var i;
|
|
4116
|
+
for (i=0; i<this.getNumLeafs(); i++) {
|
|
4117
|
+
if (this.getPageNum(i) == pageNum) {
|
|
4118
|
+
indices.push(i);
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
|
|
4122
|
+
return indices;
|
|
4123
|
+
};
|
|
4124
|
+
|
|
4125
|
+
// getPageName(index)
|
|
4126
|
+
//________
|
|
4127
|
+
// Returns the name of the page as it should be displayed in the user interface
|
|
4128
|
+
BookReader.prototype.getPageName = function(index) {
|
|
4129
|
+
return 'Page ' + this.getPageNum(index);
|
|
4130
|
+
};
|
|
4131
|
+
|
|
4132
|
+
|
|
4133
|
+
// canSwitchToMode
|
|
4134
|
+
//________
|
|
4135
|
+
// Returns true if we can switch to the requested mode
|
|
4136
|
+
BookReader.prototype.canSwitchToMode = function(mode) {
|
|
4137
|
+
if (mode == this.constMode2up || mode == this.constModeThumb) {
|
|
4138
|
+
// check there are enough pages to display
|
|
4139
|
+
// $$$ this is a workaround for the mis-feature that we can't display
|
|
4140
|
+
// short books in 2up mode
|
|
4141
|
+
if (this.getNumLeafs() < 2) {
|
|
4142
|
+
return false;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
|
|
4146
|
+
return true;
|
|
4147
|
+
};
|
|
4148
|
+
|
|
4149
|
+
// _getPageWidth
|
|
4150
|
+
//--------
|
|
4151
|
+
// Returns the page width for the given index, or first or last page if out of range
|
|
4152
|
+
BookReader.prototype._getPageWidth = function(index) {
|
|
4153
|
+
// Synthesize a page width for pages not actually present in book.
|
|
4154
|
+
// May or may not be the best approach.
|
|
4155
|
+
// If index is out of range we return the width of first or last page
|
|
4156
|
+
index = BookReader.util.clamp(index, 0, this.getNumLeafs() - 1);
|
|
4157
|
+
return this.getPageWidth(index);
|
|
4158
|
+
};
|
|
4159
|
+
|
|
4160
|
+
// _getPageHeight
|
|
4161
|
+
//--------
|
|
4162
|
+
// Returns the page height for the given index, or first or last page if out of range
|
|
4163
|
+
BookReader.prototype._getPageHeight = function(index) {
|
|
4164
|
+
index = BookReader.util.clamp(index, 0, this.getNumLeafs() - 1);
|
|
4165
|
+
return this.getPageHeight(index);
|
|
4166
|
+
};
|
|
4167
|
+
|
|
4168
|
+
// _getPageURI
|
|
4169
|
+
//--------
|
|
4170
|
+
// Returns the page URI or transparent image if out of range
|
|
4171
|
+
BookReader.prototype._getPageURI = function(index, reduce, rotate) {
|
|
4172
|
+
if (index < 0 || index >= this.getNumLeafs()) { // Synthesize page
|
|
4173
|
+
return this.imagesBaseURL + "transparent.png";
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
if ('undefined' == typeof(reduce)) {
|
|
4177
|
+
// reduce not passed in
|
|
4178
|
+
// $$$ this probably won't work for thumbnail mode
|
|
4179
|
+
var ratio = this.getPageHeight(index) / this.twoPage.height;
|
|
4180
|
+
var scale;
|
|
4181
|
+
// $$$ we make an assumption here that the scales are available pow2 (like kakadu)
|
|
4182
|
+
if (ratio < 2) {
|
|
4183
|
+
scale = 1;
|
|
4184
|
+
} else if (ratio < 4) {
|
|
4185
|
+
scale = 2;
|
|
4186
|
+
} else if (ratio < 8) {
|
|
4187
|
+
scale = 4;
|
|
4188
|
+
} else if (ratio < 16) {
|
|
4189
|
+
scale = 8;
|
|
4190
|
+
} else if (ratio < 32) {
|
|
4191
|
+
scale = 16;
|
|
4192
|
+
} else {
|
|
4193
|
+
scale = 32;
|
|
4194
|
+
}
|
|
4195
|
+
reduce = scale;
|
|
4196
|
+
}
|
|
4197
|
+
|
|
4198
|
+
return this.getPageURI(index, reduce, rotate);
|
|
4199
|
+
};
|
|
4200
|
+
|
|
4201
|
+
|
|
4202
|
+
// showProgressPopup
|
|
4203
|
+
//______________________________________________________________________________
|
|
4204
|
+
BookReader.prototype.showProgressPopup = function(msg) {
|
|
4205
|
+
if (this.popup) return;
|
|
4206
|
+
|
|
4207
|
+
this.popup = document.createElement("div");
|
|
4208
|
+
$(this.popup).css({
|
|
4209
|
+
top: (this.refs.$br.height()*0.5-100) + 'px',
|
|
4210
|
+
left: (this.refs.$br.width()-300)*0.5 + 'px'
|
|
4211
|
+
}).prop('className', 'BRprogresspopup');
|
|
4212
|
+
|
|
4213
|
+
var bar = document.createElement("div");
|
|
4214
|
+
$(bar).css({
|
|
4215
|
+
height: '20px'
|
|
4216
|
+
}).prop('className', 'BRprogressbar');
|
|
4217
|
+
$(this.popup).append(bar);
|
|
4218
|
+
|
|
4219
|
+
if (msg) {
|
|
4220
|
+
var msgdiv = document.createElement("div");
|
|
4221
|
+
msgdiv.innerHTML = msg;
|
|
4222
|
+
$(this.popup).append(msgdiv);
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
$(this.popup).appendTo(this.refs.$br);
|
|
4226
|
+
};
|
|
4227
|
+
|
|
4228
|
+
// removeProgressPopup
|
|
4229
|
+
//______________________________________________________________________________
|
|
4230
|
+
BookReader.prototype.removeProgressPopup = function() {
|
|
4231
|
+
$(this.popup).remove();
|
|
4232
|
+
this.$('.BRprogresspopup').remove();
|
|
4233
|
+
this.popup=null;
|
|
4234
|
+
};
|
|
4235
|
+
|
|
4236
|
+
|
|
4237
|
+
BookReader.prototype.buildShareDiv = function(jShareDiv) {
|
|
4238
|
+
var pageView = document.location + '';
|
|
4239
|
+
var bookView = (pageView + '').replace(/#.*/,'');
|
|
4240
|
+
var self = this;
|
|
4241
|
+
|
|
4242
|
+
var embedHtml = '';
|
|
4243
|
+
if (this.getEmbedCode) {
|
|
4244
|
+
embedHtml = [
|
|
4245
|
+
'<div class="share-embed">',
|
|
4246
|
+
'<p class="share-embed-prompt">Copy and paste one of these options to share this book elsewhere.</p>',
|
|
4247
|
+
'<form method="post" action="">',
|
|
4248
|
+
'<fieldset class="fieldset-share-pageview">',
|
|
4249
|
+
'<label for="pageview">Link to this page view</label>',
|
|
4250
|
+
'<input type="text" name="pageview" class="BRpageviewValue" value="' + pageView + '"/>',
|
|
4251
|
+
'</fieldset>',
|
|
4252
|
+
'<fieldset class="fieldset-share-book-link">',
|
|
4253
|
+
'<label for="booklink">Link to the book</label>',
|
|
4254
|
+
'<input type="text" name="booklink" class="booklink" value="' + bookView + '"/>',
|
|
4255
|
+
'</fieldset>',
|
|
4256
|
+
'<fieldset class="fieldset-embed">',
|
|
4257
|
+
'<label for="iframe">Embed a mini Book Reader</label>',
|
|
4258
|
+
'<fieldset class="sub">',
|
|
4259
|
+
'<label class="sub">',
|
|
4260
|
+
'<input type="radio" name="pages" value="' + this.constMode1up + '" checked="checked"/>',
|
|
4261
|
+
'1 page',
|
|
4262
|
+
'</label>',
|
|
4263
|
+
'<label class="sub">',
|
|
4264
|
+
'<input type="radio" name="pages" value="' + this.constMode2up + '"/>',
|
|
4265
|
+
'2 pages',
|
|
4266
|
+
'</label>',
|
|
4267
|
+
'<label class="sub">',
|
|
4268
|
+
'<input type="checkbox" name="thispage" value="thispage"/>',
|
|
4269
|
+
'Open to this page?',
|
|
4270
|
+
'</label>',
|
|
4271
|
+
'</fieldset>',
|
|
4272
|
+
'<textarea cols="30" rows="4" name="iframe" class="BRframeEmbed"></textarea>',
|
|
4273
|
+
'</fieldset>',
|
|
4274
|
+
'</form>',
|
|
4275
|
+
'</div>'
|
|
4276
|
+
].join('\n');
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
var jForm = $([
|
|
4280
|
+
'<div class="share-title">Share this book</div>',
|
|
4281
|
+
'<div class="share-social">',
|
|
4282
|
+
'<label class="sub open-to-this-page">',
|
|
4283
|
+
'<input class="thispage-social" type="checkbox" />',
|
|
4284
|
+
'Open to this page?',
|
|
4285
|
+
'</label>',
|
|
4286
|
+
'<div><button class="BRaction share facebook-share-button"><i class="BRicon fb" /> Facebook</button></div>',
|
|
4287
|
+
'<div><button class="BRaction share twitter-share-button"><i class="BRicon twitter" /> Twitter</button></div>',
|
|
4288
|
+
'<div><button class="BRaction share email-share-button"><i class="BRicon email" /> Email</button></div>',
|
|
4289
|
+
'</div>',
|
|
4290
|
+
embedHtml,
|
|
4291
|
+
'<div class="BRfloatFoot">',
|
|
4292
|
+
'<button class="share-finished" type="button" onclick="$.fn.colorbox.close();">Finished</button>',
|
|
4293
|
+
'</div>'
|
|
4294
|
+
].join('\n'));
|
|
4295
|
+
|
|
4296
|
+
jForm.appendTo(jShareDiv);
|
|
4297
|
+
|
|
4298
|
+
jForm.find('.fieldset-embed input').bind('change', function() {
|
|
4299
|
+
var form = $(this).parents('form:first');
|
|
4300
|
+
var params = {};
|
|
4301
|
+
params.mode = $(form.find('.fieldset-embed input[name=pages]:checked')).val();
|
|
4302
|
+
if (form.find('.fieldset-embed input[name=thispage]').prop('checked')) {
|
|
4303
|
+
params.page = self.getPageNum(self.currentIndex());
|
|
4304
|
+
}
|
|
4305
|
+
|
|
4306
|
+
if (self.getEmbedCode) {
|
|
4307
|
+
// $$$ changeable width/height to be added to share UI
|
|
4308
|
+
var frameWidth = "480px";
|
|
4309
|
+
var frameHeight = "430px";
|
|
4310
|
+
form.find('.BRframeEmbed').val(self.getEmbedCode(frameWidth, frameHeight, params));
|
|
4311
|
+
}
|
|
4312
|
+
});
|
|
4313
|
+
|
|
4314
|
+
jForm.find('input, textarea').bind('focus', function() {
|
|
4315
|
+
this.select();
|
|
4316
|
+
});
|
|
4317
|
+
|
|
4318
|
+
// Bind share buttons
|
|
4319
|
+
|
|
4320
|
+
// Use url without hashes
|
|
4321
|
+
jForm.find('.facebook-share-button').click(function(){
|
|
4322
|
+
var params = $.param({ u: self._getSocialShareUrl() });
|
|
4323
|
+
var url = 'https://www.facebook.com/sharer.php?' + params;
|
|
4324
|
+
self.createPopup(url, 600, 400, 'Share')
|
|
4325
|
+
});
|
|
4326
|
+
jForm.find('.twitter-share-button').click(function(){
|
|
4327
|
+
var params = $.param({
|
|
4328
|
+
url: self._getSocialShareUrl(),
|
|
4329
|
+
text: self.bookTitle
|
|
4330
|
+
});
|
|
4331
|
+
var url = 'https://twitter.com/intent/tweet?' + params;
|
|
4332
|
+
self.createPopup(url, 600, 400, 'Share')
|
|
4333
|
+
});
|
|
4334
|
+
jForm.find('.email-share-button').click(function(){
|
|
4335
|
+
var body = self.bookTitle + "\n\n" + self._getSocialShareUrl();
|
|
4336
|
+
window.location.href = 'mailto:?subject=' + encodeURI(self.bookTitle) + '&body=' + encodeURI(body);
|
|
4337
|
+
});
|
|
4338
|
+
|
|
4339
|
+
jForm.find('input[name=thispage]').trigger('change');
|
|
4340
|
+
|
|
4341
|
+
jForm.appendTo(jShareDiv);
|
|
4342
|
+
};
|
|
4343
|
+
|
|
4344
|
+
BookReader.prototype._getSocialShareUrl = function() {
|
|
4345
|
+
var shareThisPage = this.$('.thispage-social').prop('checked');
|
|
4346
|
+
if (shareThisPage) {
|
|
4347
|
+
return window.location.href;
|
|
4348
|
+
} else {
|
|
4349
|
+
return document.location.protocol + "//" + window.location.hostname + window.location.pathname;
|
|
4350
|
+
}
|
|
4351
|
+
};
|
|
4352
|
+
|
|
4353
|
+
/**
|
|
4354
|
+
* @param JInfoDiv DOM element. Appends info to this element
|
|
4355
|
+
* Can be overridden or extended
|
|
4356
|
+
*/
|
|
4357
|
+
BookReader.prototype.buildInfoDiv = function(jInfoDiv) {
|
|
4358
|
+
// Remove these legacy elements
|
|
4359
|
+
jInfoDiv.find('.BRfloatBody, .BRfloatCover, .BRfloatFoot').remove();
|
|
4360
|
+
|
|
4361
|
+
var $leftCol = $("<div class=\"BRinfoLeftCol\"></div>");
|
|
4362
|
+
if (this.thumbnail) {
|
|
4363
|
+
$leftCol.append($("<div class=\"BRimageW\">"
|
|
4364
|
+
+" <img src=\""+this.thumbnail+"\" "
|
|
4365
|
+
+" alt=\""+BookReader.util.escapeHTML(this.bookTitle)+"\" />"
|
|
4366
|
+
+"</div>"));
|
|
4367
|
+
}
|
|
4368
|
+
|
|
4369
|
+
var $rightCol = $("<div class=\"BRinfoRightCol\">");
|
|
4370
|
+
|
|
4371
|
+
// A loop to build fields
|
|
4372
|
+
var extraClass;
|
|
4373
|
+
for (var i = 0; i < this.metadata.length; i++) {
|
|
4374
|
+
extraClass = this.metadata[i].extraValueClass || '';
|
|
4375
|
+
$rightCol.append($("<div class=\"BRinfoValueWrapper\">"
|
|
4376
|
+
+" <div class=\"BRinfoLabel\">"
|
|
4377
|
+
+ this.metadata[i].label
|
|
4378
|
+
+" </div>"
|
|
4379
|
+
+" <div class=\"BRinfoValue " + extraClass + "\">"
|
|
4380
|
+
+ this.metadata[i].value
|
|
4381
|
+
+" </div>"
|
|
4382
|
+
+"</div>"));
|
|
4383
|
+
}
|
|
4384
|
+
|
|
4385
|
+
var moreInfoText;
|
|
4386
|
+
if (this.bookUrlMoreInfo) {
|
|
4387
|
+
moreInfoText = this.bookUrlMoreInfo;
|
|
4388
|
+
} else if (this.bookTitle) {
|
|
4389
|
+
moreInfoText = this.bookTitle;
|
|
4390
|
+
}
|
|
4391
|
+
|
|
4392
|
+
if (moreInfoText && this.bookUrl) {
|
|
4393
|
+
$rightCol.append($("<div class=\"BRinfoValueWrapper\">"
|
|
4394
|
+
+"<div class=\"BRinfoMoreInfoWrapper\">"
|
|
4395
|
+
+" <a class=\"BRinfoMoreInfo\" href=\""+this.bookUrl+"\">"
|
|
4396
|
+
+ moreInfoText
|
|
4397
|
+
+" </a>"
|
|
4398
|
+
+"</div>"
|
|
4399
|
+
+"</div>"));
|
|
4400
|
+
}
|
|
4401
|
+
|
|
4402
|
+
|
|
4403
|
+
var footerEl = "<div class=\"BRfloatFoot BRinfoFooter\"></div>";
|
|
4404
|
+
|
|
4405
|
+
var children = [
|
|
4406
|
+
$leftCol,
|
|
4407
|
+
$rightCol,
|
|
4408
|
+
'<br style="clear:both"/>'
|
|
4409
|
+
];
|
|
4410
|
+
var childrenEl = $('<div class="BRinfoW mv20-lg">').append(children);
|
|
4411
|
+
|
|
4412
|
+
jInfoDiv.append(
|
|
4413
|
+
childrenEl,
|
|
4414
|
+
$(footerEl)
|
|
4415
|
+
).addClass('wide');
|
|
4416
|
+
};
|
|
4417
|
+
|
|
4418
|
+
// Can be overriden
|
|
4419
|
+
BookReader.prototype.initUIStrings = function() {
|
|
4420
|
+
// Navigation handlers will be bound after all UI is in place -- makes moving icons between
|
|
4421
|
+
// the toolbar and nav bar easier
|
|
4422
|
+
|
|
4423
|
+
// Setup tooltips -- later we could load these from a file for i18n
|
|
4424
|
+
var titles = { '.logo': 'Go to Archive.org', // $$$ update after getting OL record
|
|
4425
|
+
'.zoom_in': 'Zoom in',
|
|
4426
|
+
'.zoom_out': 'Zoom out',
|
|
4427
|
+
'.onepg': 'One-page view',
|
|
4428
|
+
'.twopg': 'Two-page view',
|
|
4429
|
+
'.thumb': 'Thumbnail view',
|
|
4430
|
+
'.print': 'Print this page',
|
|
4431
|
+
'.embed': 'Embed BookReader',
|
|
4432
|
+
'.link': 'Link to this book (and page)',
|
|
4433
|
+
'.bookmark': 'Bookmark this page',
|
|
4434
|
+
'.read': 'Read this book aloud',
|
|
4435
|
+
'.share': 'Share this book',
|
|
4436
|
+
'.info': 'About this book',
|
|
4437
|
+
'.full': 'Toggle fullscreen',
|
|
4438
|
+
'.book_left': 'Flip left',
|
|
4439
|
+
'.book_right': 'Flip right',
|
|
4440
|
+
'.book_up': 'Page up',
|
|
4441
|
+
'.book_down': 'Page down',
|
|
4442
|
+
'.play': 'Play',
|
|
4443
|
+
'.pause': 'Pause',
|
|
4444
|
+
'.BRdn': 'Show/hide nav bar', // Would have to keep updating on state change to have just "Hide nav bar"
|
|
4445
|
+
'.BRup': 'Show/hide nav bar',
|
|
4446
|
+
'.book_top': 'First page',
|
|
4447
|
+
'.book_bottom': 'Last page'
|
|
4448
|
+
};
|
|
4449
|
+
if ('rl' == this.pageProgression) {
|
|
4450
|
+
titles['.book_leftmost'] = 'Last page';
|
|
4451
|
+
titles['.book_rightmost'] = 'First page';
|
|
4452
|
+
} else { // LTR
|
|
4453
|
+
titles['.book_leftmost'] = 'First page';
|
|
4454
|
+
titles['.book_rightmost'] = 'Last page';
|
|
4455
|
+
}
|
|
4456
|
+
|
|
4457
|
+
for (var icon in titles) {
|
|
4458
|
+
if (titles.hasOwnProperty(icon)) {
|
|
4459
|
+
this.$(icon).prop('title', titles[icon]);
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
|
|
4464
|
+
/**
|
|
4465
|
+
* Helper opens a popup window. On mobile it only opens a new tab. Used for share.
|
|
4466
|
+
*/
|
|
4467
|
+
BookReader.prototype.createPopup = function(href, width, height, name) {
|
|
4468
|
+
// Fixes dual-screen position
|
|
4469
|
+
var dualScreenLeft = window.screenLeft != undefined ? window.screenLeft : screen.left;
|
|
4470
|
+
var dualScreenTop = window.screenTop != undefined ? window.screenTop : screen.top;
|
|
4471
|
+
|
|
4472
|
+
var win_w = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width;
|
|
4473
|
+
var win_h = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height;
|
|
4474
|
+
|
|
4475
|
+
var left = ((win_w / 2) - (width / 2)) + dualScreenLeft,
|
|
4476
|
+
top = ((win_h / 2) - (height / 2)) + dualScreenTop,
|
|
4477
|
+
url = href,
|
|
4478
|
+
opts = 'status=1' +
|
|
4479
|
+
',width=' + width +
|
|
4480
|
+
',height=' + height +
|
|
4481
|
+
',top=' + top +
|
|
4482
|
+
',left=' + left;
|
|
4483
|
+
|
|
4484
|
+
window.open(url, name, opts);
|
|
4485
|
+
};
|
|
4486
|
+
|
|
4487
|
+
/**
|
|
4488
|
+
* Reloads images. Useful when some images might have failed.
|
|
4489
|
+
*/
|
|
4490
|
+
BookReader.prototype.reloadImages = function() {
|
|
4491
|
+
this.refs.$brContainer.find('img').each(function(index, elem) {
|
|
4492
|
+
if (!elem.complete || elem.naturalHeight === 0) {
|
|
4493
|
+
var src = elem.src;
|
|
4494
|
+
elem.src = '';
|
|
4495
|
+
setTimeout(function() {
|
|
4496
|
+
elem.src = src;
|
|
4497
|
+
}, 1000);
|
|
4498
|
+
}
|
|
4499
|
+
});
|
|
4500
|
+
};
|
|
4501
|
+
|
|
4502
|
+
BookReader.prototype.getToolBarHeight = function() {
|
|
4503
|
+
if (this.refs.$BRtoolbar && this.refs.$BRtoolbar.css('display') === 'block') {
|
|
4504
|
+
return (this.refs.$BRtoolbar.outerHeight() + parseInt(this.refs.$BRtoolbar.css('top')));
|
|
4505
|
+
} else {
|
|
4506
|
+
return 0;
|
|
4507
|
+
}
|
|
4508
|
+
}
|
|
4509
|
+
|
|
4510
|
+
/**
|
|
4511
|
+
* @param {boolean} ignoreDisplay - bypass the display check
|
|
4512
|
+
* @return {Number}
|
|
4513
|
+
*/
|
|
4514
|
+
BookReader.prototype.getNavHeight = function() {
|
|
4515
|
+
if (this.refs.$BRnav) {
|
|
4516
|
+
var outerHeight = this.refs.$BRnav.outerHeight();
|
|
4517
|
+
var bottom = parseInt(this.refs.$BRnav.css('bottom'));
|
|
4518
|
+
if (!isNaN(outerHeight) && !isNaN(bottom)) {
|
|
4519
|
+
return outerHeight + bottom;
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
return 0;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
4525
|
+
//------------------------------------------------------------------------------
|
|
4526
|
+
// Basic Usage built-in Methods (can be overridden through options)
|
|
4527
|
+
// This implementation uses options.data value for populating BookReader
|
|
4528
|
+
|
|
4529
|
+
/**
|
|
4530
|
+
* @return {Number} the total number of leafs (like an array length)
|
|
4531
|
+
*/
|
|
4532
|
+
BookReader.prototype.getNumLeafs = function() {
|
|
4533
|
+
// For deprecated interface support, if numLeafs is set, use that.
|
|
4534
|
+
if (this.numLeafs !== undefined)
|
|
4535
|
+
return this.numLeafs;
|
|
4536
|
+
return this._getDataFlattened().length;
|
|
4537
|
+
};
|
|
4538
|
+
|
|
4539
|
+
/**
|
|
4540
|
+
* @param {Number} index
|
|
4541
|
+
* @return {Number|undefined}
|
|
4542
|
+
*/
|
|
4543
|
+
BookReader.prototype.getPageWidth = function(index) {
|
|
4544
|
+
return this._getDataProp(index, 'width');
|
|
4545
|
+
};
|
|
4546
|
+
|
|
4547
|
+
/**
|
|
4548
|
+
* @param {Number} index
|
|
4549
|
+
* @return {Number|undefined}
|
|
4550
|
+
*/
|
|
4551
|
+
BookReader.prototype.getPageHeight = function(index) {
|
|
4552
|
+
return this._getDataProp(index, 'height');
|
|
4553
|
+
};
|
|
4554
|
+
|
|
4555
|
+
/**
|
|
4556
|
+
* @param {Number} index
|
|
4557
|
+
* @param {Number} reduce - not used in default implementation
|
|
4558
|
+
* @param {Number} rotate - not used in default implementation
|
|
4559
|
+
* @return {Number|undefined}
|
|
4560
|
+
*/
|
|
4561
|
+
BookReader.prototype.getPageURI = function(index, reduce, rotate) {
|
|
4562
|
+
return this._getDataProp(index, 'uri');
|
|
4563
|
+
};
|
|
4564
|
+
|
|
4565
|
+
/**
|
|
4566
|
+
* @param {Number} index
|
|
4567
|
+
* @return {String} - L or R
|
|
4568
|
+
*/
|
|
4569
|
+
BookReader.prototype.getPageSide = function(index) {
|
|
4570
|
+
var pageSide = this._getDataProp(index, 'pageSide');
|
|
4571
|
+
if (!pageSide) {
|
|
4572
|
+
// Fallback for invalid values
|
|
4573
|
+
pageSide = index % 2 === 0 ? 'R' : 'L';
|
|
4574
|
+
}
|
|
4575
|
+
return pageSide;
|
|
4576
|
+
};
|
|
4577
|
+
|
|
4578
|
+
/**
|
|
4579
|
+
* @param {Number} index
|
|
4580
|
+
* @return {String}
|
|
4581
|
+
*/
|
|
4582
|
+
BookReader.prototype.getPageNum = function(index) {
|
|
4583
|
+
var pageNum = this._getDataProp(index, 'pageNum');
|
|
4584
|
+
if (pageNum === undefined) {
|
|
4585
|
+
pageNum = 'n' + index;
|
|
4586
|
+
}
|
|
4587
|
+
return pageNum;
|
|
4588
|
+
};
|
|
4589
|
+
|
|
4590
|
+
/**
|
|
4591
|
+
* @param {Number} pindex
|
|
4592
|
+
* @return {Array} - eg [0, 1]
|
|
4593
|
+
*/
|
|
4594
|
+
BookReader.prototype.getSpreadIndices = function(pindex) {
|
|
4595
|
+
var spreadIndices = [null, null];
|
|
4596
|
+
if ('rl' == this.pageProgression) {
|
|
4597
|
+
// Right to Left
|
|
4598
|
+
if (this.getPageSide(pindex) == 'R') {
|
|
4599
|
+
spreadIndices[1] = pindex;
|
|
4600
|
+
spreadIndices[0] = pindex + 1;
|
|
4601
|
+
} else {
|
|
4602
|
+
// Given index was LHS
|
|
4603
|
+
spreadIndices[0] = pindex;
|
|
4604
|
+
spreadIndices[1] = pindex - 1;
|
|
4605
|
+
}
|
|
4606
|
+
} else {
|
|
4607
|
+
// Left to right
|
|
4608
|
+
if (this.getPageSide(pindex) == 'L') {
|
|
4609
|
+
spreadIndices[0] = pindex;
|
|
4610
|
+
spreadIndices[1] = pindex + 1;
|
|
4611
|
+
} else {
|
|
4612
|
+
// Given index was RHS
|
|
4613
|
+
spreadIndices[1] = pindex;
|
|
4614
|
+
spreadIndices[0] = pindex - 1;
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4617
|
+
return spreadIndices;
|
|
4618
|
+
};
|
|
4619
|
+
|
|
4620
|
+
/**
|
|
4621
|
+
* Override if "leafNum" does not correspond to "index"
|
|
4622
|
+
* @param {Number} index
|
|
4623
|
+
* @return {Number}
|
|
4624
|
+
*/
|
|
4625
|
+
BookReader.prototype.leafNumToIndex = function(index) {
|
|
4626
|
+
return index;
|
|
4627
|
+
};
|
|
4628
|
+
|
|
4629
|
+
/**
|
|
4630
|
+
* Create a params object from the current parameters.
|
|
4631
|
+
* @return {Object}
|
|
4632
|
+
*/
|
|
4633
|
+
BookReader.prototype.paramsFromCurrent = function() {
|
|
4634
|
+
var params = {};
|
|
4635
|
+
|
|
4636
|
+
var index = this.currentIndex();
|
|
4637
|
+
var pageNum = this.getPageNum(index);
|
|
4638
|
+
if ((pageNum === 0) || pageNum) {
|
|
4639
|
+
params.page = pageNum;
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4642
|
+
params.index = index;
|
|
4643
|
+
params.mode = this.mode;
|
|
4644
|
+
|
|
4645
|
+
// $$$ highlight
|
|
4646
|
+
// $$$ region
|
|
4647
|
+
|
|
4648
|
+
// search
|
|
4649
|
+
if (this.enableSearch) {
|
|
4650
|
+
params.search = this.searchTerm;
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
return params;
|
|
4654
|
+
};
|
|
4655
|
+
|
|
4656
|
+
/**
|
|
4657
|
+
* Return an object with configuration parameters from a fragment string.
|
|
4658
|
+
*
|
|
4659
|
+
* Fragments are formatted as a URL path but may be used outside of URLs as a
|
|
4660
|
+
* serialization format for BookReader parameters
|
|
4661
|
+
*
|
|
4662
|
+
* @see http://openlibrary.org/dev/docs/bookurls for fragment syntax
|
|
4663
|
+
*
|
|
4664
|
+
* @param {String} fragment initial # is allowed for backwards compatibility
|
|
4665
|
+
* but is deprecated
|
|
4666
|
+
* @return {Object}
|
|
4667
|
+
*/
|
|
4668
|
+
BookReader.prototype.paramsFromFragment = function(fragment) {
|
|
4669
|
+
var params = {};
|
|
4670
|
+
|
|
4671
|
+
// For backwards compatibility we allow an initial # character
|
|
4672
|
+
// (as from window.location.hash) but don't require it
|
|
4673
|
+
if (fragment.substr(0, 1) == '#') {
|
|
4674
|
+
fragment = fragment.substr(1);
|
|
4675
|
+
}
|
|
4676
|
+
|
|
4677
|
+
// Simple #nn syntax
|
|
4678
|
+
var oldStyleLeafNum = parseInt( /^\d+$/.exec(fragment) );
|
|
4679
|
+
if ( !isNaN(oldStyleLeafNum) ) {
|
|
4680
|
+
params.index = oldStyleLeafNum;
|
|
4681
|
+
|
|
4682
|
+
// Done processing if using old-style syntax
|
|
4683
|
+
return params;
|
|
4684
|
+
}
|
|
4685
|
+
|
|
4686
|
+
// Split into key-value pairs
|
|
4687
|
+
var urlArray = fragment.split('/');
|
|
4688
|
+
var urlHash = {};
|
|
4689
|
+
for (var i = 0; i < urlArray.length; i += 2) {
|
|
4690
|
+
urlHash[urlArray[i]] = urlArray[i+1];
|
|
4691
|
+
}
|
|
4692
|
+
|
|
4693
|
+
// Mode
|
|
4694
|
+
if ('1up' == urlHash['mode']) {
|
|
4695
|
+
params.mode = this.constMode1up;
|
|
4696
|
+
} else if ('2up' == urlHash['mode']) {
|
|
4697
|
+
params.mode = this.constMode2up;
|
|
4698
|
+
} else if ('thumb' == urlHash['mode']) {
|
|
4699
|
+
params.mode = this.constModeThumb;
|
|
4700
|
+
}
|
|
4701
|
+
|
|
4702
|
+
// Index and page
|
|
4703
|
+
if ('undefined' != typeof(urlHash['page'])) {
|
|
4704
|
+
// page was set -- may not be int
|
|
4705
|
+
params.page = urlHash['page'];
|
|
4706
|
+
}
|
|
4707
|
+
|
|
4708
|
+
// $$$ process /region
|
|
4709
|
+
// $$$ process /search
|
|
4710
|
+
|
|
4711
|
+
if (urlHash['search'] != undefined) {
|
|
4712
|
+
params.search = BookReader.util.decodeURIComponentPlus(urlHash['search']);
|
|
4713
|
+
}
|
|
4714
|
+
|
|
4715
|
+
// $$$ process /highlight
|
|
4716
|
+
|
|
4717
|
+
// $$$ process /theme
|
|
4718
|
+
if (urlHash['theme'] != undefined) {
|
|
4719
|
+
params.theme = urlHash['theme']
|
|
4720
|
+
}
|
|
4721
|
+
return params;
|
|
4722
|
+
};
|
|
4723
|
+
|
|
4724
|
+
/**
|
|
4725
|
+
* Create a fragment string from the params object.
|
|
4726
|
+
*
|
|
4727
|
+
* Fragments are formatted as a URL path but may be used outside of URLs as a
|
|
4728
|
+
* serialization format for BookReader parameters
|
|
4729
|
+
*
|
|
4730
|
+
* @see https://openlibrary.org/dev/docs/bookurls for fragment syntax
|
|
4731
|
+
*
|
|
4732
|
+
* @param {Object} params
|
|
4733
|
+
* @return {String}
|
|
4734
|
+
*/
|
|
4735
|
+
BookReader.prototype.fragmentFromParams = function(params) {
|
|
4736
|
+
var separator = '/';
|
|
4737
|
+
|
|
4738
|
+
var fragments = [];
|
|
4739
|
+
|
|
4740
|
+
if ('undefined' != typeof(params.page)) {
|
|
4741
|
+
fragments.push('page', params.page);
|
|
4742
|
+
} else {
|
|
4743
|
+
if ('undefined' != typeof(params.index)) {
|
|
4744
|
+
// Don't have page numbering but we do have the index
|
|
4745
|
+
fragments.push('page', 'n' + params.index);
|
|
4746
|
+
}
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
// $$$ highlight
|
|
4750
|
+
// $$$ region
|
|
4751
|
+
|
|
4752
|
+
// mode
|
|
4753
|
+
if ('undefined' != typeof(params.mode)) {
|
|
4754
|
+
if (params.mode == this.constMode1up) {
|
|
4755
|
+
fragments.push('mode', '1up');
|
|
4756
|
+
} else if (params.mode == this.constMode2up) {
|
|
4757
|
+
fragments.push('mode', '2up');
|
|
4758
|
+
} else if (params.mode == this.constModeThumb) {
|
|
4759
|
+
fragments.push('mode', 'thumb');
|
|
4760
|
+
} else {
|
|
4761
|
+
throw 'fragmentFromParams called with unknown mode ' + params.mode;
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
// search
|
|
4766
|
+
if (params.search) {
|
|
4767
|
+
fragments.push('search', params.search);
|
|
4768
|
+
}
|
|
4769
|
+
|
|
4770
|
+
return BookReader.util.encodeURIComponentPlus(fragments.join(separator)).replace(/%2F/g, '/');
|
|
4771
|
+
};
|
|
4772
|
+
|
|
4773
|
+
/**
|
|
4774
|
+
* Parses the pageString format
|
|
4775
|
+
* @param {string} pageNum
|
|
4776
|
+
* @return {number|undefined}
|
|
4777
|
+
*/
|
|
4778
|
+
BookReader.prototype.parsePageString = function(pageNum) {
|
|
4779
|
+
var pageIndex;
|
|
4780
|
+
// Check for special "leaf"
|
|
4781
|
+
var re = new RegExp('^leaf(\\d+)');
|
|
4782
|
+
var leafMatch = re.exec(pageNum);
|
|
4783
|
+
if (leafMatch) {
|
|
4784
|
+
pageIndex = this.leafNumToIndex(parseInt(leafMatch[1], 10));
|
|
4785
|
+
if (pageIndex === null) {
|
|
4786
|
+
pageIndex = undefined; // to match return type of getPageIndex
|
|
4787
|
+
}
|
|
4788
|
+
} else {
|
|
4789
|
+
pageIndex = this.getPageIndex(pageNum);
|
|
4790
|
+
}
|
|
4791
|
+
return pageIndex;
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
/**
|
|
4795
|
+
* Helper. Flatten the nested structure (make 1d array),
|
|
4796
|
+
* and also add pageSide prop
|
|
4797
|
+
* @return {Array}
|
|
4798
|
+
*/
|
|
4799
|
+
BookReader.prototype._getDataFlattened = function() {
|
|
4800
|
+
if (this._getDataFlattened.cached && this._getDataFlattened.cached[1] === this.data.length)
|
|
4801
|
+
return this._getDataFlattened.cached[0];
|
|
4802
|
+
|
|
4803
|
+
var flattend = [];
|
|
4804
|
+
var prevPageSide = null;
|
|
4805
|
+
for (var i = 0; i < this.data.length; i++) {
|
|
4806
|
+
for (var j = 0; j < this.data[i].length; j++) {
|
|
4807
|
+
if (prevPageSide === null) {
|
|
4808
|
+
this.data[i][j].pageSide = this.data[i].length === 2 ? 'L' : 'R';
|
|
4809
|
+
} else {
|
|
4810
|
+
this.data[i][j].pageSide = prevPageSide === 'L' ? 'R' : 'L';
|
|
4811
|
+
}
|
|
4812
|
+
prevPageSide = this.data[i][j].pageSide;
|
|
4813
|
+
flattend.push(this.data[i][j]);
|
|
4814
|
+
}
|
|
4815
|
+
}
|
|
4816
|
+
// length is used as a cache breaker
|
|
4817
|
+
this._getDataFlattened.cached = [flattend, this.data.length];
|
|
4818
|
+
return flattend;
|
|
4819
|
+
};
|
|
4820
|
+
|
|
4821
|
+
/**
|
|
4822
|
+
* Helper. Return a prop for a given index
|
|
4823
|
+
* @param {Number} index
|
|
4824
|
+
* @param {String} prop
|
|
4825
|
+
* @return {Array}
|
|
4826
|
+
*/
|
|
4827
|
+
BookReader.prototype._getDataProp = function(index, prop) {
|
|
4828
|
+
var dataf = this._getDataFlattened();
|
|
4829
|
+
if (isNaN(index) || index < 0 || index >= dataf.length)
|
|
4830
|
+
return;
|
|
4831
|
+
if ('undefined' == typeof(dataf[index][prop]))
|
|
4832
|
+
return;
|
|
4833
|
+
return dataf[index][prop];
|
|
4834
|
+
};
|
|
4835
|
+
|
|
4836
|
+
BookReader.prototype.$ = function(selector) {
|
|
4837
|
+
return this.refs.$br.find(selector);
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
//------------------------------------------------------------------------------
|
|
4841
|
+
|
|
4842
|
+
// Fix for deprecated method
|
|
4843
|
+
jQuery.curCSS = function(element, prop, val) {
|
|
4844
|
+
return jQuery(element).css(prop, val);
|
|
4845
|
+
};
|
|
4846
|
+
|
|
4847
|
+
return BookReader;
|
|
4848
|
+
|
|
4849
|
+
})(jQuery);
|