String.prototype.trim = function() {
	return this.replace(/^\s+|\s+$/g,"");
}
String.prototype.ltrim = function() {
	return this.replace(/^\s+/,"");
}
String.prototype.rtrim = function() {
	return this.replace(/\s+$/,"");
}

String.prototype.labelize = function() {
    return this.underscore().split('_').collect(function(s) {
        return s.capitalize();
    }).join(' ');
}

isObject = function(variable) {
    if(typeof(variable) != 'undefined' && variable != null && typeof(variable) == 'object' && variable.constructor == Object) {
        return true;
    }
    return false;
}

Hash.prototype.flatten = function() {
    var newHash = $H(this);
    this.each(function(pair) {
        if(isObject(pair.value)) {
            var v = $H(pair.value);
            v.each(function(ipair) {
                newHash.set(pair.key + ipair.key.capitalize(), ipair.value);
            });
            newHash.unset(pair.key);
        } else if(Object.isArray(pair.value)) {
            for(var i=0; i<pair.value.length; i++) {
                if(isObject(pair.value[i])) {
                    pair.value[i] = $H(pair.value[i]).flatten();
                }
            }
        }
    });
    return newHash;
}

//form unserializer: http://dev.rubyonrails.org/attachment/ticket/5902/sf_function.diff
Form.Element.setValue =  function(element, value) {
    element = $(element);
    Form.Element.Unserializers.input(element, value);
}
var $SF = Form.Element.setValue;

Form.Element.Unserializers = {
  input: function(element, value) {
    switch (element.type.toLowerCase()) {
      case 'submit':
      case 'hidden':
      case 'password':
      case 'text':
        return Form.Element.Unserializers.textarea(element, value);
      case 'checkbox':
      case 'radio':
        return Form.Element.Unserializers.inputSelector(element, value);
      case 'select-one':
        return Form.Element.Unserializers.selectOne(element, value);
    }
    return false;
  },

  inputSelector: function(element, value) {
    element.checked = value;
  },

  textarea: function(element, value) {
    element.value = value;
  },

  select: function(element, value) {
    return Form.Element.Unserializers[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element, value);
  },

  selectOne: function(element, value) {
    element.value = value;
  },

  selectMany: function(element, value) {
    var option;
    for (var i = 0; i < element.options.length; i++) {
      option = element.options[i];
      option.selected = value.indexOf(option.value || option.text) != -1;
    }
  }
}
//form unserialize:
Form.unserialize = function(form, serializedData) {
    for(var key in serializedData) {
        $SF(form[key], serializedData[key]);
    }
}

// Array.insert( index, value ) - Insert value at index, without overwriting existing keys
Array.prototype.insert = function( i, v ) {
 if( i>=0 ) {
  var a = this.slice(), b = a.splice( i );
  a[i] = v;
  return a.concat( b );
 }
};


Object.genGUID = function() {
	var len = 8;
	if(!isNaN(parseInt(arguments[0]))) len = parseInt(arguments[0]);
	var chars = "abcdef0123456789";
	var output = "";
	while(output.length < len)
	{
		var rnd = Math.floor(Math.random() * (chars.length - 1));
		output += chars.charAt(rnd);
	}
	return output;
}



// following code is MIT licensed (C) Gary Haran 2007
/**
* Provide the same behavior as window.scrollTo to divs with overflow without removing
* the ability to scroll a page to a given element.
*/
Element.addMethods({
  scrollTo: function(element, left, top){
    var element = $(element);
    if (arguments.length == 1){
      var pos = element.cumulativeOffset();
      window.scrollTo(pos[0], pos[1]);
    } else {
      element.scrollLeft = left;
      element.scrollTop  = top;
    }
    return element;
  }
});

/**
* Effect.Scroll allows you to animate scrolling on a page (or div w/ overflow: scroll || auto)
*/
/*Effect.Scroll = Class.create();
Object.extend(Object.extend(Effect.Scroll.prototype, Effect.Base.prototype), {
  initialize: function(element) {
    this.element = $(element);
    if(!this.element) throw(Effect._elementDoesNotExistError);
    this.start(Object.extend({x: 0, y: 0}, arguments[1] || {}));
  },
  setup: function() {
    var scrollOffsets = (this.element == window)
                ? document.viewport.getScrollOffsets()
                : Element._returnOffset(this.element.scrollLeft, this.element.scrollTop) ;
    this.originalScrollLeft = scrollOffsets.left;
    this.originalScrollTop  = scrollOffsets.top;
  },
  update: function(pos) {
    this.element.scrollTo(Math.round(this.options.x * pos + this.originalScrollLeft), Math.round(this.options.y * pos + this.originalScrollTop));
  }
});*/


/*Element.addMethods({
    swapClassName: function(element, name1, name2){
        if(Element.hasClassName(element, name1)) {
            Element.removeClassName(element, name1);
            Element.addClassName(element, name2);
        } else{
           Element.removeClassName(element, name2);
            Element.addClassName(element, name1);
        }
    },
    scrollToOverflow: function(element, parent, offset) {
        if(!offset) {
            offset = 0;
        }
        var scrollingDiv = $(parent);
        var pageAnchor = $(element);
        var delta = pageAnchor.offsetTop - scrollingDiv.scrollTop + offset;
        new Effect.Scroll(scrollingDiv, { y: delta, duration: 0.2 });
    },
    viewable: function(element) {
        element = $(element);
        if(element.visible()) {
            return !element.ancestors().find(function(e) {
                return !e.visible()
            });
        }
        return false;
    }
});*/

var Protoload = {
    startWaiting: function(element, options) {
        element = $(element);
        options = Object.extend({
            className: 'waiting',
            opacity: 0.7
        }, options || {});

        var posistion = element.cumulativeOffset();
        element._waitingDiv = new Element('div', {
            'class': options.className
        });
        document.body.appendChild(element._waitingDiv);
        element._waitingDiv.setStyle({
            width: element.getWidth(),
            height: element.getHeight(),
            top: posistion.top,
            left: posistion.left
        });
        element._waitingDiv.setOpacity(options.opacity);
	},

	stopWaiting: function(element) {
		if(element._waitingDiv) {
			element._waitingDiv.remove();
		}
	},

    toggleWaiting: function(element) {
        if(element._waitingDiv) {
            element.stopWaiting();
        } else {
            element.startWaiting();
        }
    }
};

Element.addMethods(Protoload);
Object.extend(Element, Protoload);

var StringBuffer = Class.create({
    sb:null,
    ffx:false,
    initialize:function(){
        if(Prototype.Browser.IE){this.sb = [];}
        else{this.sb = "";this.ffx=true;}
    },
    append:function(s){
        if(this.ffx){this.sb+=s;}
        else{this.sb[this.sb.length] = s;}
        return this;
    },
    toString:function(){
        if(this.ffx){return this.sb}
        else{return this.sb.join("")}
    }
});
StringBuffer.merge = function() {
    var buff = new StringBuffer();
    for(var i=0; i<arguments.length; i++) {
        buff.append(arguments[i]);
    }
    return buff.toString();
}
