home *** CD-ROM | disk | FTP | other *** search
/ Maximum CD 2011 January / maximum-cd-2011-01.iso / DiscContents / calibre-0.7.26.msi / file_3150 < prev    next >
Encoding:
Text File  |  2010-10-30  |  82.1 KB  |  3,386 lines

  1. Monocle = {
  2.   VERSION: "1.0.0"
  3. };
  4.  
  5.  
  6. Monocle.pieceLoaded = function (piece) {
  7.   if (typeof onMonoclePiece == 'function') {
  8.     onMonoclePiece(piece);
  9.   }
  10. }
  11.  
  12.  
  13. Monocle.defer = function (fn, time) {
  14.   if (fn && typeof fn == "function") {
  15.     return setTimeout(fn, time || 0);
  16.   }
  17. }
  18.  
  19.  
  20. Monocle.Browser = { engine: 'W3C' }
  21.  
  22. Monocle.Browser.is = {
  23.   IE: (!!(window.attachEvent && navigator.userAgent.indexOf('Opera') === -1)) &&
  24.     (Monocle.Browser.engine = "IE"),
  25.   Opera: navigator.userAgent.indexOf('Opera') > -1 &&
  26.     (Monocle.Browser.engine = "Opera"),
  27.   WebKit: navigator.userAgent.indexOf('AppleWebKit/') > -1 &&
  28.     (Monocle.Browser.engine = "WebKit"),
  29.   Gecko: navigator.userAgent.indexOf('Gecko') > -1 &&
  30.     navigator.userAgent.indexOf('KHTML') === -1 &&
  31.     (Monocle.Browser.engine = "Gecko"),
  32.   MobileSafari: !!navigator.userAgent.match(/AppleWebKit.*Mobile/)
  33. } // ... with thanks to PrototypeJS.
  34.  
  35.  
  36. Monocle.Browser.on = {
  37.   iPhone: navigator.userAgent.indexOf("iPhone") != -1,
  38.   iPad: navigator.userAgent.indexOf("iPad") != -1,
  39.   BlackBerry: navigator.userAgent.indexOf("BlackBerry") != -1,
  40.   Android: navigator.userAgent.indexOf('Android') != -1,
  41.   Kindle3: navigator.userAgent.match(/Kindle\/3/)
  42. }
  43.  
  44.  
  45. if (Monocle.Browser.is.MobileSafari) {
  46.   (function () {
  47.     var ver = navigator.userAgent.match(/ OS ([\d_]+)/);
  48.     if (ver) {
  49.       Monocle.Browser.iOSVersion = ver[1].replace(/_/g, '.');
  50.     } else {
  51.       console.warn("Unknown MobileSafari user agent: "+navigator.userAgent);
  52.     }
  53.   })();
  54. }
  55. Monocle.Browser.iOSVersionBelow = function (strOrNum) {
  56.   return Monocle.Browser.iOSVersion && Monocle.Browser.iOSVersion < strOrNum;
  57. }
  58.  
  59.  
  60. Monocle.Browser.CSSProps = {
  61.   engines: ["W3C", "WebKit", "Gecko", "Opera", "IE", "Konqueror"],
  62.   prefixes: ["", "-webkit-", "-moz-", "-o-", "-ms-", "-khtml-"],
  63.   domprefixes: ["", "Webkit", "Moz", "O", "ms", "Khtml"],
  64.   guineapig: document.createElement('div')
  65. }
  66.  
  67.  
  68. Monocle.Browser.CSSProps.capStr = function (wd) {
  69.   return wd ? wd.charAt(0).toUpperCase() + wd.substr(1) : "";
  70. }
  71.  
  72.  
  73. Monocle.Browser.CSSProps.toDOMProps = function (prop, prefix) {
  74.   var parts = prop.split('-');
  75.   for (var i = parts.length; i > 0; --i) {
  76.     parts[i] = Monocle.Browser.CSSProps.capStr(parts[i]);
  77.   }
  78.  
  79.   if (typeof(prefix) != 'undefined' && prefix != null) {
  80.     if (prefix) {
  81.       parts[0] = Monocle.Browser.CSSProps.capStr(parts[0]);
  82.       return prefix+parts.join('');
  83.     } else {
  84.       return parts.join('');
  85.     }
  86.   }
  87.  
  88.   var props = [parts.join('')];
  89.   parts[0] = Monocle.Browser.CSSProps.capStr(parts[0]);
  90.   for (i = 0; i < Monocle.Browser.CSSProps.prefixes.length; ++i) {
  91.     var pf = Monocle.Browser.CSSProps.domprefixes[i];
  92.     if (!pf) { continue; }
  93.     props.push(pf+parts.join(''));
  94.   }
  95.   return props;
  96. }
  97.  
  98.  
  99. Monocle.Browser.CSSProps.toDOMProp = function (prop) {
  100.   return Monocle.Browser.CSSProps.toDOMProps(
  101.     prop,
  102.     Monocle.Browser.CSSProps.domprefixes[
  103.       Monocle.Browser.CSSProps.engines.indexOf(Monocle.Browser.engine)
  104.     ]
  105.   );
  106. }
  107.  
  108.  
  109. Monocle.Browser.CSSProps.isSupported = function (props) {
  110.   for (var i in props) {
  111.     if (Monocle.Browser.CSSProps.guineapig.style[props[i]] !== undefined) {
  112.       return true;
  113.     }
  114.   }
  115.   return false;
  116. } // Thanks modernizr!
  117.  
  118.  
  119. Monocle.Browser.CSSProps.isSupportedForAnyPrefix = function (prop) {
  120.   return Monocle.Browser.CSSProps.isSupported(
  121.     Monocle.Browser.CSSProps.toDOMProps(prop)
  122.   );
  123. }
  124.  
  125.  
  126. Monocle.Browser.CSSProps.supportsMediaQuery = function (query) {
  127.   var gpid = "monocle_guineapig";
  128.   var div = Monocle.Browser.CSSProps.guineapig;
  129.   div.id = gpid;
  130.   var st = document.createElement('style');
  131.   st.textContent = query+'{#'+gpid+'{height:3px}}';
  132.   (document.head || document.getElementsByTagName('head')[0]).appendChild(st);
  133.   document.documentElement.appendChild(div);
  134.  
  135.   var result = Monocle.Browser.CSSProps.guineapig.offsetHeight === 3;
  136.  
  137.   st.parentNode.removeChild(st);
  138.   div.parentNode.removeChild(div);
  139.  
  140.   return result;
  141. } // Thanks modernizr!
  142.  
  143.  
  144. Monocle.Browser.CSSProps.supportsMediaQueryProperty = function (prop) {
  145.   return Monocle.Browser.CSSProps.supportsMediaQuery(
  146.     '@media ('+Monocle.Browser.CSSProps.prefixes.join(prop+'),(')+'monocle__)'
  147.   );
  148. }
  149.  
  150.  
  151.  
  152. Monocle.Browser.has = {}
  153. Monocle.Browser.has.touch = ('ontouchstart' in window) ||
  154.   Monocle.Browser.CSSProps.supportsMediaQueryProperty('touch-enabled');
  155. Monocle.Browser.has.columns = Monocle.Browser.CSSProps.isSupportedForAnyPrefix(
  156.   'column-width'
  157. );
  158. Monocle.Browser.has.transform3d = Monocle.Browser.CSSProps.isSupported([
  159.   'perspectiveProperty',
  160.   'WebkitPerspective',
  161.   'MozPerspective',
  162.   'OPerspective',
  163.   'msPerspective'
  164. ]) && Monocle.Browser.CSSProps.supportsMediaQueryProperty('transform-3d');
  165. Monocle.Browser.has.iframeTouchBug = Monocle.Browser.iOSVersionBelow("4.2");
  166. Monocle.Browser.has.selectThruBug = Monocle.Browser.iOSVersionBelow("4.2");
  167. Monocle.Browser.has.mustScrollSheaf = Monocle.Browser.is.MobileSafari;
  168. Monocle.Browser.has.iframeDoubleWidthBug = Monocle.Browser.has.mustScrollSheaf;
  169. Monocle.Browser.has.floatColumnBug = Monocle.Browser.is.WebKit;
  170.  
  171.  
  172. if (typeof window.console == "undefined") {
  173.   window.console = {
  174.     messages: [],
  175.     log: function (msg) {
  176.       this.messages.push(msg);
  177.     }
  178.   }
  179. }
  180.  
  181.  
  182. window.console.compatDir = function (obj) {
  183.   var stringify = function (o) {
  184.     var parts = [];
  185.     for (x in o) {
  186.       parts.push(x + ": " + o[x]);
  187.     }
  188.     return parts.join("; ");
  189.   }
  190.  
  191.   window.console.log(stringify(obj));
  192. }
  193.  
  194.  
  195. if (!Array.prototype.indexOf) {
  196.   Array.prototype.indexOf = function(elt /*, from*/) {
  197.     var len = this.length >>> 0;
  198.  
  199.     var from = Number(arguments[1]) || 0;
  200.     from = (from < 0)
  201.       ? Math.ceil(from)
  202.       : Math.floor(from);
  203.     if (from < 0) {
  204.       from += len;
  205.     }
  206.  
  207.     for (; from < len; from++) {
  208.       if (from in this && this[from] === elt) {
  209.         return from;
  210.       }
  211.     }
  212.     return -1;
  213.   };
  214. }
  215.  
  216.  
  217. Monocle.pieceLoaded('compat');
  218. Monocle.Factory = function (element, label, index, reader) {
  219.  
  220.   var API = { constructor: Monocle.Factory };
  221.   var k = API.constants = API.constructor;
  222.   var p = API.properties = {
  223.     element: element,
  224.     label: label,
  225.     index: index,
  226.     reader: reader,
  227.     prefix: reader.properties.classPrefix || ''
  228.   }
  229.  
  230.  
  231.   function initialize() {
  232.     var node = p.reader.properties.graph;
  233.     node[p.label] = node[p.label] || [];
  234.     if (typeof p.index == 'undefined' && node[p.label][p.index]) {
  235.       throw('Element already exists in graph: '+p.label+'['+p.index+']');
  236.     } else {
  237.       p.index = p.index || node[p.label].length;
  238.     }
  239.     node[p.label][p.index] = p.element;
  240.  
  241.     addClass(p.label);
  242.   }
  243.  
  244.  
  245.   function find(oLabel, oIndex) {
  246.     if (!p.reader.properties.graph[oLabel]) {
  247.       return null;
  248.     }
  249.     return p.reader.properties.graph[oLabel][oIndex || 0];
  250.   }
  251.  
  252.  
  253.   function claim(oElement, oLabel, oIndex) {
  254.     return oElement.dom = new Monocle.Factory(
  255.       oElement,
  256.       oLabel,
  257.       oIndex,
  258.       p.reader
  259.     );
  260.   }
  261.  
  262.  
  263.   function make(tagName, oLabel, index_or_options, or_options) {
  264.     var oIndex, options;
  265.     if (arguments.length == 2) {
  266.       oIndex = 0;
  267.       options = {};
  268.     } else if (arguments.length == 4) {
  269.       oIndex = arguments[2];
  270.       options = arguments[3];
  271.     } else if (arguments.length == 3) {
  272.       var lastArg = arguments[arguments.length - 1];
  273.       if (typeof lastArg == "number") {
  274.         oIndex = lastArg;
  275.         options = {};
  276.       } else {
  277.         oIndex = 0;
  278.         options = lastArg;
  279.       }
  280.     }
  281.  
  282.     var oElement = document.createElement(tagName);
  283.     claim(oElement, oLabel, oIndex);
  284.     if (options['class']) {
  285.       oElement.className += " "+p.prefix+options['class'];
  286.     }
  287.     if (options['html']) {
  288.       oElement.innerHTML = options['html'];
  289.     }
  290.     if (options['text']) {
  291.       oElement.appendChild(document.createTextNode(options['text']));
  292.     }
  293.  
  294.     return oElement;
  295.   }
  296.  
  297.  
  298.   function append(tagName, oLabel, index_or_options, or_options) {
  299.     var oElement = make.apply(this, arguments);
  300.     p.element.appendChild(oElement);
  301.     return oElement;
  302.   }
  303.  
  304.  
  305.   function address() {
  306.     return [p.label, p.index, p.reader];
  307.   }
  308.  
  309.  
  310.   function setStyles(rules) {
  311.     return Monocle.Styles.applyRules(p.element, rules);
  312.   }
  313.  
  314.  
  315.   function setBetaStyle(property, value) {
  316.     return Monocle.Styles.affix(p.element, property, value);
  317.   }
  318.  
  319.  
  320.  
  321.   function hasClass(name) {
  322.     name = p.prefix + name;
  323.     var klass = p.element.className;
  324.     if (!klass) { return false; }
  325.     if (klass == name) { return true; }
  326.     return new RegExp("(^|\\s)"+name+"(\\s|$)").test(klass);
  327.   }
  328.  
  329.  
  330.   function addClass(name) {
  331.     if (hasClass(name)) { return; }
  332.     var gap = p.element.className ? ' ' : '';
  333.     return p.element.className += gap+p.prefix+name;
  334.   }
  335.  
  336.  
  337.   function removeClass(name) {
  338.     var reName = new RegExp("(^|\\s+)"+p.prefix+name+"(\\s+|$)");
  339.     var reTrim = /^\s+|\s+$/g;
  340.     var klass = p.element.className;
  341.     p.element.className = klass.replace(reName, ' ').replace(reTrim, '');
  342.     return p.element.className;
  343.   }
  344.  
  345.  
  346.   API.find = find;
  347.   API.claim = claim;
  348.   API.make = make;
  349.   API.append = append;
  350.   API.address = address;
  351.  
  352.   API.setStyles = setStyles;
  353.   API.setBetaStyle = setBetaStyle;
  354.   API.hasClass = hasClass;
  355.   API.addClass = addClass;
  356.   API.removeClass = removeClass;
  357.  
  358.   initialize();
  359.  
  360.   return API;
  361. }
  362.  
  363. Monocle.pieceLoaded('factory');
  364. Monocle.Events = {}
  365.  
  366.  
  367. Monocle.Events.listen = function (elem, evtType, fn, useCapture) {
  368.   if (elem.addEventListener) {
  369.     return elem.addEventListener(evtType, fn, useCapture || false);
  370.   } else if (elem.attachEvent) {
  371.     return elem.attachEvent('on'+evtType, fn);
  372.   }
  373. }
  374.  
  375.  
  376. Monocle.Events.deafen = function (elem, evtType, fn, useCapture) {
  377.   if (elem.removeEventListener) {
  378.     return elem.removeEventListener(evtType, fn, useCapture || false);
  379.   } else if (elem.detachEvent) {
  380.     try {
  381.       return elem.detachEvent('on'+evtType, fn);
  382.     } catch(e) {}
  383.   }
  384. }
  385.  
  386.  
  387. Monocle.Events.listenForContact = function (elem, fns, options) {
  388.   var listeners = {};
  389.  
  390.   var cursorInfo = function (evt, ci) {
  391.     evt.m = {
  392.       pageX: ci.pageX,
  393.       pageY: ci.pageY
  394.     };
  395.  
  396.     var target = evt.target || window.srcElement;
  397.     while (target.nodeType != 1 && target.parentNode) {
  398.       target = target.parentNode;
  399.     }
  400.  
  401.     var offset = offsetFor(evt, target);
  402.     evt.m.offsetX = offset[0];
  403.     evt.m.offsetY = offset[1];
  404.  
  405.     if (evt.currentTarget) {
  406.       offset = offsetFor(evt, evt.currentTarget);
  407.       evt.m.registrantX = offset[0];
  408.       evt.m.registrantY = offset[1];
  409.     }
  410.  
  411.     return evt;
  412.   }
  413.  
  414.  
  415.   var offsetFor = function (evt, elem) {
  416.     var r;
  417.     if (elem.getBoundingClientRect) {
  418.       var er = elem.getBoundingClientRect();
  419.       var dr = document.body.getBoundingClientRect();
  420.       r = { left: er.left - dr.left, top: er.top - dr.top };
  421.     } else {
  422.       r = { left: elem.offsetLeft, top: elem.offsetTop }
  423.       while (elem = elem.parentNode) {
  424.         if (elem.offsetLeft || elem.offsetTop) {
  425.           r.left += elem.offsetLeft;
  426.           r.top += elem.offsetTop;
  427.         }
  428.       }
  429.     }
  430.     return [evt.m.pageX - r.left, evt.m.pageY - r.top];
  431.   }
  432.  
  433.  
  434.   var capture = (options && options.useCapture) || false;
  435.  
  436.   if (!Monocle.Browser.has.touch) {
  437.     if (fns.start) {
  438.       listeners.mousedown = function (evt) {
  439.         if (evt.button != 0) { return; }
  440.         fns.start(cursorInfo(evt, evt));
  441.       }
  442.       Monocle.Events.listen(elem, 'mousedown', listeners.mousedown, capture);
  443.     }
  444.     if (fns.move) {
  445.       listeners.mousemove = function (evt) {
  446.         fns.move(cursorInfo(evt, evt));
  447.       }
  448.       Monocle.Events.listen(elem, 'mousemove', listeners.mousemove, capture);
  449.     }
  450.     if (fns.end) {
  451.       listeners.mouseup = function (evt) {
  452.         fns.end(cursorInfo(evt, evt));
  453.       }
  454.       Monocle.Events.listen(elem, 'mouseup', listeners.mouseup, capture);
  455.     }
  456.     if (fns.cancel) {
  457.       listeners.mouseout = function (evt) {
  458.         obj = evt.relatedTarget || evt.fromElement;
  459.         while (obj && (obj = obj.parentNode)) {
  460.           if (obj == elem) { return; }
  461.         }
  462.         fns.cancel(cursorInfo(evt, evt));
  463.       }
  464.       Monocle.Events.listen(elem, 'mouseout', listeners.mouseout, capture);
  465.     }
  466.   } else {
  467.     if (fns.start) {
  468.       listeners.start = function (evt) {
  469.         if (evt.touches.length > 1) { return; }
  470.         fns.start(cursorInfo(evt, evt.targetTouches[0]));
  471.       }
  472.     }
  473.     if (fns.move) {
  474.       listeners.move = function (evt) {
  475.         if (evt.touches.length > 1) { return; }
  476.         fns.move(cursorInfo(evt, evt.targetTouches[0]));
  477.       }
  478.     }
  479.     if (fns.end) {
  480.       listeners.end = function (evt) {
  481.         fns.end(cursorInfo(evt, evt.changedTouches[0]));
  482.         evt.preventDefault();
  483.       }
  484.     }
  485.     if (fns.cancel) {
  486.       listeners.cancel = function (evt) {
  487.         fns.cancel(cursorInfo(evt, evt.changedTouches[0]));
  488.       }
  489.     }
  490.  
  491.     if (Monocle.Browser.has.iframeTouchBug) {
  492.       Monocle.Events.tMonitor = Monocle.Events.tMonitor ||
  493.         new Monocle.Events.TouchMonitor();
  494.       Monocle.Events.tMonitor.listen(elem, listeners, options);
  495.     } else {
  496.       for (etype in listeners) {
  497.         Monocle.Events.listen(elem, 'touch'+etype, listeners[etype], capture);
  498.       }
  499.     }
  500.   }
  501.  
  502.   return listeners;
  503. }
  504.  
  505.  
  506. Monocle.Events.deafenForContact = function (elem, listeners) {
  507.   var prefix = "";
  508.   if (Monocle.Browser.has.touch) {
  509.     prefix = Monocle.Browser.has.iframeTouchBug ? "contact" : "touch";
  510.   }
  511.  
  512.   for (evtType in listeners) {
  513.     Monocle.Events.deafen(elem, prefix + evtType, listeners[evtType]);
  514.   }
  515. }
  516.  
  517.  
  518. Monocle.Events.listenForTap = function (elem, fn) {
  519.   var startPos;
  520.  
  521.   if (Monocle.Browser.on.Kindle3) {
  522.     Monocle.Events.listen(elem, 'click', function () {});
  523.   }
  524.  
  525.   var annulIfOutOfBounds = function (evt) {
  526.     if (evt.type.match(/^mouse/)) {
  527.       return;
  528.     }
  529.     if (Monocle.Browser.is.MobileSafari && Monocle.Browser.iOSVersion < "3.2") {
  530.       return;
  531.     }
  532.     if (
  533.       evt.m.registrantX < 0 || evt.m.registrantX > elem.offsetWidth ||
  534.       evt.m.registrantY < 0 || evt.m.registrantY > elem.offsetHeight
  535.     ) {
  536.       startPos = null;
  537.     } else {
  538.       evt.preventDefault();
  539.     }
  540.   }
  541.  
  542.   return Monocle.Events.listenForContact(
  543.     elem,
  544.     {
  545.       start: function (evt) {
  546.         startPos = [evt.m.pageX, evt.m.pageY];
  547.         evt.preventDefault();
  548.       },
  549.       move: annulIfOutOfBounds,
  550.       end: function (evt) {
  551.         annulIfOutOfBounds(evt);
  552.         if (startPos) {
  553.           evt.m.startOffset = startPos;
  554.           fn(evt);
  555.         }
  556.       },
  557.       cancel: function (evt) {
  558.         startPos = null;
  559.       }
  560.     },
  561.     {
  562.       useCapture: false
  563.     }
  564.   );
  565. }
  566.  
  567.  
  568. Monocle.Events.deafenForTap = Monocle.Events.deafenForContact;
  569.  
  570.  
  571. Monocle.Events.TouchMonitor = function () {
  572.   if (Monocle.Events == this) {
  573.     return new Monocle.Events.TouchMonitor();
  574.   }
  575.  
  576.   var API = { constructor: Monocle.Events.TouchMonitor }
  577.   var k = API.constants = API.constructor;
  578.   var p = API.properties = {
  579.     touching: null,
  580.     edataPrev: null,
  581.     originator: null,
  582.     brokenModel_4_1: navigator.userAgent.match(/ OS 4_1/)
  583.   }
  584.  
  585.  
  586.   function listenOnIframe(iframe) {
  587.     if (iframe.contentDocument) {
  588.       enableTouchProxy(iframe.contentDocument);
  589.       iframe.contentDocument.isTouchFrame = true;
  590.     }
  591.  
  592.     if (p.brokenModel_4_1) {
  593.       enableTouchProxy(iframe);
  594.     }
  595.   }
  596.  
  597.  
  598.   function listen(element, fns, useCapture) {
  599.     for (etype in fns) {
  600.       Monocle.Events.listen(element, 'contact'+etype, fns[etype], useCapture);
  601.     }
  602.     enableTouchProxy(element, useCapture);
  603.   }
  604.  
  605.  
  606.   function enableTouchProxy(element, useCapture) {
  607.     if (element.monocleTouchProxy) {
  608.       return;
  609.     }
  610.     element.monocleTouchProxy = true;
  611.  
  612.     var fn = function (evt) { touchProxyHandler(element, evt) }
  613.     Monocle.Events.listen(element, "touchstart", fn, useCapture);
  614.     Monocle.Events.listen(element, "touchmove", fn, useCapture);
  615.     Monocle.Events.listen(element, "touchend", fn, useCapture);
  616.     Monocle.Events.listen(element, "touchcancel", fn, useCapture);
  617.   }
  618.  
  619.  
  620.   function touchProxyHandler(element, evt) {
  621.     var edata = {
  622.       start: evt.type == "touchstart",
  623.       move: evt.type == "touchmove",
  624.       end: evt.type == "touchend" || evt.type == "touchcancel",
  625.       time: new Date().getTime(),
  626.       frame: element.isTouchFrame
  627.     }
  628.  
  629.     if (!p.touching) {
  630.       p.originator = element;
  631.     }
  632.  
  633.     var target = element;
  634.     var touch = evt.touches[0] || evt.changedTouches[0];
  635.     target = document.elementFromPoint(touch.screenX, touch.screenY);
  636.  
  637.     if (target) {
  638.       translateTouchEvent(element, target, evt, edata);
  639.     }
  640.   }
  641.  
  642.  
  643.   function translateTouchEvent(element, target, evt, edata) {
  644.     if (
  645.       p.brokenModel_4_1 &&
  646.       !edata.frame &&
  647.       !p.touching &&
  648.       edata.start &&
  649.       p.edataPrev &&
  650.       p.edataPrev.end &&
  651.       (edata.time - p.edataPrev.time) < 30
  652.     ) {
  653.       evt.preventDefault();
  654.       return;
  655.     }
  656.  
  657.     if (!p.touching && !edata.end) {
  658.       return fireStart(evt, target, edata);
  659.     }
  660.  
  661.     if (edata.move && p.touching) {
  662.       return fireMove(evt, edata);
  663.     }
  664.  
  665.     if (p.brokenModel_4_1) {
  666.       if (p.touching && !edata.frame) {
  667.         return fireProvisionalEnd(evt, edata);
  668.       }
  669.     } else {
  670.       if (edata.end && p.touching) {
  671.         return fireProvisionalEnd(evt, edata);
  672.       }
  673.     }
  674.  
  675.     if (
  676.       p.brokenModel_4_1 &&
  677.       p.originator != element &&
  678.       edata.frame &&
  679.       edata.end
  680.     ) {
  681.       evt.preventDefault();
  682.       return;
  683.     }
  684.  
  685.     if (edata.frame && edata.end && p.touching) {
  686.       return fireProvisionalEnd(evt, edata);
  687.     }
  688.   }
  689.  
  690.  
  691.   function fireStart(evt, target, edata) {
  692.     p.touching = target;
  693.     p.edataPrev = edata;
  694.     return fireTouchEvent(p.touching, 'start', evt);
  695.   }
  696.  
  697.  
  698.   function fireMove(evt, edata) {
  699.     clearProvisionalEnd();
  700.     p.edataPrev = edata;
  701.     return fireTouchEvent(p.touching, 'move', evt);
  702.   }
  703.  
  704.  
  705.   function fireEnd(evt, edata) {
  706.     var result = fireTouchEvent(p.touching, 'end', evt);
  707.     p.edataPrev = edata;
  708.     p.touching = null;
  709.     return result;
  710.   }
  711.  
  712.  
  713.   function fireProvisionalEnd(evt, edata) {
  714.     clearProvisionalEnd();
  715.     var mimicEvt = mimicTouchEvent(p.touching, 'end', evt);
  716.     p.edataPrev = edata;
  717.  
  718.     p.provisionalEnd = setTimeout(
  719.       function() {
  720.         if (p.touching) {
  721.           p.touching.dispatchEvent(mimicEvt);
  722.           p.touching = null;
  723.         }
  724.       },
  725.       30
  726.     );
  727.   }
  728.  
  729.  
  730.   function clearProvisionalEnd() {
  731.     if (p.provisionalEnd) {
  732.       clearTimeout(p.provisionalEnd);
  733.       p.provisionalEnd = null;
  734.     }
  735.   }
  736.  
  737.  
  738.   function mimicTouchEvent(target, newtype, evt) {
  739.     var cloneTouch = function (t) {
  740.       return document.createTouch(
  741.         document.defaultView,
  742.         target,
  743.         t.identifier,
  744.         t.screenX,
  745.         t.screenY,
  746.         t.screenX,
  747.         t.screenY
  748.       );
  749.     }
  750.  
  751.     var findTouch = function (id) {
  752.       for (var i = 0; i < touches.all.length; ++i) {
  753.         if (touches.all[i].identifier == id) {
  754.           return touches.all[i];
  755.         }
  756.       }
  757.     }
  758.  
  759.     var touches = { all: [], target: [], changed: [] };
  760.     for (var i = 0; i < evt.touches.length; ++i) {
  761.       touches.all.push(cloneTouch(evt.touches[i]));
  762.     }
  763.     for (var i = 0; i < evt.targetTouches.length; ++i) {
  764.       touches.target.push(
  765.         findTouch(evt.targetTouches[i].identifier) ||
  766.         cloneTouch(evt.targetTouches[i])
  767.       );
  768.     }
  769.     for (var i = 0; i < evt.changedTouches.length; ++i) {
  770.       touches.changed.push(
  771.         findTouch(evt.changedTouches[i].identifier) ||
  772.         cloneTouch(evt.changedTouches[i])
  773.       );
  774.     }
  775.  
  776.     var mimicEvt = document.createEvent('TouchEvent');
  777.     mimicEvt.initTouchEvent(
  778.       "contact"+newtype,
  779.       true,
  780.       true,
  781.       document.defaultView,
  782.       evt.detail,
  783.       evt.screenX,
  784.       evt.screenY,
  785.       evt.screenX,
  786.       evt.screenY,
  787.       evt.ctrlKey,
  788.       evt.altKey,
  789.       evt.shiftKey,
  790.       evt.metaKey,
  791.       document.createTouchList.apply(document, touches.all),
  792.       document.createTouchList.apply(document, touches.target),
  793.       document.createTouchList.apply(document, touches.changed),
  794.       evt.scale,
  795.       evt.rotation
  796.     );
  797.  
  798.     return mimicEvt;
  799.   }
  800.  
  801.  
  802.   function fireTouchEvent(target, newtype, evt) {
  803.     var mimicEvt = mimicTouchEvent(target, newtype, evt);
  804.     var result = target.dispatchEvent(mimicEvt);
  805.     if (!result) {
  806.       evt.preventDefault();
  807.     }
  808.     return result;
  809.   }
  810.  
  811.  
  812.   API.listen = listen;
  813.   API.listenOnIframe = listenOnIframe;
  814.  
  815.   return API;
  816. }
  817.  
  818.  
  819. Monocle.Events.listenOnIframe = function (frame) {
  820.   if (!Monocle.Browser.has.iframeTouchBug) {
  821.     return;
  822.   }
  823.   Monocle.Events.tMonitor = Monocle.Events.tMonitor ||
  824.     new Monocle.Events.TouchMonitor();
  825.   Monocle.Events.tMonitor.listenOnIframe(frame);
  826. }
  827.  
  828. Monocle.pieceLoaded('events');
  829. Monocle.Styles = {
  830.   applyRules: function (elem, rules) {
  831.     if (typeof rules != 'string') {
  832.       var parts = [];
  833.       for (var declaration in rules) {
  834.         parts.push(declaration+": "+rules[declaration]+";")
  835.       }
  836.       rules = parts.join(" ");
  837.     }
  838.     elem.style.cssText += ';'+rules;
  839.     return elem.style.cssText;
  840.   },
  841.  
  842.   affix: function (elem, property, value) {
  843.     var target = elem.style ? elem.style : elem;
  844.     target[Monocle.Browser.CSSProps.toDOMProp(property)] = value;
  845.   },
  846.  
  847.   setX: function (elem, x) {
  848.     var s = elem.style;
  849.     if (typeof x == "number") { x += "px"; }
  850.     if (Monocle.Browser.has.transform3d) {
  851.       s.webkitTransform = "translate3d("+x+", 0, 0)";
  852.     } else {
  853.       s.webkitTransform = "translateX("+x+")";
  854.     }
  855.     s.MozTransform = s.OTransform = s.transform = "translateX("+x+")";
  856.     return x;
  857.   },
  858.  
  859.   setY: function (elem, y) {
  860.     var s = elem.style;
  861.     if (typeof y == "number") { y += "px"; }
  862.     if (Monocle.Browser.has.transform3d) {
  863.       s.webkitTransform = "translate3d(0, "+y+", 0)";
  864.     } else {
  865.       s.webkitTransform = "translateY("+y+")";
  866.     }
  867.     s.MozTransform = s.OTransform = s.transform = "translateY("+y+")";
  868.     return y;
  869.   }
  870. }
  871.  
  872.  
  873. Monocle.Styles.container = {
  874.   "position": "absolute",
  875.   "top": "0",
  876.   "left": "0",
  877.   "bottom": "0",
  878.   "right": "0"
  879. }
  880.  
  881. Monocle.Styles.page = {
  882.   "position": "absolute",
  883.   "z-index": "1",
  884.   "-webkit-user-select": "none",
  885.   "-moz-user-select": "none",
  886.   "user-select": "none",
  887.   "-webkit-transform": "translate3d(0,0,0)"
  888.  
  889.   /*
  890.   "background": "white",
  891.   "top": "0",
  892.   "left": "0",
  893.   "bottom": "0",
  894.   "right": "0"
  895.   */
  896. }
  897.  
  898. Monocle.Styles.sheaf = {
  899.   "position": "absolute",
  900.   "overflow": "hidden" // Required by MobileSafari to constrain inner iFrame.
  901.  
  902.   /*
  903.   "top": "0",
  904.   "left": "0",
  905.   "bottom": "0",
  906.   "right": "0"
  907.   */
  908. }
  909.  
  910. Monocle.Styles.component = {
  911.   "display": "block",
  912.   "width": "100%",
  913.   "height": "100%",
  914.   "border": "none",
  915.   "overflow": "hidden",
  916.   "-webkit-user-select": "none",
  917.   "-moz-user-select": "none",
  918.   "user-select": "none"
  919. }
  920.  
  921. Monocle.Styles.control = {
  922.   "z-index": "100",
  923.   "cursor": "pointer"
  924. }
  925.  
  926. Monocle.Styles.overlay = {
  927.   "position": "absolute",
  928.   "display": "none",
  929.   "width": "100%",
  930.   "height": "100%",
  931.   "z-index": "1000"
  932. }
  933.  
  934.  
  935.  
  936. Monocle.pieceLoaded('styles');
  937. Monocle.Reader = function (node, bookData, options, onLoadCallback) {
  938.   if (Monocle == this) {
  939.     return new Monocle.Reader(node, bookData, options, onLoadCallback);
  940.   }
  941.  
  942.   var API = { constructor: Monocle.Reader }
  943.   var k = API.constants = API.constructor;
  944.   var p = API.properties = {
  945.     initialized: false,
  946.  
  947.     book: null,
  948.  
  949.     graph: {},
  950.  
  951.     pageStylesheets: [],
  952.  
  953.     systemId: (options ? options.systemId : null) || k.DEFAULT_SYSTEM_ID,
  954.  
  955.     classPrefix: k.DEFAULT_CLASS_PREFIX,
  956.  
  957.     controls: [],
  958.  
  959.     resizeTimer: null
  960.   }
  961.  
  962.   var dom;
  963.  
  964.  
  965.   function initialize(node, bookData, options, onLoadCallback) {
  966.     var box = typeof(node) == "string" ?  document.getElementById(node) : node;
  967.     dom = API.dom = box.dom = new Monocle.Factory(box, 'box', 0, API);
  968.  
  969.     options = options || {}
  970.  
  971.     dispatchEvent("monocle:initializing");
  972.  
  973.     var bk;
  974.     if (bookData) {
  975.       bk = new Monocle.Book(bookData);
  976.     } else {
  977.       bk = Monocle.Book.fromNodes([box.cloneNode(true)]);
  978.     }
  979.     box.innerHTML = "";
  980.  
  981.     positionBox();
  982.  
  983.     attachFlipper(options.flipper);
  984.  
  985.     createReaderElements();
  986.  
  987.     p.defaultStyles = addPageStyles(k.DEFAULT_STYLE_RULES, false);
  988.  
  989.     primeFrames(options.primeURL, function () {
  990.       applyStyles();
  991.  
  992.       listen('monocle:componentchange', persistPageStylesOnComponentChange);
  993.  
  994.       p.flipper.listenForInteraction(options.panels);
  995.  
  996.       setBook(bk, options.place, function () {
  997.         p.initialized = true;
  998.         if (onLoadCallback) { onLoadCallback(API); }
  999.         dispatchEvent("monocle:loaded");
  1000.       });
  1001.     });
  1002.   }
  1003.  
  1004.  
  1005.   function positionBox() {
  1006.     var currPosVal;
  1007.     var box = dom.find('box');
  1008.     if (document.defaultView) {
  1009.       var currStyle = document.defaultView.getComputedStyle(box, null);
  1010.       currPosVal = currStyle.getPropertyValue('position');
  1011.     } else if (box.currentStyle) {
  1012.       currPosVal = box.currentStyle.position
  1013.     }
  1014.     if (["absolute", "relative"].indexOf(currPosVal) == -1) {
  1015.       box.style.position = "relative";
  1016.     }
  1017.   }
  1018.  
  1019.  
  1020.   function attachFlipper(flipperClass) {
  1021.     if (!Monocle.Browser.has.columns) {
  1022.       flipperClass = Monocle.Flippers[k.FLIPPER_LEGACY_CLASS];
  1023.       if (!flipperClass) {
  1024.         return dom.append(
  1025.           'div',
  1026.           'abortMsg',
  1027.           { 'class': k.abortMessage.CLASSNAME, 'html': k.abortMessage.TEXT }
  1028.         );
  1029.       }
  1030.     } else if (!flipperClass) {
  1031.       flipperClass = Monocle.Flippers[k.FLIPPER_DEFAULT_CLASS];
  1032.       if (!flipperClass) {
  1033.         throw("No flipper class");
  1034.       }
  1035.     }
  1036.     p.flipper = new flipperClass(API, null, p.readerOptions);
  1037.   }
  1038.  
  1039.  
  1040.   function createReaderElements() {
  1041.     var cntr = dom.append('div', 'container');
  1042.     for (var i = 0; i < p.flipper.pageCount; ++i) {
  1043.       var page = cntr.dom.append('div', 'page', i);
  1044.       page.m = { reader: API, pageIndex: i, place: null }
  1045.       page.m.sheafDiv = page.dom.append('div', 'sheaf', i);
  1046.       page.m.activeFrame = page.m.sheafDiv.dom.append('iframe', 'component', i);
  1047.       page.m.activeFrame.m = { 'pageDiv': page }
  1048.       p.flipper.addPage(page);
  1049.       Monocle.Events.listenOnIframe(page.m.activeFrame);
  1050.     }
  1051.     dom.append('div', 'overlay');
  1052.     dispatchEvent("monocle:loading");
  1053.   }
  1054.  
  1055.  
  1056.   function primeFrames(url, callback) {
  1057.     url = url || "about:blank";
  1058.  
  1059.     var pageMax = p.flipper.pageCount;
  1060.     var pageCount = 0;
  1061.  
  1062.     var cb = function (evt) {
  1063.       var frame = evt.target || evt.srcElement;
  1064.       Monocle.Events.deafen(frame, 'load', cb);
  1065.       if (Monocle.Browser.is.WebKit) {
  1066.         frame.contentDocument.documentElement.style.overflow = "hidden";
  1067.       }
  1068.       if ((pageCount += 1) == pageMax) {
  1069.         Monocle.defer(callback);
  1070.       }
  1071.     }
  1072.  
  1073.     for (var i = 0; i < pageMax; ++i) {
  1074.       var page = dom.find('page', i);
  1075.       page.m.activeFrame.style.visibility = "hidden";
  1076.       page.m.activeFrame.setAttribute('frameBorder', 0);
  1077.       page.m.activeFrame.setAttribute('scrolling', 'no');
  1078.       Monocle.Events.listen(page.m.activeFrame, 'load', cb);
  1079.       page.m.activeFrame.src = url;
  1080.     }
  1081.   }
  1082.  
  1083.  
  1084.   function applyStyles() {
  1085.     dom.find('container').dom.setStyles(Monocle.Styles.container);
  1086.     for (var i = 0; i < p.flipper.pageCount; ++i) {
  1087.       var page = dom.find('page', i);
  1088.       page.dom.setStyles(Monocle.Styles.page);
  1089.       dom.find('sheaf', i).dom.setStyles(Monocle.Styles.sheaf);
  1090.       var cmpt = dom.find('component', i)
  1091.       cmpt.dom.setStyles(Monocle.Styles.component);
  1092.       Monocle.Styles.applyRules(cmpt.contentDocument.body, Monocle.Styles.body);
  1093.     }
  1094.     dom.find('overlay').dom.setStyles(Monocle.Styles.overlay);
  1095.     dispatchEvent('monocle:styles');
  1096.   }
  1097.  
  1098.  
  1099.   function setBook(bk, place, callback) {
  1100.     p.book = bk;
  1101.     var pageCount = 0;
  1102.     if (typeof callback == 'function') {
  1103.       var watcher = function (evt) {
  1104.         if ((pageCount += 1) == p.flipper.pageCount) {
  1105.           deafen('monocle:componentchange', watcher);
  1106.           callback();
  1107.         }
  1108.       }
  1109.       listen('monocle:componentchange', watcher);
  1110.     }
  1111.     p.flipper.moveTo(place || { page: 1 });
  1112.   }
  1113.  
  1114.  
  1115.   function getBook() {
  1116.     return p.book;
  1117.   }
  1118.  
  1119.  
  1120.   function resized() {
  1121.     if (!p.initialized) {
  1122.       console.warn('Attempt to resize book before initialization.');
  1123.     }
  1124.     if (!dispatchEvent("monocle:resizing", {}, true)) {
  1125.       return;
  1126.     }
  1127.     clearTimeout(p.resizeTimer);
  1128.     p.resizeTimer = setTimeout(
  1129.       function () {
  1130.         p.flipper.moveTo({ page: pageNumber() });
  1131.         dispatchEvent("monocle:resize");
  1132.       },
  1133.       k.durations.RESIZE_DELAY
  1134.     );
  1135.   }
  1136.  
  1137.  
  1138.   function pageNumber(pageDiv) {
  1139.     var place = getPlace(pageDiv);
  1140.     return place ? (place.pageNumber() || 1) : 1;
  1141.   }
  1142.  
  1143.  
  1144.   function getPlace(pageDiv) {
  1145.     if (!p.initialized) {
  1146.       console.warn('Attempt to access place before initialization.');
  1147.     }
  1148.     return p.flipper.getPlace(pageDiv);
  1149.   }
  1150.  
  1151.  
  1152.   function moveTo(locus, callback) {
  1153.     if (!p.initialized) {
  1154.       console.warn('Attempt to move place before initialization.');
  1155.     }
  1156.     var fn = callback;
  1157.     if (!locus.direction) {
  1158.       dispatchEvent('monocle:jumping', { locus: locus });
  1159.       fn = function () {
  1160.         dispatchEvent('monocle:jump', { locus: locus });
  1161.         if (callback) { callback(); }
  1162.       }
  1163.     }
  1164.     p.flipper.moveTo(locus, fn);
  1165.   }
  1166.  
  1167.  
  1168.   function skipToChapter(src) {
  1169.     var locus = p.book.locusOfChapter(src);
  1170.     if (locus) {
  1171.       moveTo(locus);
  1172.       return true;
  1173.     } else {
  1174.       dispatchEvent("monocle:notfound", { href: src });
  1175.       return false;
  1176.     }
  1177.   }
  1178.  
  1179.  
  1180.   function addControl(ctrl, cType, options) {
  1181.     for (var i = 0; i < p.controls.length; ++i) {
  1182.       if (p.controls[i].control == ctrl) {
  1183.         console.warn("Already added control: " + ctrl);
  1184.         return;
  1185.       }
  1186.     }
  1187.  
  1188.     options = options || {};
  1189.  
  1190.     var ctrlData = {
  1191.       control: ctrl,
  1192.       elements: [],
  1193.       controlType: cType
  1194.     }
  1195.     p.controls.push(ctrlData);
  1196.  
  1197.     var ctrlElem;
  1198.     var cntr = dom.find('container'), overlay = dom.find('overlay');
  1199.     if (!cType || cType == "standard") {
  1200.       ctrlElem = ctrl.createControlElements(cntr);
  1201.       cntr.appendChild(ctrlElem);
  1202.       ctrlData.elements.push(ctrlElem);
  1203.     } else if (cType == "page") {
  1204.       for (var i = 0; i < p.flipper.pageCount; ++i) {
  1205.         var page = dom.find('page', i);
  1206.         var runner = ctrl.createControlElements(page);
  1207.         page.appendChild(runner);
  1208.         ctrlData.elements.push(runner);
  1209.       }
  1210.     } else if (cType == "modal" || cType == "popover") {
  1211.       ctrlElem = ctrl.createControlElements(overlay);
  1212.       overlay.appendChild(ctrlElem);
  1213.       ctrlData.elements.push(ctrlElem);
  1214.       ctrlData.usesOverlay = true;
  1215.     } else if (cType == "invisible") {
  1216.       if (
  1217.         typeof(ctrl.createControlElements) == "function" &&
  1218.         (ctrlElem = ctrl.createControlElements(cntr))
  1219.       ) {
  1220.         cntr.appendChild(ctrlElem);
  1221.         ctrlData.elements.push(ctrlElem);
  1222.       }
  1223.     } else {
  1224.       console.warn("Unknown control type: " + cType);
  1225.     }
  1226.  
  1227.     for (var i = 0; i < ctrlData.elements.length; ++i) {
  1228.       Monocle.Styles.applyRules(ctrlData.elements[i], Monocle.Styles.control);
  1229.     }
  1230.  
  1231.     if (options.hidden) {
  1232.       hideControl(ctrl);
  1233.     } else {
  1234.       showControl(ctrl);
  1235.     }
  1236.  
  1237.     if (typeof ctrl.assignToReader == 'function') {
  1238.       ctrl.assignToReader(API);
  1239.     }
  1240.  
  1241.     return ctrl;
  1242.   }
  1243.  
  1244.  
  1245.   function dataForControl(ctrl) {
  1246.     for (var i = 0; i < p.controls.length; ++i) {
  1247.       if (p.controls[i].control == ctrl) {
  1248.         return p.controls[i];
  1249.       }
  1250.     }
  1251.   }
  1252.  
  1253.  
  1254.   function hideControl(ctrl) {
  1255.     var controlData = dataForControl(ctrl);
  1256.     if (!controlData) {
  1257.       console.warn("No data for control: " + ctrl);
  1258.       return;
  1259.     }
  1260.     if (controlData.hidden) {
  1261.       return;
  1262.     }
  1263.     for (var i = 0; i < controlData.elements.length; ++i) {
  1264.       controlData.elements[i].style.display = "none";
  1265.     }
  1266.     if (controlData.usesOverlay) {
  1267.       var overlay = dom.find('overlay');
  1268.       overlay.style.display = "none";
  1269.       Monocle.Events.deafenForContact(overlay, overlay.listeners);
  1270.     }
  1271.     controlData.hidden = true;
  1272.     if (ctrl.properties) {
  1273.       ctrl.properties.hidden = true;
  1274.     }
  1275.     dispatchEvent('controlhide', ctrl, false);
  1276.   }
  1277.  
  1278.  
  1279.   function showControl(ctrl) {
  1280.     var controlData = dataForControl(ctrl);
  1281.     if (!controlData) {
  1282.       console.warn("No data for control: " + ctrl);
  1283.       return;
  1284.     }
  1285.     if (controlData.hidden == false) {
  1286.       return;
  1287.     }
  1288.     for (var i = 0; i < controlData.elements.length; ++i) {
  1289.       controlData.elements[i].style.display = "block";
  1290.     }
  1291.     var overlay = dom.find('overlay');
  1292.     if (controlData.usesOverlay) {
  1293.       overlay.style.display = "block";
  1294.     }
  1295.     if (controlData.controlType == "popover") {
  1296.       overlay.listeners = Monocle.Events.listenForContact(
  1297.         overlay,
  1298.         {
  1299.           start: function (evt) {
  1300.             obj = evt.target || window.event.srcElement;
  1301.             do {
  1302.               if (obj == controlData.elements[0]) { return true; }
  1303.             } while (obj && (obj = obj.parentNode));
  1304.             hideControl(ctrl);
  1305.           },
  1306.           move: function (evt) {
  1307.             evt.preventDefault();
  1308.           }
  1309.         }
  1310.       );
  1311.     }
  1312.     controlData.hidden = false;
  1313.     if (ctrl.properties) {
  1314.       ctrl.properties.hidden = false;
  1315.     }
  1316.     dispatchEvent('controlshow', ctrl, false);
  1317.   }
  1318.  
  1319.  
  1320.   function dispatchEvent(evtType, data, cancelable) {
  1321.     if (!document.createEvent) {
  1322.       return true;
  1323.     }
  1324.     var evt = document.createEvent("Events");
  1325.     evt.initEvent(evtType, false, cancelable || false);
  1326.     evt.m = data;
  1327.     try {
  1328.       return dom.find('box').dispatchEvent(evt);
  1329.     } catch(e) {
  1330.       console.warn("Failed to dispatch event: " + evtType);
  1331.       return false;
  1332.     }
  1333.   }
  1334.  
  1335.  
  1336.   function listen(evtType, fn, useCapture) {
  1337.     Monocle.Events.listen(dom.find('box'), evtType, fn, useCapture);
  1338.   }
  1339.  
  1340.  
  1341.   function deafen(evtType, fn) {
  1342.     Monocle.Events.deafen(dom.find('box'), evtType, fn);
  1343.   }
  1344.  
  1345.  
  1346.   /* PAGE STYLESHEETS */
  1347.  
  1348.   function addPageStyles(styleRules, restorePlace) {
  1349.     return changingStylesheet(function () {
  1350.       p.pageStylesheets.push(styleRules);
  1351.       var sheetIndex = p.pageStylesheets.length - 1;
  1352.  
  1353.       for (var i = 0; i < p.flipper.pageCount; ++i) {
  1354.         var doc = dom.find('component', i).contentDocument;
  1355.         addPageStylesheet(doc, sheetIndex);
  1356.       }
  1357.       return sheetIndex;
  1358.     }, restorePlace);
  1359.   }
  1360.  
  1361.  
  1362.   function updatePageStyles(sheetIndex, styleRules, restorePlace) {
  1363.     return changingStylesheet(function () {
  1364.       p.pageStylesheets[sheetIndex] = styleRules;
  1365.       if (typeof styleRules.join == "function") {
  1366.         styleRules = styleRules.join("\n");
  1367.       }
  1368.       for (var i = 0; i < p.flipper.pageCount; ++i) {
  1369.         var doc = dom.find('component', i).contentDocument;
  1370.         var styleTag = doc.getElementById('monStylesheet'+sheetIndex);
  1371.         if (!styleTag) {
  1372.           console.warn('No such stylesheet: ' + sheetIndex);
  1373.           return;
  1374.         }
  1375.         if (styleTag.styleSheet) {
  1376.           styleTag.styleSheet.cssText = styleRules;
  1377.         } else {
  1378.           styleTag.replaceChild(
  1379.             doc.createTextNode(styleRules),
  1380.             styleTag.firstChild
  1381.           );
  1382.         }
  1383.       }
  1384.     }, restorePlace);
  1385.   }
  1386.  
  1387.  
  1388.   function removePageStyles(sheetIndex, restorePlace) {
  1389.     return changingStylesheet(function () {
  1390.       p.pageStylesheets[sheetIndex] = null;
  1391.       for (var i = 0; i < p.flipper.pageCount; ++i) {
  1392.         var doc = dom.find('component', i).contentDocument;
  1393.         var styleTag = doc.getElementById('monStylesheet'+sheetIndex);
  1394.         styleTag.parentNode.removeChild(styleTag);
  1395.       }
  1396.     }, restorePlace);
  1397.   }
  1398.  
  1399.  
  1400.   function persistPageStylesOnComponentChange(evt) {
  1401.     var doc = evt.m['document'];
  1402.     doc.documentElement.id = p.systemId;
  1403.     for (var i = 0; i < p.pageStylesheets.length; ++i) {
  1404.       if (p.pageStylesheets[i]) {
  1405.         addPageStylesheet(doc, i);
  1406.       }
  1407.     }
  1408.   }
  1409.  
  1410.  
  1411.   function changingStylesheet(callback, restorePlace) {
  1412.     restorePlace = (restorePlace === false) ? false : true;
  1413.     if (restorePlace) {
  1414.       dispatchEvent("monocle:stylesheetchanging", {});
  1415.     }
  1416.     var result = callback();
  1417.     if (restorePlace) {
  1418.       p.flipper.moveTo({ page: pageNumber() });
  1419.       Monocle.defer(
  1420.         function () { dispatchEvent("monocle:stylesheetchange", {}); }
  1421.       );
  1422.     }
  1423.     return result;
  1424.   }
  1425.  
  1426.  
  1427.   function addPageStylesheet(doc, sheetIndex) {
  1428.     var styleRules = p.pageStylesheets[sheetIndex];
  1429.  
  1430.     if (!styleRules) {
  1431.       return;
  1432.     }
  1433.  
  1434.     var head = doc.getElementsByTagName('head')[0];
  1435.     if (!head) {
  1436.       if (!doc.documentElement) { return; } // FIXME: IE doesn't like docElem.
  1437.       head = doc.createElement('head');
  1438.       doc.documentElement.appendChild(head);
  1439.     }
  1440.  
  1441.     if (typeof styleRules.join == "function") {
  1442.       styleRules = styleRules.join("\n");
  1443.     }
  1444.  
  1445.     var styleTag = doc.createElement('style');
  1446.     styleTag.type = 'text/css';
  1447.     styleTag.id = "monStylesheet"+sheetIndex;
  1448.     if (styleTag.styleSheet) {
  1449.       styleTag.styleSheet.cssText = styleRules;
  1450.     } else {
  1451.       styleTag.appendChild(doc.createTextNode(styleRules));
  1452.     }
  1453.  
  1454.     head.appendChild(styleTag);
  1455.  
  1456.     return styleTag;
  1457.   }
  1458.  
  1459.  
  1460.   function visiblePages() {
  1461.     return p.flipper.visiblePages ? p.flipper.visiblePages() : [dom.find('page')];
  1462.   }
  1463.  
  1464.  
  1465.   API.getBook = getBook;
  1466.   API.getPlace = getPlace;
  1467.   API.moveTo = moveTo;
  1468.   API.skipToChapter = skipToChapter;
  1469.   API.resized = resized;
  1470.   API.addControl = addControl;
  1471.   API.hideControl = hideControl;
  1472.   API.showControl = showControl;
  1473.   API.dispatchEvent = dispatchEvent;
  1474.   API.listen = listen;
  1475.   API.deafen = deafen;
  1476.   API.addPageStyles = addPageStyles;
  1477.   API.updatePageStyles = updatePageStyles;
  1478.   API.removePageStyles = removePageStyles;
  1479.   API.visiblePages = visiblePages;
  1480.  
  1481.   initialize(node, bookData, options, onLoadCallback);
  1482.  
  1483.   return API;
  1484. }
  1485.  
  1486. Monocle.Reader.durations = {
  1487.   RESIZE_DELAY: 100
  1488. }
  1489. Monocle.Reader.abortMessage = {
  1490.   CLASSNAME: "monocleAbortMessage",
  1491.   TEXT: "Your browser does not support this technology."
  1492. }
  1493. Monocle.Reader.DEFAULT_SYSTEM_ID = 'RS:monocle'
  1494. Monocle.Reader.DEFAULT_CLASS_PREFIX = 'monelem_'
  1495. Monocle.Reader.FLIPPER_DEFAULT_CLASS = "Slider";
  1496. Monocle.Reader.FLIPPER_LEGACY_CLASS = "Legacy";
  1497. Monocle.Reader.DEFAULT_STYLE_RULES = [
  1498.   "html * {" +
  1499.     "text-rendering: auto !important;" +
  1500.     "word-wrap: break-word !important;" +
  1501.     (Monocle.Browser.has.floatColumnBug ? "float: none !important;" : "") +
  1502.   "}" +
  1503.   "body {" +
  1504.     "margin: 0 !important;" +
  1505.     "padding: 0 !important;" +
  1506.     "-webkit-text-size-adjust: none;" +
  1507.   "}" +
  1508.   "table, img {" +
  1509.     "max-width: 100% !important;" +
  1510.     "max-height: 90% !important;" +
  1511.   "}"
  1512. ]
  1513.  
  1514.  
  1515. Monocle.pieceLoaded('reader');
  1516. /* BOOK */
  1517.  
  1518. /* The Book handles movement through the content by the reader page elements.
  1519.  *
  1520.  * It's responsible for instantiating components as they are required,
  1521.  * and for calculating which component and page number to move to (based on
  1522.  * requests from the Reader).
  1523.  *
  1524.  * It should set and know the place of each page element too.
  1525.  *
  1526.  */
  1527. Monocle.Book = function (dataSource) {
  1528.   if (Monocle == this) { return new Monocle.Book(dataSource); }
  1529.  
  1530.   var API = { constructor: Monocle.Book }
  1531.   var k = API.constants = API.constructor;
  1532.   var p = API.properties = {
  1533.     dataSource: dataSource,
  1534.     components: [],
  1535.     chapters: {} // flat arrays of chapters per component
  1536.   }
  1537.  
  1538.  
  1539.   function initialize() {
  1540.     p.componentIds = dataSource.getComponents();
  1541.     p.contents = dataSource.getContents();
  1542.     p.lastCIndex = p.componentIds.length - 1;
  1543.   }
  1544.  
  1545.  
  1546.   function pageNumberAt(pageDiv, locus) {
  1547.     locus.load = false;
  1548.     var currComponent = pageDiv.m.activeFrame ?
  1549.       pageDiv.m.activeFrame.m.component :
  1550.       null;
  1551.     var component = null;
  1552.     var cIndex = p.componentIds.indexOf(locus.componentId);
  1553.     if (cIndex < 0 && !currComponent) {
  1554.       locus.load = true;
  1555.       locus.componentId = p.componentIds[0];
  1556.       return locus;
  1557.     } else if (cIndex < 0) {
  1558.       component = currComponent;
  1559.       locus.componentId = pageDiv.m.activeFrame.m.component.properties.id;
  1560.       cIndex = p.componentIds.indexOf(locus.componentId);
  1561.     } else if (!p.components[cIndex] || p.components[cIndex] != currComponent) {
  1562.       locus.load = true;
  1563.       return locus;
  1564.     } else {
  1565.       component = currComponent;
  1566.     }
  1567.  
  1568.     var result = { load: false, componentId: locus.componentId, page: 1 }
  1569.  
  1570.     var lastPageNum = { 'old': component.lastPageNumber() }
  1571.     var changedDims = component.updateDimensions(pageDiv);
  1572.     lastPageNum['new'] = component.lastPageNumber();
  1573.  
  1574.     if (typeof(locus.page) == "number") {
  1575.       result.page = locus.page;
  1576.     } else if (typeof(locus.pagesBack) == "number") {
  1577.       result.page = lastPageNum['new'] + locus.pagesBack;
  1578.     } else if (typeof(locus.percent) == "number") {
  1579.       var place = new Monocle.Place();
  1580.       place.setPlace(component, 1);
  1581.       result.page = place.pageAtPercentageThrough(locus.percent);
  1582.     } else if (typeof(locus.direction) == "number") {
  1583.       if (!pageDiv.m.place) {
  1584.         console.warn("Can't move in a direction if pageDiv has no place.");
  1585.       }
  1586.       result.page = pageDiv.m.place.pageNumber();
  1587.       result.page += locus.direction;
  1588.     } else if (typeof(locus.anchor) == "string") {
  1589.       result.page = component.pageForChapter(locus.anchor, pageDiv);
  1590.     } else if (typeof(locus.position) == "string") {
  1591.       if (locus.position == "start") {
  1592.         result.page = 1;
  1593.       } else if (locus.position == "end") {
  1594.         result.page = lastPageNum['new'];
  1595.       }
  1596.     } else {
  1597.       console.warn("Unrecognised locus: " + locus);
  1598.     }
  1599.  
  1600.     if (changedDims && lastPageNum['old']) {
  1601.       result.page = Math.round(
  1602.         lastPageNum['new'] * (result.page / lastPageNum['old'])
  1603.       );
  1604.     }
  1605.  
  1606.     if (result.page < 1) {
  1607.       if (cIndex == 0) {
  1608.         result.page = 1;
  1609.       } else {
  1610.         result.load = true;
  1611.         result.componentId = p.componentIds[cIndex - 1];
  1612.         result.pagesBack = result.page;
  1613.         result.page = null;
  1614.       }
  1615.     } else if (result.page > lastPageNum['new']) {
  1616.       if (cIndex == p.lastCIndex) {
  1617.         result.page = lastPageNum['new'];
  1618.       } else {
  1619.         result.load = true;
  1620.         result.componentId = p.componentIds[cIndex + 1];
  1621.         result.page -= lastPageNum['new'];
  1622.       }
  1623.     }
  1624.  
  1625.     return result;
  1626.   }
  1627.  
  1628.  
  1629.   function setPageAt(pageDiv, locus) {
  1630.     locus = pageNumberAt(pageDiv, locus);
  1631.     if (!locus.load) {
  1632.       var component = p.components[p.componentIds.indexOf(locus.componentId)];
  1633.       pageDiv.m.place = pageDiv.m.place || new Monocle.Place();
  1634.       pageDiv.m.place.setPlace(component, locus.page);
  1635.  
  1636.       var evtData = {
  1637.         page: pageDiv,
  1638.         locus: locus,
  1639.         pageNumber: pageDiv.m.place.pageNumber(),
  1640.         componentId: locus.componentId
  1641.       }
  1642.       pageDiv.m.reader.dispatchEvent("monocle:pagechange", evtData);
  1643.     }
  1644.     return locus;
  1645.   }
  1646.  
  1647.  
  1648.   function loadPageAt(pageDiv, locus, callback, progressCallback) {
  1649.     var cIndex = p.componentIds.indexOf(locus.componentId);
  1650.     if (!locus.load || cIndex < 0) {
  1651.       locus = pageNumberAt(pageDiv, locus);
  1652.     }
  1653.  
  1654.     if (!locus.load) {
  1655.       callback(locus);
  1656.       return;
  1657.     }
  1658.  
  1659.     var findPageNumber = function () {
  1660.       locus = setPageAt(pageDiv, locus);
  1661.       if (locus.load) {
  1662.         loadPageAt(pageDiv, locus, callback, progressCallback)
  1663.       } else {
  1664.         callback(locus);
  1665.       }
  1666.     }
  1667.  
  1668.     var pgFindPageNumber = function () {
  1669.       progressCallback ? progressCallback(findPageNumber) : findPageNumber();
  1670.     }
  1671.  
  1672.     var applyComponent = function (component) {
  1673.       component.applyTo(pageDiv, pgFindPageNumber);
  1674.     }
  1675.  
  1676.     var pgApplyComponent = function (component) {
  1677.       progressCallback ?
  1678.         progressCallback(function () { applyComponent(component) }) :
  1679.         applyComponent(component);
  1680.     }
  1681.  
  1682.     loadComponent(cIndex, pgApplyComponent, pageDiv);
  1683.   }
  1684.  
  1685.  
  1686.   function setOrLoadPageAt(pageDiv, locus, callback, progressCallback) {
  1687.     locus = setPageAt(pageDiv, locus);
  1688.     if (locus.load) {
  1689.       loadPageAt(pageDiv, locus, callback, progressCallback);
  1690.     } else {
  1691.       callback(locus);
  1692.     }
  1693.   }
  1694.  
  1695.  
  1696.   function loadComponent(index, callback, pageDiv) {
  1697.     if (p.components[index]) {
  1698.       return callback(p.components[index]);
  1699.     }
  1700.     var cmptId = p.componentIds[index];
  1701.     if (pageDiv) {
  1702.       var evtData = { 'page': pageDiv, 'component': cmptId, 'index': index };
  1703.       pageDiv.m.reader.dispatchEvent('monocle:componentloading', evtData);
  1704.     }
  1705.     var fn = function (cmptSource) {
  1706.       if (pageDiv) {
  1707.         evtData['source'] = cmptSource;
  1708.         pageDiv.m.reader.dispatchEvent('monocle:componentloaded', evtData);
  1709.         html = evtData['html'];
  1710.       }
  1711.       p.components[index] = new Monocle.Component(
  1712.         API,
  1713.         cmptId,
  1714.         index,
  1715.         chaptersForComponent(cmptId),
  1716.         cmptSource
  1717.       );
  1718.       callback(p.components[index]);
  1719.     }
  1720.     var cmptSource = p.dataSource.getComponent(cmptId, fn);
  1721.     if (cmptSource && !p.components[index]) {
  1722.       fn(cmptSource);
  1723.     }
  1724.   }
  1725.  
  1726.  
  1727.   function chaptersForComponent(cmptId) {
  1728.     if (p.chapters[cmptId]) {
  1729.       return p.chapters[cmptId];
  1730.     }
  1731.     p.chapters[cmptId] = [];
  1732.     var matcher = new RegExp('^'+cmptId+"(\#(.+)|$)");
  1733.     var matches;
  1734.     var recurser = function (chp) {
  1735.       if (matches = chp.src.match(matcher)) {
  1736.         p.chapters[cmptId].push({
  1737.           title: chp.title,
  1738.           fragment: matches[2] || null
  1739.         });
  1740.       }
  1741.       if (chp.children) {
  1742.         for (var i = 0; i < chp.children.length; ++i) {
  1743.           recurser(chp.children[i]);
  1744.         }
  1745.       }
  1746.     }
  1747.  
  1748.     for (var i = 0; i < p.contents.length; ++i) {
  1749.       recurser(p.contents[i]);
  1750.     }
  1751.     return p.chapters[cmptId];
  1752.   }
  1753.  
  1754.  
  1755.   function locusOfChapter(src) {
  1756.     var matcher = new RegExp('^(.+?)(#(.*))?$');
  1757.     var matches = src.match(matcher);
  1758.     if (!matches) { return null; }
  1759.     var cmptId = componentIdMatching(matches[1]);
  1760.     if (!cmptId) { return null; }
  1761.     var locus = { componentId: cmptId }
  1762.     matches[3] ? locus.anchor = matches[3] : locus.position = "start";
  1763.     return locus;
  1764.   }
  1765.  
  1766.  
  1767.   function componentIdMatching(str) {
  1768.     for (var i = 0; i < p.componentIds.length; ++i) {
  1769.       if (str.indexOf(p.componentIds[i]) > -1) {
  1770.         return p.componentIds[i];
  1771.       }
  1772.     }
  1773.     return null;
  1774.   }
  1775.  
  1776.  
  1777.   API.getMetaData = dataSource.getMetaData;
  1778.   API.pageNumberAt = pageNumberAt;
  1779.   API.setPageAt = setPageAt;
  1780.   API.loadPageAt = loadPageAt;
  1781.   API.setOrLoadPageAt = setOrLoadPageAt;
  1782.   API.chaptersForComponent = chaptersForComponent;
  1783.   API.locusOfChapter = locusOfChapter;
  1784.  
  1785.   initialize();
  1786.  
  1787.   return API;
  1788. }
  1789.  
  1790.  
  1791. Monocle.Book.fromNodes = function (nodes) {
  1792.   var bookData = {
  1793.     getComponents: function () {
  1794.       return ['anonymous'];
  1795.     },
  1796.     getContents: function () {
  1797.       return [];
  1798.     },
  1799.     getComponent: function (n) {
  1800.       return { 'nodes': nodes };
  1801.     },
  1802.     getMetaData: function (key) {
  1803.     }
  1804.   }
  1805.  
  1806.   return new Monocle.Book(bookData);
  1807. }
  1808.  
  1809. Monocle.pieceLoaded('book');
  1810.  
  1811. Monocle.Place = function () {
  1812.  
  1813.   var API = { constructor: Monocle.Place }
  1814.   var k = API.constants = API.constructor;
  1815.   var p = API.properties = {
  1816.     component: null,
  1817.     percent: null
  1818.   }
  1819.  
  1820.  
  1821.   function setPlace(cmpt, pageN) {
  1822.     p.component = cmpt;
  1823.     p.percent = pageN / cmpt.lastPageNumber();
  1824.     p.chapter = null;
  1825.   }
  1826.  
  1827.  
  1828.   function setPercentageThrough(cmpt, percent) {
  1829.     p.component = cmpt;
  1830.     p.percent = percent;
  1831.     p.chapter = null;
  1832.   }
  1833.  
  1834.  
  1835.   function componentId() {
  1836.     return p.component.properties.id;
  1837.   }
  1838.  
  1839.  
  1840.   function percentageThrough() {
  1841.     return p.percent;
  1842.   }
  1843.  
  1844.  
  1845.   function pageAtPercentageThrough(pc) {
  1846.     return Math.max(Math.round(p.component.lastPageNumber() * pc), 1);
  1847.   }
  1848.  
  1849.  
  1850.   function pageNumber() {
  1851.     return pageAtPercentageThrough(p.percent);
  1852.   }
  1853.  
  1854.  
  1855.   function chapterInfo() {
  1856.     if (p.chapter) {
  1857.       return p.chapter;
  1858.     }
  1859.     return p.chapter = p.component.chapterForPage(pageNumber());
  1860.   }
  1861.  
  1862.  
  1863.   function chapterTitle() {
  1864.     var chp = chapterInfo();
  1865.     return chp ? chp.title : null;
  1866.   }
  1867.  
  1868.  
  1869.   function chapterSrc() {
  1870.     var src = componentId();
  1871.     var cinfo = chapterInfo();
  1872.     if (cinfo && cinfo.fragment) {
  1873.       src += "#" + cinfo.fragment;
  1874.     }
  1875.     return src;
  1876.   }
  1877.  
  1878.  
  1879.   function getLocus(options) {
  1880.     options = options || {};
  1881.     var locus = {
  1882.       page: pageNumber(),
  1883.       componentId: componentId()
  1884.     }
  1885.     if (options.direction) {
  1886.       locus.page += options.direction;
  1887.     }
  1888.     return locus;
  1889.   }
  1890.  
  1891.  
  1892.   function percentageOfBook() {
  1893.     componentIds = p.component.properties.book.properties.componentIds;
  1894.     componentSize = 1.0 / componentIds.length;
  1895.     var pc = componentIds.indexOf(componentId()) * componentSize;
  1896.     pc += componentSize * p.percent;
  1897.     return pc;
  1898.   }
  1899.  
  1900.  
  1901.   function onFirstPageOfBook() {
  1902.     return p.component.properties.index == 0 && pageNumber() == 1;
  1903.   }
  1904.  
  1905.  
  1906.   function onLastPageOfBook() {
  1907.     return (
  1908.       p.component.properties.index ==
  1909.         p.component.properties.book.properties.lastCIndex &&
  1910.       pageNumber() == p.component.lastPageNumber()
  1911.     );
  1912.   }
  1913.  
  1914.  
  1915.   API.setPlace = setPlace;
  1916.   API.setPercentageThrough = setPercentageThrough;
  1917.   API.componentId = componentId;
  1918.   API.percentageThrough = percentageThrough;
  1919.   API.pageAtPercentageThrough = pageAtPercentageThrough;
  1920.   API.pageNumber = pageNumber;
  1921.   API.chapterInfo = chapterInfo;
  1922.   API.chapterTitle = chapterTitle;
  1923.   API.chapterSrc = chapterSrc;
  1924.   API.getLocus = getLocus;
  1925.   API.percentageOfBook = percentageOfBook;
  1926.   API.onFirstPageOfBook = onFirstPageOfBook;
  1927.   API.onLastPageOfBook = onLastPageOfBook;
  1928.  
  1929.   return API;
  1930. }
  1931.  
  1932.  
  1933. Monocle.Place.FromPageNumber = function (component, pageNumber) {
  1934.   var place = new Monocle.Place();
  1935.   place.setPlace(component, pageNumber);
  1936.   return place;
  1937. }
  1938.  
  1939. Monocle.Place.FromPercentageThrough = function (component, percent) {
  1940.   var place = new Monocle.Place();
  1941.   place.setPercentageThrough(component, percent);
  1942.   return place;
  1943. }
  1944.  
  1945. Monocle.pieceLoaded('place');
  1946. /* COMPONENT */
  1947.  
  1948. Monocle.Component = function (book, id, index, chapters, source) {
  1949.  
  1950.   var API = { constructor: Monocle.Component }
  1951.   var k = API.constants = API.constructor;
  1952.   var p = API.properties = {
  1953.     book: book,
  1954.  
  1955.     id: id,
  1956.  
  1957.     index: index,
  1958.  
  1959.     chapters: chapters,
  1960.  
  1961.     source: source
  1962.   }
  1963.  
  1964.  
  1965.   function applyTo(pageDiv, callback) {
  1966.     var evtData = { 'page': pageDiv, 'source': p.source };
  1967.     pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);
  1968.  
  1969.     return loadFrame(
  1970.       pageDiv,
  1971.       function () {
  1972.         setupFrame(pageDiv, pageDiv.m.activeFrame);
  1973.         callback(pageDiv, API);
  1974.       }
  1975.     );
  1976.   }
  1977.  
  1978.  
  1979.   function loadFrame(pageDiv, callback) {
  1980.     var frame = pageDiv.m.activeFrame;
  1981.  
  1982.     frame.m.component = API;
  1983.  
  1984.     frame.style.visibility = "hidden";
  1985.  
  1986.  
  1987.     if (p.source.html || (typeof p.source == "string")) {   // HTML
  1988.       return loadFrameFromHTML(p.source.html || p.source, frame, callback);
  1989.     } else if (p.source.url) {                              // URL
  1990.       return loadFrameFromURL(p.source.url, frame, callback);
  1991.     } else if (p.source.nodes) {                            // NODES
  1992.       return loadFrameFromNodes(p.source.nodes, frame, callback);
  1993.     } else if (p.source.doc) {                              // DOCUMENT
  1994.       return loadFrameFromDocument(p.source.doc, frame, callback);
  1995.     }
  1996.   }
  1997.  
  1998.  
  1999.   function loadFrameFromHTML(src, frame, callback) {
  2000.     src = src.replace(/\s+/g, ' ');
  2001.  
  2002.     src = src.replace(/\'/g, '\\\'');
  2003.  
  2004.  
  2005.     if (Monocle.Browser.is.Gecko) {
  2006.       var doctypeFragment = "<!DOCTYPE[^>]*>";
  2007.       src = src.replace(new RegExp(doctypeFragment, 'm'), '');
  2008.     }
  2009.  
  2010.     src = "javascript: '" + src + "';";
  2011.  
  2012.     frame.onload = function () {
  2013.       frame.onload = null;
  2014.       Monocle.defer(callback);
  2015.     }
  2016.     frame.src = src;
  2017.   }
  2018.  
  2019.  
  2020.   function loadFrameFromURL(url, frame, callback) {
  2021.     frame.onload = function () {
  2022.       frame.onload = null;
  2023.       Monocle.defer(callback);
  2024.     }
  2025.     frame.contentWindow.location.replace(url);
  2026.   }
  2027.  
  2028.  
  2029.   function loadFrameFromNodes(nodes, frame, callback) {
  2030.     var destDoc = frame.contentDocument;
  2031.     destDoc.documentElement.innerHTML = "";
  2032.     var destHd = destDoc.createElement("head");
  2033.     var destBdy = destDoc.createElement("body");
  2034.  
  2035.     for (var i = 0; i < nodes.length; ++i) {
  2036.       var node = destDoc.importNode(nodes[i], true);
  2037.       destBdy.appendChild(node);
  2038.     }
  2039.  
  2040.     var oldHead = destDoc.getElementsByTagName('head')[0];
  2041.     if (oldHead) {
  2042.       destDoc.documentElement.replaceChild(destHd, oldHead);
  2043.     } else {
  2044.       destDoc.documentElement.appendChild(destHd);
  2045.     }
  2046.     if (destDoc.body) {
  2047.       destDoc.documentElement.replaceChild(destBdy, destDoc.body);
  2048.     } else {
  2049.       destDoc.documentElement.appendChild(destBdy);
  2050.     }
  2051.  
  2052.     if (callback) { callback(); }
  2053.   }
  2054.  
  2055.  
  2056.   function loadFrameFromDocument(srcDoc, frame, callback) {
  2057.     var destDoc = frame.contentDocument;
  2058.  
  2059.     var srcBases = srcDoc.getElementsByTagName('base');
  2060.     if (srcBases[0]) {
  2061.       var head = destDoc.getElementsByTagName('head')[0];
  2062.       if (!head) {
  2063.         try {
  2064.           head = destDoc.createElement('head');
  2065.           if (destDoc.body) {
  2066.             destDoc.insertBefore(head, destDoc.body);
  2067.           } else {
  2068.             destDoc.appendChild(head);
  2069.           }
  2070.         } catch (e) {
  2071.           head = destDoc.body;
  2072.         }
  2073.       }
  2074.       var bases = destDoc.getElementsByTagName('base');
  2075.       var base = bases[0] ? bases[0] : destDoc.createElement('base');
  2076.       base.setAttribute('href', srcBases[0].getAttribute('href'));
  2077.       head.appendChild(base);
  2078.     }
  2079.  
  2080.     destDoc.replaceChild(
  2081.       destDoc.importNode(srcDoc.documentElement, true),
  2082.       destDoc.documentElement
  2083.     );
  2084.  
  2085.  
  2086.     Monocle.defer(callback);
  2087.   }
  2088.  
  2089.  
  2090.   function setupFrame(pageDiv, frame) {
  2091.     Monocle.Events.listenOnIframe(frame);
  2092.  
  2093.     var evtData = {
  2094.       'page': pageDiv,
  2095.       'document': frame.contentDocument,
  2096.       'component': API
  2097.     };
  2098.     pageDiv.m.reader.dispatchEvent('monocle:componentchange', evtData);
  2099.  
  2100.     var doc = frame.contentDocument;
  2101.     var win = doc.defaultView;
  2102.     var currStyle = win.getComputedStyle(doc.body, null);
  2103.     var lh = parseFloat(currStyle.getPropertyValue('line-height'));
  2104.     var fs = parseFloat(currStyle.getPropertyValue('font-size'));
  2105.     doc.body.style.lineHeight = lh / fs;
  2106.  
  2107.     p.pageLength = pageDiv.m.dimensions.measure();
  2108.     frame.style.visibility = "visible";
  2109.  
  2110.     locateChapters(pageDiv);
  2111.   }
  2112.  
  2113.  
  2114.   function updateDimensions(pageDiv) {
  2115.     if (pageDiv.m.dimensions.hasChanged()) {
  2116.       p.pageLength = pageDiv.m.dimensions.measure();
  2117.       return true;
  2118.     } else {
  2119.       return false;
  2120.     }
  2121.   }
  2122.  
  2123.  
  2124.   function locateChapters(pageDiv) {
  2125.     if (p.chapters[0] && typeof p.chapters[0].percent == "number") {
  2126.       return;
  2127.     }
  2128.     for (var i = 0; i < p.chapters.length; ++i) {
  2129.       var chp = p.chapters[i];
  2130.       chp.percent = 0;
  2131.       if (chp.fragment) {
  2132.         chp.percent = pageDiv.m.dimensions.percentageThroughOfId(chp.fragment);
  2133.       }
  2134.     }
  2135.     return p.chapters;
  2136.   }
  2137.  
  2138.  
  2139.   function chapterForPage(pageN) {
  2140.     var cand = null;
  2141.     var percent = (pageN - 1) / p.pageLength;
  2142.     for (var i = 0; i < p.chapters.length; ++i) {
  2143.       if (percent >= p.chapters[i].percent) {
  2144.         cand = p.chapters[i];
  2145.       } else {
  2146.         return cand;
  2147.       }
  2148.     }
  2149.     return cand;
  2150.   }
  2151.  
  2152.  
  2153.   function pageForChapter(fragment, pageDiv) {
  2154.     if (!fragment) {
  2155.       return 1;
  2156.     }
  2157.     var pc2pn = function (pc) { return Math.floor(pc * p.pageLength) + 1 }
  2158.     for (var i = 0; i < p.chapters.length; ++i) {
  2159.       if (p.chapters[i].fragment == fragment) {
  2160.         return pc2pn(p.chapters[i].percent);
  2161.       }
  2162.     }
  2163.     var percent = pageDiv.m.dimensions.percentageThroughOfId(fragment);
  2164.     return pc2pn(percent);
  2165.   }
  2166.  
  2167.  
  2168.   function lastPageNumber() {
  2169.     return p.pageLength;
  2170.   }
  2171.  
  2172.  
  2173.   API.applyTo = applyTo;
  2174.   API.updateDimensions = updateDimensions;
  2175.   API.chapterForPage = chapterForPage;
  2176.   API.pageForChapter = pageForChapter;
  2177.   API.lastPageNumber = lastPageNumber;
  2178.  
  2179.   return API;
  2180. }
  2181.  
  2182. Monocle.pieceLoaded('component');
  2183.  
  2184. Monocle.Dimensions = {}
  2185. Monocle.Controls = {};
  2186. Monocle.Flippers = {};
  2187. Monocle.Panels = {};
  2188.  
  2189. Monocle.Controls.Panel = function () {
  2190.  
  2191.   var API = { constructor: Monocle.Controls.Panel }
  2192.   var k = API.constants = API.constructor;
  2193.   var p = API.properties = {
  2194.     evtCallbacks: {}
  2195.   }
  2196.  
  2197.   function createControlElements(cntr) {
  2198.     p.div = cntr.dom.make('div', k.CLS.panel);
  2199.     p.div.dom.setStyles(k.DEFAULT_STYLES);
  2200.     Monocle.Events.listenForContact(
  2201.       p.div,
  2202.       {
  2203.         'start': start,
  2204.         'move': move,
  2205.         'end': end,
  2206.         'cancel': cancel
  2207.       },
  2208.       { useCapture: false }
  2209.     );
  2210.     return p.div;
  2211.   }
  2212.  
  2213.  
  2214.   function listenTo(evtCallbacks) {
  2215.     p.evtCallbacks = evtCallbacks;
  2216.   }
  2217.  
  2218.  
  2219.   function deafen() {
  2220.     p.evtCallbacks = {}
  2221.   }
  2222.  
  2223.  
  2224.   function start(evt) {
  2225.     p.contact = true;
  2226.     evt.m.offsetX += p.div.offsetLeft;
  2227.     evt.m.offsetY += p.div.offsetTop;
  2228.     expand();
  2229.     invoke('start', evt);
  2230.   }
  2231.  
  2232.  
  2233.   function move(evt) {
  2234.     if (!p.contact) {
  2235.       return;
  2236.     }
  2237.     invoke('move', evt);
  2238.   }
  2239.  
  2240.  
  2241.   function end(evt) {
  2242.     if (!p.contact) {
  2243.       return;
  2244.     }
  2245.     Monocle.Events.deafenForContact(p.div, p.listeners);
  2246.     contract();
  2247.     p.contact = false;
  2248.     invoke('end', evt);
  2249.   }
  2250.  
  2251.  
  2252.   function cancel(evt) {
  2253.     if (!p.contact) {
  2254.       return;
  2255.     }
  2256.     Monocle.Events.deafenForContact(p.div, p.listeners);
  2257.     contract();
  2258.     p.contact = false;
  2259.     invoke('cancel', evt);
  2260.   }
  2261.  
  2262.  
  2263.   function invoke(evtType, evt) {
  2264.     if (p.evtCallbacks[evtType]) {
  2265.       p.evtCallbacks[evtType](API, evt.m.offsetX, evt.m.offsetY);
  2266.     }
  2267.     evt.preventDefault();
  2268.   }
  2269.  
  2270.  
  2271.   function expand() {
  2272.     if (p.expanded) {
  2273.       return;
  2274.     }
  2275.     p.div.dom.addClass(k.CLS.expanded);
  2276.     p.expanded = true;
  2277.   }
  2278.  
  2279.  
  2280.   function contract(evt) {
  2281.     if (!p.expanded) {
  2282.       return;
  2283.     }
  2284.     p.div.dom.removeClass(k.CLS.expanded);
  2285.     p.expanded = false;
  2286.   }
  2287.  
  2288.  
  2289.   API.createControlElements = createControlElements;
  2290.   API.listenTo = listenTo;
  2291.   API.deafen = deafen;
  2292.   API.expand = expand;
  2293.   API.contract = contract;
  2294.  
  2295.   return API;
  2296. }
  2297.  
  2298.  
  2299. Monocle.Controls.Panel.CLS = {
  2300.   panel: 'panel',
  2301.   expanded: 'controls_panel_expanded'
  2302. }
  2303. Monocle.Controls.Panel.DEFAULT_STYLES = {
  2304.   position: 'absolute',
  2305.   height: '100%'
  2306. }
  2307.  
  2308.  
  2309. Monocle.pieceLoaded('controls/panel');
  2310. Monocle.Panels.TwoPane = function (flipper, evtCallbacks) {
  2311.  
  2312.   var API = { constructor: Monocle.Panels.TwoPane }
  2313.   var k = API.constants = API.constructor;
  2314.   var p = API.properties = {}
  2315.  
  2316.  
  2317.   function initialize() {
  2318.     p.panels = {
  2319.       forwards: new Monocle.Controls.Panel(),
  2320.       backwards: new Monocle.Controls.Panel()
  2321.     }
  2322.  
  2323.     for (dir in p.panels) {
  2324.       flipper.properties.reader.addControl(p.panels[dir]);
  2325.       p.panels[dir].listenTo(evtCallbacks);
  2326.       p.panels[dir].properties.direction = flipper.constants[dir.toUpperCase()];
  2327.       var style = { "width": k.WIDTH };
  2328.       style[(dir == "forwards" ? "right" : "left")] = 0;
  2329.       p.panels[dir].properties.div.dom.setStyles(style);
  2330.     }
  2331.   }
  2332.  
  2333.  
  2334.   initialize();
  2335.  
  2336.   return API;
  2337. }
  2338.  
  2339. Monocle.Panels.TwoPane.WIDTH = "50%";
  2340.  
  2341. Monocle.pieceLoaded('panels/twopane');
  2342. Monocle.Dimensions.Vert = function (pageDiv) {
  2343.  
  2344.   var API = { constructor: Monocle.Dimensions.Vert }
  2345.   var k = API.constants = API.constructor;
  2346.   var p = API.properties = {
  2347.     page: pageDiv,
  2348.     reader: pageDiv.m.reader
  2349.   }
  2350.  
  2351.  
  2352.   function initialize() {
  2353.     p.reader.listen('monocle:componentchange', componentChanged);
  2354.   }
  2355.  
  2356.  
  2357.   function hasChanged() {
  2358.     return getBodyHeight() != p.bodyHeight || getPageHeight != p.pageHeight;
  2359.   }
  2360.  
  2361.  
  2362.   function measure() {
  2363.     p.bodyHeight = getBodyHeight();
  2364.     p.pageHeight = getPageHeight();
  2365.     p.length = Math.ceil(p.bodyHeight / p.pageHeight);
  2366.     return p.length;
  2367.   }
  2368.  
  2369.  
  2370.   function pages() {
  2371.     return p.length;
  2372.   }
  2373.  
  2374.  
  2375.   function getBodyHeight() {
  2376.     return p.page.m.activeFrame.contentDocument.body.scrollHeight;
  2377.   }
  2378.  
  2379.  
  2380.   function getPageHeight() {
  2381.     return p.page.m.activeFrame.offsetHeight - k.GUTTER;
  2382.   }
  2383.  
  2384.  
  2385.   function percentageThroughOfId(id) {
  2386.     var doc = p.page.m.activeFrame.contentDocument;
  2387.     var target = doc.getElementById(id);
  2388.     var offset = 0;
  2389.     if (target.getBoundingClientRect) {
  2390.       offset = target.getBoundingClientRect().top;
  2391.       offset -= doc.body.getBoundingClientRect().top;
  2392.     } else {
  2393.       var oldScrollTop = doc.body.scrollTop;
  2394.       target.scrollIntoView();
  2395.       offset = doc.body.scrollTop;
  2396.       doc.body.scrollLeft = 0;
  2397.       doc.body.scrollTop = oldScrollTop;
  2398.     }
  2399.  
  2400.     var percent = offset / p.bodyHeight;
  2401.     return percent;
  2402.   }
  2403.  
  2404.  
  2405.   function componentChanged(evt) {
  2406.     if (evt.m['page'] != p.page) { return; }
  2407.     var sheaf = p.page.m.sheafDiv;
  2408.     var cmpt = p.page.m.activeFrame;
  2409.     sheaf.dom.setStyles(k.SHEAF_STYLES);
  2410.     cmpt.dom.setStyles(k.COMPONENT_STYLES);
  2411.     var doc = evt.m['document'];
  2412.     doc.documentElement.style.overflow = 'hidden';
  2413.     doc.body.style.marginRight = '10px !important';
  2414.     cmpt.contentWindow.scrollTo(0,0);
  2415.   }
  2416.  
  2417.  
  2418.   function locusToOffset(locus) {
  2419.     return p.pageHeight * (locus.page - 1);
  2420.   }
  2421.  
  2422.  
  2423.   API.hasChanged = hasChanged;
  2424.   API.measure = measure;
  2425.   API.pages = pages;
  2426.   API.percentageThroughOfId = percentageThroughOfId;
  2427.   API.locusToOffset = locusToOffset;
  2428.  
  2429.   initialize();
  2430.  
  2431.   return API;
  2432. }
  2433.  
  2434. Monocle.Dimensions.Vert.GUTTER = 10;
  2435. Monocle.Flippers.Legacy = function (reader) {
  2436.  
  2437.   var API = { constructor: Monocle.Flippers.Legacy }
  2438.   var k = API.constants = API.constructor;
  2439.   var p = API.properties = {
  2440.     pageCount: 1,
  2441.     divs: {}
  2442.   }
  2443.  
  2444.  
  2445.   function initialize() {
  2446.     p.reader = reader;
  2447.   }
  2448.  
  2449.  
  2450.   function addPage(pageDiv) {
  2451.     pageDiv.m.dimensions = new Monocle.Dimensions.Vert(pageDiv);
  2452.   }
  2453.  
  2454.  
  2455.   function getPlace() {
  2456.     return page().m.place;
  2457.   }
  2458.  
  2459.  
  2460.   function moveTo(locus, callback) {
  2461.     var fn = frameToLocus;
  2462.     if (typeof callback == "function") {
  2463.       fn = function () { frameToLocus(); callback(); }
  2464.     }
  2465.     p.reader.getBook().setOrLoadPageAt(page(), locus, fn);
  2466.   }
  2467.  
  2468.  
  2469.   function listenForInteraction(panelClass) {
  2470.     if (typeof panelClass != "function") {
  2471.       panelClass = k.DEFAULT_PANELS_CLASS;
  2472.       if (!panelClass) {
  2473.         console.warn("Invalid panel class.")
  2474.       }
  2475.     }
  2476.     p.panels = new panelClass(API, { 'end': turn });
  2477.   }
  2478.  
  2479.  
  2480.   function page() {
  2481.     return p.reader.dom.find('page');
  2482.   }
  2483.  
  2484.  
  2485.   function turn(panel) {
  2486.     var dir = panel.properties.direction;
  2487.     var place = getPlace();
  2488.     if (
  2489.       (dir < 0 && place.onFirstPageOfBook()) ||
  2490.       (dir > 0 && place.onLastPageOfBook())
  2491.     ) { return; }
  2492.     moveTo({ page: getPlace().pageNumber() + dir });
  2493.   }
  2494.  
  2495.  
  2496.   function frameToLocus(locus) {
  2497.     var cmpt = p.reader.dom.find('component');
  2498.     var win = cmpt.contentWindow;
  2499.     var srcY = scrollPos(win);
  2500.     var dims = page().m.dimensions;
  2501.     var pageHeight = dims.properties.pageHeight;
  2502.     var destY = dims.locusToOffset(locus);
  2503.  
  2504.     if (Math.abs(destY - srcY) > pageHeight) {
  2505.       return win.scrollTo(0, destY);
  2506.     }
  2507.  
  2508.     showIndicator(win, srcY < destY ? srcY + pageHeight : srcY);
  2509.     Monocle.defer(
  2510.       function () { smoothScroll(win, srcY, destY, 300, scrollingFinished); },
  2511.       150
  2512.     );
  2513.   }
  2514.  
  2515.  
  2516.   function scrollPos(win) {
  2517.     if (win.pageYOffset) {
  2518.       return win.pageYOffset;
  2519.     }
  2520.     if (win.document.documentElement && win.document.documentElement.scrollTop) {
  2521.       return win.document.documentElement.scrollTop;
  2522.     }
  2523.     if (win.document.body.scrollTop) {
  2524.       return win.document.body.scrollTop;
  2525.     }
  2526.     return 0;
  2527.   }
  2528.  
  2529.  
  2530.   function smoothScroll(win, currY, finalY, duration, callback) {
  2531.     clearTimeout(win.smoothScrollInterval);
  2532.     var stamp = (new Date()).getTime();
  2533.     var frameRate = 40;
  2534.     var step = (finalY - currY) * (frameRate / duration);
  2535.     var stepFn = function () {
  2536.       var destY = currY + step;
  2537.       if (
  2538.         (new Date()).getTime() - stamp > duration ||
  2539.         Math.abs(currY - finalY) < Math.abs((currY + step) - finalY)
  2540.       ) {
  2541.         clearTimeout(win.smoothScrollInterval);
  2542.         win.scrollTo(0, finalY);
  2543.         if (callback) { callback(); }
  2544.       } else {
  2545.         win.scrollTo(0, destY);
  2546.         currY = destY;
  2547.       }
  2548.     }
  2549.     win.smoothScrollInterval = setInterval(stepFn, frameRate);
  2550.   }
  2551.  
  2552.  
  2553.   function scrollingFinished() {
  2554.     hideIndicator(page().m.activeFrame.contentWindow);
  2555.     p.reader.dispatchEvent('monocle:turn');
  2556.   }
  2557.  
  2558.  
  2559.   function showIndicator(win, pos) {
  2560.     if (p.hideTO) { clearTimeout(p.hideTO); }
  2561.  
  2562.     var doc = win.document;
  2563.     if (!doc.body.indicator) {
  2564.       doc.body.indicator = createIndicator(doc);
  2565.       doc.body.appendChild(doc.body.indicator);
  2566.     }
  2567.     doc.body.indicator.line.style.display = "block";
  2568.     doc.body.indicator.style.opacity = 1;
  2569.     positionIndicator(pos);
  2570.   }
  2571.  
  2572.  
  2573.   function hideIndicator(win) {
  2574.     var doc = win.document;
  2575.     p.hideTO = Monocle.defer(
  2576.       function () {
  2577.         if (!doc.body.indicator) {
  2578.           doc.body.indicator = createIndicator(doc);
  2579.           doc.body.appendChild(doc.body.indicator);
  2580.         }
  2581.         var dims = page().m.dimensions;
  2582.         positionIndicator(
  2583.           dims.locusToOffset(getPlace().getLocus()) + dims.properties.pageHeight
  2584.         )
  2585.         doc.body.indicator.line.style.display = "none";
  2586.         doc.body.indicator.style.opacity = 0.5;
  2587.       },
  2588.       600
  2589.     );
  2590.   }
  2591.  
  2592.  
  2593.   function createIndicator(doc) {
  2594.     var iBox = doc.createElement('div');
  2595.     doc.body.appendChild(iBox);
  2596.     Monocle.Styles.applyRules(iBox, k.STYLES.iBox);
  2597.  
  2598.     iBox.arrow = doc.createElement('div');
  2599.     iBox.appendChild(iBox.arrow);
  2600.     Monocle.Styles.applyRules(iBox.arrow, k.STYLES.arrow);
  2601.  
  2602.     iBox.line = doc.createElement('div');
  2603.     iBox.appendChild(iBox.line);
  2604.     Monocle.Styles.applyRules(iBox.line, k.STYLES.line);
  2605.  
  2606.     return iBox;
  2607.   }
  2608.  
  2609.  
  2610.   function positionIndicator(y) {
  2611.     var p = page();
  2612.     var doc = p.m.activeFrame.contentDocument;
  2613.     var maxHeight = p.m.dimensions.properties.bodyHeight;
  2614.     maxHeight -= doc.body.indicator.offsetHeight;
  2615.     if (y > maxHeight) {
  2616.       y = maxHeight;
  2617.     }
  2618.     doc.body.indicator.style.top = y + "px";
  2619.   }
  2620.  
  2621.  
  2622.   API.pageCount = p.pageCount;
  2623.   API.addPage = addPage;
  2624.   API.getPlace = getPlace;
  2625.   API.moveTo = moveTo;
  2626.   API.listenForInteraction = listenForInteraction;
  2627.  
  2628.   initialize();
  2629.  
  2630.   return API;
  2631. }
  2632.  
  2633. Monocle.Flippers.Legacy.FORWARDS = 1;
  2634. Monocle.Flippers.Legacy.BACKWARDS = -1;
  2635. Monocle.Flippers.Legacy.DEFAULT_PANELS_CLASS = Monocle.Panels.TwoPane;
  2636.  
  2637. Monocle.Flippers.Legacy.STYLES = {
  2638.   iBox: {
  2639.     'position': 'absolute',
  2640.     'right': 0,
  2641.     'left': 0,
  2642.     'height': '10px'
  2643.   },
  2644.   arrow: {
  2645.     'position': 'absolute',
  2646.     'right': 0,
  2647.     'height': '10px',
  2648.     'width': '10px',
  2649.     'background': '#333',
  2650.     'border-radius': '6px'
  2651.   },
  2652.   line: {
  2653.     'width': '100%',
  2654.     'border-top': '2px dotted #333',
  2655.     'margin-top': '5px'
  2656.   }
  2657. }
  2658.  
  2659. Monocle.pieceLoaded('flippers/legacy');
  2660. Monocle.Dimensions.Columns = function (pageDiv) {
  2661.  
  2662.   var API = { constructor: Monocle.Dimensions.Columns }
  2663.   var k = API.constants = API.constructor;
  2664.   var p = API.properties = {
  2665.     page: pageDiv,
  2666.     reader: pageDiv.m.reader,
  2667.     dirty: true
  2668.   }
  2669.  
  2670.  
  2671.   function initialize() {
  2672.     p.reader.listen('monocle:componentchange', componentChanged);
  2673.   }
  2674.  
  2675.  
  2676.   function hasChanged() {
  2677.     if (p.dirty) { return true; }
  2678.     var newMeasurements = rawMeasurements();
  2679.     return (
  2680.       (!p.measurements) ||
  2681.       (p.measurements.width != newMeasurements.width) ||
  2682.       (p.measurements.height != newMeasurements.height) ||
  2683.       (p.measurements.scrollWidth != newMeasurements.scrollWidth) ||
  2684.       (p.measurements.fontSize != newMeasurements.fontSize)
  2685.     );
  2686.   }
  2687.  
  2688.  
  2689.   function measure() {
  2690.     setColumnWidth();
  2691.     p.measurements = rawMeasurements();
  2692.  
  2693.     if (
  2694.       Monocle.Browser.has.iframeDoubleWidthBug &&
  2695.       p.measurements.scrollWidth == p.measurements.width * 2
  2696.     ) {
  2697.       var doc = p.page.m.activeFrame.contentDocument;
  2698.       var lc;
  2699.       for (var i = doc.body.childNodes.length - 1; i >= 0; --i) {
  2700.         lc = doc.body.childNodes[i];
  2701.         if (lc.getBoundingClientRect) { break; }
  2702.       }
  2703.       if (!lc || !lc.getBoundingClientRect) {
  2704.         console.warn('Empty document for page['+p.page.m.pageIndex+']');
  2705.         p.measurements.scrollWidth = p.measurements.width;
  2706.       } else if (lc.getBoundingClientRect().bottom > p.measurements.height) {
  2707.         p.measurements.scrollWidth = p.measurements.width * 2;
  2708.       } else {
  2709.         p.measurements.scrollWidth = p.measurements.width;
  2710.       }
  2711.     }
  2712.  
  2713.     p.length = Math.ceil(p.measurements.scrollWidth / p.measurements.width);
  2714.     p.dirty = false;
  2715.     return p.length;
  2716.   }
  2717.  
  2718.  
  2719.   function pages() {
  2720.     if (p.dirty) {
  2721.       console.warn('Accessing pages() when dimensions are dirty.')
  2722.       return 0;
  2723.     }
  2724.     return p.length;
  2725.   }
  2726.  
  2727.  
  2728.   function percentageThroughOfId(id) {
  2729.     var doc = p.page.m.activeFrame.contentDocument;
  2730.     var target = doc.getElementById(id);
  2731.     if (!target) {
  2732.       return 0;
  2733.     }
  2734.     var offset = 0;
  2735.     if (target.getBoundingClientRect) {
  2736.       offset = target.getBoundingClientRect().left;
  2737.       offset -= doc.body.getBoundingClientRect().left;
  2738.     } else {
  2739.       var scroller = scrollerElement();
  2740.       var oldScrollLeft = scroller.scrollLeft;
  2741.       target.scrollIntoView();
  2742.       offset = scroller.scrollLeft;
  2743.       scroller.scrollTop = 0;
  2744.       scroller.scrollLeft = oldScrollLeft;
  2745.     }
  2746.  
  2747.     var percent = offset / p.measurements.scrollWidth;
  2748.     return percent;
  2749.   }
  2750.  
  2751.  
  2752.   function componentChanged(evt) {
  2753.     if (evt.m['page'] != p.page) { return; }
  2754.     var doc = evt.m['document'];
  2755.     Monocle.Styles.applyRules(doc.body, k.BODY_STYLES);
  2756.  
  2757.     if (Monocle.Browser.is.WebKit) {
  2758.       doc.documentElement.style.overflow = 'hidden';
  2759.     }
  2760.     p.dirty = true;
  2761.   }
  2762.  
  2763.  
  2764.   function setColumnWidth() {
  2765.     var cw = p.page.m.sheafDiv.clientWidth;
  2766.     var doc = p.page.m.activeFrame.contentDocument;
  2767.     if (currBodyStyleValue('column-width') != cw+"px") {
  2768.       Monocle.Styles.affix(doc.body, 'column-width', cw+"px");
  2769.       p.dirty = true;
  2770.     }
  2771.   }
  2772.  
  2773.  
  2774.   function rawMeasurements() {
  2775.     var sheaf = p.page.m.sheafDiv;
  2776.     return {
  2777.       width: sheaf.clientWidth,
  2778.       height: sheaf.clientHeight,
  2779.       scrollWidth: scrollerWidth(),
  2780.       fontSize: currBodyStyleValue('font-size')
  2781.     }
  2782.   }
  2783.  
  2784.  
  2785.   function scrollerElement() {
  2786.     if (Monocle.Browser.has.mustScrollSheaf) {
  2787.       return p.page.m.sheafDiv;
  2788.     } else {
  2789.       return p.page.m.activeFrame.contentDocument.body;
  2790.     }
  2791.   }
  2792.  
  2793.  
  2794.   function scrollerWidth() {
  2795.     var bdy = p.page.m.activeFrame.contentDocument.body;
  2796.     if (Monocle.Browser.has.iframeDoubleWidthBug) {
  2797.       if (Monocle.Browser.iOSVersion < "4.1") {
  2798.         var hbw = bdy.scrollWidth / 2;
  2799.         var sew = scrollerElement().scrollWidth;
  2800.         return Math.max(sew, hbw);
  2801.       } else {
  2802.         bdy.scrollWidth; // Throw one away. Nuts.
  2803.         var hbw = bdy.scrollWidth / 2;
  2804.         return hbw;
  2805.       }
  2806.     } else if (Monocle.Browser.is.Gecko) {
  2807.       var lc = bdy.lastChild;
  2808.       while (lc && lc.nodeType != 1) {
  2809.         lc = lc.previousSibling;
  2810.       }
  2811.       if (lc && lc.getBoundingClientRect) {
  2812.         return lc.getBoundingClientRect().right;
  2813.       }
  2814.     }
  2815.     return scrollerElement().scrollWidth;
  2816.   }
  2817.  
  2818.  
  2819.   function currBodyStyleValue(property) {
  2820.     var win = p.page.m.activeFrame.contentWindow;
  2821.     var doc = win.document;
  2822.     if (!doc.body) { return null; }
  2823.     var currStyle = win.getComputedStyle(doc.body, null);
  2824.     return currStyle.getPropertyValue(property);
  2825.   }
  2826.  
  2827.  
  2828.   function locusToOffset(locus) {
  2829.     return 0 - (p.measurements.width * (locus.page - 1));
  2830.   }
  2831.  
  2832.  
  2833.   function translateToLocus(locus) {
  2834.     var offset = locusToOffset(locus);
  2835.     var bdy = p.page.m.activeFrame.contentDocument.body;
  2836.     Monocle.Styles.affix(bdy, "transform", "translateX("+offset+"px)");
  2837.     return offset;
  2838.   }
  2839.  
  2840.  
  2841.   API.hasChanged = hasChanged;
  2842.   API.measure = measure;
  2843.   API.pages = pages;
  2844.   API.percentageThroughOfId = percentageThroughOfId;
  2845.  
  2846.   API.locusToOffset = locusToOffset;
  2847.   API.translateToLocus = translateToLocus;
  2848.  
  2849.   initialize();
  2850.  
  2851.   return API;
  2852. }
  2853.  
  2854.  
  2855. Monocle.Dimensions.Columns.BODY_STYLES = {
  2856.   "position": "absolute",
  2857.   "height": "100%",
  2858.   "-webkit-column-gap": "0",
  2859.   "-webkit-column-fill": "auto",
  2860.   "-moz-column-gap": "0",
  2861.   "-moz-column-fill": "auto",
  2862.   "column-gap": "0",
  2863.   "column-fill": "auto"
  2864. }
  2865.  
  2866. if (Monocle.Browser.has.iframeDoubleWidthBug) {
  2867.   Monocle.Dimensions.Columns.BODY_STYLES["min-width"] = "200%";
  2868. } else {
  2869.   Monocle.Dimensions.Columns.BODY_STYLES["width"] = "100%";
  2870. }
  2871. Monocle.Flippers.Slider = function (reader) {
  2872.   if (Monocle.Flippers == this) {
  2873.     return new Monocle.Flippers.Slider(reader);
  2874.   }
  2875.  
  2876.   var API = { constructor: Monocle.Flippers.Slider }
  2877.   var k = API.constants = API.constructor;
  2878.   var p = API.properties = {
  2879.     pageCount: 2,
  2880.     activeIndex: 1,
  2881.     turnData: {}
  2882.   }
  2883.  
  2884.  
  2885.   function initialize() {
  2886.     p.reader = reader;
  2887.   }
  2888.  
  2889.  
  2890.   function addPage(pageDiv) {
  2891.     pageDiv.m.dimensions = new Monocle.Dimensions.Columns(pageDiv);
  2892.   }
  2893.  
  2894.  
  2895.   function visiblePages() {
  2896.     return [upperPage()];
  2897.   }
  2898.  
  2899.  
  2900.   function listenForInteraction(panelClass) {
  2901.     interactiveMode(true);
  2902.     interactiveMode(false);
  2903.  
  2904.     if (typeof panelClass != "function") {
  2905.       panelClass = k.DEFAULT_PANELS_CLASS;
  2906.       if (!panelClass) {
  2907.         console.warn("Invalid panel class.")
  2908.       }
  2909.     }
  2910.     var q = function (action, panel, x) {
  2911.       var dir = panel.properties.direction;
  2912.       if (action == "lift") {
  2913.         lift(dir, x);
  2914.       } else if (action == "release") {
  2915.         release(dir, x);
  2916.       }
  2917.     }
  2918.     p.panels = new panelClass(
  2919.       API,
  2920.       {
  2921.         'start': function (panel, x) { q('lift', panel, x); },
  2922.         'move': function (panel, x) { turning(panel.properties.direction, x); },
  2923.         'end': function (panel, x) { q('release', panel, x); },
  2924.         'cancel': function (panel, x) { q('release', panel, x); }
  2925.       }
  2926.     );
  2927.   }
  2928.  
  2929.  
  2930.   function interactiveMode(bState) {
  2931.     if (!Monocle.Browser.has.selectThruBug) {
  2932.       return;
  2933.     }
  2934.     if (p.interactive = bState) {
  2935.       if (p.activeIndex != 0) {
  2936.         var place = getPlace();
  2937.         if (place) {
  2938.           setPage(
  2939.             p.reader.dom.find('page', 0),
  2940.             place.getLocus(),
  2941.             function () {
  2942.               flipPages();
  2943.               prepareNextPage();
  2944.             }
  2945.           );
  2946.         } else {
  2947.           flipPages();
  2948.         }
  2949.       }
  2950.     }
  2951.   }
  2952.  
  2953.  
  2954.   function getPlace(pageDiv) {
  2955.     pageDiv = pageDiv || upperPage();
  2956.     return pageDiv.m ? pageDiv.m.place : null;
  2957.   }
  2958.  
  2959.  
  2960.   function moveTo(locus, callback) {
  2961.     var fn = function () {
  2962.       prepareNextPage(announceTurn);
  2963.       if (typeof callback == "function") {
  2964.         callback();
  2965.       }
  2966.     }
  2967.     setPage(upperPage(), locus, fn);
  2968.   }
  2969.  
  2970.  
  2971.   function setPage(pageDiv, locus, callback) {
  2972.     p.reader.getBook().setOrLoadPageAt(
  2973.       pageDiv,
  2974.       locus,
  2975.       function (locus) {
  2976.         pageDiv.m.dimensions.translateToLocus(locus);
  2977.         if (callback) { callback(); }
  2978.       }
  2979.     );
  2980.   }
  2981.  
  2982.  
  2983.   function upperPage() {
  2984.     return p.reader.dom.find('page', p.activeIndex);
  2985.   }
  2986.  
  2987.  
  2988.   function lowerPage() {
  2989.     return p.reader.dom.find('page', (p.activeIndex + 1) % 2);
  2990.   }
  2991.  
  2992.  
  2993.   function flipPages() {
  2994.     upperPage().style.zIndex = 1;
  2995.     lowerPage().style.zIndex = 2;
  2996.     return p.activeIndex = (p.activeIndex + 1) % 2;
  2997.   }
  2998.  
  2999.  
  3000.   function lift(dir, boxPointX) {
  3001.     if (p.turnData.lifting || p.turnData.releasing) { return; }
  3002.  
  3003.     p.turnData.points = {
  3004.       start: boxPointX,
  3005.       min: boxPointX,
  3006.       max: boxPointX
  3007.     }
  3008.     p.turnData.lifting = true;
  3009.  
  3010.     if (dir == k.FORWARDS) {
  3011.       if (getPlace().onLastPageOfBook()) {
  3012.         resetTurnData();
  3013.         return;
  3014.       }
  3015.       onGoingForward(boxPointX);
  3016.     } else if (dir == k.BACKWARDS) {
  3017.       if (getPlace().onFirstPageOfBook()) {
  3018.         resetTurnData();
  3019.         return;
  3020.       }
  3021.       onGoingBackward(boxPointX);
  3022.     } else {
  3023.       console.warn("Invalid direction: " + dir);
  3024.     }
  3025.   }
  3026.  
  3027.  
  3028.   function turning(dir, boxPointX) {
  3029.     if (!p.turnData.points) { return; }
  3030.     if (p.turnData.lifting || p.turnData.releasing) { return; }
  3031.     checkPoint(boxPointX);
  3032.     slideToCursor(boxPointX, null, "0");
  3033.   }
  3034.  
  3035.  
  3036.   function release(dir, boxPointX) {
  3037.     if (!p.turnData.points) {
  3038.       return;
  3039.     }
  3040.     if (p.turnData.lifting) {
  3041.       p.turnData.releaseArgs = [dir, boxPointX];
  3042.       return;
  3043.     }
  3044.     if (p.turnData.releasing) {
  3045.       return;
  3046.     }
  3047.  
  3048.     checkPoint(boxPointX);
  3049.  
  3050.     p.turnData.releasing = true;
  3051.  
  3052.     if (dir == k.FORWARDS) {
  3053.       if (
  3054.         p.turnData.points.tap ||
  3055.         p.turnData.points.start - boxPointX > 60 ||
  3056.         p.turnData.points.min >= boxPointX
  3057.       ) {
  3058.         slideOut(afterGoingForward);
  3059.       } else {
  3060.         slideIn(afterCancellingForward);
  3061.       }
  3062.     } else if (dir == k.BACKWARDS) {
  3063.       if (
  3064.         p.turnData.points.tap ||
  3065.         boxPointX - p.turnData.points.start > 60 ||
  3066.         p.turnData.points.max <= boxPointX
  3067.       ) {
  3068.         slideIn(afterGoingBackward);
  3069.       } else {
  3070.         slideOut(afterCancellingBackward);
  3071.       }
  3072.     } else {
  3073.       console.warn("Invalid direction: " + dir);
  3074.     }
  3075.   }
  3076.  
  3077.  
  3078.   function checkPoint(boxPointX) {
  3079.     p.turnData.points.min = Math.min(p.turnData.points.min, boxPointX);
  3080.     p.turnData.points.max = Math.max(p.turnData.points.max, boxPointX);
  3081.     p.turnData.points.tap = p.turnData.points.max - p.turnData.points.min < 10;
  3082.   }
  3083.  
  3084.  
  3085.   function onGoingForward(x) {
  3086.     lifted(x);
  3087.   }
  3088.  
  3089.  
  3090.   function onGoingBackward(x) {
  3091.     var lp = lowerPage();
  3092.     jumpOut(lp, // move lower page off-screen
  3093.       function () {
  3094.         flipPages(); // flip lower to upper
  3095.         setPage( // set upper page to previous
  3096.           lp,
  3097.           getPlace(lowerPage()).getLocus({ direction: k.BACKWARDS }),
  3098.           function () { lifted(x); }
  3099.         );
  3100.       }
  3101.     );
  3102.   }
  3103.  
  3104.  
  3105.   function afterGoingForward() {
  3106.     var up = upperPage();
  3107.     if (p.interactive) {
  3108.       setPage( // set upper (off screen) to current
  3109.         up,
  3110.         getPlace().getLocus({ direction: k.FORWARDS }),
  3111.         function () {
  3112.           jumpIn(up, function () { prepareNextPage(announceTurn); });
  3113.         }
  3114.       );
  3115.     } else {
  3116.       flipPages();
  3117.       jumpIn(up, function () { prepareNextPage(announceTurn); });
  3118.     }
  3119.   }
  3120.  
  3121.  
  3122.   function afterGoingBackward() {
  3123.     if (p.interactive) {
  3124.       setPage( // set lower page to current
  3125.         lowerPage(),
  3126.         getPlace().getLocus(),
  3127.         function () {
  3128.           flipPages(); // flip lower to upper
  3129.           prepareNextPage(announceTurn);
  3130.         }
  3131.       );
  3132.     } else {
  3133.       announceTurn();
  3134.     }
  3135.   }
  3136.  
  3137.  
  3138.   function afterCancellingForward() {
  3139.     resetTurnData();
  3140.   }
  3141.  
  3142.  
  3143.   function afterCancellingBackward() {
  3144.     flipPages(); // flip upper to lower
  3145.     jumpIn( // move lower back onto screen
  3146.       lowerPage(),
  3147.       function () { prepareNextPage(resetTurnData); }
  3148.     );
  3149.   }
  3150.  
  3151.  
  3152.   function prepareNextPage(callback) {
  3153.     setPage(
  3154.       lowerPage(),
  3155.       getPlace().getLocus({ direction: k.FORWARDS }),
  3156.       callback
  3157.     );
  3158.   }
  3159.  
  3160.  
  3161.   function lifted(x) {
  3162.     p.turnData.lifting = false;
  3163.     var releaseArgs = p.turnData.releaseArgs;
  3164.     if (releaseArgs) {
  3165.       p.turnData.releaseArgs = null;
  3166.       release(releaseArgs[0], releaseArgs[1]);
  3167.     } else if (x) {
  3168.       slideToCursor(x);
  3169.     }
  3170.   }
  3171.  
  3172.  
  3173.   function announceTurn() {
  3174.     p.reader.dispatchEvent('monocle:turn');
  3175.     resetTurnData();
  3176.   }
  3177.  
  3178.  
  3179.   function resetTurnData() {
  3180.     p.turnData = {};
  3181.   }
  3182.  
  3183.  
  3184.   function setX(elem, x, options, callback) {
  3185.     var duration;
  3186.  
  3187.     if (!options.duration) {
  3188.       duration = 0;
  3189.     } else {
  3190.       duration = parseInt(options['duration']);
  3191.     }
  3192.  
  3193.     if (typeof(x) == "number") { x = x + "px"; }
  3194.  
  3195.     if (typeof WebKitTransitionEvent != "undefined") {
  3196.       if (duration) {
  3197.         transition = '-webkit-transform';
  3198.         transition += ' ' + duration + "ms";
  3199.         transition += ' ' + (options['timing'] || 'linear');
  3200.         transition += ' ' + (options['delay'] || 0) + 'ms';
  3201.       } else {
  3202.         transition = 'none';
  3203.       }
  3204.       elem.style.webkitTransition = transition;
  3205.       if (Monocle.Browser.has.transform3d) {
  3206.         elem.style.webkitTransform = "translate3d("+x+",0,0)";
  3207.       } else {
  3208.         elem.style.webkitTransform = "translateX("+x+")";
  3209.       }
  3210.  
  3211.     } else if (duration > 0) {
  3212.       clearTimeout(elem.setXTransitionInterval)
  3213.  
  3214.       var stamp = (new Date()).getTime();
  3215.       var frameRate = 40;
  3216.       var finalX = parseInt(x);
  3217.       var currX = getX(elem);
  3218.       var step = (finalX - currX) * (frameRate / duration);
  3219.       var stepFn = function () {
  3220.         var destX = currX + step;
  3221.         if (
  3222.           (new Date()).getTime() - stamp > duration ||
  3223.           Math.abs(currX - finalX) <= Math.abs((currX + step) - finalX)
  3224.         ) {
  3225.           clearTimeout(elem.setXTransitionInterval)
  3226.           Monocle.Styles.setX(elem, finalX);
  3227.           if (elem.setXTCB) {
  3228.             elem.setXTCB();
  3229.           }
  3230.         } else {
  3231.           Monocle.Styles.setX(elem, destX);
  3232.           currX = destX;
  3233.         }
  3234.       }
  3235.  
  3236.       elem.setXTransitionInterval = setInterval(stepFn, frameRate);
  3237.     } else {
  3238.       Monocle.Styles.setX(elem, x);
  3239.     }
  3240.  
  3241.     if (elem.setXTCB) {
  3242.       Monocle.Events.deafen(elem, 'webkitTransitionEnd', elem.setXTCB);
  3243.       elem.setXTCB = null;
  3244.     }
  3245.  
  3246.     elem.setXTCB = function () {
  3247.       if (callback) { callback(); }
  3248.     }
  3249.  
  3250.     var sX = getX(elem);
  3251.     if (!duration || sX == parseInt(x)) {
  3252.       elem.setXTCB();
  3253.     } else {
  3254.       Monocle.Events.listen(elem, 'webkitTransitionEnd', elem.setXTCB);
  3255.     }
  3256.   }
  3257.  
  3258.  
  3259.   /*
  3260.   function setX(elem, x, options, callback) {
  3261.     var duration, transition;
  3262.  
  3263.     if (!Monocle.Browser.has.transitions) {
  3264.       duration = 0;
  3265.     } else if (!options.duration) {
  3266.       duration = 0;
  3267.     } else {
  3268.       duration = parseInt(options['duration']);
  3269.     }
  3270.  
  3271.     if (typeof(x) == "number") { x = x + "px"; }
  3272.  
  3273.     if (duration) {
  3274.       transition = duration + "ms";
  3275.       transition += ' ' + (options['timing'] || 'linear');
  3276.       transition += ' ' + (options['delay'] || 0) + 'ms';
  3277.     } else {
  3278.       transition = "none";
  3279.     }
  3280.  
  3281.     if (elem.setXTCB) {
  3282.       Monocle.Events.deafen(elem, 'webkitTransitionEnd', elem.setXTCB);
  3283.       Monocle.Events.deafen(elem, 'transitionend', elem.setXTCB);
  3284.       elem.setXTCB = null;
  3285.     }
  3286.  
  3287.     elem.setXTCB = function () {
  3288.       if (callback) { callback(); }
  3289.     }
  3290.  
  3291.     elem.dom.setBetaStyle('transition', transition);
  3292.     if (Monocle.Browser.has.transform3d) {
  3293.       elem.dom.setBetaStyle('transform', 'translate3d('+x+',0,0)');
  3294.     } else {
  3295.       elem.dom.setBetaStyle('transform', 'translateX('+x+')');
  3296.     }
  3297.  
  3298.     if (!duration) {
  3299.       elem.setXTCB();
  3300.     } else {
  3301.       Monocle.Events.listen(elem, 'webkitTransitionEnd', elem.setXTCB);
  3302.       Monocle.Events.listen(elem, 'transitionend', elem.setXTCB);
  3303.     }
  3304.   }
  3305.   */
  3306.  
  3307.  
  3308.   function getX(elem) {
  3309.     if (typeof WebKitCSSMatrix == "object") {
  3310.       var matrix = window.getComputedStyle(elem).webkitTransform;
  3311.       matrix = new WebKitCSSMatrix(matrix);
  3312.       return matrix.m41;
  3313.     } else {
  3314.       var prop = elem.style.MozTransform;
  3315.       if (!prop || prop == "") { return 0; }
  3316.       return parseFloat((/translateX\((\-?.*)px\)/).exec(prop)[1]) || 0;
  3317.     }
  3318.   }
  3319.  
  3320.  
  3321.   function jumpIn(pageDiv, callback) {
  3322.     setX(pageDiv, 0, { duration: 1 }, callback);
  3323.   }
  3324.  
  3325.  
  3326.   function jumpOut(pageDiv, callback) {
  3327.     setX(pageDiv, 0 - pageDiv.offsetWidth, { duration: 1 }, callback);
  3328.   }
  3329.  
  3330.  
  3331.  
  3332.   function slideIn(callback) {
  3333.     var slideOpts = {
  3334.       duration: k.durations.SLIDE,
  3335.       timing: 'ease-in'
  3336.     };
  3337.     setX(upperPage(), 0, slideOpts, callback);
  3338.   }
  3339.  
  3340.  
  3341.   function slideOut(callback) {
  3342.     var slideOpts = {
  3343.       duration: k.durations.SLIDE,
  3344.       timing: 'ease-in'
  3345.     };
  3346.     setX(upperPage(), 0 - upperPage().offsetWidth, slideOpts, callback);
  3347.   }
  3348.  
  3349.  
  3350.   function slideToCursor(cursorX, callback, duration) {
  3351.     setX(
  3352.       upperPage(),
  3353.       Math.min(0, cursorX - upperPage().offsetWidth),
  3354.       { duration: duration || k.durations.FOLLOW_CURSOR },
  3355.       callback
  3356.     );
  3357.   }
  3358.  
  3359.  
  3360.   API.pageCount = p.pageCount;
  3361.   API.addPage = addPage;
  3362.   API.getPlace = getPlace;
  3363.   API.moveTo = moveTo;
  3364.   API.listenForInteraction = listenForInteraction;
  3365.  
  3366.   API.visiblePages = visiblePages;
  3367.   API.interactiveMode = interactiveMode;
  3368.  
  3369.   initialize();
  3370.  
  3371.   return API;
  3372. }
  3373.  
  3374.  
  3375. Monocle.Flippers.Slider.DEFAULT_PANELS_CLASS = Monocle.Panels.TwoPane;
  3376. Monocle.Flippers.Slider.FORWARDS = 1;
  3377. Monocle.Flippers.Slider.BACKWARDS = -1;
  3378. Monocle.Flippers.Slider.durations = {
  3379.   SLIDE: 220,
  3380.   FOLLOW_CURSOR: 100
  3381. }
  3382.  
  3383. Monocle.pieceLoaded('flippers/slider');
  3384.  
  3385. Monocle.pieceLoaded('monocle');
  3386.