/*
 * Scaling Framework (with Page Initialization) v1.0 <http://www.woutervandergraaf.nl/scaling/>
 * Copyright 2006-2007 Wouter van der Graaf
 * This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
 */

/* prevent flickering of background images in IE */
try {
  document.execCommand("BackgroundImageCache", false, true); 
} catch(e) {}


/* user agent sniffing shortcuts */
var UA = {};
with (UA) {
  UA.toString = function() { return navigator.userAgent; };
  UA.test = function(s) { return toString().toLowerCase().indexOf(s.toLowerCase()) > -1; };
  UA.gecko = test("gecko") && !test("webkit");
  UA.webkit = test("webkit");
  UA.opera = test("opera");
  UA.ie = test("msie");
  UA.ie6 = test("msie 6");
  UA.linux = test("linux");
  UA.mac = test("mac");
  UA.win = test("windows");
}


/* Initialize when DOM content is loaded */
var Init = {
  functions: [ // functions to be executed on init
    function() {
      document.body.className += " js-on";
    }
  ],
  add: function(fn) { // add fn to init functions
    if (typeof fn != "function") return;
    Init.functions.push(fn);
  },
  execute: function() { // run init functions once
    if (arguments.callee.done) return;
    arguments.callee.done = true;
    for (var i = 0, l = Init.functions.length; i < l; i++)
      Init.functions[i]();
  },
  setup: function() { // listeners for different browsers
    if (document.addEventListener) document.addEventListener("DOMContentLoaded", Init.execute, false);
    if (UA.ie) {
      document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>");
      var script = document.getElementById("__ie_onload");
      script.onreadystatechange = function() {
        if (this.readyState == "complete") Init.execute();
      };
    }
    if (UA.webkit && document.readyState) {
      function checkState() {
        if (document.readyState != "loading") Init.execute();
        else setTimeout(checkState, 10);
      }
      checkState();
    }
    window.onload = window.onunload = window.onabort = Init.execute; // fallback
  }
};
Init.setup(); // execute now
/* Initialization end */


/* Scaling framework to handle viewport width/text size ratio */
var Scaling = {
  Node: function(n) {
    /* scaling node is returned by modifier function, using original properties
       of n to revert to previous state */
    var that = this;
    this.parentNode = n.parentNode;
    this.nextSibling = n.nextSibling;
    if (n.nodeType == 1) this.className = n.className;
    this.revert = function() {
      if (n.nodeType == 1) n.className = that.className;
      if (n != document.documentElement) {
        if (that.parentNode) { // look up original parentNode
          if (that.nextSibling) // move node before original nextSibling
            that.parentNode.insertBefore(n, that.nextSibling);
          else that.parentNode.appendChild(n);
        }
        else // node newly created, so remove it
          n.parentNode.removeChild(n);
      }
    }
  },
  Function: function(fn) {
    // scaling function using fn as reverting function
    this.revert = fn;
  },
  Rule: function(ratio, fn) {
    // rule handle depending on ratio of viewport width and font size
    var that = this;
    this.objects = null;
    this.run = function() { // activation of rule
      if (Scaling.getRatio() >= ratio) { // activate modifier functions in fn
        if (!that.objects) { // objects are returned by modifiers
          var r = fn();
          that.objects = r.length ? r : [r]; // return value of fn can be array
        }
      }
      else if (that.objects) { // revert modifiers in reverse order
        that.objects.reverse();
        for (var i = 0, l = that.objects.length; i < l; i++)
          that.objects[i].revert();
        that.objects = null;
      }
    };
  },
  rules: [], // rules array
  addRule: function(width, fontSize, fn) {
    /* arguments: viewport width, computed font size on body element at normal text zoom level,
       wrapper function containing modifiers */
    if (!(width && fontSize && fn)) return;
    Scaling.rules.push(new Scaling.Rule(width / fontSize, fn));
  },
  timerId: 0, // identifier for scaling loop
  runRules: function() { // scaling loop, checking conditions for each rule
    var r = Scaling.getRatio();
    if (r != Scaling.lastRatio) {
      var narrowing = r < Scaling.lastRatio; // is ratio smaller than last time, effectively narrowing readable area?
      if (narrowing) Scaling.rules.reverse(); // then run rules in reverse order
      for (var i = 0, l = Scaling.rules.length; i < l; i++)
        Scaling.rules[i].run();
      if (narrowing) Scaling.rules.reverse(); // back to normal
      Scaling.lastRatio = r;
      if (typeof Scaling.onRatioChange == "function") // custom event trigger
        Scaling.onRatioChange();
    }
    Scaling.timerId = setTimeout(Scaling.runRules, 1000); // loop through rules every 1000 ms
  },
  ratioElement: null,
  createRatioElement: function() { // create element to calculate runtime ratio
    var n = document.body.appendChild(document.createElement("div"));
    n.style.position = "absolute";
    n.style.top = "0";
    n.style.left = "-1234em";
    n.style.width = n.style.height = "1em";
    n.style.overflow = "hidden";
    n.style.fontSize = "100%";
    Scaling.ratioElement = n;
  },
  lastRatio: null, // previous ratio
  getViewportWidth: function() {
	var w = 0;
    if (document.documentElement && document.documentElement.clientWidth)
      w = document.documentElement.clientWidth;
    else if (document.body && document.body.clientWidth)
      w = document.body.clientWidth;
    else if (window.innerWidth)
      w = window.innerWidth - 18;
	return w;
  },
  getFontSize: function() {
	return Scaling.ratioElement.offsetHeight; // return 1em in pixels
  },
  getRatio: function() { // divide viewport width by font size
    return Scaling.getViewportWidth() / Scaling.getFontSize(); 
  },
  onRatioChange: null, // custom event
  hideRendering: function() { // hide rendering of page to prevent layout jumping caused by scaling
    if (!Init.execute.done && screen.width >= 640) { // mostly only necessary on larger screens
      if (document.body) document.body.style.display = "none";
      else setTimeout(Scaling.hideRendering, 100);
    }
  },
  showRendering: function() { // draw page after scaling
    if (document.body) document.body.style.display = "";
  },
  setup: function() { // use setup function on DOM content loaded event
    if (Scaling.timerId == 0) { // only one instance of scaling loop allowed
      Scaling.createRatioElement();
      Scaling.runRules();
      Scaling.showRendering();
    }
  },
  // modifier functions below; return object to make reverting possible
  execute: function(fn1, fn2) {
    if (!(fn1 && fn2)) return;
    fn1();
    return new Scaling.Function(fn2);
  },
  appendChild: function(n, ref) {
    if (!(n && ref)) return;
    var sn = new Scaling.Node(n);
    ref.appendChild(n);
    return sn;
  },
  insertBefore: function(n, ref) {
    if (!(n && ref)) return;
    var sn = new Scaling.Node(n);
    ref.parentNode.insertBefore(n, ref);
    return sn;
  },
  insertAfter: function(n, ref) {
    if (!(n && ref)) return;
    var sn = new Scaling.Node(n);
    ref.parentNode.insertBefore(n, ref).parentNode.insertBefore(ref, n);
    return sn;
  },
  addClassName: function(n, s) {
    if (!(n && s)) return;
    var sn = new Scaling.Node(n);
    n.className += " " + s;
    return sn;
  },
  replaceClassName: function(n, s) {
    if (!(n && s)) return;
    var sn = new Scaling.Node(n);
    n.className = s;
    return sn;
  }
};
Scaling.hideRendering(); // must execute now, undone after scaling
Init.add(Scaling.setup); // add scaling to init
/* Scaling framework end */


/* add Scaling Rules in ascending order of ratio (viewport width / normal font size) */
/* syntax:
Scaling.addRule('viewport width', 'body font size at normal zoom', function() {
  return [
    Scaling.'modifier function 1'(),
    Scaling.'modifier function 2'()
  ];
});
*/
