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.
Files changed (50) hide show
  1. data/.DS_Store +0 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/lib/monocle/rails.rb +8 -0
  8. data/lib/monocle/rails/version.rb +5 -0
  9. data/monocle-rails.gemspec +23 -0
  10. data/vendor/.DS_Store +0 -0
  11. data/vendor/assets/.DS_Store +0 -0
  12. data/vendor/assets/javascripts/.DS_Store +0 -0
  13. data/vendor/assets/javascripts/compat/browser.js +120 -0
  14. data/vendor/assets/javascripts/compat/css.js +145 -0
  15. data/vendor/assets/javascripts/compat/env.js +463 -0
  16. data/vendor/assets/javascripts/compat/gala.js +469 -0
  17. data/vendor/assets/javascripts/compat/stubs.js +50 -0
  18. data/vendor/assets/javascripts/controls/contents.js +59 -0
  19. data/vendor/assets/javascripts/controls/magnifier.js +51 -0
  20. data/vendor/assets/javascripts/controls/panel.js +136 -0
  21. data/vendor/assets/javascripts/controls/placesaver.js +100 -0
  22. data/vendor/assets/javascripts/controls/scrubber.js +140 -0
  23. data/vendor/assets/javascripts/controls/spinner.js +99 -0
  24. data/vendor/assets/javascripts/controls/stencil.js +410 -0
  25. data/vendor/assets/javascripts/core/billboard.js +120 -0
  26. data/vendor/assets/javascripts/core/book.js +467 -0
  27. data/vendor/assets/javascripts/core/bookdata.js +59 -0
  28. data/vendor/assets/javascripts/core/component.js +413 -0
  29. data/vendor/assets/javascripts/core/events.js +56 -0
  30. data/vendor/assets/javascripts/core/factory.js +194 -0
  31. data/vendor/assets/javascripts/core/formatting.js +317 -0
  32. data/vendor/assets/javascripts/core/monocle.js +16 -0
  33. data/vendor/assets/javascripts/core/place.js +210 -0
  34. data/vendor/assets/javascripts/core/reader.js +683 -0
  35. data/vendor/assets/javascripts/core/selection.js +158 -0
  36. data/vendor/assets/javascripts/core/styles.js +155 -0
  37. data/vendor/assets/javascripts/dimensions/columns.js +218 -0
  38. data/vendor/assets/javascripts/flippers/instant.js +78 -0
  39. data/vendor/assets/javascripts/flippers/scroller.js +128 -0
  40. data/vendor/assets/javascripts/flippers/slider.js +469 -0
  41. data/vendor/assets/javascripts/monocore.js +27 -0
  42. data/vendor/assets/javascripts/monoctrl.js +1 -0
  43. data/vendor/assets/javascripts/panels/eink.js +61 -0
  44. data/vendor/assets/javascripts/panels/imode.js +180 -0
  45. data/vendor/assets/javascripts/panels/magic.js +297 -0
  46. data/vendor/assets/javascripts/panels/marginal.js +50 -0
  47. data/vendor/assets/javascripts/panels/twopane.js +34 -0
  48. data/vendor/assets/stylesheets/monocore.css +194 -0
  49. data/vendor/assets/stylesheets/monoctrl.css +168 -0
  50. metadata +129 -0
@@ -0,0 +1,99 @@
1
+ Monocle.Controls.Spinner = function (reader) {
2
+
3
+ var API = { constructor: Monocle.Controls.Spinner }
4
+ var k = API.constants = API.constructor;
5
+ var p = API.properties = {
6
+ reader: reader,
7
+ divs: [],
8
+ repeaters: {},
9
+ showForPages: []
10
+ }
11
+
12
+
13
+ function createControlElements(cntr) {
14
+ var anim = cntr.dom.make('div', 'controls_spinner_anim');
15
+ anim.dom.append('div', 'controls_spinner_inner');
16
+ p.divs.push(anim);
17
+ return anim;
18
+ }
19
+
20
+
21
+ function registerSpinEvent(startEvtType, stopEvtType) {
22
+ var label = startEvtType;
23
+ p.reader.listen(startEvtType, function (evt) { spin(label, evt) });
24
+ p.reader.listen(stopEvtType, function (evt) { spun(label, evt) });
25
+ }
26
+
27
+
28
+ // Registers spin/spun event handlers for certain time-consuming events.
29
+ //
30
+ function listenForUsualDelays() {
31
+ registerSpinEvent('monocle:componentloading', 'monocle:componentloaded');
32
+ registerSpinEvent('monocle:componentchanging', 'monocle:componentchange');
33
+ registerSpinEvent('monocle:resizing', 'monocle:resize');
34
+ registerSpinEvent('monocle:jumping', 'monocle:jump');
35
+ registerSpinEvent('monocle:recalculating', 'monocle:recalculated');
36
+ p.reader.listen('monocle:notfound', forceSpun);
37
+ p.reader.listen('monocle:componentfailed', forceSpun);
38
+ }
39
+
40
+
41
+ // Displays the spinner. Both arguments are optional.
42
+ //
43
+ function spin(label, evt) {
44
+ label = label || k.GENERIC_LABEL;
45
+ p.repeaters[label] = true;
46
+ p.reader.showControl(API);
47
+
48
+ // If the delay is on a page other than the page we've been assigned to,
49
+ // don't show the animation. p.global ensures that if an event affects
50
+ // all pages, the animation is always shown, even if other events in this
51
+ // spin cycle are page-specific.
52
+ var page = (evt && evt.m && evt.m.page) ? evt.m.page : null;
53
+ if (page && p.divs.length > 1) {
54
+ p.showForPages[page.m.pageIndex] = true;
55
+ } else {
56
+ p.global = true;
57
+ p.reader.dispatchEvent('monocle:modal:on');
58
+ }
59
+ for (var i = 0; i < p.divs.length; ++i) {
60
+ var show = (p.global || p.showForPages[i]) ? true : false;
61
+ p.divs[i].dom[show ? 'removeClass' : 'addClass']('dormant');
62
+ }
63
+ }
64
+
65
+
66
+ // Stops displaying the spinner. Both arguments are optional.
67
+ //
68
+ function spun(label, evt) {
69
+ label = label || k.GENERIC_LABEL;
70
+ p.repeaters[label] = false;
71
+ for (var l in p.repeaters) {
72
+ if (p.repeaters[l]) { return; }
73
+ }
74
+ forceSpun();
75
+ }
76
+
77
+
78
+ function forceSpun() {
79
+ if (p.global) { p.reader.dispatchEvent('monocle:modal:off'); }
80
+ p.global = false;
81
+ p.repeaters = {};
82
+ p.showForPages = [];
83
+ for (var i = 0; i < p.divs.length; ++i) {
84
+ p.divs[i].dom.addClass('dormant');
85
+ }
86
+ }
87
+
88
+
89
+ API.createControlElements = createControlElements;
90
+ API.registerSpinEvent = registerSpinEvent;
91
+ API.listenForUsualDelays = listenForUsualDelays;
92
+ API.spin = spin;
93
+ API.spun = spun;
94
+ API.forceSpun = forceSpun;
95
+
96
+ return API;
97
+ }
98
+
99
+ Monocle.Controls.Spinner.GENERIC_LABEL = "generic";
@@ -0,0 +1,410 @@
1
+ Monocle.Controls.Stencil = function (reader, behaviorClasses) {
2
+
3
+ var API = { constructor: Monocle.Controls.Stencil }
4
+ var k = API.constants = API.constructor;
5
+ var p = API.properties = {
6
+ reader: reader,
7
+ behaviors: [],
8
+ components: {},
9
+ masks: []
10
+ }
11
+
12
+
13
+ // Create the stencil container and listen for draw/update events.
14
+ //
15
+ function createControlElements(holder) {
16
+ behaviorClasses = behaviorClasses || k.DEFAULT_BEHAVIORS;
17
+ for (var i = 0, ii = behaviorClasses.length; i < ii; ++i) {
18
+ addBehavior(behaviorClasses[i]);
19
+ }
20
+ p.container = holder.dom.make('div', k.CLS.container);
21
+ p.reader.listen('monocle:turning', hide);
22
+ p.reader.listen('monocle:turn:cancel', show);
23
+ p.reader.listen('monocle:turn', update);
24
+ p.reader.listen('monocle:stylesheetchange', update);
25
+ p.reader.listen('monocle:resize', update);
26
+ update();
27
+ return p.container;
28
+ }
29
+
30
+
31
+ // Pass this method an object that responds to 'findElements(doc)' with
32
+ // an array of DOM elements for that document, and to 'fitMask(elem, mask)'.
33
+ //
34
+ // After you have added all your behaviors this way, you would typically
35
+ // call update() to make them take effect immediately.
36
+ //
37
+ function addBehavior(bhvrClass) {
38
+ var bhvr = new bhvrClass(API);
39
+ if (typeof bhvr.findElements != 'function') {
40
+ console.warn('Missing "findElements" method for behavior: %o', bhvr);
41
+ }
42
+ if (typeof bhvr.fitMask != 'function') {
43
+ console.warn('Missing "fitMask" method for behavior: %o', bhvr);
44
+ }
45
+ p.behaviors.push(bhvr);
46
+ }
47
+
48
+
49
+ // Resets any pre-calculated rectangles for the active component,
50
+ // recalculates them, and forces masks to be "drawn" (moved into the new
51
+ // rectangular locations).
52
+ //
53
+ function update() {
54
+ var visPages = p.reader.visiblePages();
55
+ if (!visPages || !visPages.length) { return; }
56
+ var pageDiv = visPages[0];
57
+ var cmptId = pageComponentId(pageDiv);
58
+ if (!cmptId) { return; }
59
+ p.components[cmptId] = null;
60
+ calculateRectangles(pageDiv);
61
+ draw();
62
+ }
63
+
64
+
65
+ function hide() {
66
+ p.container.style.display = 'none';
67
+ }
68
+
69
+
70
+ function show() {
71
+ p.container.style.display = 'block';
72
+ }
73
+
74
+
75
+ // Removes any existing masks.
76
+ function clear() {
77
+ while (p.container.childNodes.length) {
78
+ p.container.removeChild(p.container.lastChild);
79
+ }
80
+ }
81
+
82
+
83
+ // Aligns the stencil container to the shape of the page, then moves the
84
+ // masks to sit above any currently visible rectangles.
85
+ //
86
+ function draw() {
87
+ var pageDiv = p.reader.visiblePages()[0];
88
+ var cmptId = pageComponentId(pageDiv);
89
+ if (!p.components[cmptId]) {
90
+ return;
91
+ }
92
+
93
+ // Position the container.
94
+ alignToComponent(pageDiv);
95
+
96
+ // Clear old masks.
97
+ clear();
98
+
99
+ // Layout the masks.
100
+ if (!p.disabled) {
101
+ show();
102
+ var rects = p.components[cmptId];
103
+ if (rects && rects.length) {
104
+ layoutRectangles(pageDiv, rects);
105
+ }
106
+ }
107
+ }
108
+
109
+
110
+ // Iterate over all the <a> elements in the active component, and
111
+ // create an array of rectangular points corresponding to their positions.
112
+ //
113
+ function calculateRectangles(pageDiv) {
114
+ var cmptId = pageComponentId(pageDiv);
115
+ if (!p.components[cmptId]) {
116
+ p.components[cmptId] = [];
117
+ } else {
118
+ return;
119
+ }
120
+
121
+ var doc = pageDiv.m.activeFrame.contentDocument;
122
+ var offset = getOffset(pageDiv);
123
+
124
+ for (var b = 0, bb = p.behaviors.length; b < bb; ++b) {
125
+ var bhvr = p.behaviors[b];
126
+ var elems = bhvr.findElements(doc);
127
+ for (var i = 0; i < elems.length; ++i) {
128
+ var elem = elems[i];
129
+ if (elem.getClientRects) {
130
+ var r = elem.getClientRects();
131
+ for (var j = 0; j < r.length; j++) {
132
+ p.components[cmptId].push({
133
+ element: elem,
134
+ behavior: bhvr,
135
+ left: Math.ceil(r[j].left + offset.l),
136
+ top: Math.ceil(r[j].top),
137
+ width: Math.floor(r[j].width),
138
+ height: Math.floor(r[j].height)
139
+ });
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ return p.components[cmptId];
146
+ }
147
+
148
+
149
+ // Update location of visible rectangles - creating as required.
150
+ //
151
+ function layoutRectangles(pageDiv, rects) {
152
+ var offset = getOffset(pageDiv);
153
+ var visRects = [];
154
+ for (var i = 0; i < rects.length; ++i) {
155
+ if (rectVisible(rects[i], offset.l, offset.l + offset.w)) {
156
+ visRects.push(rects[i]);
157
+ }
158
+ }
159
+
160
+ for (i = 0; i < visRects.length; ++i) {
161
+ var r = visRects[i];
162
+ var cr = {
163
+ left: r.left - offset.l,
164
+ top: r.top,
165
+ width: r.width,
166
+ height: r.height
167
+ };
168
+ var mask = createMask(r.element, r.behavior);
169
+ mask.dom.setStyles({
170
+ display: 'block',
171
+ left: cr.left+"px",
172
+ top: cr.top+"px",
173
+ width: cr.width+"px",
174
+ height: cr.height+"px",
175
+ position: 'absolute'
176
+ });
177
+ mask.stencilRect = cr;
178
+ }
179
+ }
180
+
181
+
182
+ // Find the offset position in pixels from the left of the current page.
183
+ //
184
+ function getOffset(pageDiv) {
185
+ return {
186
+ l: pageDiv.m.offset || 0,
187
+ w: pageDiv.m.dimensions.properties.width
188
+ };
189
+ }
190
+
191
+
192
+ // Is this area presently on the screen?
193
+ //
194
+ function rectVisible(rect, l, r) {
195
+ var mid = rect.left + (rect.width * 0.5);
196
+ return mid >= l && mid < r;
197
+ }
198
+
199
+
200
+ // Returns the active component id for the given page, or the current
201
+ // page if no argument passed in.
202
+ //
203
+ function pageComponentId(pageDiv) {
204
+ pageDiv = pageDiv || p.reader.visiblePages()[0];
205
+ if (!pageDiv.m.activeFrame.m.component) { return; }
206
+ return pageDiv.m.activeFrame.m.component.properties.id;
207
+ }
208
+
209
+
210
+ // Positions the stencil container over the active frame.
211
+ //
212
+ function alignToComponent(pageDiv) {
213
+ var cmpt = pageDiv.m.activeFrame.parentNode;
214
+ p.container.dom.setStyles({
215
+ left: cmpt.offsetLeft+"px",
216
+ top: cmpt.offsetTop+"px"
217
+ });
218
+ }
219
+
220
+
221
+ function createMask(element, bhvr) {
222
+ var mask = p.container.dom.append(bhvr.maskTagName || 'div', k.CLS.mask);
223
+ Monocle.Events.listenForContact(mask, {
224
+ start: function () { p.reader.dispatchEvent('monocle:magic:halt'); },
225
+ move: function (evt) { evt.preventDefault(); },
226
+ end: function () { p.reader.dispatchEvent('monocle:magic:init'); }
227
+ });
228
+ bhvr.fitMask(element, mask);
229
+ return mask;
230
+ }
231
+
232
+
233
+ // Make the active masks visible (by giving them a class -- override style
234
+ // in monoctrl.css).
235
+ //
236
+ function toggleHighlights() {
237
+ var cls = k.CLS.highlights;
238
+ if (p.container.dom.hasClass(cls)) {
239
+ p.container.dom.removeClass(cls);
240
+ } else {
241
+ p.container.dom.addClass(cls);
242
+ }
243
+ }
244
+
245
+
246
+ function disable() {
247
+ p.disabled = true;
248
+ draw();
249
+ }
250
+
251
+
252
+ function enable() {
253
+ p.disabled = false;
254
+ draw();
255
+ }
256
+
257
+
258
+ function filterElement(elem, behavior) {
259
+ if (typeof behavior.filterElement == 'function') {
260
+ return behavior.filterElement(elem);
261
+ }
262
+ return elem;
263
+ }
264
+
265
+
266
+ function maskAssigned(elem, mask, behavior) {
267
+ if (typeof behavior.maskAssigned == 'function') {
268
+ return behavior.maskAssigned(elem, mask);
269
+ }
270
+ return false;
271
+ }
272
+
273
+
274
+ API.createControlElements = createControlElements;
275
+ API.addBehavior = addBehavior;
276
+ API.draw = draw;
277
+ API.update = update;
278
+ API.toggleHighlights = toggleHighlights;
279
+
280
+ return API;
281
+ }
282
+
283
+
284
+ Monocle.Controls.Stencil.CLS = {
285
+ container: 'controls_stencil_container',
286
+ mask: 'controls_stencil_mask',
287
+ highlights: 'controls_stencil_highlighted'
288
+ }
289
+
290
+
291
+
292
+ Monocle.Controls.Stencil.Links = function (stencil) {
293
+ var API = { constructor: Monocle.Controls.Stencil.Links }
294
+
295
+ // Optionally specify the HTML tagname of the mask.
296
+ API.maskTagName = 'a';
297
+
298
+ // Returns an array of all the elements in the given doc that should
299
+ // be covered with a stencil mask for interactivity.
300
+ //
301
+ // (Hint: doc.querySelectorAll() is your friend.)
302
+ //
303
+ API.findElements = function (doc) {
304
+ return doc.querySelectorAll('a[href]');
305
+ }
306
+
307
+
308
+ // Return an element. It should usually be a child of the container element,
309
+ // with a className of the given maskClass. You set up the interactivity of
310
+ // the mask element here.
311
+ //
312
+ API.fitMask = function (link, mask) {
313
+ var hrefObject = deconstructHref(link);
314
+ var rdr = stencil.properties.reader;
315
+ var evtData = { href: hrefObject, link: link, mask: mask }
316
+
317
+ if (hrefObject.pass) {
318
+ mask.onclick = function (evt) { return link.click(); }
319
+ } else {
320
+ link.onclick = function (evt) {
321
+ evt.preventDefault();
322
+ return false;
323
+ }
324
+ if (hrefObject.internal) {
325
+ mask.setAttribute('href', 'javascript:"Skip to chapter"');
326
+ mask.onclick = function (evt) {
327
+ if (rdr.dispatchEvent('monocle:link:internal', evtData, true)) {
328
+ rdr.skipToChapter(hrefObject.internal);
329
+ }
330
+ evt.preventDefault();
331
+ return false;
332
+ }
333
+ } else {
334
+ mask.setAttribute('href', hrefObject.external);
335
+ mask.setAttribute('target', '_blank');
336
+ mask.onclick = function (evt) {
337
+ return rdr.dispatchEvent('monocle:link:external', evtData, true);
338
+ }
339
+ }
340
+ }
341
+ }
342
+
343
+
344
+ // Returns an object with either:
345
+ //
346
+ // - an 'external' property -- an absolute URL with a protocol,
347
+ // host & etc, which should be treated as an external resource (eg,
348
+ // open in new window)
349
+ //
350
+ // OR
351
+ //
352
+ // - an 'internal' property -- a relative URL (with optional hash anchor),
353
+ // that is treated as a link to component in the book
354
+ //
355
+ // A weird but useful property of <a> tags is that while
356
+ // link.getAttribute('href') will return the actual string value of the
357
+ // attribute (eg, 'foo.html'), link.href will return the absolute URL (eg,
358
+ // 'http://example.com/monocles/foo.html').
359
+ //
360
+ function deconstructHref(elem) {
361
+ var loc = document.location;
362
+ var origin = loc.protocol+'//'+loc.host;
363
+ var href = elem.href;
364
+ var path = href.substring(origin.length);
365
+ var ext = { external: href };
366
+
367
+ if (href.toLowerCase().match(/^javascript:/)) {
368
+ return { pass: true };
369
+ }
370
+
371
+ // Anchor tags with 'target' attributes are always external URLs.
372
+ if (elem.getAttribute('target')) {
373
+ return ext;
374
+ }
375
+ // URLs with a different protocol or domain are always external.
376
+ //console.log("Domain test: %s <=> %s", origin, href);
377
+ if (href.indexOf(origin) !== 0) {
378
+ return ext;
379
+ }
380
+
381
+ // If it is in a sub-path of the current path, it's internal.
382
+ var topPath = loc.pathname.replace(/[^\/]*\.[^\/]+$/,'');
383
+ if (topPath[topPath.length - 1] != '/') {
384
+ topPath += '/';
385
+ }
386
+ //console.log("Sub-path test: %s <=> %s", topPath, path);
387
+ if (path.indexOf(topPath) === 0) {
388
+ return { internal: path.substring(topPath.length) }
389
+ }
390
+
391
+ // If it's a root-relative URL and it's in our list of component ids,
392
+ // it's internal.
393
+ var cmptIds = stencil.properties.reader.getBook().properties.componentIds;
394
+ for (var i = 0, ii = cmptIds.length; i < ii; ++i) {
395
+ //console.log("Component test: %s <=> %s", cmptIds[i], path);
396
+ if (path.indexOf(cmptIds[i]) === 0) {
397
+ return { internal: path }
398
+ }
399
+ }
400
+
401
+ // Otherwise it's external.
402
+ return ext;
403
+ }
404
+
405
+
406
+ return API;
407
+ }
408
+
409
+
410
+ Monocle.Controls.Stencil.DEFAULT_BEHAVIORS = [Monocle.Controls.Stencil.Links];