// encoding: utf-8
if (!window.DOM)
{

  is_ie = msie = 0;

  (function(l,s,w,n,u){

    // User agent sniffing
    var ua = n.userAgent.toString();

    opera  = w.opera || 0;
    webkit = (/webkit/i.test(ua));
    mozilla = (!webkit && /mozilla/i.test(ua) && !/compatible/i.test(ua));

    /* MSIE sniffing */
    /*@cc_on

      is_ie = msie = {
        version:    parseFloat(n.appVersion.match(/MSIE ([^;]+)/)[1]),
        quirksmode: (document.compatMode=="BackCompat")
      };

      /*@if (@_jscript_version < 5.5)
        w.undefined = u;
        l('ie5.js');
      /*@end

      // Remedial fix for setTimeout in IE, allowing parameters to be sent to the function.
      // usage:  var myTimeout = window.setTimeout(func, ms, param1, param2, ..., paramN);
      w.setTimeout = function(f,t)
      {
        if (arguments.length == 2)
        {
          return s(f,t);
        }
        else
        {
          var p = [].slice.call(arguments, 2);
          return s(function(){ f.apply(null, p); }, t);
        }
      };

    @*/;

    "".replace(/^/,String)  &&  l('replace.js');

  })(function(n){ document.write('<script src="https://secure.eplica.is/codecentre/js/remedial/'+n+'" charset="iso-8859-1"></script>'); }, setTimeout, window, navigator);


(function(){

  var _doc = document,
      _docLoc = _doc.location,
      _win = window,
      R = RegExp;


  // Further utility functions
  R.escape = function(s) { return s.replace(/([\\\^\$*+[\]?{}.=!:(|)])/g, '\\$1'); }; // useful when injecting alien strings into new RegExp()es
  Object.merge = function(a, b, l) {  // Merges the properties of associative object `b` into associative object `a`.
    if (typeof(a)=='string') { a = Object.namespace(a); }
    for (var k in b)                   // l == true  triggers "Lazy-merge" where the original - non-null - properties of `a` are *not* overloaded.
        if (!l || a[k] == undefined || a[k]===null)
            a[k] = b[k];
    return a;
  };



  // Object methods
  Object.merge(Object, {
    isEmpty: function (o) { for (var i in o) { return false; } return true; }, // Checks if an associative object is empty or not.
    beget: function (o, v)
    {
      var F = function () {};
      F.prototype = o;
      var i = new F();
      return v ? Object.merge(i,v) : i;
    },
    namespace: function (path, base)
    {
      path = path.split('.');
      base = base || _win;
      for (var i=0, l=path.length; i<l; i++)
      {
        base = base[path[i]] || (base[path[i]] = {});
      }
      return base;
    }
  }, 1);



  _ = {}; // A generic "placeholder"/empty-slot variable  -  used by GOO.bind()

  // General purpose utility methods.
  Object.merge('GOO', {
    array: (msie) ? // turn any Array-like object into a real Array.
          function (o,i,l) { var _newArr = [],j=0; for (i=i||0, l=l||o.length; i<l; i++) { _newArr[j++] = o[i]; } return _newArr; } :
          function (o,i,l) { return [].slice.call(o, i||0, l||o.length); },

    // Usage:
    //  * var delay = GOO.bind(window.setTimeout, null, _, 200);
    //  * delay(myFunc, param1, param2);
    bind: function (_method, _context/* [, param]* */)
    {
      var _args = GOO.array(arguments,2);
      return function () {
        var _localArgs = arguments,
            _argsLength = _args.length,
            i = 0, j = 0;
        for (;  i < _argsLength || j < _localArgs.length;  i++)
        {
          if (_args[i] === _  ||  i >= _argsLength)
          {
            _args[i] = _localArgs[j++];
          }
        }
        return _method.apply(_context||this, _args);
      };
    },


    bindAfter: function (_method, _context/* [, param]* */)
    {
      var A = GOO.array,
          _args = A(arguments,2);
      return function() {
        return _method.apply(_context||this, A(arguments).concat(_args));
      };
    }

  }, 1);


  // depricated because they clashes with Prototype's methods
  Array.from = GOO.array;
  Function.prototype.bind      = function() { return GOO.bind.apply(GOO, [this].concat(GOO.array(arguments))); };
  Function.prototype.bindAfter = function() { return GOO.bindAfter.apply(GOO, [this].concat(GOO.array(arguments))); };






  // Array methods
  Object.merge(Array, {
    indexOf: ([].indexOf) ?
        function (a, o, p) { return a.indexOf(o, p); } :
        function (a, o, p) { // (for IE6)
          for (var i=(p||0),l=a.length; i<l; i++) { if (a[i] === o) { return i; } }
          return -1;
        },
    forEach: function (o,f,t) // Note: this must be defined *BEFORE* `Function.prototype.forEach`
    {
      for (var i=0,l=o.length; i<l; i++)  // `l` is defined *before* we start looping, by spec.
      {
        if (o[i] != undefined)  // skip `undefined` items, by spec.
        {
          // minimize the use of `.call` to speed up MSIE
          t ? f.call(t, o[i],i,o) : f(o[i],i,o);  // Mental Note: this sloppy evaluation of `t` fails in bizarre edge-cases such as `Array.forEach(myArr, "".toLowerCase, "");`
        }
      }
    },
    map: function (o,f,t)
    {
      var a = [];
      for (var i=0,l=o.length; i<l; i++) { a[i] = t ? f.call(t, o[i],i,o) : f(o[i],i,o); }
      return a;
    },
    filter: function (o,f,t)
    {
      var a = [];
      for (var i=0,l=o.length; i<l; i++) { if (t ? f.call(t, o[i],i,o) : f(o[i],i,o)) { a[a.length] = o[i]; } }
      return a;
    }
  }, 1);



  // Function methods
  Object.merge(Function.prototype, {  // Note: this must be defined *AFTER* `Array.forEach`
    forEach: function (o,f,t)
    {
      for (var i in o) {
        if (this.prototype[i] != undefined || this.prototype[i] !== o[i]) {
          t ? f.call(t, o[i],i,o) : f(o[i],i,o);
        }
      }
    }
  }, 1);



  // String methods
  var SP = String.prototype;
  Object.merge(SP, {
    lc:             SP.toLowerCase,
    uc:             SP.toUpperCase,
    toInt:          function (radix) { return parseInt(this, radix||10); },
    trim:           function () { return this.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); },
    decodeEntities: function () { return this.replace(/&lt;/g, '<').replace(/&gt;/g, '>').replace(/&quot;/g, '"').replace(/&amp;/g, '&'); },
    encodeEntities: function () { return this.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); },
    injectVars:     function(_vars)
    {
      var _newString = ''+this, // copy the original string (otherwise we might return some random object type)
          _cache = this.injectVarsRegExpCache,
          _keys = [],
          l = _vars.length,
          i;
      if (this.indexOf('%{')>-1)
      {
        // NOTE: this is a fairly ugly way to collect the "keys" depending on if _vars is a List or an Object.
        if (isNaN(l)) { for (i in _vars)    { _keys.push(i); } }
        else          { while (l--) { _keys.push(l); } }
        // now loop through the _keys and perform the replacement
        i = _keys.length;
        while (i--)
        {
          var _key = _keys[i],
              re   = _cache[_key] || (_cache[_key] = new R(R.escape('%{'+_key+'}'),'g')); // retieve the corresponding RegExp from the cache.
          _newString = _newString.replace(re, _vars[_key]);
        }
      }
      return _newString;
    },
    injectVarsRegExpCache: {}
  }, 1);





  cookieU = {

    del: function (_name, _path, _domain)
    {
      cookieU.set(_name, '', _path, -1, _domain);
    },

    set: function (_name, _value, _path, _maxage, _domain, _secure) // _expires is the max-age in seconds.
    {
      _maxage = (_maxage && !isNaN(_maxage)) ? _maxage : 31536000; // default to 1 year (365*24*60*60 = 31536000)
      // create date object, correct for
      var _expires = new Date(),
          _tmpSkew = (new Date(0)).getTime();
      _expires.setTime(_expires.getTime() + (_maxage * 1000) - _tmpSkew);
      _doc.cookie = _name + '=' + escape(_value) +
                        '; expires=' + _expires.toGMTString() +
                        (_path   ? '; path='+_path     : '') +
                        (_domain ? '; domain='+_domain : '') +
                        (_secure ? '; secure'          : '');
    },

    getValue: function (_theName)
    {
      var c = _doc.cookie || '';
      var _match = (";"+c).match( new R("(; ?)" + _theName + "=([^;]+)") );
      return !!_match  &&  unescape( _match[2] );
    }

  };

  // for backwards-compatibility:
  setCookie = cookieU.set;
  getCookieValue = cookieU.getValue;



  stripHref = function t(_theUrl)
  {
    var r = t.re || (t.re = new R( '^' + R.escape(_docLoc.protocol + '//' + _docLoc.host) ));
    return _theUrl.replace(r, '');
  };






  // Based on Dean Edwards' addEvent (http://dean.edwards.name/weblog/2005/10/add-event/)
  // Modified and extended by Mar Orlygsson.
  // Note: The EEvent.* functions are written so they run correctly in any `this` context
  var _Event = EEvent= {
    _all: {},

    add: function (_object, _type, _handler, _context)  // any additional parameters are passed on to the _handler
    {
      var _elmId = DOM.guid(_object),
          _funcId = DOM.guid(_handler),
          _elmEvents = _Event._all[_elmId] || (_Event._all[_elmId] = {}),
          _handlers = _elmEvents[_type],
          _onType = 'on'+_type,
          a = arguments,
          _plugin = _Event.plugins[_type],
          _firstHandler;

      if (a.length>3)
      {
        _handler = GOO.bindAfter.apply(null, GOO.array(a,2));
        _handler.__guid = (_funcId += (_context ? '_'+DOM.guid(_context) : '') );
      }

      if (_firstHandler = !_handlers)
      {
        _handlers = _elmEvents[_type] = {};
        if (_object[_onType]) { _handlers[0] = _object[_onType]; }
        _object[_onType] = _Event._createHandler(_type);
      }
      _handlers[_funcId] = _handler;

      // handle special cases (run after assignment so we can take advantage to _Event.fire)
      _plugin && _plugin.add && _plugin.add(_object, _handler, _firstHandler);
    },


    remove: function (_object, _type, _handler, _context)
    {
      var _elmEvents,
          _handlers,
          _plugin = _Event.plugins[_type],
          _isLastHandler;

      if ((_elmEvents = _Event._all[_object.__guid])  &&  (_handlers = _elmEvents[_type]))
      {
        delete _handlers[_handler.__guid + (_context ? '_'+_context.__guid : '')];
        if (_isLastHandler = Object.isEmpty(_handlers))
        {
          delete _elmEvents[_type];
          _object["on"+_type] = null;
        }
        _plugin && _plugin.remove && _plugin.remove(_object, _handler, _isLastHandler);
      }
    },


    fire: function (_object, _type, _param)
    {
      _type = "on"+_type;
      if (typeof _object[_type] == "function")
      {
        return (arguments.length>2) ? _object[_type](_param) : _object[_type]();
      }
    },



    // Virtually stops an event dead in its tracks...
    // Common usage 1:  EEvent.kill(e); // useful shorthand to use within event handlers.
    // Common usage 2:  EEvent.add(_myContainerElm, 'click', EEvent.kill);
    kill: function (e)
    {
      e.stopPropagation();
      e.preventDefault();
      return false;
    },

    stopBubble: function(e) { e.stopPropagation(); },


    _createHandler: function (_type)  // _type of event (i.e. "click", "mousedown", etc.)
    {
      return _Event._handlerCache[_type] || (_Event._handlerCache[_type] = function(e) {
        e = _Event._normalize(e||_win.event, this);
        var r = true,
            h = _Event._all[this.__guid][_type],
            __hEv;
        for (var i in h) {
          __hEv = h[i];
          if (__hEv.call(this, e) === false) { r = false; }
        }
        return r;
      });
    },
    _handlerCache: {},


    // try to normalize event properties cross browsers
    _normalize: function (e, currentTarget) // [e]vent
    {
      if (e && !e.normalized)
      {
        if (msie)
        {
          e.target          = e.target || e.srcElement;
          e.preventDefault  = _Event._preventDefault;
          e.stopPropagation = _Event._stopPropagation;
          e.currentTarget   = currentTarget;
        }

      /*
        // BT: saved for later - does this stuff really need to be fixed? ...
        // fix textnode targets in Safari
        if (webkit && e.target.nodeType === 3) { e.target = e.target.parentNode; }

        // fix for metakey vs. ctrlkey in Macs
        if (!e.metaKey && e.ctrlKey) { e.metaKey = e.ctrlKey; }
      */

        // Mar: Can we break this up into smaller/faster/browser specific chunks?
        switch (e.type)
        {
          case 'keydown' :
          case 'keyup' :
          case 'keypress' :
            // normalize keycodes
            e.key = (e.charCode || e.keyCode);
          case 'click' :
          case 'mousedown' :
          case 'mouseup' :
          case 'dblclick' :
            // fix mouseclick properties:
            //  *  provides safe .(left|middle|right)button booloen properties
            var _btnmeth = (e.button && !e.which);
            e.leftButton   = !!(_btnmeth ? e.button & 1 : e.which == 1);
            e.rightButton  = !!(_btnmeth ? e.button & 2 : e.which == 3);
            e.middleButton = !!(_btnmeth ? e.button & 4 : e.which == 2);
            // NOTE: fallthrough for position & reltarget...
          case 'mouseout' :
            if (!e.relatedTarget && e.toElement) { e.relatedTarget = e.toElement; }
            // NOTE: fallthrough for position...
          case 'mouseover' :
            if (!e.relatedTarget && e.fromElement) { e.relatedTarget = e.fromElement; }
            // NOTE: fallthrough for position...
          case 'mousemove' :
          case 'selectstart' :
          case 'selectend' :
            // fix e.pageXY
            if ((!e.pageX && e.clientX) || (!e.pageY && e.clientY))
            {
              var _scrollPos = DOM.scrollPos();
              e.pageX = e.clientX + _scrollPos[0];
              e.pageY = e.clientY + _scrollPos[1];
            }
            // layerX && layerY
            if (!e.layerX && e.offsetX) { e.layerX = e.offsetX; }
            if (!e.layerY && e.offsetY) { e.layerY = e.offsetY; }

            break;

          case 'DOMMouseScroll' :
          case 'mousewheel' :
            // normalize mouse wheel delta
            e.wheel = (e.wheelDelta) ? e.wheelDelta / 120 : -(e.detail || 0) / 3;  // fix mouse wheel delta
            break;
        }
        e.normalized = 1;
      }
      return e;
    },
    _preventDefault:  function () { this.returnValue = false; },
    _stopPropagation: function () { this.cancelBubble = true; },



    plugins: {

      ready: (function(){
        var _domIsReady, _alreadySet, _timer,
            _fireDomReadyEvent = function ()
            {
              if (!_domIsReady)
              {
                // clear any DOM timeout, or listener, that may exist ...
                if (opera || mozilla) {
                  _doc.removeEventListener('DOMContentLoaded', _fireDomReadyEvent, false);
                }
                _timer && clearInterval(_timer);
                _Event.remove(_win, 'load', _fireDomReadyEvent);
                // fire the events
                _Event.fire(_doc, 'ready');
                _domIsReady = 1;
              }
            };
        return {
          add: function (_object, _handler/*, _isFirst */)
          {
            if (_object === _doc)
            {
              if (_domIsReady)
              {
                // dom is loaded so we fire handler right away ...
                _handler();
              }
              else if (!_alreadySet)  // let's not add more events than needed
              {
                if (opera || mozilla)
                {
                  _doc.addEventListener('DOMContentLoaded', _fireDomReadyEvent, false);
                }
                else // if (webkit || msie)
                {
                  _timer = setInterval(function(){
                    if (msie)
                    {
                      try { _doc.documentElement.doScroll('left');}  // http://javascript.nwbox.com/IEContentLoaded/
                      catch (x) { return; } // still running ...
                    }
                    if (msie || /^(loaded|complete)$/.test(_doc.readyState))
                    {
                      _fireDomReadyEvent(); // done!
                    }
                  }, 2);
                }
                // failsafe that fires the ready-event on load in case we missed the real event
                _Event.add(_win, 'load', _fireDomReadyEvent);
                _alreadySet = 1;
              }
            }
          }
          // remove : function (_object, _handler, _isLast) {} // not Needed here
        };
      })(),


      fragment: (function(){
        var _lastFragment = _docLoc.hash,
            _fragmentMonitor;
        return {
          add: function (_object, _handler, _isFirst)
          {
            if (_isFirst && _object === _doc)
            {
              _fragmentMonitor = setInterval(function(){
                // NOTE: this only detects forward motion in IE6. Navigating back updates the address in the address bar, but doesn't update the document.location object.
                // NOTE: navigating back through fragments in IE7 doesn't update the "forward" history buffer.
                if (_docLoc.hash != _lastFragment)
                {
                  _lastFragment = _docLoc.hash;
                  _Event.fire(_doc, 'fragment');
                }
              }, 200);
            }
          },
          remove: function (_object, _handler, _isLast)
          {
            if (_isLast  &&  (_object === _doc)) { clearInterval(_fragmentMonitor); }
          }
        };
      })()

    
    }

  };

  var _RigUpDOMEventAsEventPlugin = function (_type, _DOMtype, _useCapture)
  {
    var _triggerFunc = function (e) { _Event.fire(this, _type, e); };
    _Event.plugins[_type] = {
      add: function (_object, _handler, _isFirst)
      {
        _isFirst  &&  _object.addEventListener  &&  
            _object.addEventListener(_DOMtype, _triggerFunc, !!_useCapture)
      },
      remove: function (_object, _handler, _isLast)
      {
        _isLast  &&  _object.addEventListener  &&  
            _object.removeEventListener(_DOMtype, _triggerFunc, !!_useCapture);
      }
    };
  };


  if (mozilla)
  {
    _RigUpDOMEventAsEventPlugin('mousewheel', 'DOMMouseScroll');
  }

  if (!msie)
  {
    if (mozilla)
    {
      // Since Firefox (as of 3.0) doesn't support DOMFocus[In|Out] we resort to nasty capture events.
      // ...run to the hills and expect the worst!! 
      _RigUpDOMEventAsEventPlugin('focusin', 'focus', true);
      _RigUpDOMEventAsEventPlugin('focusout', 'blur', true);
    }
    else
    {
      _RigUpDOMEventAsEventPlugin('focusin', 'DOMFocusIn');
      _RigUpDOMEventAsEventPlugin('focusout', 'DOMFocusOut');
    }

    var mousefoo = function (ENTERLEAVE, OVEROUT)
    {
      var _triggerFunc = function(e)
      {
        var c = e.currentTarget, r = e.relatedTarget;
        if (c != r && !(r && DOM.isAncestor(c, r))) { _Event.fire(this, ENTERLEAVE, e); }
      };
      _Event.plugins[ENTERLEAVE] = {
        add: function (_object, _handler, _isFirst)
        {
          if (_isFirst) { _Event.add(_object, OVEROUT, _triggerFunc); }
        },
        remove: function (_object, _handler, _isLast)
        {
          if (_isLast) { _Event.remove(_object, OVEROUT, _triggerFunc); }
        }
      };
    };
    mousefoo('mouseenter', 'mouseover');
    mousefoo('mouseleave', 'mouseout');
  }



  Event = (_win.Prototype&&_win.Prototype.Version&&_win.Event) || _Event;  // Left in for backwards compatibility, but avoids overloading Prototype.js' Event object.
  addEvent = _Event.add;          // Left in for backwards compatibility.
  removeEvent = _Event.remove;    // Left in for backwards compatibility.







  var _nodeContainer = _doc.createElement("body"), // static .innerHTML container for DOM.node() to create/store new elements...
      _hasOuterHTML = _nodeContainer.outerHTML != undefined,
      _emptyTags = { img:1, br:1, input:1, meta:1, link:1, param:1, hr:1 },
      _htmlAttrNameMap = msie ? {} : { 'class': 'className',  'for': 'htmlFor' };


  // Note: The DOM.* functions are written so they run correctly in any `this` context
  DOM = {
    innerText: function (e)    { return e.innerText  ||  e.textContent  ||  e.innerHTML.replace(/<[^>]+>/g,"").decodeEntities(); },  // obj.innerText emulator for non-IE browsers.

/*  // Use instead:  elm.innerHTML = text.encodeEntities();
    //          or:  elm.innerHTML = text.replace(/</g, '&lt;')
    setInnerText:
        _nodeContainer.innerText != undefined ?
            function (elm, txt) { elm.innerText = txt; } :
        _nodeContainer.textContent != undefined ?
            function (elm, txt) { elm.textContent = txt; } :
            function (elm, txt) { elm.innerHTML = txt.encodeEntities(); },
*/

    outerHTML: 
        _hasOuterHTML ?
            function (elm) { return elm.outerHTML; } :
            function (elm) // Firefox and old browsers
            {
              var attrs = elm.attributes,
                  i = attrs.length,
                  tn = elm.tagName.lc(),
                  h = '<'+tn;
              while (i--) { h += ' '+attrs[i].name+'="'+attrs[i].value+'"'; }
              return h+  (_emptyTags[tn] ?  ' />'  :  '>'+elm.innerHTML+'</'+tn+'>');
            },

    setOuterHTML:
        _hasOuterHTML ?
            function (elm, html) { elm.outerHTML = html; } :
            function (elm, html) // Firefox and old browsers <-- does this work in other browsers???
            {
              var r = elm.ownerDocument.createRange();
              r.setStartBefore(elm);
              var df = r.createContextualFragment(html);
              elm.parentNode.replaceChild(df, elm);
            },



    insertBefore:  function (n, r) { r.parentNode.insertBefore(n, r);  },
    insertAfter:   function (n, r) { r.parentNode.insertBefore(n, r.nextSibling);  },
    prependChild:  function (n, p) { p.insertBefore(n, p.firstChild);  },
    appendChild:   function (n, p) { p.appendChild(n);  },
    replaceNode:   function (n, o) { DOM.insertBefore(n, o);  DOM.removeNode(o);  },
    removeNode:    function (n)    { n.parentNode.removeChild(n);  },
    //injectNode:    function (n)      { var t = _doc; while (t.lastChild.nodeType == 1) { t = t.lastChild; } t.parentNode.appendChild(n); }, // X(HT)ML-friendly replacement for document.write
    injectNode:    function (n)    { var t = _doc.getElementsByTagName('*'); DOM.insertAfter(n, t[t.length-1]); }, // X(HT)ML-friendly replacement for document.write

    firstChildTag: function (p) // finds the first childElement that contains text (i.e. not <img/>, <hr />, <p></p>, etc.
    {
      var i=0,n;
      while (n = p.childNodes[i++])
      {
        if (n.nodeType == 1  &&  DOM.innerText(n)) { return n; }
      }
      return null;
    },

    processOffline: function (e, f, c)  // [e]lement, [f]unction, [c]ontextObject
    {
      if (!e || !f) { return; }
      var s = _doc.createElement(e.tagName); // [s]tand-in element
      DOM.replaceNode(s,e);            // take the `e` offline (and leave `s` in its place)
      var r = c ? f.call(c,e) : f(e); // process `e`
      DOM.replaceNode(e,s);            // put `e` back online
      return r;
    },

    firstElmOf: function (e, r) // [e]lement, [r]everse
    {
      var n = e[(r ? 'la':'fir') + 'stChild'],
          m;  // [n]ode
      while (n)
      {
        if (n.nodeType == 1 /*Element*/) { return n; }
        m = m || (r || 'next') + 'Sibling'; // [m]ethodName
        n = n[m];
      }
    },
    lastElmOf: function (e) { return DOM.firstElmOf(e, 'previous'); },

    getChildElms: function (e,t)
    {
      var a = [],
          e = e.firstChild;
      while (e)
      {
        if (e.nodeType == 1 /*Element*/ && (!t || e.tagName.lc() == t)) { a[a.length] = e; }
        e = e.nextSibling;
      }
      return a;
    },

    // return the parent element-node of a given element or given 
    // a tagname, return the first ancestor of that element type
    // NOTE: will fail on n == document.body.parentNode;
    parent: function (n, tn) 
    {
      tn = (tn && tn != '*') ? tn.uc() : '';
      while (n = n.parentNode) {
        if (!tn || n.nodeName == tn) { return n; }
      }
    },
  
    // return the previous sibling element-node to a given element, or,
    // given a tagname, return the youngest sibling to query-node of 
    // that element type
    elmAfter: function (n, tn, M)
    {
      var m = (M || 'next') + 'Sibling';
      tn = (tn && tn != '*') ? tn.uc() : '';
      while (n = n[m]) {
        if (n.nodeType == 1 /*Element*/ && (!tn || tn == n.nodeName)) {
          return n;
        }
      }
    },

    // return the next sibling element-node to a given element, or,
    // given a tagname, return the next oldest sibling to query-node of 
    // that element type
    elmBefore: function (n, tn) { return DOM.elmAfter(n, tn, 'previous'); },


    // DOM.node() may be used to bypass IE's arbitrary limitation on _elm.setAttribute("style", myCssRules);
    // It seems however impossible to use this function to create <link /> elements.  (maybe because <link>s are only allowed inside <head>
    // ASSUMES: all html element names are lower-case
    node: function (html)
    {
      // Handle <table> innards... ick!
      var _htmlStart = html.substr(0,9).replace(/ /,'>'), // <-- '<caption>'.length
          _dpt  = (/^<t[dh]>/.test(_htmlStart) && [3, '<table><tbody><tr>', '</tr></tbody></table>'])
              || (!_htmlStart.indexOf('<tr>') && [2, '<table><tbody>', '</tbody></table>'])
              || (/^<(t(body|head|foot)|caption)>/.test(_htmlStart) && [1, '<table>', '</table>'])  // NOTE: in case of <thead>, MSIE will append an empty <tbody></tbody> to the table.
              || [0,'',''];
      html = _dpt[1] + html + _dpt[2];

      _nodeContainer.innerHTML = html;
      var n = _nodeContainer.firstChild;  // NOTE: DOM.node returns either Element or Textnode ...
      while (_dpt[0]--) { n = n.firstChild; } // in case of <table> innards -- extract the original element by digging the required number of levels into the <table> wrapper element.
      DOM.removeNode(n); // <-- This bit is important ... because otherwise n gets destroyed next time this method is run.
      return n;
    },


    newElm: function (tagName, attribs)
    {
      var newElm = _doc.createElement(tagName);
      attribs  &&  DOM.setAttribs(newElm, attribs);
      return newElm;
    },

    setAttribs: function (elm, attribs)
    {
      for (var attrName in attribs)
      {
        elm.setAttribute(_htmlAttrNameMap[attrName]||attrName, attribs[attrName]);
      }
    },


    getLang: function (_element, returnFull)  // if !arguments[1] then return a two-digit vesion of the lang value.
    {
      var e = _element = _element || _doc.documentElement; // default to checking the <html> element
      //while (!(_foundLang = DOM._extractLang(e)) && (e.tagName == "HTML")) { e = e.parentNode; }
      while (!e.lang && (e.tagName != "HTML")) { e = e.parentNode; }
      return  (e.lang) ? (_element.lang = e.lang).substr(0, (returnFull ? 99 : 2) ) : false;
    },

    getFullLang: function (_element) { return DOM.getLang(_element, true); },

/*
    //bah, nobody uses xml:lang anyway.  onfirstcomplaint="putThisBackIn()"
    _extractLang: function (_element)
    {
      var _elmLang = _element.lang;  // use @lang by default
      if (!_elmLang)
      {
        try { _elmLang = _element.getAttribute("xml:lang"); } catch(err) {  } // but fall back to @xml:lang using try{}catch{} because IE5&6 crash when searching for non-existing "xml:lang" attributes on <table> elements (go figure!).
        if (_element.getAttributeNS) { _elmLang = _element.getAttributeNS("xml","lang"); }  // fall back to use .getAttributeNS() for Opera's sake
      }
      return (_elmLang) ? _elmLang.lc() : null;
    },
*/


    isAncestor: function (haystack, needle) // looks for needle in haystack and returns boolean
    {
      var compareDocumentPosition = 'compareDocumentPosition';
      // first time this is run, redefine the function with an optimized version of itself which is run thereafter.
      // mmm... self-optimizing javascript goodness
      DOM.isAncestor =
        (haystack.contains && !webkit) ?  // Only use IE's "contains" (safari "contains" is broken)
            function(haystack, needle) { return haystack.contains(needle); } :
        (haystack[compareDocumentPosition]) ? // Firefox doesn't have "contains"
            function(haystack, needle) { return !!(haystack[compareDocumentPosition](needle) & 16); } :
            function (haystack, needle) // default (inefficient) search method
            {
              // Loop up the DOM tree and test each node
              while (needle = needle.parentNode) { if (needle == haystack) { return true; /* Success!! */ } }
              return false; // default to false
            };
      return DOM.isAncestor(haystack, needle);
    },

    _classRegExpCache: {},
    _classRegExpCacheG: {},



    // accepts RegExp *String* for `c`
    hasClass: function (el, c)
    {
      var ec = el.className, rc = DOM._classRegExpCache;
      return !!ec  &&  !!c  &&  ( rc[c]||(rc[c] = new R('(^| )'+c+'( |$)')) ).test(ec);
    },

    addClass: function (el, c, _beLiberal, _skipCheck)
    {
      var ec = el.className;
      return  (c  &&  ( (!_skipCheck && DOM.hasClass(el, c))  ||  (ec != (el.className += ((ec)?' ':'') + c) )))
               ||  _beLiberal;
    },

    // accepts RegExp *String* for `c`
    removeClass: function (el, c)
    {
      var ec = el.className, rc = DOM._classRegExpCacheG;
      return !!ec  &&  !!c  &&  (ec !=  (el.className = ec.replace((rc[c]||(rc[c]=new R('(^| )('+c+'( |$))+','g'))),'$1'))); // yields true if something (i.e. `c`) was removed
    },

    // accepts RegExp *String* for `oldClass`
    replaceClass: function (el, oldClass, newClass, _beLiberal, _skipAddCheck/* for private use only */)
    {
      return (DOM.removeClass(el, oldClass) || _beLiberal)  &&  DOM.addClass(el, newClass, 1, _skipAddCheck);
    },

    toggleClass: function (el, c1, c2, _beStrict)
    {
      c1 = c1||'';
      c2 = c2||'';
      if (c2  &&  (typeof(c2)!='string'))  // Ack! This parameter flexibility is causing inefficiency!
      {
        _beStrict = c2;
        c2 = '';
      }
      return DOM.replaceClass(el, c1, c2)  ||  DOM.replaceClass(el, c2, c1, 0, 1)  ||  (!_beStrict  &&  DOM.addClass(el, c1||c2, 0, 1));
    },


    insertLink: function (u, m, r) {  // Params: [u]rl, [m]edia, [r]el
      var l = _doc.createElement('link');
      l.type = 'text/css';
      l.href = u;
      l.rel = (r ? r+' ':'')+ 'stylesheet';
      l.media = m || 'all';
      return DOM.get('head')[0].appendChild(l);
    },


    aquireId: function (el)  // el is an optional parameter.
    {
      if (!el || !el.id)
      {
        var id = DOM._guidPrefix + DOM._guid++;
        if (!el) { return id; }
        else if (!el.id) { el.id = id; }
      }
      return el.id;
    },


    guid: function (o)
    {
      if (!o.__guid ||  o.__guid >= DOM._guid  ||  DOM._guidMap[o.__guid] != o ) // make sure _obj doesn't have an "invalid" (or already used) __guid
      {                                                                             // (this can happen if HTML is built from an element's .innerHTML contents)
        o.__guid = DOM._guid++;
        DOM._guidMap[o.__guid] = o;
      }
      return o.__guid;
    },
    getByGuid: function (g) { return DOM._guidMap[g]; },
    _guidMap: {}, // used by DOM.guid() to keep track of which _obj has wich __guid

    _guidPrefix: "tmp_"+(new Date()).getTime()+"_",  // suffix and prefix used to generate temporary @id-values for HTMLelements without an @id
    _guid: 1,      // a counter that should be incremented with each use.


    // _getElementsBySelector is written by Simon Willison.
    // Original source at: http://simon.incutio.com/archive/2003/03/25/getElementsBySelector
    // Modified, fixed, and optimized (for speed and compression) by Mar Orlygsson.
    // It accepts two arguments 1) a CSS-style selector and 2) context/scoping element
    // Example:
    //   var _popupMenuLinks = DOM.get("a.popup", myMenuDiv);
    //
    get: function (_selector, _scopeElm, _forceArray) // forceArray flag overrides the (sad) default behaviour of returning live node lists for simple DOM.get('tagName') queries;
    {
      var _currentContext = _scopeElm || _doc;

      if (/^\w+$/.test(_selector))
      {
        var _nodeList = _currentContext.getElementsByTagName(_selector);
        return _forceArray ? Array.from(_nodeList) : _nodeList;
      }
      else if (/,/.test(_selector))
      {
        var _foundElms = [];
        Array.forEach(_selector.split(/\s*,\s*/), function(_selector){
          if (_selector) { 
            _foundElms.push.apply(
              _foundElms, Array.from( DOM.get(_selector, _currentContext)) 
            ); 
          }
        });
        return DOM._unique(_foundElms);
      }

      var _tokens = _selector.trim().split(/\s+/),
          _elements = [],
          _activeTokenCount = 0,
          _childElmsOnly = false,
          i, j, h, len, leng, _length, _item, _elm, _contextElm;

      _currentContext = [_currentContext];

      for (i=0, _length = _tokens.length; i<_length; i++)
      {
        var _token = _tokens[i];

        if (_token == '>')
        {
          _childElmsOnly = true;
          continue;
        }
        _activeTokenCount++;


        if (_token.indexOf('[') == -1)
        {

          if (_token.indexOf('#') > -1)
          {
            var _bits = _token.split('#'),
                _tagName = _bits[0],
                _element = _doc.getElementById(_bits[1]);

            if (!_element) { return []; }

            if (i==0 && !_scopeElm)
            {
              if (_tagName && _tagName != '*' && _element.nodeName.lc() != _tagName)
              {
                return [];
              }
              _currentContext = [_element];
              continue;
            }

            var _success = (_childElmsOnly && Array.indexOf(_currentContext, _element.parentNode) > -1);
            if (!_success)
            {
              var _ancestorElm = _element;
              while (_ancestorElm = _ancestorElm.parentNode)
              {
                if (Array.indexOf(_currentContext, _ancestorElm) > -1)
                {
                  _success = true;
                  break;
                }
              }
            }

            if (_success)
            {
              _currentContext = [_element];
              continue;
            }

          }

          if (_token.indexOf('.') > -1)
          {
            _bits = _token.split('.');
            _tagName = _bits[0] || '*';
            var _className = _bits[1],
                _found = [];
            h = 0;
            while (_contextElm = _currentContext[h++])
            {
              _elements = DOM._getSubElms(_contextElm, _tagName, _childElmsOnly);
              j = 0;
              while (_elm = _elements[j++])
              {
                DOM.hasClass(_elm, _className)  &&  (_found[_found.length] = _elm);
              }
            }
            _currentContext = _found;
            _childElmsOnly = false;
            continue;
          }

        }


        if (_token.match(/^(\w*|\*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/))
        {
          _tagName = R.$1 || '*';
          _found = [];

          var _attrName = DOM._attrNameMap[R.$2] || R.$2,
              //_attrOperator = R.$3,
              _attrValue = R.$4;
              _checkFunction = DOM._getElementsBySelectorAttributeFunctions[R.$3/* (_attrOperator) */] || DOM._getElementsBySelectorAttributeFunctions.d;

          h = 0;
          while (_contextElm = _currentContext[h++])
          {
            _elements = DOM._getSubElms(_contextElm, _tagName, _childElmsOnly);
            j = 0;
            while (_elm = _elements[j++])
            {
              var a = _elm.getAttribute(_attrName);
              _checkFunction((a!==null ? a:''), _attrValue)  &&  (_found[_found.length] = _elm);
            }
          }

          _currentContext = _found;
          _childElmsOnly = false;
          continue;
        }

        _tagName = _token;
        _found = [];
        h = 0;
        while (_contextElm = _currentContext[h++])
        {
          _elements = DOM._getSubElms(_contextElm, _tagName, _childElmsOnly);
          j = 0;
          while (_elm = _elements[j++]) { _found[_found.length] = _elm; }
        }
        _currentContext = _found;
        _childElmsOnly = false;

      }
      return (_activeTokenCount > 1) ? DOM._unique(_currentContext) : _currentContext;
    },
    _getRegExpCache: {},
    _attrNameMap: msie ? { 'class':'className', 'for':'htmlFor' } : {},
    _getElementsBySelectorAttributeFunctions: {
      '=': function(a,v) { return a == v; },
      '~': function(a,v) { var _re = DOM._classRegExpCache[v] || (DOM._classRegExpCache[v] = new R('(^| )' +v+ '( |$)')); return _re.test(a); },
      '|': function(a,v) { var _re = DOM._getRegExpCache[v] || (DOM._getRegExpCache[v] || new R('^' +v+ '(-|$)')); return _re.test(a); },
      '^': function(a,v) { return a.indexOf(v) === 0; },
      '$': function(a,v) { return a.lastIndexOf(v) == (a.length-v.length); },
      '*': function(a,v) { return a.indexOf(v) > -1; },
      d:   function(a)   { return a; } // default function for unknown/unsupported operators.
    },
    _getSubElms: function (_contextElm, _tagName, _childElmsOnly)
    {
      if (_childElmsOnly)
      {
        return DOM.getChildElms(_contextElm, (_tagName == '*') ? false : _tagName );
      }
      return (_doc.all && _tagName == '*') ? _contextElm.all : _contextElm.getElementsByTagName(_tagName);
    },
    _unique: (msie) ?  // faster unique for IE using .sourceIndex
        function(nodes)
        {
          var _results = [], _counted = [], s, i, l;   // TODO: is object faster/slower -- what about mem??
          for (i = 0, l = nodes.length; i < l; i++) {
            s = nodes[i].sourceIndex;
            if (!_counted[s]) {
              _results[_results.length] = nodes[i];
              _counted[s] = 1;
            }
          }
          return _results;
        } :
        function (nodes)
        {
          var _results = [], n, i, l;
          for (i = 0, l = nodes.length; i < l; i++) {
            if (!(n = nodes[i]).dom_get_counted) {
              n.dom_get_counted = true;
              _results[_results.length] = n;
            }
          }
          i = 0;
          while (n = _results[i++]) { delete n.dom_get_counted; }
          return _results;
        },
    // End of code by Simon Willison


    // Similar to the prototype function by the same name ... but with the addition
    // of allowing the first argument to be an Array of _elmIDs or _elmRefs.
    // this allows for a much wider range of uses - such as:
    //
    //   var _blockIdArray = ["myId1", "MyId2", "myId3", "myId4"];
    //   var _myBlocks = $(_blockIdArray);
    //
    $: function(_arg) {
      if (!_arg) { return _arg; }
      if (arguments.length > 1) { _arg = GOO.array(arguments); }

      if (typeof(_arg) == 'string')  // _arg is a single _elmID  (most common case)
      {
        return _doc.getElementById(_arg);
      }
      if (_arg.join)  // _arg is an array of _elmIDs or _elmRefs
      {
        var _elms = [];
        for (var i=0,l=_arg.length; i<l; i++)
        {
          var _elm = _arg[i];
          if (typeof(_elm) == 'string') { _elm = _doc.getElementById(_elm); }
          _elms.push(_elm);
        }
        return _elms;  // since we received an array as an argument - return an array
      }
      return _arg; // _arg is an _elmRef or null/""/undefined (least common case)
    },


    getOptionValue: function (o)  // Param: [o]ption-element
    {
      // Note: IE and Opera don't distinguish between <option>foo</option> and <option value="">foo</option>. So we need a workaround!
      // Solution: if the browser supports .outerHTML (e.g. IE or Opera) and the value="" attribute *really* *really* isn't in the HTML
      // then _optionElm.text is the "de-facto" value (as per the HTML 4.01 spec http://www.w3.org/TR/html4/interact/forms.html#edef-OPTION)
      return o.value || ( o.outerHTML && !/^[^>]+ value/.test(o.outerHTML) && o.text ) || "";
    },


    formFields: function (theForm, byName)
    {
      var fieldMap = {},
          i = 0,
          field,
          name;
      while (field = theForm.elements[i++])
      {
        // skip `name=`less buttons etc.
        if ((name = field.name) && (!byName || name == byName))
        {
          (fieldMap[name] || (fieldMap[name]=[])).push(field);
        }
      }
      return byName ? fieldMap[byName] : fieldMap;
    },



    scrollPos:
      (msie) ?
          function(o) // IE
          {
              var d = _doc.documentElement;  // default to IE6 strict
              if (!d || !d.scrollTop) { d = _doc.body; }  // fallback for other values of IE
              var x = d.scrollLeft,
                  y = d.scrollTop;
              return o?{x:x,y:y}:[x,y];
          } :
          function (o) // all except IE
          {
            var x = _win.pageXOffset,
                y = _win.pageYOffset;
            return o?{x:x,y:y}:[x,y];
          },

    viewportSize: function(o) // with scrollbars subtracted!
    {
      var d = (_doc.documentElement.clientWidth) ? _doc.documentElement : _doc.body,
          w = d.clientWidth,
          h = d.clientHeight;
      return o?{w:w,h:h}:[w,h];
    },

    docSize: function(o)
    {
      var d = _doc.body.parentNode,
          w = d.scrollWidth,
          h = d.scrollHeight;
      return o?{w:w,h:h}:[w,h];
    },


    // sets the document.location.hash while suppressing the viewport scrolling
    setHash: function (_hash)
    {
      var _elm = DOM.$(_hash);
      if (_elm)
      {
        // temporaily defuse the section block's id 
        _elm.id = '';
        // then insert and position a hidden dummy element to make sure that
        //   a) the hash-change populates the browser's history buffer.
        //   b) the viewport doesn't scroll/jump
        // (NOTE: This may be buggy in IE5 - but that's life)
        _dummyElm.id = _hash;
        _doc.body.appendChild(_dummyElm);
        _dummyElm.style.top = DOM.scrollPos(1).y+'px';
      }
      _docLoc.hash = _hash;  // set the damn hash...
      if (_elm)
      {
        DOM.removeNode(_dummyElm);
        // put the old tab-id back in it's place
        _elm.id = _hash;
      }
    },


    // focus an _element (or it's first focusable _subElm) 
    setFocus: function (_elm)
    {
      var _focusElm = _elm;
      if (!_focusElm.focus)
      {
        var _subElms = DOM.get('*', _elm),
            l = _subElms.length,
            i = -1;
        while (++i<l && !(_focusElm = _subElms[i]).focus) {}
      }
      if (_focusElm.focus)
      {
        var _scrollPosBefore = DOM.scrollPos(1);

        // emulate the click action - putting the focus inside the section
        // (the browser only scrolls the page if the _focusElm is outside the viewport)
        _focusElm.focus();

        var _currentScroll;
        if (DOM.getPos  &&  (_currentScroll = DOM.scrollPos(1)).y != _scrollPosBefore.y)  // if the browser jumped to the anchor...
        {
          // then scroll the window to place the anchor at the top of the viewport.
          // (NOTE: We do this because most browsers place the artificially .focus()ed link at the bottom of the viewport.)
          var _scrollPos = DOM.getPos(_elm, 1).y - 30;
          if (_scrollPos < 10) { _scrollPos = 0; }
          _win.scrollTo(_currentScroll.x, _scrollPos);
        }
      }
    }


  };


  var _dummyElm = DOM.node('<i style="position:absolute;visibility:hidden;overflow:hidden;"></i>');



  DOM.makeElement = DOM.node;
  $ = _win.$ || DOM.$;     // still used!
  $$ = _win.$$ || DOM.get; // depricated because it clashes with Prototype's version of the same.
  _doc.getElementsBySelector = DOM.get; // backwards compatibility
  basicMeta = DOM;           // backwards compatibility
  cssU = DOM;                // backwards compatibility
  scrollPos = function(){ return DOM.scrollPos(1); }; // backwards compatibility






  // ==============================================================================================
  // ==============================================================================================
  // ==============================================================================================


  //  EPLICA.JsModule
  //  ---------------------------------------------------------------------------------------
  //  Constructor for a utility wrapper/interface for standard Eplica javascript modules
  //  ...such as tableSort, zebraLists, etc.
  //
  //  Usage:
  //
  //      var zebraLists = new EPLICA.JsModule({
  //          processBlock: function (b, c) { /*...processing logic...*/ },
  //          defaults:     { /*...default values...*/ },
  //          config:       { 'table.zebra' : { items:'tr' } }
  //          /*...more methods/properties...*/
  //      });
  //
  //      ...
  //
  //      zebraLists.config['ul.filelist'] = { handle:'files', items:'li' };
  //      zebraLists.init();
  //
  //

  (
    Object.namespace('EPLICA').JsModule = function (_methods)
    {
      // set defaults.
      Object.merge(this,
      {
        _cfgs:    {},  // storage for begotten (individual) config objects.
        defaults: {},  // default values for all config objects.
        config:   {}   // list of config objects
      });

      // add methods
      Object.merge(this, _methods||{});

    }
  ).prototype = {

  /*
    // Optional method run from within the `init` method
    initConfig: function(_config)
    {
      // Here's your chance to make any runtime adjustments/enhancements
      // to the config objects *before* we start getting and processing the blockElms.
    },
  */

    // Public utility method
    // mainly intended for reprocessing the block at runtime
    // ... automatigally retrieves _config when neccessary, etc,
    process: function(_blockElm, _conf /* optional */)
    {
      if (_blockElm  &&  (_conf || (_conf = this.getCfg(_blockElm)) ) )
      {
        this.processBlock(_blockElm, _conf);
      }
    },


    // This method SHOULD be overloaded for each JsModule instance, otherwise the new JsModule won't do anything.
    processBlock: function(_blockElm, _conf) { /* do preparation stuff to the blockElm */ },


    // replace/overload this method to perform one-/first-time processing to the block
    // (i.e. processing that should not happen when/if the blockElm is reprocessed)
    initBlock: function (_blockElm, _conf) { this.processBlock(_blockElm, _conf); },


    getCfg: function (obj)      { return obj ? this._cfgs[ obj.__guid ] : this._cfgs;  },

    setCfg: function (obj, cfg) { return this._cfgs[ DOM.guid(obj) ] = cfg; },


    init: function (_config, _scopeElm)
    {
      var t = this,
          _thisConfig = t.config,
          _seen = {}; // list of elements encountered during this run of `.init()`

      Object.merge(_thisConfig, _config||{});

      _Event.fire(this, 'InitStart');

      for (var _blockSelector in _thisConfig)
      {
        var _conf = _thisConfig[_blockSelector],
            _firstRun = !_conf.EPLICA_JsModule,
            _blocks = DOM.get(_blockSelector, _scopeElm),
            _blockElm, i = 0;

        if (_firstRun)
        {
          _conf = _thisConfig[_blockSelector] = Object.beget(t.defaults, _conf);
          _conf.EPLICA_JsModule = 1;
        }
        _firstRun  &&  t.initConfig  &&  t.initConfig(_conf);

        while (_blockElm = _blocks[i++])
        {
          var _guid = DOM.guid(_blockElm);
          if (!_seen[_guid]) // only process each blockElm once during this run of `.init()`
          {
            var _blockConf = t.getCfg(_blockElm) || t.setCfg(_blockElm, Object.beget(_conf));  // (avoid repeated begets each time `init()` is run.)
            t.initBlock(_blockElm, _blockConf);
            t.doEnforceSeen && (_seen[_guid] = true);
          }
        }
      }

      _Event.fire(this, 'InitEnd');

      return true;
    },

    doEnforceSeen: true // Defines whether the init method should enforce "only process each blockElm once" policy.

  };


})();

}
