Class.Mutators.Binds = function(self, methods) {
 
   $splat(methods).each(function(method){
      var fn = self[method];
      self[method] = function(){
         return fn.apply(self, arguments);
      };
   });
 
};

/*
Script: Class.Refactor.js
        Extends a class onto itself with new property, preserving any items attached to the class's namespace.

License:
        http://clientside.cnet.com/wiki/cnet-libraries#license
*/
Class.refactor = function(orig, props) {
  $extend(props, { Extends: orig });
  var update = new Class(props);
  $each(orig, function(v, k) {
    update[k] = update[k] || v;
  });
  return update;
};

$extend(Class.prototype, { 
  refactor: function(props){ 
    this.prototype = Class.refactor(this, props).prototype;
    return this;
  } 
});


Element.counter = 1;

Element.implement({

  upwards: function(iterator) {
    var element = this;
    while (element) {
      if (iterator(element)) return element;
      element = element.getParent ? element.getParent() : null;
    }
    return element;
  },

  upwardsElement: function(classname) {
    return this.upwards(function(e) {
      if (e.hasClass)
        return e.hasClass(classname);
    });
    return null;
  },

  hasAttribute: function(attribute) {
    return this.getAttributeNode(attribute);
  },

  identify: function() {
    var id = this.getAttribute('id');
    if (id) return id;
    do { id = 'anonymous_element_' + Element.counter++ } while ($(id));
    this.setAttribute('id', id);
    return id;
  },

  focusFirstElement: function() {
    if (this.get('tag') != 'form') return;
    if (this.elements.length <= 0) return;
    // Find first non-hidden input
    for (var i = 0; i < this.elements.length; i++) {
      if (this.elements[i].type == 'hidden') continue;
      if (this.elements[i].type == 'radio') continue;
      this.elements[i].focus();
      break;
    }
  },
  
  overText: function(text, options) {
    if (Browser.Engine.presto) {
      var div = new Element('span').
        setStyles({
          'position': 'relative'
        }).addClass('overtext').set('html', text).injectBefore(this);
      div.setStyle('margin-right', -(div.offsetWidth + 5));
    } else {
      var wrapper = new Element('span', {
        styles: {
          position: 'relative'
        }
      }).injectAfter(this);
      this.injectInside(wrapper);
      
      if (!options) options = {};
      if (!options.top) options.top = 0;
      
      var top = (Browser.Engine.trident ? 5 : 0) + options.top;
      var div = new Element('span').
        setStyles({
          'position': 'absolute',
          'left': 4,
          'top': top,
          'visibility': 'hidden'
        }).addClass('overtext').set('html', text).injectBefore(this);
    }
    
    var input = this;

    var focus_handler = function() {
      if (div.getStyle('visibility') == 'hidden') return;
      div.setStyle('visibility', 'hidden');
      try {
        input.focus();
      } catch(e) {}
    }
    
    div.addEvent('click', focus_handler);
    input.addEvent('focus', focus_handler);

    var blurHandler = function() {
      if (input.value.trim() == '') {
        div.setStyle('visibility', 'visible');
      };
    };

    input.addEvents({
      'blur': blurHandler,
      'change': blurHandler,
      'paste': blurHandler
    });
    $(input.form).addEvent('reset', function() { blurHandler.delay(100); });
    
    blurHandler();
  },
  
  pin: function(options) {
    if (!options) options = {};
    if (!options.left) options.left = 0;
    if (!options.top) options.top = 0;
    if (!options.right) options.right = 0;
    if (!options.bottom) options.bottom = 0;
    
    var el = this;
    var resize = function() {
      var sizes = {
        scroll: window.getScroll(),
        scrollSize: window.getScrollSize(),
        size: window.getSize()
      }
      if (Browser.Engine.trident4) {
        el.setStyles({
          'left': sizes.scroll.x + options.left,
          'top': sizes.scroll.y + options.top
        });
      }
      el.setStyles({
        'width': sizes.size.x - (options.left + options.right),
        'height': sizes.size.y - (options.top + options.bottom)
      });
    };
    
    el.setStyles({
      'position': Browser.Engine.trident4 ? 'absolute' : 'fixed',      
      'left': options.left,
      'top': options.top,
      'width': 1,
      'height': 1
    }).store('pin', resize);
    resize();
    
    window.addEvents({
      'resize': resize,
      'scroll': resize      
    });
    
    return this;
  },

  unpin: function(reset) {
    if (reset == null) reset = true;
    
    var resize = this.retrieve('pin', null);
    if (resize) {
      window.removeEvents({
        'resize': resize,
        'scroll': resize      
      });
      this.store('pin', null);
      if (reset) {
        this.setStyles({
          'position': 'static',
          'left': 'auto',
          'top': 'auto'
        });
      }
    }
    
    return this;
  },
  
  scrollToView: function() {
    new Fx.Scroll(window).toElement(this);
  }

});

Element.Events.keyescape = {
  base: 'keyup',
  condition: function(e) {
    return e.key == 'esc';
  }
};

String.implement({

  sprintf: function(params) {
    var _params = $A(params).copy();
    return this.replace(/%[a-z0-9]{1}/ig, function(value) {
      try {
        return (_params.length > 0) ? _params.shift() : '';
      } catch(e) {
        return '';
      }
    });
  },
  
  extractFileName: function(sep) {
    return this.split(sep || '\\').getLast();
  }

});

Number.implement({

  pad: function(len, chr) {
    chr = chr || ' ';
    var times = function(chr, count) {
      var result = '';
      for (var i = 0; i < count; i++) result += chr;
      return result;
    }
    var s = this.toString();
    return times(chr, len - s.length) + s;
  }

});
