@vaadin-component-factory/vcf-pdf-viewer 1.1.1 → 1.3.0
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.
- package/package.json +1 -1
- package/pdfjs/dist/display_utils.js +714 -714
- package/pdfjs/dist/fetch_stream.js +293 -293
- package/pdfjs/dist/l10n_utils.js +122 -122
- package/pdfjs/dist/message_handler.js +524 -524
- package/pdfjs/dist/network.js +552 -552
- package/pdfjs/dist/network_utils.js +309 -309
- package/pdfjs/dist/node_stream.js +481 -481
- package/pdfjs/dist/pdf.js +11687 -11687
- package/pdfjs/dist/pdf_link_service.js +534 -534
- package/pdfjs/dist/pdf_rendering_queue.js +154 -154
- package/pdfjs/dist/pdf_thumbnail_viewer.js +738 -738
- package/pdfjs/dist/pdf_viewer.js +3207 -3207
- package/pdfjs/dist/ui_utils.js +881 -881
- package/pdfjs/dist/util.js +991 -991
- package/pdfjs/dist/worker.js +60846 -60846
- package/src/vcf-pdf-viewer.js +46 -5
package/pdfjs/dist/ui_utils.js
CHANGED
|
@@ -11,74 +11,74 @@
|
|
|
11
11
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
* See the License for the specific language governing permissions and
|
|
13
13
|
* limitations under the License.
|
|
14
|
-
*/
|
|
15
|
-
const CSS_UNITS = 96.0 / 72.0;
|
|
16
|
-
const DEFAULT_SCALE_VALUE = "auto";
|
|
17
|
-
const DEFAULT_SCALE = 1.0;
|
|
18
|
-
const MIN_SCALE = 0.1;
|
|
19
|
-
const MAX_SCALE = 10.0;
|
|
20
|
-
const UNKNOWN_SCALE = 0;
|
|
21
|
-
const MAX_AUTO_SCALE = 1.25;
|
|
22
|
-
const SCROLLBAR_PADDING = 40;
|
|
23
|
-
const VERTICAL_PADDING = 5;
|
|
24
|
-
const LOADINGBAR_END_OFFSET_VAR = "--loadingBar-end-offset";
|
|
25
|
-
const PresentationModeState = {
|
|
26
|
-
UNKNOWN: 0,
|
|
27
|
-
NORMAL: 1,
|
|
28
|
-
CHANGING: 2,
|
|
29
|
-
FULLSCREEN: 3
|
|
30
|
-
};
|
|
31
|
-
const SidebarView = {
|
|
32
|
-
UNKNOWN: -1,
|
|
33
|
-
NONE: 0,
|
|
34
|
-
THUMBS: 1,
|
|
35
|
-
// Default value.
|
|
36
|
-
OUTLINE: 2,
|
|
37
|
-
ATTACHMENTS: 3,
|
|
38
|
-
LAYERS: 4
|
|
39
|
-
};
|
|
40
|
-
const RendererType = {
|
|
41
|
-
CANVAS: "canvas",
|
|
42
|
-
SVG: "svg"
|
|
43
|
-
};
|
|
44
|
-
const TextLayerMode = {
|
|
45
|
-
DISABLE: 0,
|
|
46
|
-
ENABLE: 1,
|
|
47
|
-
ENABLE_ENHANCE: 2
|
|
48
|
-
};
|
|
49
|
-
const ScrollMode = {
|
|
50
|
-
UNKNOWN: -1,
|
|
51
|
-
VERTICAL: 0,
|
|
52
|
-
// Default value.
|
|
53
|
-
HORIZONTAL: 1,
|
|
54
|
-
WRAPPED: 2
|
|
55
|
-
};
|
|
56
|
-
const SpreadMode = {
|
|
57
|
-
UNKNOWN: -1,
|
|
58
|
-
NONE: 0,
|
|
59
|
-
// Default value.
|
|
60
|
-
ODD: 1,
|
|
61
|
-
EVEN: 2
|
|
62
|
-
}; // Used by `PDFViewerApplication`, and by the API unit-tests.
|
|
63
|
-
|
|
64
|
-
const AutoPrintRegExp = /\bprint\s*\(/;
|
|
14
|
+
*/
|
|
15
|
+
const CSS_UNITS = 96.0 / 72.0;
|
|
16
|
+
const DEFAULT_SCALE_VALUE = "auto";
|
|
17
|
+
const DEFAULT_SCALE = 1.0;
|
|
18
|
+
const MIN_SCALE = 0.1;
|
|
19
|
+
const MAX_SCALE = 10.0;
|
|
20
|
+
const UNKNOWN_SCALE = 0;
|
|
21
|
+
const MAX_AUTO_SCALE = 1.25;
|
|
22
|
+
const SCROLLBAR_PADDING = 40;
|
|
23
|
+
const VERTICAL_PADDING = 5;
|
|
24
|
+
const LOADINGBAR_END_OFFSET_VAR = "--loadingBar-end-offset";
|
|
25
|
+
const PresentationModeState = {
|
|
26
|
+
UNKNOWN: 0,
|
|
27
|
+
NORMAL: 1,
|
|
28
|
+
CHANGING: 2,
|
|
29
|
+
FULLSCREEN: 3
|
|
30
|
+
};
|
|
31
|
+
const SidebarView = {
|
|
32
|
+
UNKNOWN: -1,
|
|
33
|
+
NONE: 0,
|
|
34
|
+
THUMBS: 1,
|
|
35
|
+
// Default value.
|
|
36
|
+
OUTLINE: 2,
|
|
37
|
+
ATTACHMENTS: 3,
|
|
38
|
+
LAYERS: 4
|
|
39
|
+
};
|
|
40
|
+
const RendererType = {
|
|
41
|
+
CANVAS: "canvas",
|
|
42
|
+
SVG: "svg"
|
|
43
|
+
};
|
|
44
|
+
const TextLayerMode = {
|
|
45
|
+
DISABLE: 0,
|
|
46
|
+
ENABLE: 1,
|
|
47
|
+
ENABLE_ENHANCE: 2
|
|
48
|
+
};
|
|
49
|
+
const ScrollMode = {
|
|
50
|
+
UNKNOWN: -1,
|
|
51
|
+
VERTICAL: 0,
|
|
52
|
+
// Default value.
|
|
53
|
+
HORIZONTAL: 1,
|
|
54
|
+
WRAPPED: 2
|
|
55
|
+
};
|
|
56
|
+
const SpreadMode = {
|
|
57
|
+
UNKNOWN: -1,
|
|
58
|
+
NONE: 0,
|
|
59
|
+
// Default value.
|
|
60
|
+
ODD: 1,
|
|
61
|
+
EVEN: 2
|
|
62
|
+
}; // Used by `PDFViewerApplication`, and by the API unit-tests.
|
|
63
|
+
|
|
64
|
+
const AutoPrintRegExp = /\bprint\s*\(/;
|
|
65
65
|
/**
|
|
66
66
|
* Returns scale factor for the canvas. It makes sense for the HiDPI displays.
|
|
67
67
|
* @returns {Object} The object with horizontal (sx) and vertical (sy)
|
|
68
68
|
* scales. The scaled property is set to false if scaling is
|
|
69
69
|
* not required, true otherwise.
|
|
70
|
-
*/
|
|
71
|
-
|
|
72
|
-
function getOutputScale(ctx) {
|
|
73
|
-
const devicePixelRatio = window.devicePixelRatio || 1;
|
|
74
|
-
const backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
|
|
75
|
-
const pixelRatio = devicePixelRatio / backingStoreRatio;
|
|
76
|
-
return {
|
|
77
|
-
sx: pixelRatio,
|
|
78
|
-
sy: pixelRatio,
|
|
79
|
-
scaled: pixelRatio !== 1
|
|
80
|
-
};
|
|
81
|
-
}
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
function getOutputScale(ctx) {
|
|
73
|
+
const devicePixelRatio = window.devicePixelRatio || 1;
|
|
74
|
+
const backingStoreRatio = ctx.webkitBackingStorePixelRatio || ctx.mozBackingStorePixelRatio || ctx.backingStorePixelRatio || 1;
|
|
75
|
+
const pixelRatio = devicePixelRatio / backingStoreRatio;
|
|
76
|
+
return {
|
|
77
|
+
sx: pixelRatio,
|
|
78
|
+
sy: pixelRatio,
|
|
79
|
+
scaled: pixelRatio !== 1
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
82
|
/**
|
|
83
83
|
* Scrolls specified element into view of its parent.
|
|
84
84
|
* @param {Object} element - The element to be visible.
|
|
@@ -87,110 +87,110 @@ function getOutputScale(ctx) {
|
|
|
87
87
|
* @param {boolean} [scrollMatches] - When scrolling search results into view,
|
|
88
88
|
* ignore elements that either: Contains marked content identifiers,
|
|
89
89
|
* or have the CSS-rule `overflow: hidden;` set. The default value is `false`.
|
|
90
|
-
*/
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
function scrollIntoView(element, spot, scrollMatches = false) {
|
|
94
|
-
// Assuming offsetParent is available (it's not available when viewer is in
|
|
95
|
-
// hidden iframe or object). We have to scroll: if the offsetParent is not set
|
|
96
|
-
// producing the error. See also animationStarted.
|
|
97
|
-
let parent = element.offsetParent;
|
|
98
|
-
|
|
99
|
-
if (!parent) {
|
|
100
|
-
console.error("offsetParent is not set -- cannot scroll");
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
let offsetY = element.offsetTop + element.clientTop;
|
|
105
|
-
let offsetX = element.offsetLeft + element.clientLeft;
|
|
106
|
-
|
|
107
|
-
while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
|
|
108
|
-
offsetY += parent.offsetTop;
|
|
109
|
-
offsetX += parent.offsetLeft;
|
|
110
|
-
parent = parent.offsetParent;
|
|
111
|
-
|
|
112
|
-
if (!parent) {
|
|
113
|
-
return; // no need to scroll
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (spot) {
|
|
118
|
-
if (spot.top !== undefined) {
|
|
119
|
-
offsetY += spot.top;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (spot.left !== undefined) {
|
|
123
|
-
offsetX += spot.left;
|
|
124
|
-
parent.scrollLeft = offsetX;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
parent.scrollTop = offsetY;
|
|
129
|
-
}
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
function scrollIntoView(element, spot, scrollMatches = false) {
|
|
94
|
+
// Assuming offsetParent is available (it's not available when viewer is in
|
|
95
|
+
// hidden iframe or object). We have to scroll: if the offsetParent is not set
|
|
96
|
+
// producing the error. See also animationStarted.
|
|
97
|
+
let parent = element.offsetParent;
|
|
98
|
+
|
|
99
|
+
if (!parent) {
|
|
100
|
+
console.error("offsetParent is not set -- cannot scroll");
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
let offsetY = element.offsetTop + element.clientTop;
|
|
105
|
+
let offsetX = element.offsetLeft + element.clientLeft;
|
|
106
|
+
|
|
107
|
+
while (parent.clientHeight === parent.scrollHeight && parent.clientWidth === parent.scrollWidth || scrollMatches && (parent.classList.contains("markedContent") || getComputedStyle(parent).overflow === "hidden")) {
|
|
108
|
+
offsetY += parent.offsetTop;
|
|
109
|
+
offsetX += parent.offsetLeft;
|
|
110
|
+
parent = parent.offsetParent;
|
|
111
|
+
|
|
112
|
+
if (!parent) {
|
|
113
|
+
return; // no need to scroll
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (spot) {
|
|
118
|
+
if (spot.top !== undefined) {
|
|
119
|
+
offsetY += spot.top;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (spot.left !== undefined) {
|
|
123
|
+
offsetX += spot.left;
|
|
124
|
+
parent.scrollLeft = offsetX;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
parent.scrollTop = offsetY;
|
|
129
|
+
}
|
|
130
130
|
/**
|
|
131
131
|
* Helper function to start monitoring the scroll event and converting them into
|
|
132
132
|
* PDF.js friendly one: with scroll debounce and scroll direction.
|
|
133
|
-
*/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
function watchScroll(viewAreaElement, callback) {
|
|
137
|
-
const debounceScroll = function (evt) {
|
|
138
|
-
if (rAF) {
|
|
139
|
-
return;
|
|
140
|
-
} // schedule an invocation of scroll for next animation frame.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
|
|
144
|
-
rAF = null;
|
|
145
|
-
const currentX = viewAreaElement.scrollLeft;
|
|
146
|
-
const lastX = state.lastX;
|
|
147
|
-
|
|
148
|
-
if (currentX !== lastX) {
|
|
149
|
-
state.right = currentX > lastX;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
state.lastX = currentX;
|
|
153
|
-
const currentY = viewAreaElement.scrollTop;
|
|
154
|
-
const lastY = state.lastY;
|
|
155
|
-
|
|
156
|
-
if (currentY !== lastY) {
|
|
157
|
-
state.down = currentY > lastY;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
state.lastY = currentY;
|
|
161
|
-
callback(state);
|
|
162
|
-
});
|
|
163
|
-
};
|
|
164
|
-
|
|
165
|
-
const state = {
|
|
166
|
-
right: true,
|
|
167
|
-
down: true,
|
|
168
|
-
lastX: viewAreaElement.scrollLeft,
|
|
169
|
-
lastY: viewAreaElement.scrollTop,
|
|
170
|
-
_eventHandler: debounceScroll
|
|
171
|
-
};
|
|
172
|
-
let rAF = null;
|
|
173
|
-
viewAreaElement.addEventListener("scroll", debounceScroll, true);
|
|
174
|
-
return state;
|
|
175
|
-
}
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
function watchScroll(viewAreaElement, callback) {
|
|
137
|
+
const debounceScroll = function (evt) {
|
|
138
|
+
if (rAF) {
|
|
139
|
+
return;
|
|
140
|
+
} // schedule an invocation of scroll for next animation frame.
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
rAF = window.requestAnimationFrame(function viewAreaElementScrolled() {
|
|
144
|
+
rAF = null;
|
|
145
|
+
const currentX = viewAreaElement.scrollLeft;
|
|
146
|
+
const lastX = state.lastX;
|
|
147
|
+
|
|
148
|
+
if (currentX !== lastX) {
|
|
149
|
+
state.right = currentX > lastX;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
state.lastX = currentX;
|
|
153
|
+
const currentY = viewAreaElement.scrollTop;
|
|
154
|
+
const lastY = state.lastY;
|
|
155
|
+
|
|
156
|
+
if (currentY !== lastY) {
|
|
157
|
+
state.down = currentY > lastY;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
state.lastY = currentY;
|
|
161
|
+
callback(state);
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
const state = {
|
|
166
|
+
right: true,
|
|
167
|
+
down: true,
|
|
168
|
+
lastX: viewAreaElement.scrollLeft,
|
|
169
|
+
lastY: viewAreaElement.scrollTop,
|
|
170
|
+
_eventHandler: debounceScroll
|
|
171
|
+
};
|
|
172
|
+
let rAF = null;
|
|
173
|
+
viewAreaElement.addEventListener("scroll", debounceScroll, true);
|
|
174
|
+
return state;
|
|
175
|
+
}
|
|
176
176
|
/**
|
|
177
177
|
* Helper function to parse query string (e.g. ?param1=value&parm2=...).
|
|
178
|
-
*/
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
function parseQueryString(query) {
|
|
182
|
-
const parts = query.split("&");
|
|
183
|
-
const params = Object.create(null);
|
|
184
|
-
|
|
185
|
-
for (let i = 0, ii = parts.length; i < ii; ++i) {
|
|
186
|
-
const param = parts[i].split("=");
|
|
187
|
-
const key = param[0].toLowerCase();
|
|
188
|
-
const value = param.length > 1 ? param[1] : null;
|
|
189
|
-
params[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return params;
|
|
193
|
-
}
|
|
178
|
+
*/
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
function parseQueryString(query) {
|
|
182
|
+
const parts = query.split("&");
|
|
183
|
+
const params = Object.create(null);
|
|
184
|
+
|
|
185
|
+
for (let i = 0, ii = parts.length; i < ii; ++i) {
|
|
186
|
+
const param = parts[i].split("=");
|
|
187
|
+
const key = param[0].toLowerCase();
|
|
188
|
+
const value = param.length > 1 ? param[1] : null;
|
|
189
|
+
params[decodeURIComponent(key)] = decodeURIComponent(value);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return params;
|
|
193
|
+
}
|
|
194
194
|
/**
|
|
195
195
|
* Use binary search to find the index of the first item in a given array which
|
|
196
196
|
* passes a given condition. The items are expected to be sorted in the sense
|
|
@@ -199,123 +199,123 @@ function parseQueryString(query) {
|
|
|
199
199
|
*
|
|
200
200
|
* @returns {number} Index of the first array element to pass the test,
|
|
201
201
|
* or |items.length| if no such element exists.
|
|
202
|
-
*/
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
function binarySearchFirstItem(items, condition) {
|
|
206
|
-
let minIndex = 0;
|
|
207
|
-
let maxIndex = items.length - 1;
|
|
208
|
-
|
|
209
|
-
if (maxIndex < 0 || !condition(items[maxIndex])) {
|
|
210
|
-
return items.length;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
if (condition(items[minIndex])) {
|
|
214
|
-
return minIndex;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
while (minIndex < maxIndex) {
|
|
218
|
-
const currentIndex = minIndex + maxIndex >> 1;
|
|
219
|
-
const currentItem = items[currentIndex];
|
|
220
|
-
|
|
221
|
-
if (condition(currentItem)) {
|
|
222
|
-
maxIndex = currentIndex;
|
|
223
|
-
} else {
|
|
224
|
-
minIndex = currentIndex + 1;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return minIndex;
|
|
229
|
-
/* === maxIndex */
|
|
230
|
-
}
|
|
202
|
+
*/
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
function binarySearchFirstItem(items, condition) {
|
|
206
|
+
let minIndex = 0;
|
|
207
|
+
let maxIndex = items.length - 1;
|
|
208
|
+
|
|
209
|
+
if (maxIndex < 0 || !condition(items[maxIndex])) {
|
|
210
|
+
return items.length;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (condition(items[minIndex])) {
|
|
214
|
+
return minIndex;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
while (minIndex < maxIndex) {
|
|
218
|
+
const currentIndex = minIndex + maxIndex >> 1;
|
|
219
|
+
const currentItem = items[currentIndex];
|
|
220
|
+
|
|
221
|
+
if (condition(currentItem)) {
|
|
222
|
+
maxIndex = currentIndex;
|
|
223
|
+
} else {
|
|
224
|
+
minIndex = currentIndex + 1;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return minIndex;
|
|
229
|
+
/* === maxIndex */
|
|
230
|
+
}
|
|
231
231
|
/**
|
|
232
232
|
* Approximates float number as a fraction using Farey sequence (max order
|
|
233
233
|
* of 8).
|
|
234
234
|
* @param {number} x - Positive float number.
|
|
235
235
|
* @returns {Array} Estimated fraction: the first array item is a numerator,
|
|
236
236
|
* the second one is a denominator.
|
|
237
|
-
*/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
function approximateFraction(x) {
|
|
241
|
-
// Fast paths for int numbers or their inversions.
|
|
242
|
-
if (Math.floor(x) === x) {
|
|
243
|
-
return [x, 1];
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
const xinv = 1 / x;
|
|
247
|
-
const limit = 8;
|
|
248
|
-
|
|
249
|
-
if (xinv > limit) {
|
|
250
|
-
return [1, limit];
|
|
251
|
-
} else if (Math.floor(xinv) === xinv) {
|
|
252
|
-
return [1, xinv];
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
const x_ = x > 1 ? xinv : x; // a/b and c/d are neighbours in Farey sequence.
|
|
256
|
-
|
|
257
|
-
let a = 0,
|
|
258
|
-
b = 1,
|
|
259
|
-
c = 1,
|
|
260
|
-
d = 1; // Limiting search to order 8.
|
|
261
|
-
|
|
262
|
-
while (true) {
|
|
263
|
-
// Generating next term in sequence (order of q).
|
|
264
|
-
const p = a + c,
|
|
265
|
-
q = b + d;
|
|
266
|
-
|
|
267
|
-
if (q > limit) {
|
|
268
|
-
break;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
if (x_ <= p / q) {
|
|
272
|
-
c = p;
|
|
273
|
-
d = q;
|
|
274
|
-
} else {
|
|
275
|
-
a = p;
|
|
276
|
-
b = q;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
let result; // Select closest of the neighbours to x.
|
|
281
|
-
|
|
282
|
-
if (x_ - a / b < c / d - x_) {
|
|
283
|
-
result = x_ === x ? [a, b] : [b, a];
|
|
284
|
-
} else {
|
|
285
|
-
result = x_ === x ? [c, d] : [d, c];
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
return result;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function roundToDivide(x, div) {
|
|
292
|
-
const r = x % div;
|
|
293
|
-
return r === 0 ? x : Math.round(x - r + div);
|
|
294
|
-
}
|
|
237
|
+
*/
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
function approximateFraction(x) {
|
|
241
|
+
// Fast paths for int numbers or their inversions.
|
|
242
|
+
if (Math.floor(x) === x) {
|
|
243
|
+
return [x, 1];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const xinv = 1 / x;
|
|
247
|
+
const limit = 8;
|
|
248
|
+
|
|
249
|
+
if (xinv > limit) {
|
|
250
|
+
return [1, limit];
|
|
251
|
+
} else if (Math.floor(xinv) === xinv) {
|
|
252
|
+
return [1, xinv];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const x_ = x > 1 ? xinv : x; // a/b and c/d are neighbours in Farey sequence.
|
|
256
|
+
|
|
257
|
+
let a = 0,
|
|
258
|
+
b = 1,
|
|
259
|
+
c = 1,
|
|
260
|
+
d = 1; // Limiting search to order 8.
|
|
261
|
+
|
|
262
|
+
while (true) {
|
|
263
|
+
// Generating next term in sequence (order of q).
|
|
264
|
+
const p = a + c,
|
|
265
|
+
q = b + d;
|
|
266
|
+
|
|
267
|
+
if (q > limit) {
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (x_ <= p / q) {
|
|
272
|
+
c = p;
|
|
273
|
+
d = q;
|
|
274
|
+
} else {
|
|
275
|
+
a = p;
|
|
276
|
+
b = q;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
let result; // Select closest of the neighbours to x.
|
|
281
|
+
|
|
282
|
+
if (x_ - a / b < c / d - x_) {
|
|
283
|
+
result = x_ === x ? [a, b] : [b, a];
|
|
284
|
+
} else {
|
|
285
|
+
result = x_ === x ? [c, d] : [d, c];
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
return result;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function roundToDivide(x, div) {
|
|
292
|
+
const r = x % div;
|
|
293
|
+
return r === 0 ? x : Math.round(x - r + div);
|
|
294
|
+
}
|
|
295
295
|
/**
|
|
296
296
|
* Gets the size of the specified page, converted from PDF units to inches.
|
|
297
297
|
* @param {Object} An Object containing the properties: {Array} `view`,
|
|
298
298
|
* {number} `userUnit`, and {number} `rotate`.
|
|
299
299
|
* @returns {Object} An Object containing the properties: {number} `width`
|
|
300
300
|
* and {number} `height`, given in inches.
|
|
301
|
-
*/
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
function getPageSizeInches({
|
|
305
|
-
view,
|
|
306
|
-
userUnit,
|
|
307
|
-
rotate
|
|
308
|
-
}) {
|
|
309
|
-
const [x1, y1, x2, y2] = view; // We need to take the page rotation into account as well.
|
|
310
|
-
|
|
311
|
-
const changeOrientation = rotate % 180 !== 0;
|
|
312
|
-
const width = (x2 - x1) / 72 * userUnit;
|
|
313
|
-
const height = (y2 - y1) / 72 * userUnit;
|
|
314
|
-
return {
|
|
315
|
-
width: changeOrientation ? height : width,
|
|
316
|
-
height: changeOrientation ? width : height
|
|
317
|
-
};
|
|
318
|
-
}
|
|
301
|
+
*/
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
function getPageSizeInches({
|
|
305
|
+
view,
|
|
306
|
+
userUnit,
|
|
307
|
+
rotate
|
|
308
|
+
}) {
|
|
309
|
+
const [x1, y1, x2, y2] = view; // We need to take the page rotation into account as well.
|
|
310
|
+
|
|
311
|
+
const changeOrientation = rotate % 180 !== 0;
|
|
312
|
+
const width = (x2 - x1) / 72 * userUnit;
|
|
313
|
+
const height = (y2 - y1) / 72 * userUnit;
|
|
314
|
+
return {
|
|
315
|
+
width: changeOrientation ? height : width,
|
|
316
|
+
height: changeOrientation ? width : height
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
319
|
/**
|
|
320
320
|
* Helper function for getVisibleElements.
|
|
321
321
|
*
|
|
@@ -326,83 +326,83 @@ function getPageSizeInches({
|
|
|
326
326
|
* before the first visible element in `views`, but not by too much. (Usually,
|
|
327
327
|
* this will be the first element in the first partially visible row in
|
|
328
328
|
* `views`, although sometimes it goes back one row further.)
|
|
329
|
-
*/
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
function backtrackBeforeAllVisibleElements(index, views, top) {
|
|
333
|
-
// binarySearchFirstItem's assumption is that the input is ordered, with only
|
|
334
|
-
// one index where the conditions flips from false to true: [false ...,
|
|
335
|
-
// true...]. With vertical scrolling and spreads, it is possible to have
|
|
336
|
-
// [false ..., true, false, true ...]. With wrapped scrolling we can have a
|
|
337
|
-
// similar sequence, with many more mixed true and false in the middle.
|
|
338
|
-
//
|
|
339
|
-
// So there is no guarantee that the binary search yields the index of the
|
|
340
|
-
// first visible element. It could have been any of the other visible elements
|
|
341
|
-
// that were preceded by a hidden element.
|
|
342
|
-
// Of course, if either this element or the previous (hidden) element is also
|
|
343
|
-
// the first element, there's nothing to worry about.
|
|
344
|
-
if (index < 2) {
|
|
345
|
-
return index;
|
|
346
|
-
} // That aside, the possible cases are represented below.
|
|
347
|
-
//
|
|
348
|
-
// **** = fully hidden
|
|
349
|
-
// A*B* = mix of partially visible and/or hidden pages
|
|
350
|
-
// CDEF = fully visible
|
|
351
|
-
//
|
|
352
|
-
// (1) Binary search could have returned A, in which case we can stop.
|
|
353
|
-
// (2) Binary search could also have returned B, in which case we need to
|
|
354
|
-
// check the whole row.
|
|
355
|
-
// (3) Binary search could also have returned C, in which case we need to
|
|
356
|
-
// check the whole previous row.
|
|
357
|
-
//
|
|
358
|
-
// There's one other possibility:
|
|
359
|
-
//
|
|
360
|
-
// **** = fully hidden
|
|
361
|
-
// ABCD = mix of fully and/or partially visible pages
|
|
362
|
-
//
|
|
363
|
-
// (4) Binary search could only have returned A.
|
|
364
|
-
// Initially assume that we need to find the beginning of the current row
|
|
365
|
-
// (case 1, 2, or 4), which means finding a page that is above the current
|
|
366
|
-
// page's top. If the found page is partially visible, we're definitely not in
|
|
367
|
-
// case 3, and this assumption is correct.
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
let elt = views[index].div;
|
|
371
|
-
let pageTop = elt.offsetTop + elt.clientTop;
|
|
372
|
-
|
|
373
|
-
if (pageTop >= top) {
|
|
374
|
-
// The found page is fully visible, so we're actually either in case 3 or 4,
|
|
375
|
-
// and unfortunately we can't tell the difference between them without
|
|
376
|
-
// scanning the entire previous row, so we just conservatively assume that
|
|
377
|
-
// we do need to backtrack to that row. In both cases, the previous page is
|
|
378
|
-
// in the previous row, so use its top instead.
|
|
379
|
-
elt = views[index - 1].div;
|
|
380
|
-
pageTop = elt.offsetTop + elt.clientTop;
|
|
381
|
-
} // Now we backtrack to the first page that still has its bottom below
|
|
382
|
-
// `pageTop`, which is the top of a page in the first visible row (unless
|
|
383
|
-
// we're in case 4, in which case it's the row before that).
|
|
384
|
-
// `index` is found by binary search, so the page at `index - 1` is
|
|
385
|
-
// invisible and we can start looking for potentially visible pages from
|
|
386
|
-
// `index - 2`. (However, if this loop terminates on its first iteration,
|
|
387
|
-
// which is the case when pages are stacked vertically, `index` should remain
|
|
388
|
-
// unchanged, so we use a distinct loop variable.)
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
for (let i = index - 2; i >= 0; --i) {
|
|
392
|
-
elt = views[i].div;
|
|
393
|
-
|
|
394
|
-
if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) {
|
|
395
|
-
// We have reached the previous row, so stop now.
|
|
396
|
-
// This loop is expected to terminate relatively quickly because the
|
|
397
|
-
// number of pages per row is expected to be small.
|
|
398
|
-
break;
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
index = i;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return index;
|
|
405
|
-
}
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
function backtrackBeforeAllVisibleElements(index, views, top) {
|
|
333
|
+
// binarySearchFirstItem's assumption is that the input is ordered, with only
|
|
334
|
+
// one index where the conditions flips from false to true: [false ...,
|
|
335
|
+
// true...]. With vertical scrolling and spreads, it is possible to have
|
|
336
|
+
// [false ..., true, false, true ...]. With wrapped scrolling we can have a
|
|
337
|
+
// similar sequence, with many more mixed true and false in the middle.
|
|
338
|
+
//
|
|
339
|
+
// So there is no guarantee that the binary search yields the index of the
|
|
340
|
+
// first visible element. It could have been any of the other visible elements
|
|
341
|
+
// that were preceded by a hidden element.
|
|
342
|
+
// Of course, if either this element or the previous (hidden) element is also
|
|
343
|
+
// the first element, there's nothing to worry about.
|
|
344
|
+
if (index < 2) {
|
|
345
|
+
return index;
|
|
346
|
+
} // That aside, the possible cases are represented below.
|
|
347
|
+
//
|
|
348
|
+
// **** = fully hidden
|
|
349
|
+
// A*B* = mix of partially visible and/or hidden pages
|
|
350
|
+
// CDEF = fully visible
|
|
351
|
+
//
|
|
352
|
+
// (1) Binary search could have returned A, in which case we can stop.
|
|
353
|
+
// (2) Binary search could also have returned B, in which case we need to
|
|
354
|
+
// check the whole row.
|
|
355
|
+
// (3) Binary search could also have returned C, in which case we need to
|
|
356
|
+
// check the whole previous row.
|
|
357
|
+
//
|
|
358
|
+
// There's one other possibility:
|
|
359
|
+
//
|
|
360
|
+
// **** = fully hidden
|
|
361
|
+
// ABCD = mix of fully and/or partially visible pages
|
|
362
|
+
//
|
|
363
|
+
// (4) Binary search could only have returned A.
|
|
364
|
+
// Initially assume that we need to find the beginning of the current row
|
|
365
|
+
// (case 1, 2, or 4), which means finding a page that is above the current
|
|
366
|
+
// page's top. If the found page is partially visible, we're definitely not in
|
|
367
|
+
// case 3, and this assumption is correct.
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
let elt = views[index].div;
|
|
371
|
+
let pageTop = elt.offsetTop + elt.clientTop;
|
|
372
|
+
|
|
373
|
+
if (pageTop >= top) {
|
|
374
|
+
// The found page is fully visible, so we're actually either in case 3 or 4,
|
|
375
|
+
// and unfortunately we can't tell the difference between them without
|
|
376
|
+
// scanning the entire previous row, so we just conservatively assume that
|
|
377
|
+
// we do need to backtrack to that row. In both cases, the previous page is
|
|
378
|
+
// in the previous row, so use its top instead.
|
|
379
|
+
elt = views[index - 1].div;
|
|
380
|
+
pageTop = elt.offsetTop + elt.clientTop;
|
|
381
|
+
} // Now we backtrack to the first page that still has its bottom below
|
|
382
|
+
// `pageTop`, which is the top of a page in the first visible row (unless
|
|
383
|
+
// we're in case 4, in which case it's the row before that).
|
|
384
|
+
// `index` is found by binary search, so the page at `index - 1` is
|
|
385
|
+
// invisible and we can start looking for potentially visible pages from
|
|
386
|
+
// `index - 2`. (However, if this loop terminates on its first iteration,
|
|
387
|
+
// which is the case when pages are stacked vertically, `index` should remain
|
|
388
|
+
// unchanged, so we use a distinct loop variable.)
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
for (let i = index - 2; i >= 0; --i) {
|
|
392
|
+
elt = views[i].div;
|
|
393
|
+
|
|
394
|
+
if (elt.offsetTop + elt.clientTop + elt.clientHeight <= pageTop) {
|
|
395
|
+
// We have reached the previous row, so stop now.
|
|
396
|
+
// This loop is expected to terminate relatively quickly because the
|
|
397
|
+
// number of pages per row is expected to be small.
|
|
398
|
+
break;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
index = i;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return index;
|
|
405
|
+
}
|
|
406
406
|
/**
|
|
407
407
|
* @typedef {Object} GetVisibleElementsParameters
|
|
408
408
|
* @property {HTMLElement} scrollEl - A container that can possibly scroll.
|
|
@@ -416,8 +416,8 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
|
|
|
416
416
|
* laid out horizontally instead of vertically. The default value is `false`.
|
|
417
417
|
* @property {boolean} rtl - If `true`, the `scrollEl` container is assumed to
|
|
418
418
|
* be in right-to-left mode. The default value is `false`.
|
|
419
|
-
*/
|
|
420
|
-
|
|
419
|
+
*/
|
|
420
|
+
|
|
421
421
|
/**
|
|
422
422
|
* Generic helper to find out what elements are visible within a scroll pane.
|
|
423
423
|
*
|
|
@@ -437,186 +437,186 @@ function backtrackBeforeAllVisibleElements(index, views, top) {
|
|
|
437
437
|
*
|
|
438
438
|
* @param {GetVisibleElementsParameters}
|
|
439
439
|
* @returns {Object} `{ first, last, views: [{ id, x, y, view, percent }] }`
|
|
440
|
-
*/
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
function getVisibleElements({
|
|
444
|
-
scrollEl,
|
|
445
|
-
views,
|
|
446
|
-
sortByVisibility = false,
|
|
447
|
-
horizontal = false,
|
|
448
|
-
rtl = false
|
|
449
|
-
}) {
|
|
450
|
-
const top = scrollEl.scrollTop,
|
|
451
|
-
bottom = top + scrollEl.clientHeight;
|
|
452
|
-
const left = scrollEl.scrollLeft,
|
|
453
|
-
right = left + scrollEl.clientWidth; // Throughout this "generic" function, comments will assume we're working with
|
|
454
|
-
// PDF document pages, which is the most important and complex case. In this
|
|
455
|
-
// case, the visible elements we're actually interested is the page canvas,
|
|
456
|
-
// which is contained in a wrapper which adds no padding/border/margin, which
|
|
457
|
-
// is itself contained in `view.div` which adds no padding (but does add a
|
|
458
|
-
// border). So, as specified in this function's doc comment, this function
|
|
459
|
-
// does all of its work on the padding edge of the provided views, starting at
|
|
460
|
-
// offsetLeft/Top (which includes margin) and adding clientLeft/Top (which is
|
|
461
|
-
// the border). Adding clientWidth/Height gets us the bottom-right corner of
|
|
462
|
-
// the padding edge.
|
|
463
|
-
|
|
464
|
-
function isElementBottomAfterViewTop(view) {
|
|
465
|
-
const element = view.div;
|
|
466
|
-
const elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
|
|
467
|
-
return elementBottom > top;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function isElementNextAfterViewHorizontally(view) {
|
|
471
|
-
const element = view.div;
|
|
472
|
-
const elementLeft = element.offsetLeft + element.clientLeft;
|
|
473
|
-
const elementRight = elementLeft + element.clientWidth;
|
|
474
|
-
return rtl ? elementLeft < right : elementRight > left;
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
const visible = [],
|
|
478
|
-
numViews = views.length;
|
|
479
|
-
let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop); // Please note the return value of the `binarySearchFirstItem` function when
|
|
480
|
-
// no valid element is found (hence the `firstVisibleElementInd` check below).
|
|
481
|
-
|
|
482
|
-
if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) {
|
|
483
|
-
// In wrapped scrolling (or vertical scrolling with spreads), with some page
|
|
484
|
-
// sizes, isElementBottomAfterViewTop doesn't satisfy the binary search
|
|
485
|
-
// condition: there can be pages with bottoms above the view top between
|
|
486
|
-
// pages with bottoms below. This function detects and corrects that error;
|
|
487
|
-
// see it for more comments.
|
|
488
|
-
firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
|
|
489
|
-
} // lastEdge acts as a cutoff for us to stop looping, because we know all
|
|
490
|
-
// subsequent pages will be hidden.
|
|
491
|
-
//
|
|
492
|
-
// When using wrapped scrolling or vertical scrolling with spreads, we can't
|
|
493
|
-
// simply stop the first time we reach a page below the bottom of the view;
|
|
494
|
-
// the tops of subsequent pages on the same row could still be visible. In
|
|
495
|
-
// horizontal scrolling, we don't have that issue, so we can stop as soon as
|
|
496
|
-
// we pass `right`, without needing the code below that handles the -1 case.
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
let lastEdge = horizontal ? right : -1;
|
|
500
|
-
|
|
501
|
-
for (let i = firstVisibleElementInd; i < numViews; i++) {
|
|
502
|
-
const view = views[i],
|
|
503
|
-
element = view.div;
|
|
504
|
-
const currentWidth = element.offsetLeft + element.clientLeft;
|
|
505
|
-
const currentHeight = element.offsetTop + element.clientTop;
|
|
506
|
-
const viewWidth = element.clientWidth,
|
|
507
|
-
viewHeight = element.clientHeight;
|
|
508
|
-
const viewRight = currentWidth + viewWidth;
|
|
509
|
-
const viewBottom = currentHeight + viewHeight;
|
|
510
|
-
|
|
511
|
-
if (lastEdge === -1) {
|
|
512
|
-
// As commented above, this is only needed in non-horizontal cases.
|
|
513
|
-
// Setting lastEdge to the bottom of the first page that is partially
|
|
514
|
-
// visible ensures that the next page fully below lastEdge is on the
|
|
515
|
-
// next row, which has to be fully hidden along with all subsequent rows.
|
|
516
|
-
if (viewBottom >= bottom) {
|
|
517
|
-
lastEdge = viewBottom;
|
|
518
|
-
}
|
|
519
|
-
} else if ((horizontal ? currentWidth : currentHeight) > lastEdge) {
|
|
520
|
-
break;
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) {
|
|
524
|
-
continue;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
|
|
528
|
-
const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
|
|
529
|
-
const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
|
|
530
|
-
fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
|
|
531
|
-
const percent = fractionHeight * fractionWidth * 100 | 0;
|
|
532
|
-
visible.push({
|
|
533
|
-
id: view.id,
|
|
534
|
-
x: currentWidth,
|
|
535
|
-
y: currentHeight,
|
|
536
|
-
view,
|
|
537
|
-
percent,
|
|
538
|
-
widthPercent: fractionWidth * 100 | 0
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
const first = visible[0],
|
|
543
|
-
last = visible[visible.length - 1];
|
|
544
|
-
|
|
545
|
-
if (sortByVisibility) {
|
|
546
|
-
visible.sort(function (a, b) {
|
|
547
|
-
const pc = a.percent - b.percent;
|
|
548
|
-
|
|
549
|
-
if (Math.abs(pc) > 0.001) {
|
|
550
|
-
return -pc;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
return a.id - b.id; // ensure stability
|
|
554
|
-
});
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
return {
|
|
558
|
-
first,
|
|
559
|
-
last,
|
|
560
|
-
views: visible
|
|
561
|
-
};
|
|
562
|
-
}
|
|
440
|
+
*/
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
function getVisibleElements({
|
|
444
|
+
scrollEl,
|
|
445
|
+
views,
|
|
446
|
+
sortByVisibility = false,
|
|
447
|
+
horizontal = false,
|
|
448
|
+
rtl = false
|
|
449
|
+
}) {
|
|
450
|
+
const top = scrollEl.scrollTop,
|
|
451
|
+
bottom = top + scrollEl.clientHeight;
|
|
452
|
+
const left = scrollEl.scrollLeft,
|
|
453
|
+
right = left + scrollEl.clientWidth; // Throughout this "generic" function, comments will assume we're working with
|
|
454
|
+
// PDF document pages, which is the most important and complex case. In this
|
|
455
|
+
// case, the visible elements we're actually interested is the page canvas,
|
|
456
|
+
// which is contained in a wrapper which adds no padding/border/margin, which
|
|
457
|
+
// is itself contained in `view.div` which adds no padding (but does add a
|
|
458
|
+
// border). So, as specified in this function's doc comment, this function
|
|
459
|
+
// does all of its work on the padding edge of the provided views, starting at
|
|
460
|
+
// offsetLeft/Top (which includes margin) and adding clientLeft/Top (which is
|
|
461
|
+
// the border). Adding clientWidth/Height gets us the bottom-right corner of
|
|
462
|
+
// the padding edge.
|
|
463
|
+
|
|
464
|
+
function isElementBottomAfterViewTop(view) {
|
|
465
|
+
const element = view.div;
|
|
466
|
+
const elementBottom = element.offsetTop + element.clientTop + element.clientHeight;
|
|
467
|
+
return elementBottom > top;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function isElementNextAfterViewHorizontally(view) {
|
|
471
|
+
const element = view.div;
|
|
472
|
+
const elementLeft = element.offsetLeft + element.clientLeft;
|
|
473
|
+
const elementRight = elementLeft + element.clientWidth;
|
|
474
|
+
return rtl ? elementLeft < right : elementRight > left;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const visible = [],
|
|
478
|
+
numViews = views.length;
|
|
479
|
+
let firstVisibleElementInd = binarySearchFirstItem(views, horizontal ? isElementNextAfterViewHorizontally : isElementBottomAfterViewTop); // Please note the return value of the `binarySearchFirstItem` function when
|
|
480
|
+
// no valid element is found (hence the `firstVisibleElementInd` check below).
|
|
481
|
+
|
|
482
|
+
if (firstVisibleElementInd > 0 && firstVisibleElementInd < numViews && !horizontal) {
|
|
483
|
+
// In wrapped scrolling (or vertical scrolling with spreads), with some page
|
|
484
|
+
// sizes, isElementBottomAfterViewTop doesn't satisfy the binary search
|
|
485
|
+
// condition: there can be pages with bottoms above the view top between
|
|
486
|
+
// pages with bottoms below. This function detects and corrects that error;
|
|
487
|
+
// see it for more comments.
|
|
488
|
+
firstVisibleElementInd = backtrackBeforeAllVisibleElements(firstVisibleElementInd, views, top);
|
|
489
|
+
} // lastEdge acts as a cutoff for us to stop looping, because we know all
|
|
490
|
+
// subsequent pages will be hidden.
|
|
491
|
+
//
|
|
492
|
+
// When using wrapped scrolling or vertical scrolling with spreads, we can't
|
|
493
|
+
// simply stop the first time we reach a page below the bottom of the view;
|
|
494
|
+
// the tops of subsequent pages on the same row could still be visible. In
|
|
495
|
+
// horizontal scrolling, we don't have that issue, so we can stop as soon as
|
|
496
|
+
// we pass `right`, without needing the code below that handles the -1 case.
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
let lastEdge = horizontal ? right : -1;
|
|
500
|
+
|
|
501
|
+
for (let i = firstVisibleElementInd; i < numViews; i++) {
|
|
502
|
+
const view = views[i],
|
|
503
|
+
element = view.div;
|
|
504
|
+
const currentWidth = element.offsetLeft + element.clientLeft;
|
|
505
|
+
const currentHeight = element.offsetTop + element.clientTop;
|
|
506
|
+
const viewWidth = element.clientWidth,
|
|
507
|
+
viewHeight = element.clientHeight;
|
|
508
|
+
const viewRight = currentWidth + viewWidth;
|
|
509
|
+
const viewBottom = currentHeight + viewHeight;
|
|
510
|
+
|
|
511
|
+
if (lastEdge === -1) {
|
|
512
|
+
// As commented above, this is only needed in non-horizontal cases.
|
|
513
|
+
// Setting lastEdge to the bottom of the first page that is partially
|
|
514
|
+
// visible ensures that the next page fully below lastEdge is on the
|
|
515
|
+
// next row, which has to be fully hidden along with all subsequent rows.
|
|
516
|
+
if (viewBottom >= bottom) {
|
|
517
|
+
lastEdge = viewBottom;
|
|
518
|
+
}
|
|
519
|
+
} else if ((horizontal ? currentWidth : currentHeight) > lastEdge) {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
if (viewBottom <= top || currentHeight >= bottom || viewRight <= left || currentWidth >= right) {
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
const hiddenHeight = Math.max(0, top - currentHeight) + Math.max(0, viewBottom - bottom);
|
|
528
|
+
const hiddenWidth = Math.max(0, left - currentWidth) + Math.max(0, viewRight - right);
|
|
529
|
+
const fractionHeight = (viewHeight - hiddenHeight) / viewHeight,
|
|
530
|
+
fractionWidth = (viewWidth - hiddenWidth) / viewWidth;
|
|
531
|
+
const percent = fractionHeight * fractionWidth * 100 | 0;
|
|
532
|
+
visible.push({
|
|
533
|
+
id: view.id,
|
|
534
|
+
x: currentWidth,
|
|
535
|
+
y: currentHeight,
|
|
536
|
+
view,
|
|
537
|
+
percent,
|
|
538
|
+
widthPercent: fractionWidth * 100 | 0
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const first = visible[0],
|
|
543
|
+
last = visible[visible.length - 1];
|
|
544
|
+
|
|
545
|
+
if (sortByVisibility) {
|
|
546
|
+
visible.sort(function (a, b) {
|
|
547
|
+
const pc = a.percent - b.percent;
|
|
548
|
+
|
|
549
|
+
if (Math.abs(pc) > 0.001) {
|
|
550
|
+
return -pc;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
return a.id - b.id; // ensure stability
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
return {
|
|
558
|
+
first,
|
|
559
|
+
last,
|
|
560
|
+
views: visible
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
563
|
/**
|
|
564
564
|
* Event handler to suppress context menu.
|
|
565
|
-
*/
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
function noContextMenuHandler(evt) {
|
|
569
|
-
evt.preventDefault();
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function normalizeWheelEventDirection(evt) {
|
|
573
|
-
let delta = Math.hypot(evt.deltaX, evt.deltaY);
|
|
574
|
-
const angle = Math.atan2(evt.deltaY, evt.deltaX);
|
|
575
|
-
|
|
576
|
-
if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
|
|
577
|
-
// All that is left-up oriented has to change the sign.
|
|
578
|
-
delta = -delta;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
return delta;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
function normalizeWheelEventDelta(evt) {
|
|
585
|
-
let delta = normalizeWheelEventDirection(evt);
|
|
586
|
-
const MOUSE_DOM_DELTA_PIXEL_MODE = 0;
|
|
587
|
-
const MOUSE_DOM_DELTA_LINE_MODE = 1;
|
|
588
|
-
const MOUSE_PIXELS_PER_LINE = 30;
|
|
589
|
-
const MOUSE_LINES_PER_PAGE = 30; // Converts delta to per-page units
|
|
590
|
-
|
|
591
|
-
if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) {
|
|
592
|
-
delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
|
|
593
|
-
} else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) {
|
|
594
|
-
delta /= MOUSE_LINES_PER_PAGE;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
return delta;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
function isValidRotation(angle) {
|
|
601
|
-
return Number.isInteger(angle) && angle % 90 === 0;
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function isValidScrollMode(mode) {
|
|
605
|
-
return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function isValidSpreadMode(mode) {
|
|
609
|
-
return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function isPortraitOrientation(size) {
|
|
613
|
-
return size.width <= size.height;
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
const WaitOnType = {
|
|
617
|
-
EVENT: "event",
|
|
618
|
-
TIMEOUT: "timeout"
|
|
619
|
-
};
|
|
565
|
+
*/
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
function noContextMenuHandler(evt) {
|
|
569
|
+
evt.preventDefault();
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
function normalizeWheelEventDirection(evt) {
|
|
573
|
+
let delta = Math.hypot(evt.deltaX, evt.deltaY);
|
|
574
|
+
const angle = Math.atan2(evt.deltaY, evt.deltaX);
|
|
575
|
+
|
|
576
|
+
if (-0.25 * Math.PI < angle && angle < 0.75 * Math.PI) {
|
|
577
|
+
// All that is left-up oriented has to change the sign.
|
|
578
|
+
delta = -delta;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
return delta;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
function normalizeWheelEventDelta(evt) {
|
|
585
|
+
let delta = normalizeWheelEventDirection(evt);
|
|
586
|
+
const MOUSE_DOM_DELTA_PIXEL_MODE = 0;
|
|
587
|
+
const MOUSE_DOM_DELTA_LINE_MODE = 1;
|
|
588
|
+
const MOUSE_PIXELS_PER_LINE = 30;
|
|
589
|
+
const MOUSE_LINES_PER_PAGE = 30; // Converts delta to per-page units
|
|
590
|
+
|
|
591
|
+
if (evt.deltaMode === MOUSE_DOM_DELTA_PIXEL_MODE) {
|
|
592
|
+
delta /= MOUSE_PIXELS_PER_LINE * MOUSE_LINES_PER_PAGE;
|
|
593
|
+
} else if (evt.deltaMode === MOUSE_DOM_DELTA_LINE_MODE) {
|
|
594
|
+
delta /= MOUSE_LINES_PER_PAGE;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
return delta;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function isValidRotation(angle) {
|
|
601
|
+
return Number.isInteger(angle) && angle % 90 === 0;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
function isValidScrollMode(mode) {
|
|
605
|
+
return Number.isInteger(mode) && Object.values(ScrollMode).includes(mode) && mode !== ScrollMode.UNKNOWN;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
function isValidSpreadMode(mode) {
|
|
609
|
+
return Number.isInteger(mode) && Object.values(SpreadMode).includes(mode) && mode !== SpreadMode.UNKNOWN;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
function isPortraitOrientation(size) {
|
|
613
|
+
return size.width <= size.height;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
const WaitOnType = {
|
|
617
|
+
EVENT: "event",
|
|
618
|
+
TIMEOUT: "timeout"
|
|
619
|
+
};
|
|
620
620
|
/**
|
|
621
621
|
* @typedef {Object} WaitOnEventOrTimeoutParameters
|
|
622
622
|
* @property {Object} target - The event target, can for example be:
|
|
@@ -624,8 +624,8 @@ const WaitOnType = {
|
|
|
624
624
|
* @property {string} name - The name of the event.
|
|
625
625
|
* @property {number} delay - The delay, in milliseconds, after which the
|
|
626
626
|
* timeout occurs (if the event wasn't already dispatched).
|
|
627
|
-
*/
|
|
628
|
-
|
|
627
|
+
*/
|
|
628
|
+
|
|
629
629
|
/**
|
|
630
630
|
* Allows waiting for an event or a timeout, whichever occurs first.
|
|
631
631
|
* Can be used to ensure that an action always occurs, even when an event
|
|
@@ -633,322 +633,322 @@ const WaitOnType = {
|
|
|
633
633
|
*
|
|
634
634
|
* @param {WaitOnEventOrTimeoutParameters}
|
|
635
635
|
* @returns {Promise} A promise that is resolved with a {WaitOnType} value.
|
|
636
|
-
*/
|
|
637
|
-
|
|
638
|
-
function waitOnEventOrTimeout({
|
|
639
|
-
target,
|
|
640
|
-
name,
|
|
641
|
-
delay = 0
|
|
642
|
-
}) {
|
|
643
|
-
return new Promise(function (resolve, reject) {
|
|
644
|
-
if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
|
|
645
|
-
throw new Error("waitOnEventOrTimeout - invalid parameters.");
|
|
646
|
-
}
|
|
647
|
-
|
|
648
|
-
function handler(type) {
|
|
649
|
-
if (target instanceof EventBus) {
|
|
650
|
-
target._off(name, eventHandler);
|
|
651
|
-
} else {
|
|
652
|
-
target.removeEventListener(name, eventHandler);
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
if (timeout) {
|
|
656
|
-
clearTimeout(timeout);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
resolve(type);
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
const eventHandler = handler.bind(null, WaitOnType.EVENT);
|
|
663
|
-
|
|
664
|
-
if (target instanceof EventBus) {
|
|
665
|
-
target._on(name, eventHandler);
|
|
666
|
-
} else {
|
|
667
|
-
target.addEventListener(name, eventHandler);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
|
|
671
|
-
const timeout = setTimeout(timeoutHandler, delay);
|
|
672
|
-
});
|
|
673
|
-
}
|
|
636
|
+
*/
|
|
637
|
+
|
|
638
|
+
function waitOnEventOrTimeout({
|
|
639
|
+
target,
|
|
640
|
+
name,
|
|
641
|
+
delay = 0
|
|
642
|
+
}) {
|
|
643
|
+
return new Promise(function (resolve, reject) {
|
|
644
|
+
if (typeof target !== "object" || !(name && typeof name === "string") || !(Number.isInteger(delay) && delay >= 0)) {
|
|
645
|
+
throw new Error("waitOnEventOrTimeout - invalid parameters.");
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function handler(type) {
|
|
649
|
+
if (target instanceof EventBus) {
|
|
650
|
+
target._off(name, eventHandler);
|
|
651
|
+
} else {
|
|
652
|
+
target.removeEventListener(name, eventHandler);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
if (timeout) {
|
|
656
|
+
clearTimeout(timeout);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
resolve(type);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
const eventHandler = handler.bind(null, WaitOnType.EVENT);
|
|
663
|
+
|
|
664
|
+
if (target instanceof EventBus) {
|
|
665
|
+
target._on(name, eventHandler);
|
|
666
|
+
} else {
|
|
667
|
+
target.addEventListener(name, eventHandler);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
|
|
671
|
+
const timeout = setTimeout(timeoutHandler, delay);
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
674
|
/**
|
|
675
675
|
* Promise that is resolved when DOM window becomes visible.
|
|
676
|
-
*/
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const animationStarted = new Promise(function (resolve) {
|
|
680
|
-
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") && typeof window === "undefined") {
|
|
681
|
-
// Prevent "ReferenceError: window is not defined" errors when running the
|
|
682
|
-
// unit-tests in Node.js environments.
|
|
683
|
-
setTimeout(resolve, 20);
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
window.requestAnimationFrame(resolve);
|
|
688
|
-
});
|
|
676
|
+
*/
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
const animationStarted = new Promise(function (resolve) {
|
|
680
|
+
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("LIB") && typeof window === "undefined") {
|
|
681
|
+
// Prevent "ReferenceError: window is not defined" errors when running the
|
|
682
|
+
// unit-tests in Node.js environments.
|
|
683
|
+
setTimeout(resolve, 20);
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
window.requestAnimationFrame(resolve);
|
|
688
|
+
});
|
|
689
689
|
/**
|
|
690
690
|
* NOTE: Only used to support various PDF viewer tests in `mozilla-central`.
|
|
691
|
-
*/
|
|
692
|
-
|
|
693
|
-
function dispatchDOMEvent(eventName, args = null) {
|
|
694
|
-
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("MOZCENTRAL")) {
|
|
695
|
-
throw new Error("Not implemented: dispatchDOMEvent");
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
const details = Object.create(null);
|
|
699
|
-
|
|
700
|
-
if ((args === null || args === void 0 ? void 0 : args.length) > 0) {
|
|
701
|
-
const obj = args[0];
|
|
702
|
-
|
|
703
|
-
for (const key in obj) {
|
|
704
|
-
const value = obj[key];
|
|
705
|
-
|
|
706
|
-
if (key === "source") {
|
|
707
|
-
if (value === window || value === document) {
|
|
708
|
-
return; // No need to re-dispatch (already) global events.
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
continue; // Ignore the `source` property.
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
details[key] = value;
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const event = document.createEvent("CustomEvent");
|
|
719
|
-
event.initCustomEvent(eventName, true, true, details);
|
|
720
|
-
document.dispatchEvent(event);
|
|
721
|
-
}
|
|
691
|
+
*/
|
|
692
|
+
|
|
693
|
+
function dispatchDOMEvent(eventName, args = null) {
|
|
694
|
+
if (typeof PDFJSDev !== "undefined" && !PDFJSDev.test("MOZCENTRAL")) {
|
|
695
|
+
throw new Error("Not implemented: dispatchDOMEvent");
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
const details = Object.create(null);
|
|
699
|
+
|
|
700
|
+
if ((args === null || args === void 0 ? void 0 : args.length) > 0) {
|
|
701
|
+
const obj = args[0];
|
|
702
|
+
|
|
703
|
+
for (const key in obj) {
|
|
704
|
+
const value = obj[key];
|
|
705
|
+
|
|
706
|
+
if (key === "source") {
|
|
707
|
+
if (value === window || value === document) {
|
|
708
|
+
return; // No need to re-dispatch (already) global events.
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
continue; // Ignore the `source` property.
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
details[key] = value;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
const event = document.createEvent("CustomEvent");
|
|
719
|
+
event.initCustomEvent(eventName, true, true, details);
|
|
720
|
+
document.dispatchEvent(event);
|
|
721
|
+
}
|
|
722
722
|
/**
|
|
723
723
|
* Simple event bus for an application. Listeners are attached using the `on`
|
|
724
724
|
* and `off` methods. To raise an event, the `dispatch` method shall be used.
|
|
725
|
-
*/
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
class EventBus {
|
|
729
|
-
constructor(options) {
|
|
730
|
-
this._listeners = Object.create(null);
|
|
731
|
-
|
|
732
|
-
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) {
|
|
733
|
-
this._isInAutomation = (options === null || options === void 0 ? void 0 : options.isInAutomation) === true;
|
|
734
|
-
}
|
|
735
|
-
}
|
|
725
|
+
*/
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
class EventBus {
|
|
729
|
+
constructor(options) {
|
|
730
|
+
this._listeners = Object.create(null);
|
|
731
|
+
|
|
732
|
+
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) {
|
|
733
|
+
this._isInAutomation = (options === null || options === void 0 ? void 0 : options.isInAutomation) === true;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
736
|
/**
|
|
737
737
|
* @param {string} eventName
|
|
738
738
|
* @param {function} listener
|
|
739
739
|
* @param {Object} [options]
|
|
740
|
-
*/
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
on(eventName, listener, options = null) {
|
|
744
|
-
this._on(eventName, listener, {
|
|
745
|
-
external: true,
|
|
746
|
-
once: options === null || options === void 0 ? void 0 : options.once
|
|
747
|
-
});
|
|
748
|
-
}
|
|
740
|
+
*/
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
on(eventName, listener, options = null) {
|
|
744
|
+
this._on(eventName, listener, {
|
|
745
|
+
external: true,
|
|
746
|
+
once: options === null || options === void 0 ? void 0 : options.once
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
749
|
/**
|
|
750
750
|
* @param {string} eventName
|
|
751
751
|
* @param {function} listener
|
|
752
752
|
* @param {Object} [options]
|
|
753
|
-
*/
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
off(eventName, listener, options = null) {
|
|
757
|
-
this._off(eventName, listener, {
|
|
758
|
-
external: true,
|
|
759
|
-
once: options === null || options === void 0 ? void 0 : options.once
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
dispatch(eventName) {
|
|
764
|
-
const eventListeners = this._listeners[eventName];
|
|
765
|
-
|
|
766
|
-
if (!eventListeners || eventListeners.length === 0) {
|
|
767
|
-
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
|
|
768
|
-
const args = Array.prototype.slice.call(arguments, 1);
|
|
769
|
-
dispatchDOMEvent(eventName, args);
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
return;
|
|
773
|
-
} // Passing all arguments after the eventName to the listeners.
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const args = Array.prototype.slice.call(arguments, 1);
|
|
777
|
-
let externalListeners; // Making copy of the listeners array in case if it will be modified
|
|
778
|
-
// during dispatch.
|
|
779
|
-
|
|
780
|
-
for (const {
|
|
781
|
-
listener,
|
|
782
|
-
external,
|
|
783
|
-
once
|
|
784
|
-
} of eventListeners.slice(0)) {
|
|
785
|
-
if (once) {
|
|
786
|
-
this._off(eventName, listener);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (external) {
|
|
790
|
-
(externalListeners || (externalListeners = [])).push(listener);
|
|
791
|
-
continue;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
listener.apply(null, args);
|
|
795
|
-
} // Dispatch any "external" listeners *after* the internal ones, to give the
|
|
796
|
-
// viewer components time to handle events and update their state first.
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
if (externalListeners) {
|
|
800
|
-
for (const listener of externalListeners) {
|
|
801
|
-
listener.apply(null, args);
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
externalListeners = null;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
|
|
808
|
-
dispatchDOMEvent(eventName, args);
|
|
809
|
-
}
|
|
810
|
-
}
|
|
753
|
+
*/
|
|
754
|
+
|
|
755
|
+
|
|
756
|
+
off(eventName, listener, options = null) {
|
|
757
|
+
this._off(eventName, listener, {
|
|
758
|
+
external: true,
|
|
759
|
+
once: options === null || options === void 0 ? void 0 : options.once
|
|
760
|
+
});
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
dispatch(eventName) {
|
|
764
|
+
const eventListeners = this._listeners[eventName];
|
|
765
|
+
|
|
766
|
+
if (!eventListeners || eventListeners.length === 0) {
|
|
767
|
+
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
|
|
768
|
+
const args = Array.prototype.slice.call(arguments, 1);
|
|
769
|
+
dispatchDOMEvent(eventName, args);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
return;
|
|
773
|
+
} // Passing all arguments after the eventName to the listeners.
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
const args = Array.prototype.slice.call(arguments, 1);
|
|
777
|
+
let externalListeners; // Making copy of the listeners array in case if it will be modified
|
|
778
|
+
// during dispatch.
|
|
779
|
+
|
|
780
|
+
for (const {
|
|
781
|
+
listener,
|
|
782
|
+
external,
|
|
783
|
+
once
|
|
784
|
+
} of eventListeners.slice(0)) {
|
|
785
|
+
if (once) {
|
|
786
|
+
this._off(eventName, listener);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (external) {
|
|
790
|
+
(externalListeners || (externalListeners = [])).push(listener);
|
|
791
|
+
continue;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
listener.apply(null, args);
|
|
795
|
+
} // Dispatch any "external" listeners *after* the internal ones, to give the
|
|
796
|
+
// viewer components time to handle events and update their state first.
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
if (externalListeners) {
|
|
800
|
+
for (const listener of externalListeners) {
|
|
801
|
+
listener.apply(null, args);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
externalListeners = null;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
if ((typeof PDFJSDev === "undefined" || PDFJSDev.test("MOZCENTRAL")) && this._isInAutomation) {
|
|
808
|
+
dispatchDOMEvent(eventName, args);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
811
|
/**
|
|
812
812
|
* @ignore
|
|
813
|
-
*/
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
_on(eventName, listener, options = null) {
|
|
817
|
-
var _this$_listeners;
|
|
818
|
-
|
|
819
|
-
const eventListeners = (_this$_listeners = this._listeners)[eventName] || (_this$_listeners[eventName] = []);
|
|
820
|
-
eventListeners.push({
|
|
821
|
-
listener,
|
|
822
|
-
external: (options === null || options === void 0 ? void 0 : options.external) === true,
|
|
823
|
-
once: (options === null || options === void 0 ? void 0 : options.once) === true
|
|
824
|
-
});
|
|
825
|
-
}
|
|
813
|
+
*/
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
_on(eventName, listener, options = null) {
|
|
817
|
+
var _this$_listeners;
|
|
818
|
+
|
|
819
|
+
const eventListeners = (_this$_listeners = this._listeners)[eventName] || (_this$_listeners[eventName] = []);
|
|
820
|
+
eventListeners.push({
|
|
821
|
+
listener,
|
|
822
|
+
external: (options === null || options === void 0 ? void 0 : options.external) === true,
|
|
823
|
+
once: (options === null || options === void 0 ? void 0 : options.once) === true
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
826
|
/**
|
|
827
827
|
* @ignore
|
|
828
|
-
*/
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
_off(eventName, listener, options = null) {
|
|
832
|
-
const eventListeners = this._listeners[eventName];
|
|
833
|
-
|
|
834
|
-
if (!eventListeners) {
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
for (let i = 0, ii = eventListeners.length; i < ii; i++) {
|
|
839
|
-
if (eventListeners[i].listener === listener) {
|
|
840
|
-
eventListeners.splice(i, 1);
|
|
841
|
-
return;
|
|
842
|
-
}
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
function clamp(v, min, max) {
|
|
849
|
-
return Math.min(Math.max(v, min), max);
|
|
850
|
-
}
|
|
851
|
-
|
|
852
|
-
class ProgressBar {
|
|
853
|
-
constructor(id, {
|
|
854
|
-
height,
|
|
855
|
-
width,
|
|
856
|
-
units
|
|
857
|
-
} = {}) {
|
|
858
|
-
this.visible = true; // Fetch the sub-elements for later.
|
|
859
|
-
|
|
860
|
-
this.div = document.querySelector(id + " .progress"); // Get the loading bar element, so it can be resized to fit the viewer.
|
|
861
|
-
|
|
862
|
-
this.bar = this.div.parentNode; // Get options, with sensible defaults.
|
|
863
|
-
|
|
864
|
-
this.height = height || 100;
|
|
865
|
-
this.width = width || 100;
|
|
866
|
-
this.units = units || "%"; // Initialize heights.
|
|
867
|
-
|
|
868
|
-
this.div.style.height = this.height + this.units;
|
|
869
|
-
this.percent = 0;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
_updateBar() {
|
|
873
|
-
if (this._indeterminate) {
|
|
874
|
-
this.div.classList.add("indeterminate");
|
|
875
|
-
this.div.style.width = this.width + this.units;
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
this.div.classList.remove("indeterminate");
|
|
880
|
-
const progressSize = this.width * this._percent / 100;
|
|
881
|
-
this.div.style.width = progressSize + this.units;
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
get percent() {
|
|
885
|
-
return this._percent;
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
set percent(val) {
|
|
889
|
-
this._indeterminate = isNaN(val);
|
|
890
|
-
this._percent = clamp(val, 0, 100);
|
|
891
|
-
|
|
892
|
-
this._updateBar();
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
setWidth(viewer) {
|
|
896
|
-
if (!viewer) {
|
|
897
|
-
return;
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
const container = viewer.parentNode;
|
|
901
|
-
const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
|
|
902
|
-
|
|
903
|
-
if (scrollbarWidth > 0) {
|
|
904
|
-
const doc = document.documentElement;
|
|
905
|
-
doc.style.setProperty(LOADINGBAR_END_OFFSET_VAR, `${scrollbarWidth}px`);
|
|
906
|
-
}
|
|
907
|
-
}
|
|
908
|
-
|
|
909
|
-
hide() {
|
|
910
|
-
if (!this.visible) {
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
this.visible = false;
|
|
915
|
-
this.bar.classList.add("hidden");
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
show() {
|
|
919
|
-
if (this.visible) {
|
|
920
|
-
return;
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
this.visible = true;
|
|
924
|
-
this.bar.classList.remove("hidden");
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
}
|
|
828
|
+
*/
|
|
829
|
+
|
|
830
|
+
|
|
831
|
+
_off(eventName, listener, options = null) {
|
|
832
|
+
const eventListeners = this._listeners[eventName];
|
|
833
|
+
|
|
834
|
+
if (!eventListeners) {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
for (let i = 0, ii = eventListeners.length; i < ii; i++) {
|
|
839
|
+
if (eventListeners[i].listener === listener) {
|
|
840
|
+
eventListeners.splice(i, 1);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function clamp(v, min, max) {
|
|
849
|
+
return Math.min(Math.max(v, min), max);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
class ProgressBar {
|
|
853
|
+
constructor(id, {
|
|
854
|
+
height,
|
|
855
|
+
width,
|
|
856
|
+
units
|
|
857
|
+
} = {}) {
|
|
858
|
+
this.visible = true; // Fetch the sub-elements for later.
|
|
859
|
+
|
|
860
|
+
this.div = document.querySelector(id + " .progress"); // Get the loading bar element, so it can be resized to fit the viewer.
|
|
861
|
+
|
|
862
|
+
this.bar = this.div.parentNode; // Get options, with sensible defaults.
|
|
863
|
+
|
|
864
|
+
this.height = height || 100;
|
|
865
|
+
this.width = width || 100;
|
|
866
|
+
this.units = units || "%"; // Initialize heights.
|
|
867
|
+
|
|
868
|
+
this.div.style.height = this.height + this.units;
|
|
869
|
+
this.percent = 0;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
_updateBar() {
|
|
873
|
+
if (this._indeterminate) {
|
|
874
|
+
this.div.classList.add("indeterminate");
|
|
875
|
+
this.div.style.width = this.width + this.units;
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
this.div.classList.remove("indeterminate");
|
|
880
|
+
const progressSize = this.width * this._percent / 100;
|
|
881
|
+
this.div.style.width = progressSize + this.units;
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
get percent() {
|
|
885
|
+
return this._percent;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
set percent(val) {
|
|
889
|
+
this._indeterminate = isNaN(val);
|
|
890
|
+
this._percent = clamp(val, 0, 100);
|
|
891
|
+
|
|
892
|
+
this._updateBar();
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
setWidth(viewer) {
|
|
896
|
+
if (!viewer) {
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
const container = viewer.parentNode;
|
|
901
|
+
const scrollbarWidth = container.offsetWidth - viewer.offsetWidth;
|
|
902
|
+
|
|
903
|
+
if (scrollbarWidth > 0) {
|
|
904
|
+
const doc = document.documentElement;
|
|
905
|
+
doc.style.setProperty(LOADINGBAR_END_OFFSET_VAR, `${scrollbarWidth}px`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
hide() {
|
|
910
|
+
if (!this.visible) {
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
this.visible = false;
|
|
915
|
+
this.bar.classList.add("hidden");
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
show() {
|
|
919
|
+
if (this.visible) {
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
this.visible = true;
|
|
924
|
+
this.bar.classList.remove("hidden");
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
}
|
|
928
928
|
/**
|
|
929
929
|
* Moves all elements of an array that satisfy condition to the end of the
|
|
930
930
|
* array, preserving the order of the rest.
|
|
931
|
-
*/
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
function moveToEndOfArray(arr, condition) {
|
|
935
|
-
const moved = [],
|
|
936
|
-
len = arr.length;
|
|
937
|
-
let write = 0;
|
|
938
|
-
|
|
939
|
-
for (let read = 0; read < len; ++read) {
|
|
940
|
-
if (condition(arr[read])) {
|
|
941
|
-
moved.push(arr[read]);
|
|
942
|
-
} else {
|
|
943
|
-
arr[write] = arr[read];
|
|
944
|
-
++write;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
|
|
948
|
-
for (let read = 0; write < len; ++read, ++write) {
|
|
949
|
-
arr[write] = moved[read];
|
|
950
|
-
}
|
|
951
|
-
}
|
|
931
|
+
*/
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
function moveToEndOfArray(arr, condition) {
|
|
935
|
+
const moved = [],
|
|
936
|
+
len = arr.length;
|
|
937
|
+
let write = 0;
|
|
938
|
+
|
|
939
|
+
for (let read = 0; read < len; ++read) {
|
|
940
|
+
if (condition(arr[read])) {
|
|
941
|
+
moved.push(arr[read]);
|
|
942
|
+
} else {
|
|
943
|
+
arr[write] = arr[read];
|
|
944
|
+
++write;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
for (let read = 0; write < len; ++read, ++write) {
|
|
949
|
+
arr[write] = moved[read];
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
952
|
/**
|
|
953
953
|
* Get the active or focused element in current DOM.
|
|
954
954
|
*
|
|
@@ -956,22 +956,22 @@ function moveToEndOfArray(arr, condition) {
|
|
|
956
956
|
* shadow DOMs.
|
|
957
957
|
*
|
|
958
958
|
* @returns {Element} the truly active or focused element.
|
|
959
|
-
*/
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
function getActiveOrFocusedElement() {
|
|
963
|
-
let curRoot = document;
|
|
964
|
-
let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
|
|
965
|
-
|
|
966
|
-
while ((_curActiveOrFocused = curActiveOrFocused) !== null && _curActiveOrFocused !== void 0 && _curActiveOrFocused.shadowRoot) {
|
|
967
|
-
var _curActiveOrFocused;
|
|
968
|
-
|
|
969
|
-
curRoot = curActiveOrFocused.shadowRoot;
|
|
970
|
-
curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
|
|
971
|
-
}
|
|
972
|
-
|
|
973
|
-
return curActiveOrFocused;
|
|
974
|
-
}
|
|
959
|
+
*/
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
function getActiveOrFocusedElement() {
|
|
963
|
+
let curRoot = document;
|
|
964
|
+
let curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
|
|
965
|
+
|
|
966
|
+
while ((_curActiveOrFocused = curActiveOrFocused) !== null && _curActiveOrFocused !== void 0 && _curActiveOrFocused.shadowRoot) {
|
|
967
|
+
var _curActiveOrFocused;
|
|
968
|
+
|
|
969
|
+
curRoot = curActiveOrFocused.shadowRoot;
|
|
970
|
+
curActiveOrFocused = curRoot.activeElement || curRoot.querySelector(":focus");
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
return curActiveOrFocused;
|
|
974
|
+
}
|
|
975
975
|
/**
|
|
976
976
|
* Converts API PageLayout values to the format used by `BaseViewer`.
|
|
977
977
|
* NOTE: This is supported to the extent that the viewer implements the
|
|
@@ -979,26 +979,26 @@ function getActiveOrFocusedElement() {
|
|
|
979
979
|
* and TwoPageRight all suggests using non-continuous scrolling).
|
|
980
980
|
* @param {string} mode - The API PageLayout value.
|
|
981
981
|
* @returns {number} A value from {SpreadMode}.
|
|
982
|
-
*/
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
function apiPageLayoutToSpreadMode(layout) {
|
|
986
|
-
switch (layout) {
|
|
987
|
-
case "SinglePage":
|
|
988
|
-
case "OneColumn":
|
|
989
|
-
return SpreadMode.NONE;
|
|
990
|
-
|
|
991
|
-
case "TwoColumnLeft":
|
|
992
|
-
case "TwoPageLeft":
|
|
993
|
-
return SpreadMode.ODD;
|
|
994
|
-
|
|
995
|
-
case "TwoColumnRight":
|
|
996
|
-
case "TwoPageRight":
|
|
997
|
-
return SpreadMode.EVEN;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
return SpreadMode.NONE; // Default value.
|
|
1001
|
-
}
|
|
982
|
+
*/
|
|
983
|
+
|
|
984
|
+
|
|
985
|
+
function apiPageLayoutToSpreadMode(layout) {
|
|
986
|
+
switch (layout) {
|
|
987
|
+
case "SinglePage":
|
|
988
|
+
case "OneColumn":
|
|
989
|
+
return SpreadMode.NONE;
|
|
990
|
+
|
|
991
|
+
case "TwoColumnLeft":
|
|
992
|
+
case "TwoPageLeft":
|
|
993
|
+
return SpreadMode.ODD;
|
|
994
|
+
|
|
995
|
+
case "TwoColumnRight":
|
|
996
|
+
case "TwoPageRight":
|
|
997
|
+
return SpreadMode.EVEN;
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
return SpreadMode.NONE; // Default value.
|
|
1001
|
+
}
|
|
1002
1002
|
/**
|
|
1003
1003
|
* Converts API PageMode values to the format used by `PDFSidebar`.
|
|
1004
1004
|
* NOTE: There's also a "FullScreen" parameter which is not possible to support,
|
|
@@ -1006,28 +1006,28 @@ function apiPageLayoutToSpreadMode(layout) {
|
|
|
1006
1006
|
* fullscreen mode only occurs as a result of a user-initiated event.
|
|
1007
1007
|
* @param {string} mode - The API PageMode value.
|
|
1008
1008
|
* @returns {number} A value from {SidebarView}.
|
|
1009
|
-
*/
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
function apiPageModeToSidebarView(mode) {
|
|
1013
|
-
switch (mode) {
|
|
1014
|
-
case "UseNone":
|
|
1015
|
-
return SidebarView.NONE;
|
|
1016
|
-
|
|
1017
|
-
case "UseThumbs":
|
|
1018
|
-
return SidebarView.THUMBS;
|
|
1019
|
-
|
|
1020
|
-
case "UseOutlines":
|
|
1021
|
-
return SidebarView.OUTLINE;
|
|
1022
|
-
|
|
1023
|
-
case "UseAttachments":
|
|
1024
|
-
return SidebarView.ATTACHMENTS;
|
|
1025
|
-
|
|
1026
|
-
case "UseOC":
|
|
1027
|
-
return SidebarView.LAYERS;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
return SidebarView.NONE; // Default value.
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
export { AutoPrintRegExp, CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, EventBus, MAX_AUTO_SCALE, MAX_SCALE, MIN_SCALE, PresentationModeState, ProgressBar, RendererType, SCROLLBAR_PADDING, ScrollMode, SidebarView, SpreadMode, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING, WaitOnType, animationStarted, apiPageLayoutToSpreadMode, apiPageModeToSidebarView, approximateFraction, backtrackBeforeAllVisibleElements, binarySearchFirstItem, getActiveOrFocusedElement, getOutputScale, getPageSizeInches, getVisibleElements, isPortraitOrientation, isValidRotation, isValidScrollMode, isValidSpreadMode, moveToEndOfArray, noContextMenuHandler, normalizeWheelEventDelta, normalizeWheelEventDirection, parseQueryString, roundToDivide, scrollIntoView, waitOnEventOrTimeout, watchScroll };
|
|
1009
|
+
*/
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
function apiPageModeToSidebarView(mode) {
|
|
1013
|
+
switch (mode) {
|
|
1014
|
+
case "UseNone":
|
|
1015
|
+
return SidebarView.NONE;
|
|
1016
|
+
|
|
1017
|
+
case "UseThumbs":
|
|
1018
|
+
return SidebarView.THUMBS;
|
|
1019
|
+
|
|
1020
|
+
case "UseOutlines":
|
|
1021
|
+
return SidebarView.OUTLINE;
|
|
1022
|
+
|
|
1023
|
+
case "UseAttachments":
|
|
1024
|
+
return SidebarView.ATTACHMENTS;
|
|
1025
|
+
|
|
1026
|
+
case "UseOC":
|
|
1027
|
+
return SidebarView.LAYERS;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
return SidebarView.NONE; // Default value.
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
export { AutoPrintRegExp, CSS_UNITS, DEFAULT_SCALE, DEFAULT_SCALE_VALUE, EventBus, MAX_AUTO_SCALE, MAX_SCALE, MIN_SCALE, PresentationModeState, ProgressBar, RendererType, SCROLLBAR_PADDING, ScrollMode, SidebarView, SpreadMode, TextLayerMode, UNKNOWN_SCALE, VERTICAL_PADDING, WaitOnType, animationStarted, apiPageLayoutToSpreadMode, apiPageModeToSidebarView, approximateFraction, backtrackBeforeAllVisibleElements, binarySearchFirstItem, getActiveOrFocusedElement, getOutputScale, getPageSizeInches, getVisibleElements, isPortraitOrientation, isValidRotation, isValidScrollMode, isValidSpreadMode, moveToEndOfArray, noContextMenuHandler, normalizeWheelEventDelta, normalizeWheelEventDirection, parseQueryString, roundToDivide, scrollIntoView, waitOnEventOrTimeout, watchScroll };
|