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,120 @@
|
|
1
|
+
Monocle.Billboard = function (reader) {
|
2
|
+
var API = { constructor: Monocle.Billboard };
|
3
|
+
var k = API.constants = API.constructor;
|
4
|
+
var p = API.properties = {
|
5
|
+
reader: reader,
|
6
|
+
cntr: null
|
7
|
+
};
|
8
|
+
|
9
|
+
|
10
|
+
function show(urlOrElement, options) {
|
11
|
+
p.reader.dispatchEvent('monocle:modal:on');
|
12
|
+
if (p.cntr) { return console.warn("Modal billboard already showing."); }
|
13
|
+
|
14
|
+
options = options || {};
|
15
|
+
var elem = urlOrElement;
|
16
|
+
p.cntr = reader.dom.append('div', k.CLS.cntr);
|
17
|
+
if (typeof urlOrElement == 'string') {
|
18
|
+
var url = urlOrElement;
|
19
|
+
p.inner = elem = p.cntr.dom.append('iframe', k.CLS.inner);
|
20
|
+
elem.setAttribute('src', url);
|
21
|
+
} else {
|
22
|
+
p.inner = p.cntr.dom.append('div', k.CLS.inner);
|
23
|
+
p.inner.appendChild(elem);
|
24
|
+
}
|
25
|
+
p.dims = [
|
26
|
+
elem.naturalWidth || elem.offsetWidth,
|
27
|
+
elem.naturalHeight || elem.offsetHeight
|
28
|
+
];
|
29
|
+
if (options.closeButton !== false) {
|
30
|
+
var cBtn = p.cntr.dom.append('div', k.CLS.closeButton);
|
31
|
+
Monocle.Events.listenForTap(cBtn, hide);
|
32
|
+
}
|
33
|
+
align(options.align || 'left top');
|
34
|
+
p.reader.listen('monocle:resize', align);
|
35
|
+
|
36
|
+
shrink(options.from);
|
37
|
+
Monocle.defer(grow);
|
38
|
+
}
|
39
|
+
|
40
|
+
|
41
|
+
function hide(evt) {
|
42
|
+
shrink();
|
43
|
+
Monocle.Events.afterTransition(p.cntr, remove);
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
function grow() {
|
48
|
+
Monocle.Styles.transitionFor(p.cntr, 'transform', k.ANIM_MS, 'ease-in-out');
|
49
|
+
Monocle.Styles.affix(p.cntr, 'transform', 'translate(0, 0) scale(1)');
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
function shrink(from) {
|
54
|
+
p.from = from || p.from || [0,0];
|
55
|
+
var translate = 'translate('+p.from[0]+'px, '+p.from[1]+'px)';
|
56
|
+
var scale = 'scale(0)';
|
57
|
+
if (typeof p.from[2] === 'number') {
|
58
|
+
scale = 'scaleX('+(p.from[2] / p.cntr.offsetWidth)+') ';
|
59
|
+
scale += 'scaleY('+(p.from[3] / p.cntr.offsetHeight)+')';
|
60
|
+
}
|
61
|
+
Monocle.Styles.affix(p.cntr, 'transform', translate+' '+scale);
|
62
|
+
}
|
63
|
+
|
64
|
+
|
65
|
+
function remove () {
|
66
|
+
p.cntr.parentNode.removeChild(p.cntr);
|
67
|
+
p.cntr = p.inner = null;
|
68
|
+
p.reader.deafen('monocle:resize', align);
|
69
|
+
p.reader.dispatchEvent('monocle:modal:off');
|
70
|
+
}
|
71
|
+
|
72
|
+
|
73
|
+
function align(loc) {
|
74
|
+
p.alignment = (typeof loc == 'string') ? loc : p.alignment;
|
75
|
+
if (!p.alignment) { return; }
|
76
|
+
if (p.dims[0] > p.inner.offsetWidth || p.dims[1] > p.inner.offsetHeight) {
|
77
|
+
p.cntr.dom.addClass(k.CLS.oversized);
|
78
|
+
} else {
|
79
|
+
p.cntr.dom.removeClass(k.CLS.oversized);
|
80
|
+
}
|
81
|
+
|
82
|
+
var s = p.alignment.split(/\s+/);
|
83
|
+
var l = 0, t = 0;
|
84
|
+
var w = (p.inner.scrollWidth - p.inner.offsetWidth);
|
85
|
+
var h = (p.inner.scrollHeight - p.inner.offsetHeight);
|
86
|
+
if (s[0].match(/^\d+$/)) {
|
87
|
+
l = Math.max(0, parseInt(s[0], 10) - (p.inner.offsetWidth / 2));
|
88
|
+
} else if (s[0] == 'center') {
|
89
|
+
l = w / 2;
|
90
|
+
} else if (s[0] == 'right') {
|
91
|
+
l = w;
|
92
|
+
}
|
93
|
+
if (s[1] && s[1].match(/^\d+$/)) {
|
94
|
+
t = Math.max(0, parseInt(s[1], 10) - (p.inner.offsetHeight / 2));
|
95
|
+
} else if (!s[1] || s[1] == 'center') {
|
96
|
+
t = h / 2;
|
97
|
+
} else if (s[1] == 'bottom') {
|
98
|
+
t = h;
|
99
|
+
}
|
100
|
+
p.inner.scrollLeft = l;
|
101
|
+
p.inner.scrollTop = t;
|
102
|
+
}
|
103
|
+
|
104
|
+
|
105
|
+
API.show = show;
|
106
|
+
API.hide = hide;
|
107
|
+
API.align= align;
|
108
|
+
|
109
|
+
return API;
|
110
|
+
}
|
111
|
+
|
112
|
+
|
113
|
+
Monocle.Billboard.CLS = {
|
114
|
+
cntr: 'billboard_container',
|
115
|
+
inner: 'billboard_inner',
|
116
|
+
closeButton: 'billboard_close',
|
117
|
+
oversized: 'billboard_oversized'
|
118
|
+
}
|
119
|
+
|
120
|
+
Monocle.Billboard.ANIM_MS = 400;
|
@@ -0,0 +1,467 @@
|
|
1
|
+
/* BOOK */
|
2
|
+
|
3
|
+
/* The Book handles movement through the content by the reader page elements.
|
4
|
+
*
|
5
|
+
* It's responsible for instantiating components as they are required,
|
6
|
+
* and for calculating which component and page number to move to (based on
|
7
|
+
* requests from the Reader).
|
8
|
+
*
|
9
|
+
*/
|
10
|
+
Monocle.Book = function (dataSource, preloadWindow) {
|
11
|
+
|
12
|
+
var API = { constructor: Monocle.Book }
|
13
|
+
var k = API.constants = API.constructor;
|
14
|
+
var p = API.properties = {
|
15
|
+
dataSource: dataSource,
|
16
|
+
preloadWindow: preloadWindow,
|
17
|
+
cmptLoadQueue: {},
|
18
|
+
components: [],
|
19
|
+
chapters: {} // flat arrays of chapters per component
|
20
|
+
}
|
21
|
+
|
22
|
+
|
23
|
+
function initialize() {
|
24
|
+
p.componentIds = dataSource.getComponents();
|
25
|
+
p.contents = dataSource.getContents();
|
26
|
+
p.lastCIndex = p.componentIds.length - 1;
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
// Adjusts the given locus object to provide the page number within the
|
31
|
+
// current component.
|
32
|
+
//
|
33
|
+
// If the locus implies movement to another component, the locus
|
34
|
+
// 'componentId' property will be updated to point to this component, and
|
35
|
+
// the 'load' property will be set to true, which should be taken as a
|
36
|
+
// sign to call loadPageAt with a callback.
|
37
|
+
//
|
38
|
+
// The locus argument is an object that has one of the following properties:
|
39
|
+
//
|
40
|
+
// - page: positive integer. Counting up from the start of component.
|
41
|
+
// - pagesBack: negative integer. Counting back from the end of component.
|
42
|
+
// - percent: float indicating percentage through the component
|
43
|
+
// - direction: integer relative to the current page number for this pageDiv
|
44
|
+
// - position: string, one of "start" or "end", moves to corresponding point
|
45
|
+
// in the given component
|
46
|
+
// - anchor: an element id within the component
|
47
|
+
// - xpath: the node at this XPath within the component
|
48
|
+
// - selector: the first node at this CSS selector within the component
|
49
|
+
//
|
50
|
+
// The locus object can also specify a componentId. If it is not provided
|
51
|
+
// we default to the currently active component, and if that doesn't exist,
|
52
|
+
// we default to the very first component.
|
53
|
+
//
|
54
|
+
// The locus result will be an object with the following properties:
|
55
|
+
//
|
56
|
+
// - load: boolean, true if loading component required, false otherwise
|
57
|
+
// - componentId: component to load (current componentId if load is false)
|
58
|
+
// - if load is false:
|
59
|
+
// - page
|
60
|
+
// - if load is true:
|
61
|
+
// - one of page / pagesBack / percent / direction / position / anchor
|
62
|
+
//
|
63
|
+
function pageNumberAt(pageDiv, locus) {
|
64
|
+
locus.load = false;
|
65
|
+
var currComponent = pageDiv.m.activeFrame ?
|
66
|
+
pageDiv.m.activeFrame.m.component :
|
67
|
+
null;
|
68
|
+
var component = null;
|
69
|
+
var cIndex = p.componentIds.indexOf(locus.componentId);
|
70
|
+
if (cIndex < 0 && !currComponent) {
|
71
|
+
// No specified component, no current component. Load first component.
|
72
|
+
locus.load = true;
|
73
|
+
locus.componentId = p.componentIds[0];
|
74
|
+
return locus;
|
75
|
+
} else if (
|
76
|
+
cIndex < 0 &&
|
77
|
+
locus.componentId &&
|
78
|
+
currComponent.properties.id != locus.componentId
|
79
|
+
) {
|
80
|
+
// Invalid component, say not found.
|
81
|
+
pageDiv.m.reader.dispatchEvent(
|
82
|
+
"monocle:notfound",
|
83
|
+
{ href: locus.componentId }
|
84
|
+
);
|
85
|
+
return null;
|
86
|
+
} else if (cIndex < 0) {
|
87
|
+
// No specified (or invalid) component, use current component.
|
88
|
+
component = currComponent;
|
89
|
+
locus.componentId = pageDiv.m.activeFrame.m.component.properties.id;
|
90
|
+
cIndex = p.componentIds.indexOf(locus.componentId);
|
91
|
+
} else if (!p.components[cIndex] || p.components[cIndex] != currComponent) {
|
92
|
+
// Specified component differs from current component. Load specified.
|
93
|
+
locus.load = true;
|
94
|
+
return locus;
|
95
|
+
} else {
|
96
|
+
component = currComponent;
|
97
|
+
}
|
98
|
+
|
99
|
+
// If we're here, then the locus is based on the current component.
|
100
|
+
var result = { load: false, componentId: locus.componentId, page: 1 }
|
101
|
+
|
102
|
+
// Get the current last page.
|
103
|
+
var lastPageNum = component.lastPageNumber();
|
104
|
+
|
105
|
+
// Deduce the page number for the given locus.
|
106
|
+
if (typeof(locus.page) == "number") {
|
107
|
+
result.page = locus.page;
|
108
|
+
} else if (typeof(locus.pagesBack) == "number") {
|
109
|
+
result.page = lastPageNum + locus.pagesBack;
|
110
|
+
} else if (typeof(locus.percent) == "number") {
|
111
|
+
var place = new Monocle.Place();
|
112
|
+
place.setPlace(component, 1);
|
113
|
+
result.page = place.pageAtPercentageThrough(locus.percent);
|
114
|
+
} else if (typeof(locus.direction) == "number") {
|
115
|
+
if (!pageDiv.m.place) {
|
116
|
+
console.warn("Can't move in a direction if pageDiv has no place.");
|
117
|
+
}
|
118
|
+
result.page = pageDiv.m.place.pageNumber();
|
119
|
+
result.page += locus.direction;
|
120
|
+
} else if (typeof(locus.anchor) == "string") {
|
121
|
+
result.page = component.pageForChapter(locus.anchor, pageDiv);
|
122
|
+
} else if (typeof(locus.xpath) == "string") {
|
123
|
+
result.page = component.pageForXPath(locus.xpath, pageDiv);
|
124
|
+
} else if (typeof(locus.selector) == "string") {
|
125
|
+
result.page = component.pageForSelector(locus.selector, pageDiv);
|
126
|
+
} else if (typeof(locus.position) == "string") {
|
127
|
+
if (locus.position == "start") {
|
128
|
+
result.page = 1;
|
129
|
+
} else if (locus.position == "end") {
|
130
|
+
result.page = lastPageNum['new'];
|
131
|
+
}
|
132
|
+
} else {
|
133
|
+
console.warn("Unrecognised locus: " + locus);
|
134
|
+
}
|
135
|
+
|
136
|
+
if (result.page < 1) {
|
137
|
+
if (cIndex === 0) {
|
138
|
+
// On first page of book.
|
139
|
+
result.page = 1;
|
140
|
+
result.boundarystart = true;
|
141
|
+
} else {
|
142
|
+
// Moving backwards from current component.
|
143
|
+
result.load = true;
|
144
|
+
result.componentId = p.componentIds[cIndex - 1];
|
145
|
+
result.pagesBack = result.page;
|
146
|
+
result.page = null;
|
147
|
+
}
|
148
|
+
} else if (result.page > lastPageNum) {
|
149
|
+
if (cIndex == p.lastCIndex) {
|
150
|
+
// On last page of book.
|
151
|
+
result.page = lastPageNum;
|
152
|
+
result.boundaryend = true;
|
153
|
+
} else {
|
154
|
+
// Moving forwards from current component.
|
155
|
+
result.load = true;
|
156
|
+
result.componentId = p.componentIds[cIndex + 1];
|
157
|
+
result.page -= lastPageNum;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
return result;
|
162
|
+
}
|
163
|
+
|
164
|
+
|
165
|
+
// Same as pageNumberAt, but if a load is not flagged, this will
|
166
|
+
// automatically update the pageDiv's place to the given pageNumber.
|
167
|
+
//
|
168
|
+
// If you call this (ie, from a flipper), you are effectively entering into
|
169
|
+
// a contract to move the frame offset to the given page returned in the
|
170
|
+
// locus if load is false.
|
171
|
+
//
|
172
|
+
function setPageAt(pageDiv, locus) {
|
173
|
+
locus = pageNumberAt(pageDiv, locus);
|
174
|
+
if (locus && !locus.load) {
|
175
|
+
var evtData = { locus: locus, page: pageDiv }
|
176
|
+
if (locus.boundarystart) {
|
177
|
+
pageDiv.m.reader.dispatchEvent('monocle:boundarystart', evtData);
|
178
|
+
} else if (locus.boundaryend) {
|
179
|
+
pageDiv.m.reader.dispatchEvent('monocle:boundaryend', evtData);
|
180
|
+
} else {
|
181
|
+
var component = p.components[p.componentIds.indexOf(locus.componentId)];
|
182
|
+
pageDiv.m.place = pageDiv.m.place || new Monocle.Place();
|
183
|
+
pageDiv.m.place.setPlace(component, locus.page);
|
184
|
+
|
185
|
+
evtData = {
|
186
|
+
page: pageDiv,
|
187
|
+
locus: locus,
|
188
|
+
pageNumber: pageDiv.m.place.pageNumber(),
|
189
|
+
componentId: locus.componentId
|
190
|
+
}
|
191
|
+
pageDiv.m.reader.dispatchEvent("monocle:pagechange", evtData);
|
192
|
+
}
|
193
|
+
}
|
194
|
+
return locus;
|
195
|
+
}
|
196
|
+
|
197
|
+
|
198
|
+
// Will load the given component into the pageDiv's frame, then invoke the
|
199
|
+
// callback with resulting locus (provided by pageNumberAt).
|
200
|
+
//
|
201
|
+
// If the resulting page number is outside the bounds of the new component,
|
202
|
+
// (ie, pageNumberAt again requests a load), this will recurse into further
|
203
|
+
// components until non-loading locus is returned by pageNumberAt. Then the
|
204
|
+
// callback will fire with that locus.
|
205
|
+
//
|
206
|
+
// As with setPageAt, if you call this you're obliged to move the frame
|
207
|
+
// offset to the given page in the locus passed to the callback.
|
208
|
+
//
|
209
|
+
function loadPageAt(pageDiv, locus, onLoad, onFail) {
|
210
|
+
var cIndex = p.componentIds.indexOf(locus.componentId);
|
211
|
+
if (!locus.load || cIndex < 0) {
|
212
|
+
locus = pageNumberAt(pageDiv, locus);
|
213
|
+
}
|
214
|
+
|
215
|
+
if (!locus) {
|
216
|
+
return onFail ? onFail() : null;
|
217
|
+
}
|
218
|
+
|
219
|
+
if (!locus.load) {
|
220
|
+
return onLoad(locus);
|
221
|
+
}
|
222
|
+
|
223
|
+
var findPageNumber = function () {
|
224
|
+
locus = setPageAt(pageDiv, locus);
|
225
|
+
if (!locus) {
|
226
|
+
return onFail ? onFail() : null;
|
227
|
+
} else if (locus.load) {
|
228
|
+
loadPageAt(pageDiv, locus, onLoad, onFail)
|
229
|
+
} else {
|
230
|
+
onLoad(locus);
|
231
|
+
}
|
232
|
+
}
|
233
|
+
|
234
|
+
var applyComponent = function (component) {
|
235
|
+
component.applyTo(pageDiv, findPageNumber);
|
236
|
+
for (var l = 1; l <= p.preloadWindow; ++l) {
|
237
|
+
deferredPreloadComponent(cIndex+l, l*k.PRELOAD_INTERVAL);
|
238
|
+
}
|
239
|
+
}
|
240
|
+
|
241
|
+
loadComponent(cIndex, applyComponent, onFail, pageDiv);
|
242
|
+
}
|
243
|
+
|
244
|
+
|
245
|
+
// If your flipper doesn't care whether a component needs to be
|
246
|
+
// loaded before the page can be set, you can use this shortcut.
|
247
|
+
//
|
248
|
+
function setOrLoadPageAt(pageDiv, locus, onLoad, onFail) {
|
249
|
+
locus = setPageAt(pageDiv, locus);
|
250
|
+
if (!locus) {
|
251
|
+
if (onFail) { onFail(); }
|
252
|
+
} else if (locus.load) {
|
253
|
+
loadPageAt(pageDiv, locus, onLoad, onFail);
|
254
|
+
} else {
|
255
|
+
onLoad(locus);
|
256
|
+
}
|
257
|
+
}
|
258
|
+
|
259
|
+
|
260
|
+
// Fetches the component source from the dataSource.
|
261
|
+
//
|
262
|
+
// 'index' is the index of the component in the
|
263
|
+
// dataSource.getComponents array.
|
264
|
+
//
|
265
|
+
// 'onLoad' is invoked when the source is received.
|
266
|
+
//
|
267
|
+
// 'onFail' is optional, and is invoked if the source could not be fetched.
|
268
|
+
//
|
269
|
+
// 'pageDiv' is optional, and simply allows firing events on
|
270
|
+
// the reader object that has requested this component, ONLY if
|
271
|
+
// the source has not already been received.
|
272
|
+
//
|
273
|
+
function loadComponent(index, onLoad, onFail, pageDiv) {
|
274
|
+
if (p.components[index]) {
|
275
|
+
return onLoad(p.components[index]);
|
276
|
+
}
|
277
|
+
|
278
|
+
var cmptId = p.components[index];
|
279
|
+
var evtData = { 'page': pageDiv, 'component': cmptId, 'index': index };
|
280
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentloading', evtData);
|
281
|
+
|
282
|
+
var onCmptLoad = function (cmpt) {
|
283
|
+
evtData['component'] = cmpt;
|
284
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentloaded', evtData);
|
285
|
+
onLoad(cmpt);
|
286
|
+
}
|
287
|
+
|
288
|
+
var onCmptFail = function (cmptId) {
|
289
|
+
console.warn("Failed to load component: "+cmptId);
|
290
|
+
pageDiv.m.reader.dispatchEvent('monocle:componentfailed', evtData);
|
291
|
+
if (onFail) { onFail(); }
|
292
|
+
}
|
293
|
+
|
294
|
+
_loadComponent(index, onCmptLoad, onCmptFail);
|
295
|
+
}
|
296
|
+
|
297
|
+
|
298
|
+
function preloadComponent(index) {
|
299
|
+
if (p.components[index]) { return; }
|
300
|
+
var cmptId = p.componentIds[index];
|
301
|
+
if (!cmptId) { return; }
|
302
|
+
if (p.cmptLoadQueue[cmptId]) { return; }
|
303
|
+
_loadComponent(index);
|
304
|
+
}
|
305
|
+
|
306
|
+
|
307
|
+
function deferredPreloadComponent(index, delay) {
|
308
|
+
Monocle.defer(function () { preloadComponent(index); }, delay);
|
309
|
+
}
|
310
|
+
|
311
|
+
|
312
|
+
function _loadComponent(index, successCallback, failureCallback) {
|
313
|
+
var cmptId = p.componentIds[index];
|
314
|
+
var queueItem = { success: successCallback, failure: failureCallback };
|
315
|
+
if (p.cmptLoadQueue[cmptId]) {
|
316
|
+
return p.cmptLoadQueue[cmptId] = queueItem;
|
317
|
+
} else {
|
318
|
+
p.cmptLoadQueue[cmptId] = queueItem;
|
319
|
+
}
|
320
|
+
|
321
|
+
var onCmptFail = function () {
|
322
|
+
fireLoadQueue(cmptId, 'failure', cmptId);
|
323
|
+
}
|
324
|
+
|
325
|
+
var onCmptLoad = function (cmptSource) {
|
326
|
+
if (cmptSource === false) { return onCmptFail(); }
|
327
|
+
p.components[index] = new Monocle.Component(
|
328
|
+
API,
|
329
|
+
cmptId,
|
330
|
+
index,
|
331
|
+
chaptersForComponent(cmptId),
|
332
|
+
cmptSource
|
333
|
+
);
|
334
|
+
fireLoadQueue(cmptId, 'success', p.components[index]);
|
335
|
+
}
|
336
|
+
|
337
|
+
var cmptSource = p.dataSource.getComponent(cmptId, onCmptLoad);
|
338
|
+
if (cmptSource && !p.components[index]) {
|
339
|
+
onCmptLoad(cmptSource);
|
340
|
+
} else if (cmptSource === false) {
|
341
|
+
onCmptFail();
|
342
|
+
}
|
343
|
+
}
|
344
|
+
|
345
|
+
|
346
|
+
function fireLoadQueue(cmptId, cbName, args) {
|
347
|
+
if (typeof p.cmptLoadQueue[cmptId][cbName] == 'function') {
|
348
|
+
p.cmptLoadQueue[cmptId][cbName](args);
|
349
|
+
}
|
350
|
+
p.cmptLoadQueue[cmptId] = null;
|
351
|
+
}
|
352
|
+
|
353
|
+
|
354
|
+
// Returns an array of chapter objects that are found in the given component.
|
355
|
+
//
|
356
|
+
// A chapter object has this format:
|
357
|
+
//
|
358
|
+
// {
|
359
|
+
// title: "Chapter 1",
|
360
|
+
// fragment: null
|
361
|
+
// }
|
362
|
+
//
|
363
|
+
// The fragment property of a chapter object is either null (the chapter
|
364
|
+
// starts at the head of the component) or the fragment part of the URL
|
365
|
+
// (eg, "foo" in "index.html#foo").
|
366
|
+
//
|
367
|
+
function chaptersForComponent(cmptId) {
|
368
|
+
if (p.chapters[cmptId]) {
|
369
|
+
return p.chapters[cmptId];
|
370
|
+
}
|
371
|
+
p.chapters[cmptId] = [];
|
372
|
+
var matcher = new RegExp('^'+decodeURIComponent(cmptId)+"(\#(.+)|$)");
|
373
|
+
var matches;
|
374
|
+
var recurser = function (chp) {
|
375
|
+
if (matches = decodeURIComponent(chp.src).match(matcher)) {
|
376
|
+
p.chapters[cmptId].push({
|
377
|
+
title: chp.title,
|
378
|
+
fragment: matches[2] || null
|
379
|
+
});
|
380
|
+
}
|
381
|
+
if (chp.children) {
|
382
|
+
for (var i = 0; i < chp.children.length; ++i) {
|
383
|
+
recurser(chp.children[i]);
|
384
|
+
}
|
385
|
+
}
|
386
|
+
}
|
387
|
+
|
388
|
+
for (var i = 0; i < p.contents.length; ++i) {
|
389
|
+
recurser(p.contents[i]);
|
390
|
+
}
|
391
|
+
return p.chapters[cmptId];
|
392
|
+
}
|
393
|
+
|
394
|
+
|
395
|
+
// Returns a locus for the chapter that has the URL given in the
|
396
|
+
// 'src' argument.
|
397
|
+
//
|
398
|
+
// See the comments at pageNumberAt for an explanation of locus objects.
|
399
|
+
//
|
400
|
+
function locusOfChapter(src) {
|
401
|
+
var matcher = new RegExp('^(.+?)(#(.*))?$');
|
402
|
+
var matches = src.match(matcher);
|
403
|
+
if (!matches) { return null; }
|
404
|
+
var cmptId = componentIdMatching(matches[1]);
|
405
|
+
if (!cmptId) { return null; }
|
406
|
+
var locus = { componentId: cmptId }
|
407
|
+
matches[3] ? locus.anchor = matches[3] : locus.position = "start";
|
408
|
+
return locus;
|
409
|
+
}
|
410
|
+
|
411
|
+
|
412
|
+
function isValidLocus(locus) {
|
413
|
+
if (!locus) { return false; }
|
414
|
+
if (locus.componentId && !componentIdMatching(locus.componentId)) {
|
415
|
+
return false;
|
416
|
+
}
|
417
|
+
return true;
|
418
|
+
}
|
419
|
+
|
420
|
+
|
421
|
+
function componentIdMatching(str) {
|
422
|
+
str = decodeURIComponent(str);
|
423
|
+
for (var i = 0, ii = p.componentIds.length; i < ii; ++i) {
|
424
|
+
if (decodeURIComponent(p.componentIds[i]) == str) { return str; }
|
425
|
+
}
|
426
|
+
return null;
|
427
|
+
}
|
428
|
+
|
429
|
+
|
430
|
+
function componentWeights() {
|
431
|
+
if (!p.weights) {
|
432
|
+
p.weights = dataSource.getMetaData('componentWeights') || [];
|
433
|
+
if (!p.weights.length) {
|
434
|
+
var cmptSize = 1.0 / p.componentIds.length;
|
435
|
+
for (var i = 0, ii = p.componentIds.length; i < ii; ++i) {
|
436
|
+
p.weights.push(cmptSize);
|
437
|
+
}
|
438
|
+
}
|
439
|
+
}
|
440
|
+
return p.weights;
|
441
|
+
}
|
442
|
+
|
443
|
+
|
444
|
+
API.getMetaData = dataSource.getMetaData;
|
445
|
+
API.pageNumberAt = pageNumberAt;
|
446
|
+
API.setPageAt = setPageAt;
|
447
|
+
API.loadPageAt = loadPageAt;
|
448
|
+
API.setOrLoadPageAt = setOrLoadPageAt;
|
449
|
+
API.chaptersForComponent = chaptersForComponent;
|
450
|
+
API.locusOfChapter = locusOfChapter;
|
451
|
+
API.isValidLocus = isValidLocus;
|
452
|
+
API.componentWeights = componentWeights;
|
453
|
+
|
454
|
+
initialize();
|
455
|
+
|
456
|
+
return API;
|
457
|
+
}
|
458
|
+
|
459
|
+
|
460
|
+
// Legacy function. Deprecated.
|
461
|
+
//
|
462
|
+
Monocle.Book.fromNodes = function (nodes) {
|
463
|
+
console.deprecation("Book.fromNodes() will soon be removed.");
|
464
|
+
return new Monocle.Book(Monocle.bookDataFromNodes(nodes));
|
465
|
+
}
|
466
|
+
|
467
|
+
Monocle.Book.PRELOAD_INTERVAL = 1000;
|