// Version 0.92
// Berücksichtigung des URL-Anchors (location.hash)

// PROTOTYPE FIXING end EXTENSION

Object.extend(
  Enumerable, {
    sizeof:   function(){ return this.inject(0, function(count){ return count+1; })},
    first:    function(){ return (this.detect(function(){ return true; }) || null);},
    findAll:  function(iterator){
      var results = this._new();
      this.each(function(value, index) {
        if (iterator(value, index))
          results._add(value, index);
      });
      return results;
    },
    reject:   function(iterator) {
      var results = this._new();
      this.each(function(value, index) {
        if (!iterator(value, index))
          results._add(value, index);
      });
      return results;
    }
  } 
);

Object.extend(
  Array, {
    _new: function()      { return $A() },
    _add: function(value) { this.push(value) }
  } 
);

Object.extend(
  Hash, {
    _new:   function()      { return $H() }, 
    _add:   function(value) { this[value[0]] = value[1] },
    _each:  function(iterator) {
      for (key in this) {
        var value = this[key];
        if (typeof value == 'function' && !(value.enumerable)) continue;

        var pair = [key, value];
        pair.key = key;
        pair.value = value;
        iterator(pair);
      }
    }
  } 
);

Object.extend(
  Element, { 
    getWidth: function(element) {
      element = $(element);
      return element.style.pixelWidth || element.offsetWidth;
    }
  } 
);

Object.prototype.dump = function(level){
  var result = [];
  level = (level || 0);
  $H(this).each(function(el){
    var key = el[0]; var val = el[1];
    var spacer = "  ";
    for (var i=0;i<level;i++) spacer += "  ";
    if ((typeof val == "object") && (typeof val.dump == "function")){
      result.push(spacer+key+': '+val.dump(level+1))
    } else {
      result.push(spacer+key+': '+val);
    }
  });
  if (isArray(this)){
    result = (this.length)?"[\n"+result.join(",\n")+"]":'[]';  
  } else {
    result = "{\n"+result.join(",\n")+"}";  
  }
  return result;
};

Function.prototype.e = function(){
  this.enumerable = true;
  return this;
}

String.prototype.removeWhiteSpaces = function() {return this.replace(/\s+/g,"") };
String.prototype.leftTrim   = function() {return  this.replace(/^\s+/,""); };
String.prototype.rightTrim  = function() {return  this.replace(/\s+$/,""); };
String.prototype.trim       = function() {return  this.leftTrim().rightTrim(); };

Function.prototype.inherits = function(parent){
  this.prototype              = new parent();
  this.prototype.constructor  = this;
  this.prototype.uber = function(name){
    var func = this.constructor.prototype[name];
    if (func == this[name]) {
      func = parent.prototype[name];
    }
    return func.apply(this, Array.prototype.slice.apply(arguments, [1]));
  }
  return this;
};

function co(){
  return $A(arguments).detect(function(val){return (val)});
}

Function.prototype.andThen = function(g){
  var f=this; return function(){f(); g();}
}

// type checking 
function isAlien(a) {
 return isObject(a) && typeof a.constructor != 'function';
}

function isArray(a) {
  return isObject(a) && a.constructor == Array;
}

function isBoolean(a) {
  return typeof a == 'boolean';
}

function isFunction(a) {
  return typeof a == 'function';
}

function isNull(a) {
  return typeof a == 'object' && !a;
}

function isNumber(a) {
  return typeof a == 'number' && isFinite(a);
}

function isObject(a) {
  return (typeof a == 'object' && !!a) || isFunction(a);
}

function isString(a) {
  return typeof a == 'string';
}

function isUndefined(a) {
  return typeof a == 'undefined';
} 

// CLASS BASE

BaseClass = function(){
  var myself = this;

  var includedScripts     = [];
  var includedStylesheets = [];
  var initFunctions       = [];
  var superInitFunctions  = [];
  var exitFunctions       = [];
  var initComplete        = false;

  var logWin;

  // Mozilla detection
  var geckoDate = navigator.userAgent.match(/Gecko\/(\d{8})/);
  var msie      = navigator.userAgent.match(/MSIE/);

  this.Browser = {
    isGecko : (geckoDate && (1*geckoDate[1] >= 20020311)),
    isIE    : (msie)
  };

  // dynamic JS-Loader

  this.require = function(src){
    if (includedScripts[src]) {return;}
    var func = function(){
      var script    = document.createElement("script");
      script.type   = "text/javascript";
      script.src    = src;

      document.getElementsByTagName("head")[0].appendChild(script);
    };
    if (this.initComplete) func(); else this.addSuperInitFunction(func);
    includedScripts[src] = true;
  }

  this.requireCSS = function(src){
    if (includedStylesheets[src]) {return;}
    var func = function(){
      var sheet    = document.createElement("link");
      sheet.rel    = "stylesheet";
      sheet.type   = "text/css";
      sheet.href   = src;
      
      document.getElementsByTagName("head")[0].appendChild(sheet);
    };
    if (this.initComplete) func(); else this.addSuperInitFunction(func);
    includedStylesheets[src] = true;
  
  }

  // Init handling
  this.addInitFunction = function(func){
    initFunctions.push(func);
  }

  this.addSuperInitFunction = function(func){
    superInitFunctions.push(func);
  }

  this.initAll = function(){
    superInitFunctions.each (function(func){ func(); });
    initFunctions.each      (function(func){ func(); });
    this.initComplete = true; 
  }


  // Exit handling
  this.addExitFunction = function (func){
    exitFunctions.push(func);
  }

  this.exitAll = function(){
    exitFunctions.each (function(func){ func(); });
  }

  this.popup = function(url, name, params, focus){
    var myWin;
    name   = (name || 'popupwindow');
    params = Object.extend({
        width:600, height:400, resizable:'yes', status:'no', directories:'no', hotkeys:'yes', location:'no', menubar:'no', scrollbars:'yes', toolbar:'no'
      }, (params || {})
    );

    paramString = $H(params).map(function(pair){return pair.join('=')}).join(',');
    
    if ((myWin = window.open(url,name,paramString))){
      if(focus) myWin.focus();
      return myWin;  
    }
    return null;
  }

  this.log = function(txt){
    txt = txt.toString().escapeHTML();
    try{
      logWin.document.writeln(txt);
    } catch(e) {
      try{
        logWin = this.popup('','debug'); 
        logWin.document.open();
        logWin.document.writeln('<pre>');
        logWin.document.writeln(txt);
        // this.addExitFunction(function(){logWin.close()});
      } catch (e) {
        alert('something (*cough* popup blocker) prevents me from opening a window'); 
      }
    }
  }

  // Status messages
  this.setStatus = function(text){
    window.status = text;
    setTimeout("window.status=''",500);
  }

  this. setCookie = function( name, value, days) {
    var expires_date = new Date( (new Date).getTime() + days*24*60*60*1000 );
    if (days) {
      var date = new Date();
      date.setTime(date.getTime()+(days*24*60*60*1000));
      var expires = "; expires="+date.toGMTString();
    }
    else var expires = "";
    document.cookie = name+"="+value+expires+"; path=/";
  }

  this.getCookie = function(name){
    var pair =  $A(document.cookie.split(';')).
                map(function(val)   {return val.split('=');}).
                find(function(val)  {return (val[0].trim() == name)});
    return (pair)?pair[1]:null;
  }

  this.initRolloverImgs = function(){
    var img, loader, lowSrc, highSrc;
    var imgs = $A(document.images);
    var re = /(.*)\.(.*)\?ro/;
    
    imgs.each(function(img){
      if (re.exec(img.src)){
        img.lowSrc  = img.src;
        img.highSrc = RegExp.$1+'-a.'+RegExp.$2;
        loader      = new Image(); 
        loader.src  = img.highSrc;
        
        img.onmouseover = function(e){ this.src = this.highSrc; };
        img.onmouseout  = function(e){ this.src = this.lowSrc; };
      }
    });
  }

  this.addSuperInitFunction(this.initRolloverImgs);

}
var Base = new BaseClass();
window.onload   = Base.initAll;
window.onunload = Base.exitAll;


// CLASS QUERY

function QueryClass(){
  var myself      = this;
  var sessionName = 'smgsession'; // Hier null setzen, wenn nicht gebraucht

  var currentServer, currentPath, currentSearch, currentFragment, filteredSearch;
  
  Base.addSuperInitFunction(
    function(){
      currentServer   = location.protocol + '//' + location.hostname;
      currentPath     = location.pathname;
      currentSearch   = myself.searchToHash(location.search);
      currentFragment = (location.hash)?location.hash.substring(1):'';
      filteredSearch  = currentSearch.reject(function(pair){ return (pair[0].charAt(0)== "_") });  //  vars rausfiltern werden (_...)
    }
  );

  // Bestimmt den Search-Anteil (Query-String) eines hrefs als Hash
  this.searchToHash = function (search){
    if (!search.length) return $H();
    var i, pairs, pair, value, varname, oldvalue;
  
    // Führendes ? wegätzen
    if (search.charAt(0) == '?') search = search.substring(1);
    
    // replace all plusses with spaces because unescape() doesnt do it
    search = search.replace(/\+/g, ' ');
    
    // for each pair, separate, unescape and place into the associate array
    pairs = $A(search.split('&'));

    // The following implements PHP-Style []-Recognition, so that Hashes and Arrays are recognised by JS too
    return $H(pairs.inject({}, function(params, pairString) {
      var pair = pairString.split('=');
      var key;

      varname = unescape(pair[0]);
      value   = unescape(pair[1]);

      if (!params) params = $A();
      if (varname.match(/(.+)\[([a-zA-Z0-9_-]*)\]$/)){
        params  = $H(params);
        varname = RegExp.$1;
        if (!params[varname]) params[varname] = $A();
        if (key = RegExp.$2){
          params[varname]       = $H(params[varname]);
          params[varname][key]  = value;   
        } else {
          params[varname].push(value);
        }
      } else {
        params[varname] = value;
      }
      return params;
    }));
  }

  this.makeUrl = function(path, params, fragment, server){
    search = $H();
    if (params && params.keep)  search = filteredSearch.reject(function(pair){ return (!params.keep.include(pair[0])) });
    if (params && params.set)   search = search.merge($H(params.set));

    // Base.log(search.inspect());

    server    = server    || currentServer;
    path      = path      || currentPath;
    fragment  = fragment  || currentFragment;

    if (sessionName && currentSearch[sessionName]) search[sessionName] = currentSearch[sessionName];

    var search = search.toQueryString();

    return ((path[0] == '/')?server:'') + path + ((search)?'?'+search:'') + ((fragment)?("#"+fragment):'');
  }

  this.getParams = function(){
    return currentSearch;  
  }

  this.getFragment = function(){
    return currentFragment;  
  }

  this.fire = function(path, params, fragment){
    // alert(this.makeUrl(path, params, fragment));
    window.location.href = this.makeUrl(path, params, fragment);
  }
 
}

Base.addSuperInitFunction( function(){ Query = new QueryClass(); });

// SELECT

var SelectClass = {

  sweep: function(keepIndex){ 
    while(node = this.firstChild) this.removeChild(node)
    if (!(keepIndex) && this.index) this.index();
  },

  addOption: function(name,id,keepIndex){
    this.appendChild(option = document.createElement("option"));
    option.innerHTML  = name;
    option.value      = id;
    if (!(keepIndex) && this.index) this.index();
  },

  addHash: function (hash, keepIndex, defaulttext, oglabel){
    var parent = this;
    if (defaulttext) parent.addOption(defaulttext,'',true);
    if (oglabel) {
      parent = $S(document.createElement('optgroup'));
      this.appendChild(parent);
      parent.label = oglabel;
    }
    $H(hash).each(function(val){ parent.addOption(val[1],val[0],true);});
    if (!(keepIndex) && this.index) this.index();
  },

  fromHash: function (hash, keepIndex, defaulttext, oglabel){
    this.sweep(true);
    this.addHash($H(hash), true, defaulttext, oglabel);
    if (!(keepIndex) && this.index) this.index();
  },

  toHash: function(){
    return $A(this.options).inject($H(), function(hash, o){hash._add([o.value, o.text]); return hash;});
  },

  selectValue: function(value,def){
    var option;
    if (option = $A(this.options).find(function(v){ return(v.value == value) })){ option.selected = true; } 
    else if (def) this.options[0].selected = true;
  },

  selectedValue: function(){
    try {
      var index = this.selectedIndex;
      var value = (this.options[index])?this.options[index].value:null;
    } catch (e) {}
    return value;
  },

  selectedText: function(){
    var index = this.selectedIndex;
    return (this.options[index])?this.options[index].text:null;
  },

  selectedValues: function(){
    return $A(this.options).findAll(function(o){return o.selected;}).map(function(o){return o.value });
  },

  getValues: function(){
    return $A(this.options).map(function(o){return o.value });
  },

  selectedHash: function(){
    return $A(this.options).inject($H(), function(hash, o){if (o.selected) {hash._add([o.value, o.text]);} return hash;});
  },

  contains: function(needle){
    return $A(this.options).find(function(el){return (el.value == needle)});
  },

  quickref: function(input, useRegexp, currentId, create, onselect, onunselect){
    // input      - Text-Input-Element
    // useRegexp  - sollen Regular Expressions verwendet werden? (Kritisch bei Sonderzeichen)
    // currentId  - aktuelle Auswahl
    var checkFrequency  = 100;
    var keyPressLatency = 400;
    var myself          = this;

    this._onchange = this.onchange;

    onselectDo = function(val) {
      if (onselect) onselect(myself.selectedValue());
      myself._onchange();
      if (!created) input.value = myself.selectedText();
    };

    this.onchange  = onselectDo;
    
    var timeout         = false;
    var cache           = {};
    var lastTime        = false;
    var created         = false;  

    this.index = function(){ /* Base.log('indexing'); */ items = myself.toHash() }
    this.index();

    var showResult = function(hash ,string){
      myself.sweep(true);

      var preselected = -1; // voreingestellter Wert
      var matched     = -1; // genauer Match, selektiert auch bei mehreren Ergebnissen

      hash.each(function(value, index){
        myself.addOption(value[1], value[0], true);
        if ((matched < 0) && string.toLowerCase().trim() == value[1].toLowerCase().trim()) {
           matched = index;
        }
        if (currentId == value[0]) {
          preselected = index;
        }
      });

      var selectPos = -1;
      if (myself.length == 1){
        selectPos = 0; 
      } else if (matched >= 0){               // genauer Match
        selectPos = matched;
      } else if (preselected >= 0){           // voreingestellt
        selectPos = preselected;
      }

      if (selectPos >= 0){
        try {myself.options[selectPos].selected = true} catch(e) {};
        onselectDo();
      } 

      // Wert neu anlegen?
      if ((myself.length == 0) && create){   
        var option = new Option(string+' (neu)',string);
        myself.options[myself.length] = option;
        myself.options[0].selected    = true;
        created = true;
      } 
    }

    this.doSearch = function(){
      var string = input.value;
     
      if (typeof(cache[string]) != 'undefined') {
        result  = cache[string];
      } else {
        if (useRegexp){
          var regexp;
          try {
            regexp = new RegExp(string,'i');
          } catch (e) {
            regexp = new RegExp('','i');
          }
        }
        
        result = items.findAll(function(el, index){
          val = el[1];
          if (useRegexp){
            if (val.match(regexp)) return true;
          } else {
            return !($A(string.split(' ')).find(function(curval){
              return (val.toLowerCase().indexOf(curval.toLowerCase()) == -1);
            }));
          }   
          return false;
        });
        cache[string] = result;
      }

      showResult(result,string);
    }

    var triggerSearch = function(keyPressed){
      var time = new Date().getTime(); 

      if (lastTime == false || keyPressed) { 
        lastTime = time; 
        if (timeout != false) { clearTimeout(timeout); }
        timeout = setTimeout(function(){triggerSearch(false)},checkFrequency); 
        return; 
      }

      var millis = time-lastTime; 
      
      if (millis > keyPressLatency){
        // Bereit
        lastTime = false;
        myself.doSearch();
      } else {
        // Nachgeguckt, noch nicht bereit
        if (timeout != false) { clearTimeout(timeout); }
        timeout = setTimeout(function(){triggerSearch(false)},checkFrequency); 
      }
    }

    // Bei Tastendruck:
    input.onkeyup = function(evt){
      evt = (evt) ? evt : event;
      var charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : 0));

      if ( charCode<37 || charCode > 40 ){
        // Keine Pfeiltasten
        triggerSearch(true)
      }
      else {
        // Mit Pfeil-Unten in Selectfeld springen
        if (myself.length && charCode == 40) {
          myself.options[0].selected = true;
          try{ myself.focus() } catch(e) {} // Hier schmeisst Firefox einen XUL error, der geht durch das catch nich weg :(
        }
      }
    };


    // Backspace im Selectfeld:
    this.onkeydown = function(evt){
      evt = (evt) ? evt : event;
      var charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode : ((evt.which) ? evt.which : 0));

      if (charCode == 8) {
        input.focus();
        input.value=input.value;
        return;

        // Eventuelles Browser-Back abfangen:
        /* scheinbar doch nicht nötig...
          evt.cancelBubble = true;
          if (evt.stopPropagation) evt.stopPropagation();
          evt.returnValue = false;
          return false;
        */
      }

    };


  } 
}

function $S(object) {
  object = $(object);
  Object.extend(object, SelectClass);
  return object;
}


var Img = {
  setSrc : function(src, andThen, fallbackSrc){
    var myself  = this;
    var clone   = function(counter){
      if (ref.complete){
        if( ref.width>0 && ref.height>0 ){
          myself.src     = ref.src;
          myself.width   = ref.width;
          myself.height  = ref.height;
          if (andThen && (typeof(andThen) == 'function')) andThen();
        } else {
          myself.src = fallbackSrc;
        }
      } else {
        if (typeof(counter) != 'Number'){ counter=1; } else { counter++;  }
        if (counter < 20) { setTimeout(function(){ clone(counter)},200); } 
      }
    }
    var ref = new Image();
    ref.src = src+"?"+Math.random();
    clone(ref); 
  },

  setSize: function(x,y){
    if (x) this.style.width  = x+'px'; 
    if (y) this.style.height = y+'px'; 
  }
}

function $I(object) {
  object = $(object);
  Object.extend(object, Img);
  return object;
}




// DOM Manipulation

function createTextButton(imageSrc, func, text){
  var anchorNode  = document.createElement("a");
  anchorNode.appendChild(createImage(imageSrc));
  anchorNode.appendChild(document.createTextNode(' '+text));
  anchorNode.onclick = func;
  return anchorNode;
}

function createButton(imageSrc, func, width, height, title){
  var anchorNode  = document.createElement("a");
  
  var imageNode   = createImage(imageSrc,width,height,title);
  anchorNode.appendChild(imageNode);
  anchorNode.onclick = func;

  return anchorNode;
}

function createImage(src,width,height,title){
  var imageNode = document.createElement("IMG");
  imageNode.src = src;
  if (width)  imageNode.style.width   = width+"px";
  if (height) imageNode.style.height  = height+"px";
  if (title)  imageNode.title         = title;
  imageNode.style.border = "0px";

  return imageNode;
}

function hashToList(hash){
  list = [];
  for (o in hash){
    if (isFunction(hash[o])) continue;
    list.push([hash[o]["id"], hash[o]["name"]]);
  }
  return list;
}
