monocle-rails 0.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.
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/monocle/rails.rb +8 -0
- data/lib/monocle/rails/version.rb +5 -0
- data/monocle-rails.gemspec +23 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/compat/browser.js +120 -0
- data/vendor/assets/javascripts/compat/css.js +145 -0
- data/vendor/assets/javascripts/compat/env.js +463 -0
- data/vendor/assets/javascripts/compat/gala.js +469 -0
- data/vendor/assets/javascripts/compat/stubs.js +50 -0
- data/vendor/assets/javascripts/controls/contents.js +59 -0
- data/vendor/assets/javascripts/controls/magnifier.js +51 -0
- data/vendor/assets/javascripts/controls/panel.js +136 -0
- data/vendor/assets/javascripts/controls/placesaver.js +100 -0
- data/vendor/assets/javascripts/controls/scrubber.js +140 -0
- data/vendor/assets/javascripts/controls/spinner.js +99 -0
- data/vendor/assets/javascripts/controls/stencil.js +410 -0
- data/vendor/assets/javascripts/core/billboard.js +120 -0
- data/vendor/assets/javascripts/core/book.js +467 -0
- data/vendor/assets/javascripts/core/bookdata.js +59 -0
- data/vendor/assets/javascripts/core/component.js +413 -0
- data/vendor/assets/javascripts/core/events.js +56 -0
- data/vendor/assets/javascripts/core/factory.js +194 -0
- data/vendor/assets/javascripts/core/formatting.js +317 -0
- data/vendor/assets/javascripts/core/monocle.js +16 -0
- data/vendor/assets/javascripts/core/place.js +210 -0
- data/vendor/assets/javascripts/core/reader.js +683 -0
- data/vendor/assets/javascripts/core/selection.js +158 -0
- data/vendor/assets/javascripts/core/styles.js +155 -0
- data/vendor/assets/javascripts/dimensions/columns.js +218 -0
- data/vendor/assets/javascripts/flippers/instant.js +78 -0
- data/vendor/assets/javascripts/flippers/scroller.js +128 -0
- data/vendor/assets/javascripts/flippers/slider.js +469 -0
- data/vendor/assets/javascripts/monocore.js +27 -0
- data/vendor/assets/javascripts/monoctrl.js +1 -0
- data/vendor/assets/javascripts/panels/eink.js +61 -0
- data/vendor/assets/javascripts/panels/imode.js +180 -0
- data/vendor/assets/javascripts/panels/magic.js +297 -0
- data/vendor/assets/javascripts/panels/marginal.js +50 -0
- data/vendor/assets/javascripts/panels/twopane.js +34 -0
- data/vendor/assets/stylesheets/monocore.css +194 -0
- data/vendor/assets/stylesheets/monoctrl.css +168 -0
- metadata +129 -0
@@ -0,0 +1,59 @@
|
|
1
|
+
// A shortcut for creating a bookdata object from a 'data' hash.
|
2
|
+
//
|
3
|
+
// eg:
|
4
|
+
//
|
5
|
+
// Monocle.bookData({ components: ['intro.html', 'ch1.html', 'ch2.html'] });
|
6
|
+
//
|
7
|
+
// All keys in the 'data' hash are optional:
|
8
|
+
//
|
9
|
+
// components: must be an array of component urls
|
10
|
+
// chapters: must be an array of nested chapters (the usual bookdata structure)
|
11
|
+
// metadata: must be a hash of key/values
|
12
|
+
// getComponentFn: override the default way to fetch components via id
|
13
|
+
//
|
14
|
+
Monocle.bookData = function (data) {
|
15
|
+
return {
|
16
|
+
getComponents: function () {
|
17
|
+
return data.components || ['anonymous'];
|
18
|
+
},
|
19
|
+
getContents: function () {
|
20
|
+
return data.chapters || [];
|
21
|
+
},
|
22
|
+
getComponent: data.getComponent || function (id) {
|
23
|
+
return { url: id }
|
24
|
+
},
|
25
|
+
getMetaData: function (key) {
|
26
|
+
return (data.metadata || {})[key];
|
27
|
+
},
|
28
|
+
data: data
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
32
|
+
|
33
|
+
// A shortcut for creating a bookdata object from an array of element ids.
|
34
|
+
//
|
35
|
+
// eg:
|
36
|
+
//
|
37
|
+
// Monocle.bookDataFromIds(['part1', 'part2']);
|
38
|
+
//
|
39
|
+
Monocle.bookDataFromIds = function (elementIds) {
|
40
|
+
return Monocle.bookData({
|
41
|
+
components: elementIds,
|
42
|
+
getComponent: function (cmptId) {
|
43
|
+
return { nodes: [document.getElementById(cmptId)] }
|
44
|
+
}
|
45
|
+
});
|
46
|
+
}
|
47
|
+
|
48
|
+
|
49
|
+
// A shortcut for creating a bookdata object from an array of nodes.
|
50
|
+
//
|
51
|
+
// eg:
|
52
|
+
//
|
53
|
+
// Monocle.bookDataFromNodes([document.getElementById('content')]);
|
54
|
+
//
|
55
|
+
Monocle.bookDataFromNodes = function (nodes) {
|
56
|
+
return Monocle.bookData({
|
57
|
+
getComponent: function (n) { return { 'nodes': nodes }; }
|
58
|
+
});
|
59
|
+
}
|
@@ -0,0 +1,413 @@
|
|
1
|
+
/* COMPONENT */
|
2
|
+
|
3
|
+
// See the properties declaration for details of constructor arguments.
|
4
|
+
//
|
5
|
+
Monocle.Component = function (book, id, index, chapters, source) {
|
6
|
+
|
7
|
+
var API = { constructor: Monocle.Component }
|
8
|
+
var k = API.constants = API.constructor;
|
9
|
+
var p = API.properties = {
|
10
|
+
// a back-reference to the public API of the book that owns this component
|
11
|
+
book: book,
|
12
|
+
|
13
|
+
// the string that represents this component in the book's component array
|
14
|
+
id: id,
|
15
|
+
|
16
|
+
// the position in the book's components array of this component
|
17
|
+
index: index,
|
18
|
+
|
19
|
+
// The chapters argument is an array of objects that list the chapters that
|
20
|
+
// can be found in this component. A chapter object is defined as:
|
21
|
+
//
|
22
|
+
// {
|
23
|
+
// title: str,
|
24
|
+
// fragment: str, // optional anchor id
|
25
|
+
// percent: n // how far into the component the chapter begins
|
26
|
+
// }
|
27
|
+
//
|
28
|
+
// NOTE: the percent property is calculated by the component - you only need
|
29
|
+
// to pass in the title and the optional id string.
|
30
|
+
//
|
31
|
+
chapters: chapters,
|
32
|
+
|
33
|
+
// the frame provided by dataSource.getComponent() for this component
|
34
|
+
source: source
|
35
|
+
}
|
36
|
+
|
37
|
+
|
38
|
+
// Makes this component the active component for the pageDiv. There are
|
39
|
+
// several strategies for this (see loadFrame).
|
40
|
+
//
|
41
|
+
// When the component has been loaded into the pageDiv's frame, the callback
|
42
|
+
// will be invoked with the pageDiv and this component as arguments.
|
43
|
+
//
|
44
|
+
function applyTo(pageDiv, callback) {
|
45
|
+
prepareSource(pageDiv.m.reader);
|
46
|
+
|
47
|
+
var evtData = { 'page': pageDiv, 'source': p.source };
|
48
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);
|
49
|
+
|
50
|
+
var onLoaded = function () {
|
51
|
+
setupFrame(
|
52
|
+
pageDiv,
|
53
|
+
pageDiv.m.activeFrame,
|
54
|
+
function () { callback(pageDiv, API) }
|
55
|
+
);
|
56
|
+
}
|
57
|
+
|
58
|
+
Monocle.defer(function () { loadFrame(pageDiv, onLoaded); });
|
59
|
+
}
|
60
|
+
|
61
|
+
|
62
|
+
// Loads this component into the given frame, using one of the following
|
63
|
+
// strategies:
|
64
|
+
//
|
65
|
+
// * HTML - a HTML string
|
66
|
+
// * URL - a URL string
|
67
|
+
// * Nodes - an array of DOM body nodes (NB: no way to populate head)
|
68
|
+
// * Document - a DOM DocumentElement object
|
69
|
+
//
|
70
|
+
function loadFrame(pageDiv, callback) {
|
71
|
+
var frame = pageDiv.m.activeFrame;
|
72
|
+
|
73
|
+
// We own this frame now.
|
74
|
+
frame.m.component = API;
|
75
|
+
|
76
|
+
// Hide the frame while we're changing it.
|
77
|
+
frame.style.visibility = "hidden";
|
78
|
+
|
79
|
+
frame.whenDocumentReady = function () {
|
80
|
+
var doc = frame.contentDocument;
|
81
|
+
var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
|
82
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentmodify', evtData);
|
83
|
+
frame.whenDocumentReady = null;
|
84
|
+
}
|
85
|
+
|
86
|
+
if (p.source.html) {
|
87
|
+
return loadFrameFromHTML(p.source.html || p.source, frame, callback);
|
88
|
+
} else if (p.source.url) {
|
89
|
+
return loadFrameFromURL(p.source.url, frame, callback);
|
90
|
+
} else if (p.source.doc) {
|
91
|
+
return loadFrameFromDocument(p.source.doc, frame, callback);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
|
96
|
+
// LOAD STRATEGY: HTML
|
97
|
+
// Loads a HTML string into the given frame, invokes the callback once loaded.
|
98
|
+
//
|
99
|
+
function loadFrameFromHTML(src, frame, callback) {
|
100
|
+
var fn = function () {
|
101
|
+
Monocle.Events.deafen(frame, 'load', fn);
|
102
|
+
frame.whenDocumentReady();
|
103
|
+
Monocle.defer(callback);
|
104
|
+
}
|
105
|
+
Monocle.Events.listen(frame, 'load', fn);
|
106
|
+
if (Monocle.Browser.env.loadHTMLWithDocWrite) {
|
107
|
+
frame.contentDocument.open('text/html', 'replace');
|
108
|
+
frame.contentDocument.write(src);
|
109
|
+
frame.contentDocument.close();
|
110
|
+
} else {
|
111
|
+
frame.contentWindow['monCmptData'] = src;
|
112
|
+
frame.src = "javascript:window['monCmptData'];"
|
113
|
+
}
|
114
|
+
}
|
115
|
+
|
116
|
+
|
117
|
+
// LOAD STRATEGY: URL
|
118
|
+
// Loads the URL into the given frame, invokes callback once loaded.
|
119
|
+
//
|
120
|
+
function loadFrameFromURL(url, frame, callback) {
|
121
|
+
// If it's a relative path, we need to make it absolute.
|
122
|
+
if (!url.match(/^\//)) {
|
123
|
+
url = absoluteURL(url);
|
124
|
+
}
|
125
|
+
var onDocumentReady = function () {
|
126
|
+
Monocle.Events.deafen(frame, 'load', onDocumentReady);
|
127
|
+
frame.whenDocumentReady();
|
128
|
+
}
|
129
|
+
var onDocumentLoad = function () {
|
130
|
+
Monocle.Events.deafen(frame, 'load', onDocumentLoad);
|
131
|
+
Monocle.defer(callback);
|
132
|
+
}
|
133
|
+
Monocle.Events.listen(frame, 'load', onDocumentReady);
|
134
|
+
Monocle.Events.listen(frame, 'load', onDocumentLoad);
|
135
|
+
frame.contentWindow.location.replace(url);
|
136
|
+
}
|
137
|
+
|
138
|
+
|
139
|
+
// LOAD STRATEGY: DOCUMENT
|
140
|
+
// Replaces the DocumentElement of the given frame with the given srcDoc.
|
141
|
+
// Invokes the callback when loaded.
|
142
|
+
//
|
143
|
+
function loadFrameFromDocument(srcDoc, frame, callback) {
|
144
|
+
var doc = frame.contentDocument;
|
145
|
+
|
146
|
+
// WebKit has an interesting quirk. The <base> tag must exist in the
|
147
|
+
// document being replaced, not the new document.
|
148
|
+
if (Monocle.Browser.is.WebKit) {
|
149
|
+
var srcBase = srcDoc.querySelector('base');
|
150
|
+
if (srcBase) {
|
151
|
+
var head = doc.querySelector('head');
|
152
|
+
if (!head) {
|
153
|
+
try {
|
154
|
+
head = doc.createElement('head');
|
155
|
+
prependChild(doc.documentElement, head);
|
156
|
+
} catch (e) {
|
157
|
+
head = doc.body;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
var base = doc.createElement('base');
|
161
|
+
base.setAttribute('href', srcBase.href);
|
162
|
+
head.appendChild(base);
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
doc.replaceChild(
|
167
|
+
doc.importNode(srcDoc.documentElement, true),
|
168
|
+
doc.documentElement
|
169
|
+
);
|
170
|
+
|
171
|
+
// NB: It's a significant problem with this load strategy that there's
|
172
|
+
// no indication when it is complete.
|
173
|
+
Monocle.defer(callback);
|
174
|
+
}
|
175
|
+
|
176
|
+
|
177
|
+
// Once a frame is loaded with this component, call this method to style
|
178
|
+
// and measure its contents.
|
179
|
+
//
|
180
|
+
function setupFrame(pageDiv, frame, callback) {
|
181
|
+
updateDimensions(pageDiv, function () {
|
182
|
+
frame.style.visibility = "visible";
|
183
|
+
|
184
|
+
// Find the place of any chapters in the component.
|
185
|
+
locateChapters(pageDiv);
|
186
|
+
|
187
|
+
// Nothing can prevent iframe scrolling on Android, so we have to undo it.
|
188
|
+
if (Monocle.Browser.on.Android) {
|
189
|
+
Monocle.Events.listen(frame.contentWindow, 'scroll', function () {
|
190
|
+
frame.contentWindow.scrollTo(0,0);
|
191
|
+
});
|
192
|
+
}
|
193
|
+
|
194
|
+
// Announce that the component has changed.
|
195
|
+
var doc = frame.contentDocument;
|
196
|
+
var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
|
197
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentchange', evtData);
|
198
|
+
|
199
|
+
callback();
|
200
|
+
});
|
201
|
+
}
|
202
|
+
|
203
|
+
|
204
|
+
// Checks whether the pageDiv dimensions have changed. If they have,
|
205
|
+
// remeasures dimensions and returns true. Otherwise returns false.
|
206
|
+
//
|
207
|
+
function updateDimensions(pageDiv, callback) {
|
208
|
+
pageDiv.m.dimensions.update(function (pageLength) {
|
209
|
+
p.pageLength = pageLength;
|
210
|
+
if (typeof callback == "function") { callback() }
|
211
|
+
});
|
212
|
+
}
|
213
|
+
|
214
|
+
|
215
|
+
// Iterates over all the chapters that are within this component
|
216
|
+
// (according to the array we were provided on initialization) and finds
|
217
|
+
// their location (in percentage terms) within the text.
|
218
|
+
//
|
219
|
+
// Stores this percentage with the chapter object in the chapters array.
|
220
|
+
//
|
221
|
+
function locateChapters(pageDiv) {
|
222
|
+
if (p.chapters[0] && typeof p.chapters[0].percent == "number") {
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
var doc = pageDiv.m.activeFrame.contentDocument;
|
226
|
+
for (var i = 0; i < p.chapters.length; ++i) {
|
227
|
+
var chp = p.chapters[i];
|
228
|
+
chp.percent = 0;
|
229
|
+
if (chp.fragment) {
|
230
|
+
var node = doc.getElementById(chp.fragment);
|
231
|
+
chp.percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
|
232
|
+
}
|
233
|
+
}
|
234
|
+
return p.chapters;
|
235
|
+
}
|
236
|
+
|
237
|
+
|
238
|
+
// For a given page number within the component, return the chapter that
|
239
|
+
// starts on or most-recently-before this page.
|
240
|
+
//
|
241
|
+
// Useful, for example, in displaying the current chapter title as a
|
242
|
+
// running head on the page.
|
243
|
+
//
|
244
|
+
function chapterForPage(pageN) {
|
245
|
+
var cand = null;
|
246
|
+
var percent = (pageN - 1) / p.pageLength;
|
247
|
+
for (var i = 0; i < p.chapters.length; ++i) {
|
248
|
+
if (percent >= p.chapters[i].percent) {
|
249
|
+
cand = p.chapters[i];
|
250
|
+
} else {
|
251
|
+
return cand;
|
252
|
+
}
|
253
|
+
}
|
254
|
+
return cand;
|
255
|
+
}
|
256
|
+
|
257
|
+
|
258
|
+
// For a given chapter fragment (the bit after the hash
|
259
|
+
// in eg, "index.html#foo"), return the page number on which
|
260
|
+
// the chapter starts. If the fragment is null or blank, will
|
261
|
+
// return the first page of the component.
|
262
|
+
//
|
263
|
+
function pageForChapter(fragment, pageDiv) {
|
264
|
+
if (!fragment) {
|
265
|
+
return 1;
|
266
|
+
}
|
267
|
+
for (var i = 0; i < p.chapters.length; ++i) {
|
268
|
+
if (p.chapters[i].fragment == fragment) {
|
269
|
+
return percentToPageNumber(p.chapters[i].percent);
|
270
|
+
}
|
271
|
+
}
|
272
|
+
var doc = pageDiv.m.activeFrame.contentDocument;
|
273
|
+
var node = doc.getElementById(fragment);
|
274
|
+
var percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
|
275
|
+
return percentToPageNumber(percent);
|
276
|
+
}
|
277
|
+
|
278
|
+
|
279
|
+
function pageForXPath(xpath, pageDiv) {
|
280
|
+
var doc = pageDiv.m.activeFrame.contentDocument;
|
281
|
+
var percent = 0;
|
282
|
+
if (Monocle.Browser.env.supportsXPath) {
|
283
|
+
var node = doc.evaluate(xpath, doc, null, 9, null).singleNodeValue;
|
284
|
+
if (node) {
|
285
|
+
percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
|
286
|
+
}
|
287
|
+
} else {
|
288
|
+
console.warn("XPath not supported in this client.");
|
289
|
+
}
|
290
|
+
return percentToPageNumber(percent);
|
291
|
+
}
|
292
|
+
|
293
|
+
|
294
|
+
function pageForSelector(selector, pageDiv) {
|
295
|
+
var doc = pageDiv.m.activeFrame.contentDocument;
|
296
|
+
var percent = 0;
|
297
|
+
if (Monocle.Browser.env.supportsQuerySelector) {
|
298
|
+
var node = doc.querySelector(selector);
|
299
|
+
if (node) {
|
300
|
+
percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
|
301
|
+
}
|
302
|
+
} else {
|
303
|
+
console.warn("querySelector not supported in this client.");
|
304
|
+
}
|
305
|
+
return percentToPageNumber(percent);
|
306
|
+
}
|
307
|
+
|
308
|
+
|
309
|
+
function percentToPageNumber(pc) {
|
310
|
+
return Math.floor(pc * p.pageLength) + 1;
|
311
|
+
}
|
312
|
+
|
313
|
+
|
314
|
+
// A public getter for p.pageLength.
|
315
|
+
//
|
316
|
+
function lastPageNumber() {
|
317
|
+
return p.pageLength;
|
318
|
+
}
|
319
|
+
|
320
|
+
|
321
|
+
function prepareSource(reader) {
|
322
|
+
if (p.sourcePrepared) { return; }
|
323
|
+
p.sourcePrepared = true;
|
324
|
+
|
325
|
+
if (typeof p.source == "string") {
|
326
|
+
p.source = { html: p.source };
|
327
|
+
}
|
328
|
+
|
329
|
+
// If supplied as escaped javascript, unescape it to HTML by evalling it.
|
330
|
+
if (p.source.javascript) {
|
331
|
+
console.deprecation(
|
332
|
+
"Loading a component by 'javascript' is deprecated. " +
|
333
|
+
"Use { 'html': src } -- no need to escape or clean the string."
|
334
|
+
);
|
335
|
+
var src = p.source.javascript;
|
336
|
+
src = src.replace(/\\n/g, "\n");
|
337
|
+
src = src.replace(/\\r/g, "\r");
|
338
|
+
src = src.replace(/\\'/g, "'");
|
339
|
+
p.source = { html: src };
|
340
|
+
}
|
341
|
+
|
342
|
+
// If supplied as DOM nodes, convert to HTML by concatenating outerHTMLs.
|
343
|
+
if (p.source.nodes) {
|
344
|
+
var srcs = [];
|
345
|
+
for (var i = 0, ii = p.source.nodes.length; i < ii; ++i) {
|
346
|
+
var node = p.source.nodes[i];
|
347
|
+
if (node.outerHTML) {
|
348
|
+
srcs.push(node.outerHTML);
|
349
|
+
} else {
|
350
|
+
var div = document.createElement('div');
|
351
|
+
div.appendChild(node.cloneNode(true));
|
352
|
+
srcs.push(div.innerHTML);
|
353
|
+
delete(div);
|
354
|
+
}
|
355
|
+
}
|
356
|
+
p.source = { html: srcs.join('') };
|
357
|
+
}
|
358
|
+
|
359
|
+
var baseURI;
|
360
|
+
if (p.source.html && !p.source.html.match(new RegExp("<base\s.+>", "im"))) {
|
361
|
+
baseURI = computeBaseURI(reader);
|
362
|
+
if (baseURI) {
|
363
|
+
p.source.html = p.source.html.replace(
|
364
|
+
new RegExp("(<head[^>]*>)", "im"),
|
365
|
+
'$1<base href="'+baseURI+'" />'
|
366
|
+
);
|
367
|
+
}
|
368
|
+
}
|
369
|
+
|
370
|
+
if (p.source.doc && !p.source.doc.querySelector('base')) {
|
371
|
+
var srcHead = p.source.doc.querySelector('head') || p.source.doc.body;
|
372
|
+
baseURI = computeBaseURI(reader);
|
373
|
+
if (srcHead && baseURI) {
|
374
|
+
var srcBase = p.source.doc.createElement('base');
|
375
|
+
srcBase.setAttribute('href', baseURI);
|
376
|
+
prependChild(srcHead, srcBase);
|
377
|
+
}
|
378
|
+
}
|
379
|
+
}
|
380
|
+
|
381
|
+
|
382
|
+
function computeBaseURI(reader) {
|
383
|
+
var evtData = { cmptId: p.id, cmptURI: absoluteURL(p.id) }
|
384
|
+
if (reader.dispatchEvent('monocle:component:baseuri', evtData, true)) {
|
385
|
+
return evtData.cmptURI;
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
|
390
|
+
function absoluteURL(url) {
|
391
|
+
var link = document.createElement('a');
|
392
|
+
link.setAttribute('href', url);
|
393
|
+
var result = link.href;
|
394
|
+
delete(link);
|
395
|
+
return result;
|
396
|
+
}
|
397
|
+
|
398
|
+
|
399
|
+
function prependChild(pr, el) {
|
400
|
+
pr.firstChild ? pr.insertBefore(el, pr.firstChild) : pr.appendChild(el);
|
401
|
+
}
|
402
|
+
|
403
|
+
|
404
|
+
API.applyTo = applyTo;
|
405
|
+
API.updateDimensions = updateDimensions;
|
406
|
+
API.chapterForPage = chapterForPage;
|
407
|
+
API.pageForChapter = pageForChapter;
|
408
|
+
API.pageForXPath = pageForXPath;
|
409
|
+
API.pageForSelector = pageForSelector;
|
410
|
+
API.lastPageNumber = lastPageNumber;
|
411
|
+
|
412
|
+
return API;
|
413
|
+
}
|