//
//  jquery.xmlns.js:  xml-namespace selector support for jQuery
//
//  This plugin modifies the jQuery tag and attribute selectors to
//  support optional namespace specifiers as defined in CSS 3:
//
//    $("elem")      - matches 'elem' nodes in the default namespace
//    $("|elem")     - matches 'elem' nodes that don't have a namespace
//    $("NS|elem")   - matches 'elem' nodes in declared namespace 'NS'
//    $("*|elem")    - matches 'elem' nodes in any namespace
//    $("NS|*")      - matches any nodes in declared namespace 'NS'
//
//  A similar synax is also supported for attribute selectors, but note
//  that the default namespace does *not* apply to attributes - a missing
//  or empty namespace selector selects only attributes with no namespace.
//
//  In a slight break from the W3C standards, and with a nod to ease of
//  implementation, the empty namespace URI is treated as equivalent to
//  an unspecified namespace.  Plenty of browsers seem to make the same
//  assumption...
//
//  Namespace declarations live in the $.xmlns object, which is a simple
//  mapping from namespace ids to namespace URIs.  The default namespace
//  is determined by the value associated with the empty string.
//
//    $.xmlns.D = "DAV:"
//    $.xmlns.FOO = "http://www.foo.com/xmlns/foobar"
//    $.xmlns[""] = "http://www.example.com/new/default/namespace/"
//
//  Unfortunately this is a global setting - I can't find a way to do
//  query-object-specific namespaces since the jQuery selector machinery
//  is stateless.  However, you can use the 'xmlns' function to push and
//  pop namespace delcarations with ease:
//
//    $().xmlns({D:"DAV:"})     // pushes the DAV: namespace
//    $().xmlns("DAV:")         // makes DAV: the default namespace
//    $().xmlns(false)          // pops the namespace we just pushed
//    $().xmlns(false)          // pops again, returning to defaults
//
//  To execute this as a kind of "transaction", pass a function as the
//  second argument.  It will be executed in the context of the current
//  jQuery object:
//
//    $().xmlns("DAV:",function() {
//      //  The default namespace is DAV: within this function,
//      //  but it is reset to the previous value on exit.
//      return this.find("response").each(...);
//    }).find("div")
//
//  If you pass a string as a function, it will be executed against the
//  current jQuery object using find(); i.e. the following will find all
//  "href" elements in the "DAV:" namespace:
//
//    $().xmlns("DAV:","href")
//
//
//  And finally, the legal stuff:
//
//    Copyright (c) 2009, Ryan Kelly.
//    TAG and ATTR functions derived from jQuery's selector.js.
//    Dual licensed under the MIT and GPL licenses.
//    http://docs.jquery.com/License
//

(function($) {

//  Some common default namespaces, that are treated specially by browsers.
//
var default_xmlns = {
    "xml": "http://www.w3.org/XML/1998/namespace",
    "xmlns": "http://www.w3.org/2000/xmlns/",
    "html": "http://www.w3.org/1999/xhtml/"
};


//  A reverse mapping for common namespace prefixes.
//
var default_xmlns_rev = {}
for(var k in default_xmlns) {
    default_xmlns_rev[default_xmlns[k]] = k;
}


//  $.xmlns is a mapping from namespace identifiers to namespace URIs.
//  The default default-namespace is "*", and we provide some additional
//  defaults that are specified in the XML Namespaces standard.
//
$.extend({xmlns: $.extend({},default_xmlns,{"":"*"})});


//  jQuery method to push/pop namespace declarations.
//
//  If a single argument is specified:
//    * if it's a mapping, push those namespaces onto the stack
//    * if it's a string, push that as the default namespace
//    * if it evaluates to false, pop the latest namespace
//
//  If two arguments are specified, the second is executed "transactionally"
//  using the namespace declarations found in the first.  It can be either a
//  a selector string (in which case it is passed to this.find()) or a function
//  (in which case it is called in the context of the current jQuery object).
//  The given namespace mapping is automatically pushed before executing and
//  popped afterwards.
//
var xmlns_stack = [];
$.fn.extend({xmlns: function(nsmap,func) {
    if(typeof nsmap == "string") {
        nsmap = {"":nsmap};
    }
    if(nsmap) {
        xmlns_stack.push($.xmlns);
        $.xmlns = $.extend({},$.xmlns,nsmap);
        if(func !== undefined) {
            if(typeof func == "string") {
                return this.find(func).xmlns(undefined)
            } else {
                var self = this;
                try {
                    self = func.call(this);
                    if(!self) {
                        self = this;
                    }
                } finally {
                    self.xmlns(undefined);
                }
                return self
            }
        } else {
            return this;
        }
    } else {
        $.xmlns = (xmlns_stack ? xmlns_stack.pop() : {});
        return this;
    }
}});


//  Convert a namespace prefix into a namespace URI, based
//  on the delcarations made in $.xmlns.
//
var getNamespaceURI = function(id) {
    // No namespace id, use the default.
    if(!id) {
        return $.xmlns[""];
    }
    // Strip the pipe character from the specifier
    id = id.substr(0,id.length-1);
    // Certain special namespaces aren't mapped to a URI
    if(id == "" || id == "*") {
        return id;
    }
    var ns = $.xmlns[id];
    if(typeof(ns) == "undefined") {
        throw "Syntax error, undefined namespace prefix '" + id + "'";
    }
    return ns;
};


//  Update the regex used by $.expr to parse selector components for a
//  particular type of selector (e.g. "TAG" or "ATTR").
//
//  This logic is taken straight from the jQuery/Sizzle sources.
//
var setExprMatchRegex = function(type,regex) {
  $.expr.match[type] = new RegExp(regex.source + /(?![^\[]*\])(?![^\(]*\))/.source);
  if($.expr.leftMatch) {
      $.expr.leftMatch[type] = new RegExp(/(^(?:.|\r|\n)*?)/.source + $.expr.match[type].source.replace(/\\(\d+)/g, function(all, num){
          return "\\" + (num - 0 + 1);
      }));
  }
}



//  Modify the TAG match regexp to include optional namespace selector.
//  This is basically (namespace|)?(tagname).
//
setExprMatchRegex("TAG",/^((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)))/);


//  Perform some capability-testing.
//
var div = document.createElement("div");

//  Sometimes getElementsByTagName("*") will return comment nodes,
//  which we will have to remove from the results.
//
var gebtn_yields_comments = false;
div.appendChild(document.createComment(""));
if(div.getElementsByTagName("*").length > 0) {
    gebtn_yields_comments = true;
}

//  Some browsers return node.localName in upper case, some in lower case.
//
var localname_is_uppercase = true;
if(div.localName && div.localName == "div") {
    localname_is_uppercase = false;
}

//  Allow the testing div to be garbage-collected.
//
div = null;


//  Modify the TAG find function to account for a namespace selector.
//
$.expr.find.TAG = function(match,context,isXML) {
    var ns = getNamespaceURI(match[2]);
    var ln = match[3];
    var res;
    if(typeof context.getElementsByTagNameNS != "undefined") {
        //  Easy case - we have getElementsByTagNameNS
        res = context.getElementsByTagNameNS(ns,ln);
    } else if(typeof context.selectNodes != "undefined") {
        //  Use xpath if possible (not available on HTML DOM nodes in IE)
        if(context.ownerDocument) {
            context.ownerDocument.setProperty("SelectionLanguage","XPath");
        } else {
            context.setProperty("SelectionLanguage","XPath");
        }
        var predicate = "";
        if(ns != "*") {
            if(ln != "*") {
                predicate="namespace-uri()='"+ns+"' and local-name()='"+ln+"'";
            } else {
                predicate="namespace-uri()='"+ns+"'";
            }
        } else {
            if(ln != "*") {
                predicate="local-name()='"+ln+"'";
            }
        }
        if(predicate) {
            res = context.selectNodes("descendant-or-self::*["+predicate+"]");
        } else {
            res = context.selectNodes("descendant-or-self::*");
        }
    } else {
        //  Otherwise, we need to simulate using getElementsByTagName
        res = context.getElementsByTagName(ln);
        if(gebtn_yields_comments && ln == "*") {
            var tmp = [];
            for(var i=0; res[i]; i++) {
                if(res[i].nodeType == 1) {
                    tmp.push(res[i]);
                }
            }
            res = tmp;
        }
        if(res && ns != "*") {
            var tmp = [];
            for(var i=0; res[i]; i++) {
               if(res[i].namespaceURI == ns || res[i].tagUrn == ns) {
                   tmp.push(res[i]);
               }
            }
            res = tmp;
        }
    }
    return res;
};


//  Check whether a node is part of an XML document.
//  Copied verbatim from jQuery sources, needed in TAG preFilter below.
//
var isXML = function(elem){
    return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" ||
            !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML";
};


//  Modify the TAG preFilter function to work with modified match regexp.
//  This normalises case of the tag name if we're in a HTML document.
//
$.expr.preFilter.TAG = function(match, curLoop, inplace, result, not, isXML) {
  var ln = match[3];
  if(!isXML) {
      if(localname_is_uppercase) {
          ln = ln.toUpperCase();
      } else {
          ln = ln.toLowerCase();
      }
  }
  return [match[0],getNamespaceURI(match[2]),ln];
};


//  Modify the TAG filter function to account for a namespace selector.
//
$.expr.filter.TAG = function(elem,match) {
    var ns = match[1];
    var ln = match[2];
    var e_ns = elem.namespaceURI ? elem.namespaceURI : elem.tagUrn;
    var e_ln = elem.localName ? elem.localName : elem.tagName;
    if(ns == "*" || e_ns == ns || (ns == "" && !e_ns)) {
        return ((ln == "*" && elem.nodeType == 1)  || e_ln == ln);
    }
    return false;
};


//  Modify the ATTR match regexp to extract a namespace selector.
//  This is basically ([namespace|])(attrname)(op)(quote)(pattern)(quote)
//
setExprMatchRegex("ATTR",/\[\s*((?:((?:[\w\u00c0-\uFFFF\*_-]*\|)?)((?:[\w\u00c0-\uFFFF_-]|\\.)+)))\s*(?:(\S?=)\s*(['"]*)(.*?)\5|)\s*\]/);


//  Modify the ATTR preFilter function to account for new regexp match groups,
//  and normalise the namespace URI.
//
$.expr.preFilter.ATTR = function(match, curLoop, inplace, result, not, isXML) {
    var name = match[3].replace(/\\/g, "");
    if(!isXML && $.expr.attrMap[name]) {
        match[3] = $.expr.attrMap[name];
    }
    if( match[4] == "~=" ) {
        match[6] = " " + match[6] + " ";
    }
    if(!match[2] || match[2] == "|") {
        match[2] = "";
    } else {
        match[2] = getNamespaceURI(match[2]);
    }
    return match;
};


//  Modify the ATTR filter function to account for namespace selector.
//  Unfortunately this means factoring out the attribute-checking code
//  into a separate function, since it might be called multiple times.
//
var filter_attr = function(result,type,check) {
    var value = result + "";
    return result == null ?
                type === "!=" :
                type === "=" ?
                value === check :
                type === "*=" ?
                value.indexOf(check) >= 0 :
                type === "~=" ?
                (" " + value + " ").indexOf(check) >= 0 :
                !check ?
                value && result !== false :
                type === "!=" ?
                value != check :
                type === "^=" ?
                value.indexOf(check) === 0 :
                type === "$=" ?
                value.substr(value.length - check.length) === check :
                type === "|=" ?
                value === check || value.substr(0,check.length+1)===check+"-" :
                false;
}


$.expr.filter.ATTR = function(elem, match) {
    var ns = match[2];
    var name = match[3];
    var type = match[4];
    var check = match[6];
    var result;
    //  No namespace, just use ordinary attribute lookup.
    if(ns == "") {
        result = $.expr.attrHandle[name] ?
                     $.expr.attrHandle[name](elem) :
                     elem[name] != null ?
                         elem[name] :
                         elem.getAttribute(name);
        return filter_attr(result,type,check);
    }
    //  Directly use getAttributeNS if applicable and available
    if(ns != "*" && typeof elem.getAttributeNS != "undefined") {
        return filter_attr(elem.getAttributeNS(ns,name),type,check);
    }
    //  Need to iterate over all attributes, either because we couldn't
    //  look it up or because we need to match all namespaces.
    var attrs = elem.attributes;
    for(var i=0; attrs[i]; i++) {
        var ln = attrs[i].localName;
        if(!ln) {
            ln = attrs[i].nodeName
            var idx = ln.indexOf(":");
            if(idx >= 0) {
                ln = ln.substr(idx+1);
            }
        }
        if(ln == name) {
            result = attrs[i].nodeValue;
            if(ns == "*" || attrs[i].namespaceURI == ns) {
                if(filter_attr(result,type,check)) {
                    return true;
                }
            }
            if(attrs[i].namespaceURI === "" && attrs[i].prefix) {
                if(attrs[i].prefix == default_xmlns_rev[ns]) {
                    if(filter_attr(result,type,check)) {
                        return true;
                    }
                }
            }
        }
    }
    return false;
};


})(jQuery);





/*! Copyright (c) 2009 Brandon Aaron (http://brandonaaron.net)
 * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php)
 * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses.
 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
 *
 * Version: 3.0.2
 *
 * Requires: 1.2.2+
 */

(function($) {

var types = ['DOMMouseScroll', 'mousewheel'];

$.event.special.mousewheel = {
    setup: function() {
        if ( this.addEventListener )
            for ( var i=types.length; i; )
                this.addEventListener( types[--i], handler, false );
        else
            this.onmousewheel = handler;
    },

    teardown: function() {
        if ( this.removeEventListener )
            for ( var i=types.length; i; )
                this.removeEventListener( types[--i], handler, false );
        else
            this.onmousewheel = null;
    }
};

$.fn.extend({
    mousewheel: function(fn) {
        return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
    },

    unmousewheel: function(fn) {
        return this.unbind("mousewheel", fn);
    }
});


function handler(event) {
    var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;

    event = $.event.fix(event || window.event);
    event.type = "mousewheel";

    if ( event.wheelDelta ) delta = event.wheelDelta/120;
    if ( event.detail     ) delta = -event.detail/3;

    // Add events and delta to the front of the arguments
    args.unshift(event, delta);

    return $.event.handle.apply(this, args);
}

})(jQuery);



/*
 * Copyright Nicholas Wilson 2010. Original idea and code by
   Rafe Brox "etcet" (http://etcet.net/projects/columns/).
 */
    //
    //
    // Make columns
    //
    //

var view_width;  //width of the view which is populated by columns
var num_cols;    //number of columns on display at once
var total_cols;  //total number of columns the page uses
var scrolling = true;

(function($) {
    "use strict";

    function add_script(type, src, content) {
        var script = document.createElement("script");
        script.type = type;
        if (src != '') script.src = (src.match(/^https?:\/\//) ? src : folderURL + src);
        if (content != '') {
          if (window.opera) {script.innerHTML = content}
                       else {script.text = content}
        }
        document.getElementsByTagName("head")[0].appendChild(script);
    }

    //Some utilities for CSS columns
    function reset_scroll() {
      if ($(window).width() < $(document).width()) {
        $('#scroll').css('left', 10+ ($(window).scrollLeft() / ($(document).width() - $(window).width())) * ($(window).width() - 20 - $('#scroll').width()));
        $('#scroll').css('display', 'block');
      } else {
        $('#scroll').css('display', 'none');
      }

      //Fix the hanging content of columns just off-screen
      var current_scroll = $(window).scrollLeft();
      var current_cstop = Math.round(current_scroll / (view_width / num_cols));
      var cstop_error = Math.abs(current_cstop * (view_width / num_cols) - current_scroll);
      //If we are within a couple of pixels of a column stop, mask the contents of the adjacent columns:
      if (cstop_error < 5)
        $('html').addClass('mask-columns');
      else
        $('html').removeClass('mask-columns');

      //A quick fix to the above to stop it masking the FB comments
      if ($('#fbcomments').length > 0) {
        var current_cstop = Math.ceil(current_scroll / (view_width / num_cols) - 0.05/*in case of round-off*/);
        var right_most_col_in_view = current_cstop + num_cols - 1;
        if (right_most_col_in_view >= total_cols - 1)
          $('#fbcomments').addClass('on-screen');
        else
          $('#fbcomments').removeClass('on-screen');
      }
    }
    function set_scroll(val, by_screen) {
      //We have to fix the annoying case of scrolling to the right when we have to add blank columns.
      var current_spacers = $('body > article> .column-spacer').length;
      var last_cols = (total_cols - current_spacers) % num_cols;
      var using_spacers = by_screen && $('#fbcomments').length == 0;

      if (!using_spacers || current_spacers > num_cols - last_cols) {
        //The case where we have spacers to remove
        var target_spacers = 0;
        if (using_spacers)
          target_spacers = num_cols - last_cols;
        while (current_spacers > target_spacers) {
          $('body > article> .column-spacer').last().remove()
          --current_spacers;
        }
        resize_columns();
      } else if (using_spacers && current_spacers < num_cols - last_cols) {
        //The case where we have to add some spacers
        var target_spacers = num_cols - last_cols;
        while (current_spacers < target_spacers) {
          $('body > article').append('<div class="column-spacer"/>');
          ++current_spacers;
        }
        resize_columns();
      }

      $(window).scrollLeft(val);
      //FIXME move tabbing as well
      reset_scroll();
    }
    function set_scroll_element(id) {
      var target = $(id.replace('.', '\\.'));
      if (target.length < 1) return;

      if (target[0].localName == 'a' && target[0].innerHTML == '') target = target.parent();
      if (target[0].localName != 'p' && target.parent()[0].localName == 'p') target = target.parent();
      var current_scroll = $(window).scrollLeft();
      var current_lcol = Math.round(current_scroll / (view_width / num_cols));
      var current_rcol = current_lcol + num_cols - 1;
      var target_col = Math.floor(
          (target.offset().left - parseInt(body.css('margin-left')) + target.outerWidth(true)/2)
        / (view_width / num_cols)
      );

      if (target_col <= current_rcol && target_col >= current_lcol) {
        // No need to scroll
      } else {
        set_scroll(Math.floor(target_col / num_cols) * view_width, true)
      }

      target.addClass('target').delay(800).queue(function () {
        $(this).removeClass('target');
        $(this).dequeue();
      });

      //target.focus();
    }

    //resize column height and calculate new column width
    function resize_columns() {
        var offset = body.offset();

        //set column height to window 'outer' height
        var height = $(window).height() - 2 * offset.top;
        body.height(height);

        var column_width;
        if (body.css('column-width'))
            column_width = body.css('column-width');
        else if (body.css('-moz-column-width'))
            column_width = body.css('-moz-column-width');
        else
            column_width = body.css('-webkit-column-width');
        column_width = parseInt(column_width.replace(/[^\d]/g,""));
        view_width = $(window).width() - parseInt(body.css('margin-left'));
        num_cols = Math.max(Math.floor(view_width / column_width), 1.0);
        total_cols = Math.round(($(document).width() - parseInt(body.css('margin-left'))) / (view_width / num_cols));

        reset_scroll();
    }

    function introduce_columns(binding) {
        //console.log('handling resize');
        var body = $('.columns > body');
        //Whether the current layout is a single column
        var single = (body.css('-moz-column-width')    || 'auto') == 'auto'
                  && (body.css(     'column-width')    || 'auto') == 'auto'
                  && (body.css('-webkit-column-width') || 'auto') == 'auto';

        if (single || binding) {
          body.css('-moz-column-width', '400px');
          body.css('column-width', '400px');
          body.css('max-width', 'none');
          body.addClass('columns-active');
          $('html').css('overflow', 'hidden');
          body.append('<div id="scroll"/><div id="column-mask-left"/><div id="column-mask-right"/>');
        }
        if (binding)
          $(window).bind('resize', function(){introduce_columns(false);});

        resize_columns();

        if (num_cols < 1.5) {
            body.height('auto');
            body.css('column-width', 'auto');
            body.css('-moz-column-width', 'auto');
            body.css('-webkit-column-width', 'auto');
            body.removeClass('columns-active');
            $('html').css('overflow', 'auto');
            $('#scroll, #column-mask-left, #column-mask-right').remove();
            $(window).unbind('.cols');
            return;
        }

        var fbcomments = $('#fbcomments');
        if (fbcomments.length) {
          fbcomments.css('max-height', body.innerHeight()-parseInt(fbcomments.css('border-bottom-width')));
          fbcomments.width(view_width / num_cols);
        }

        if (binding)
          $(window).load(function(){
            resize_columns();
            if (document.location.hash != '')
              set_scroll_element(document.location.hash);
            else
              set_scroll(Math.round($(window).scrollLeft() / (view_width / num_cols / 5)) * (view_width / num_cols / 5));
          });

        if (!single && !binding)
          return;

        $(window).bind('keydown.cols', function (e) {
            if (!scrolling)
              return;
            var current_scroll = $(window).scrollLeft();
            var current_stop = Math.round(current_scroll / (view_width / num_cols / 5));
            var current_pstop = current_scroll / view_width;
            //Behaviour is to first realign the current page; only move to next if already aligned
            var target_pstop;
            if (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) return;
            switch (e.keyCode) {
                case 38:
                case 37: set_scroll((current_stop - 1) * (view_width / num_cols / 5), false);
                    break;
                case 40:
                case 39: set_scroll((current_stop + 1) * (view_width / num_cols / 5), false);
                    break;
                case 34:
                    target_pstop = Math.ceil(current_pstop);
                    if (Math.abs(target_pstop - current_pstop) < 0.01) target_pstop += 1;
                    set_scroll(target_pstop * view_width, true);
                    break;
                case 33:
                    target_pstop = Math.floor(current_pstop);
                    if (Math.abs(target_pstop - current_pstop) < 0.01) target_pstop -= 1;
                    set_scroll(target_pstop * view_width, false);
                    break;
                default:
                    return;
            }
            e.preventDefault();
            reset_scroll();
        });

        $(window).bind('scroll.cols', function () {reset_scroll();});

        $(window).bind('mousewheel.cols', function(e, delta) {
            if (!scrolling) return;
            var rDelta = Math.round(delta);
            if (Math.abs(rDelta) < 0.5) rDelta = delta / Math.abs(delta);
            var current_scroll = $(window).scrollLeft();
            var current_stop = Math.round(current_scroll / (view_width / num_cols / 5));
            set_scroll((current_stop - rDelta) * (view_width / num_cols / 5));
            e.preventDefault();
        });

        $('a[href^=\'#\']').bind('click.cols', function(e) {
          set_scroll_element($(e.target).attr('href'));
          e.preventDefault();
        });
    }

var folderURL = $("head > link[rel='stylesheet']").attr('href').replace(/[^\/]*$/, '');
var body = $('body');
//Set the property...
body.css('-moz-column-width', '400px');
//body.css('column-width', '400px');         Opera has some massive columns fail ATM...
//body.css('-webkit-column-width', '400px'); Boycott WebKit's bad column support...

//...then check to see if it is there, so we know it is supported.
if (   (body.css('-moz-column-width') || 'auto') != 'auto'
    || (body.css(     'column-width') || 'auto') != 'auto'
    /* || (body.css('-webkit-column-width') || 'auto') != 'auto'*/)  {
    introduce_columns(true);
}


$(window).load(function() {
  window.loaded = true;
});

$(document).ready(function() {

    //
    //
    // Fix vertical align of images and equations
    //
    //
    $.xmlns['mml'] = 'http://www.w3.org/1998/Math/MathML';
    $("mml|math[mode=display], img, object").not('form img, .inlinemediaobject > *').each(function(index){
        var el = $(this);
        el.wrap('<div style="display:table; width: 100%; table-layout: fixed;"><div style="display:table-cell; vertical-align: middle;"/></div>');
        var line_height = parseInt(el.parent().css('line-height'));
        var wrapper = el.parent();
        var height = Math.round(0.9*(wrapper.outerHeight(true) / line_height) + 0.499) * line_height;
        wrapper.parent().height(height);
        if (el.outerWidth(true) + 2/*paranoia*/ < wrapper.width()) wrapper.parent().css('table-layout', 'auto');

        // Blasted WebKit // FIXME I really want to change my CSS to avoid needing this
        if ($.browser.webkit) wrapper.parent().css('table-layout', 'auto');
    });


    //
    //
    // Make popups.
    //
    //
    var makePops = function(index, el) {
      function imgToObject(el) {
        if (el[0].localName != 'img' || !el.attr('src').match(/\.svg[^\/]*$/)) {
          el.data('loaded', true);
          return el;
        }
        var newEl = $('<iframe/>').replaceAll(el);
        newEl.data('loaded', false);
        newEl.load(function(){newEl.data('loaded', true)});
        //newEl.attr('data', el.attr('src'));
        var attrs = el[0].attributes;
        for (var i = 0; i < attrs.length; ++i)
          newEl[0].setAttributeNS(attrs[i].namespaceURI, attrs[i].localName, attrs[i].value);
        newEl[0].setAttribute('width', width+1+'');
        newEl[0].setAttribute('height', height+1+'');
        return newEl;
      }

      var width = el.scrollWidth;
      var height= el.scrollHeight;
      if (el.localName == 'img') {
        if (el.naturalWidth) {
          width = el.naturalWidth;
          height = el.naturalHeight;
        } else {//Please implement, Opera
          var tmpImage = new Image();
          tmpImage.src = el.attr("src");
          width = tmpImage.width;
          height = tmpImage.height;
        }
      }
      el = $(el);
      
      if (Math.floor(width) <= Math.ceil(el.width()) + 1) {
        imgToObject(el);
        return;
      }

      el.css('overflow-x', 'hidden');
      el.wrap('<a class="expander'+(el[0].localName == 'img' ? ' expander-img' : '')+'" href="#"/>');
      var a = el.parent();
      a.append('<div class="expander-arrow"/>');
      a.bind('click', function(e) {
        a.blur(); //For the sake of Opera... //FIXME
        scrolling = false;
        $('.expanded-wrapper, .expanded-cover').remove();

        var scroll = $('#scroll');
        if (scroll.length)
          scroll.before('<div class="expanded-wrapper"/>');
        else
          $('body').append('<div class="expanded-wrapper"/>');
        var wrapper = $('.expanded-wrapper').first();
        wrapper.css('visibility', 'hidden');
        wrapper.append(imgToObject(el.clone()));
        if (el[0].localName == 'img') {
          var img = $(wrapper[0].firstChild);
          //img.wrap('<div></div>');
          img.css({display: 'block', border: 'none', margin: '0 auto'});
        }
        if ($.browser.opera) {
          //More blasted Opera fixes... FIXME
          wrapper.children().css({'max-height': 'none', 'height': '100%'});
        }
        wrapper.append('<kbd class="expanded-esc">ESC</kbd>');
        var newEl = wrapper[0].firstChild;
        var makeBox = function(e){
          if (!wrapper) {$(this).unbind(e); return;}
          wrapper.css({top: '40px', bottom: '16px', left: '90px', right: '90px'});
          if (newEl.scrollHeight < wrapper.height()) {
            var height = (wrapper.height() - newEl.scrollHeight) / 2;
            wrapper.css('top', (parseInt(wrapper.css('top')) + height) + 'px');
            wrapper.css('bottom', (parseInt(wrapper.css('bottom')) + height) + 'px');
            $(newEl).css('overflow-y','hidden');
          } else {
            $(newEl).css('overflow-y','auto');
          }
          if (newEl.scrollWidth < wrapper.width()) {
            var width = (wrapper.width() - newEl.scrollWidth) / 2;
            wrapper.css('left', (parseInt(wrapper.css('left')) + width) + 'px');
            wrapper.css('right', (parseInt(wrapper.css('right')) + width) + 'px');
            $(newEl).css('overflow-x', 'hidden');
          } else {
            $(newEl).css('overflow-x','auto');
          }
          wrapper.css('visibility', 'visible');
        };
        if ($(newEl).data('loaded') === true)
          makeBox();
        else
          $(newEl).load(makeBox);
        $(window).bind('resize', makeBox);

        if (scroll.length)
          scroll.before('<div class="expanded-cover"/>');
        else
          $('body').append('<div class="expanded-cover"/>');

        $('.expanded-cover').bind('click', function(e) {
          $('.expanded-wrapper, .expanded-cover').remove();
          scrolling = true;
          e.preventDefault();
        });

        wrapper.bind('focusin', function(e) {
          e.preventDefault();
        });
        $(window).bind('focusin', function(e) {
          //if (!window.opera)
            $('.expanded-wrapper, .expanded-cover').remove();
          if ($('.expanded-wrapper, .expanded-cover').length == 0) {
            $(this).unbind(e);
            scrolling = true;
            return;
          }
        });

        $(document).bind('keydown', function(e) {
          if (e.keyCode == 27)
            $('.expanded-wrapper, .expanded-cover').remove();
          if ($('.expanded-wrapper, .expanded-cover').length == 0) {
            $(this).unbind(e);
            scrolling = true;
          }
        });
        e.preventDefault();
        //wrapper.mousewheel(function(e) {e.preventDefault();});
      });
    };
    $('pre.programlisting').each(makePops);
    //Overly cautious; browsers render big images progressively, so
    //the height (naturalHeight) is available long before window.load
    if (window.loaded)
      $('img').each(makePops);
    else
      $(window).load(function() {$('img').each(makePops);});



    //
    //
    // Some silly fixes
    //
    //
    var footer = $('body > footer');
    footer.focusin(function(){footer.addClass('hover');});
    footer.focusout(function(){footer.removeClass('hover');});



    //
    //
    // Cunning replacement of buttons (with text content only) by
    // anchors with image logos for OpenID login block.
    //
    //
    /* Could make collection of user data slicker ...
    $("#login-data input[type='submit']").click(function(e){

      e.preventDefault();
    });
    */



    //
    //
    // Load blasted MathJax
    //
    //
    var MathPlayer; try {new ActiveXObject("MathPlayer.Factory.1"); MathPlayer = true} catch(err) {MathPlayer = false};

    var canUseMML = (($.browser.mozilla||false) && parseInt($.browser.version, 10) > 1) ||
                    (($.browser.msie||false) && MathPlayer) ||
                    (($.browser.opera||false) && parseInt($.browser.version, 10) > 9);
    if (!canUseMML) {
      var config = 'MathJax.Hub.Config({' +
                     'extensions: ["mml2jax.js"],' +
                     'jax: ["input/MathML","output/HTML-CSS"]' +
                   '});' +
                   'MathJax.Hub.Startup.onload();'
                   
      add_script(
        "text/javascript",
        window.location.host == 'localhost' ? "MathJax/MathJax.js" : "http://cdn.mathjax.org/mathjax/latest/MathJax.js",
        config
      );
    }



    //
    //
    // Web-app style cursor hiding
    //
    //
    var timeMoved;
    function mouseMove(e) {
      if (Math.abs(e.pageX - mouseMove.x) < 3 && Math.abs(e.pageY - mouseMove.y) < 3)
        return;
      mouseMove.x = e.pageX;
      mouseMove.y = e.pageY;
      timeMoved = Date.now();
      $('html').removeClass('invisibleCursor');
      window.setTimeout(function() {
        if (Date.now() - timeMoved >= 2500) {
          $('html').addClass('invisibleCursor');
        }
      }, 3000);
    }
    mouseMove.x = mouseMove.y = 0;
    $(window).mousemove(mouseMove);
    $(window).trigger('mousemove');


});

})(jQuery);
