
// {{{  Basis Prototype Extensions.

String.prototype.toJSON = function() {
  eval('var tmp = '+this+';');
  return tmp;
};

String.prototype.alert = function() { 
  alert(this.valueOf());
  return this;
};

String.prototype.log = function() {
  console.log(this.valueOf());
  return this;
};

String.prototype.sprintf = function() {
  var result = this;
  arguments.each(function(){
    result = result.replace(/%[sd]/,this);
  });
  return result;
};

String.prototype.wrap = function(before, after){
  if(after == null)
    return before.replace('|',this.valueOf());
  return before + this + after;
};

String.prototype.insertInto = function(node){
  assert(typeof node == 'object', 'insertInto: wrong parameter.');
  if(node.constructor === $().constructor) {
    node.html(this.valueOf());
  } else {
    node.innerHTML = (this.valueOf());
  }
  return this;
};

String.prototype.hash = function() {  // a checksum for a string  // DEPRECATED! <- DANGEROUS
  var l = this.length, sum = 0, rand  = [2,1,5,3,6,4,7,9,8],
                                rand2 = [1, 1, 1, 2];
  for(var i = 0; i<l; i++) {
    sum += this.charCodeAt(i) * rand[i%9] * rand2[i%4];
  }
  // console.log('hash: length:'+l+' sum:'+sum+'\n'+this);
  return sum;
};

String.prototype.ucFirst = function()
{
   // Split the string into words if string contains multiple words.
   var x = this.split(/\s+/g);
   for(var i = 0; i < x.length; i++)
   {
      // Splits the word into two parts. One part being the first letter,
      // second being the rest of the word.
      var parts = x[i].match(/(\w)(\w*)/);
 
      // Put it back together but uppercase the first letter and lowercase the rest of thw word.
      x[i] = parts[1].toUpperCase() + parts[2].toLowerCase();
   }
 
   // Rejoin the string and return.
   return x.join(' ');
};

Array.prototype.contain = function(elem) {
 for(var i=0; i< this.length; i++) {
   if(elem == this[i])
     return true;
 }
 return false;
};

Array.prototype.isEmpty = function() {
  return !this.length;
};

Array.prototype.containsAny = function() {
  return !!this.length;
};

Array.prototype.last = function() {
  return this[this.length - 1];
};

Array.prototype.sum = function() {
  var res = 0;
  for(var i=0; i<this.length;i++) {
    res += this[i];
  }
  return res;
};

Array.prototype.clone = function(){
  return [].concat(this);
};

Array.prototype.remove = function(obj) {
   for(var i=0; i<this.length; i++) {
     if(this[i] === obj) {
        this.splice(i, 1);
     }
   } 
   return this
};

Array.prototype.each = function(f) {
  for(var i=0; i<this.length;i++) {
    f.apply(this[i], [i]);
  }
  return this;
};

Array.prototype.where = function(cond) {
  var matches = [], others = [];
  this.each(function(){
    if(cond.apply(this)) {
      matches.push(this);
    }else {
      others.push(this);
    }
  });
  matches.others = others;
  return matches;
};

Array.prototype.any = function(cond) {
  return !!this.where(cond).length;
};

Array.prototype.every = function(cond) {
  return this.where(cond).length == this.length;
};

Array.prototype.getIndex = function(elem) {
  for(var i=0; i < this.length; i++) {
    if(elem == this[i])
      return i;
  }
  return false;
};



/* //use:
['eins', 'zwei', 'drei'].each(function(){
  alert(this);
});
*/

Array.prototype.dump = function(a,b) {
  console.log(dump(this,a,b));
};

// use var x = ['Hello','World'].mapTo('first|second');
//         alert(x.first); // -> 'Hello'
Array.prototype.mapTo = function(s) {
  var self = this, map = {};
  s.split('|').each(function(i){
     map[this] = self[i];
  });
  return map;
};

Array.prototype.fold = function(template) {
  var len = this.length;
  var str = '';
  for (var i=0 ; i<len ; i++) 
	str += template.apply(this[i], [i]);
  return str; 
};

Array.prototype.reduce = function(op) {
  var first = this.shift();
  if(!this.length) return first;
  var result = op(first, this.reduce(op));
  this.unshift(first);
  return result;
};

Array.prototype.allDefined = function() { // true if all elements are defined
  for(var i = 0; i<this.length; i++) {
    if((typeof this[i]).match(/^un/))
      return false;
  }
  return true;
};

Array.prototype.allNumber = function() { // true if all elements are Numbers
  for(var i = 0; i<this.length; i++) {
    if(isNaN(this[i]) || typeof(this[i]) != 'number')
      return false;
  }
  return true;
};

Array.prototype.allString = function() {
  for(var i = 0; i<this.length; i++) {
    if(typeof(this[i]) != 'string')
      return false;
  }
  return true;
}


Function.prototype.curry = function() {
   var args = [], i = 0, self = this;
   for(i=0; i<arguments.length; i++) {
     args.push(arguments[i]);
   }
   return function(){
     var lArgs = [].concat(args), i = 0;
     for(i=0; i<arguments.length; i++) {
       lArgs.push(arguments[i]);
     }
     return self.apply(this, lArgs);
   }
};


Function.prototype.rCurry = function() {
   var args = [], i = 0, self = this;
   for(i=0; i<arguments.length; i++) {
     args.push(arguments[i]);
   }
   return function(){
     var lArgs = [],  i = 0;
     for(i=0; i<arguments.length; i++) {
       lArgs.push(arguments[i]);
     }
     return self.apply(this, lArgs.concat(args));
   }
};

Function.prototype.add = function() {
  var self = this, args = [].slice.call(arguments,0);
  return function(){
    self.apply(this, arguments);
    args.each(function(){
      this();
    });
  }
};

Function.prototype.mApply = function(elems, args) {
  var self = this;
  elems.each(function(){
    self.apply(this, args);
  });
};

/* singleton
Function.prototype.getInstance = function(a1, a2, a3, a4, a5) {
  return this._instance || (this._instance = new this(a1, a2, a3, a4, a5));
};
*/

(function(){
  function notice(val, o ,n) {
    (typeof(o) == 'function') ? o(val) : o[n].apply(o, [val]);
  };
  Boolean.prototype.notice = function(o, n) {
    notice(this.valueOf(), o, n);
  };
  Number.prototype.notice = function(o, n) {
    notice(this.valueOf(), o, n);
  };
  String.prototype.notice = function(o, n) {
    notice(this.valueOf(), o, n);
  };
  Array.prototype.notice = function(o, n) {
    notice(this, o, n);
  };
})();


Boolean.prototype.ifDo = function(f) {
  if(this.valueOf()) f();
};
Boolean.prototype.ifLog = function(text) {
  if(this.valueOf())
    console.log(text);
};

// ruby...
Number.prototype.times = function(f) {
  var i, to = this.valueOf();
  assert(typeof f === 'function');
  for(i=0; i<to; i++) {
    f(i+1);
  }
};


Date.prototype.getDayName = function() {
  var names = new Array("Sonntag",
                        "Montag",
                        "Dienstag",
                        "Mittwoch",
                        "Donnerstag",
                        "Freitag",
                        "Samstag");
  
  return names[this.getDay()];
};

Date.prototype.detDayShortName = function() {
  return this.getDayName().substring(0,2).toUpperCase();
};

Date.prototype.getMonthName = function() {
  var months = new Array("Januar",
                         "Februar",
                         "März",
                         "April",
                         "Mai",
                         "Juni",
                         "Juli",
                         "August",
                         "September",
                         "Oktober",
                         "November",
                         "Dezember");
  
  return months[this.getMonth()];
};

Date.prototype.getKW = function() {
  
  var DoDat  = donnerstag(this);
  var kwjahr = DoDat.getFullYear();
  var DoKW1  = donnerstag(new Date(kwjahr, 0, 4));
  return Math.floor(1.5 + (DoDat.getTime() - DoKW1.getTime()) / 86400000 / 7)
  
  function donnerstag(datum) {
    var Do = new Date();
    Do.setTime(datum.getTime() + (3 - ((datum.getDay() + 6) % 7)) * 86400000);
    return Do;
  }
  
};

Date.now = (function() {
               var serverTimeOffset = 1280404315000 - (new Date()).getTime();
               return function (){
                return new Date((new Date()).getTime() + serverTimeOffset); 
               };
            })();
           // Server - Time
           
// }}}


// {{{ global methods



function dump(obj, maxStages, maxPerStage) {
  
  var objects = [];
  return _dump(obj, '', 1);
  
  function _dump(obj, offset, stage) {
    if(obj === null)
      return 'null';
    if(obj instanceof RegExp)
      return obj.toString();
    
    switch(typeof(obj)) {
      case 'function':
        return obj.toString ? obj.toString().match(/^[^{]+/)[0] : 'function';
      case 'undefined':
        return 'undefined';
      case 'unknown':        // ie7 issue
        return 'unknown';
      case 'boolean':
        return obj ? 'true' : 'false';
      case 'number':
        return obj;
      case 'string':
        return '"'+obj+'"';
      case 'object': 
        if(objects.contain(obj)) {
          return typeof(obj)+ ' (loop!)';
        }
        objects.push(obj);
        var out = (obj instanceof Array) ? 'Array' : typeof(obj);
        if(maxStages && (stage >= maxStages)) { return out; }
        offset += '      ';
        var cnt = 1, index;
        for(index in obj) {
          if(!(obj instanceof Array && !index.match(/^[0-9]+$/) ) ) // suppress showing Array prototype extensions
            out += '\n' + offset + '['+index+'] => '+_dump(obj[index], offset, stage + 1);
          if(maxPerStage)
            if(cnt >= (maxPerStage+1)) return out + '\n' + offset + '...\n';
          cnt++;
        }
        return out + '\n';
      default:
        return typeof(obj);
    }
  }
}


function merge(a, b) {
  return _merge(a, b) 
  
  function _merge(a, b){
    if(b === null) return null;
    var i;
    if(typeof(b) == 'object' && b.constructor != Array) {
      if(typeof(a) != 'object') a = {};
      for(i in b) {
        a[i] = _merge(a[i], b[i]);
      }
    } else {
      a = b;
    }
    return a;
  }
}


function assert(bCondition, sErrorMessage) {
  if(!bCondition) {
     console.error('assertion failed:\n' + sErrorMessage);
     assertion_failed // <- deliberately not defined
     throw new Error(sErrorMessage);
  }
}


// }}}





