// http://ejohn.org/blog/simple-javascript-inheritance/
// Inspired by base2 and Prototype
(function(){
  var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;

  // The base Class implementation (does nothing)
  this.Class = function(){};
 
  // Create a new Class that inherits from this class
  Class.extend = function(prop) {
    var _super = this.prototype;
   
    // Instantiate a base class (but only create the instance,
    // don't run the init constructor)
    initializing = true;
    var prototype = new this();
    initializing = false;
   
    // Copy the properties over onto the new prototype
    for (var name in prop) {
      // Check if we're overwriting an existing function
      prototype[name] = typeof prop[name] == "function" &&
        typeof _super[name] == "function" && fnTest.test(prop[name]) ?
        (function(name, fn){
          return function() {
            var tmp = this._super;
           
            // Add a new ._super() method that is the same method
            // but on the super-class
            this._super = _super[name];
           
            // The method only need to be bound temporarily, so we
            // remove it when we're done executing
            var ret = fn.apply(this, arguments);       
            this._super = tmp;
           
            return ret;
          };
        })(name, prop[name]) :
        prop[name];
    }
   
    // The dummy class constructor
    function Class() {
      // All construction is actually done in the init method
      if ( !initializing && this.init )
        this.init.apply(this, arguments);
    }
   
    // Populate our constructed prototype object
    Class.prototype = prototype;
   
    // Enforce the constructor to be what we expect
    Class.constructor = Class;

    // And make this class extendable
    Class.extend = arguments.callee;
   
    return Class;
  };
})();

var Webkit = {};

Webkit.Rotator = Class.extend({
	init: function(axis)
	{
		this._axis = axis.toUpperCase();
		this._started = false;
	},
	start: function()
	{
		this._started = true;
	},
	started: function()
	{
		return this._started;
	},
	stop: function()
	{
		this._started = false;
	},
	rotating: function()
	{
		if (arguments.length > 0)
		{
			if (arguments[0] === true) 
			 this.start();
			else 
			 this.stop();
		}
		
		return this.started();
	}
});

Webkit.Transformable = Class.extend({
  init: function(el)
  {
    this.element = el;
		this._refreshInterval = 200;
		this._rotateIncrement = 20;
		this._perspective = 200;
    this.props = {rotateX: 0, rotateY: 0, rotateZ: 0, scaleX: 1, scaleY: 1, skewX: 0, skewY: 0};
		
		this._rotators  = {
			'X': new Webkit.Rotator('X'),
			'Y': new Webkit.Rotator('Y'),
			'Z': new Webkit.Rotator('Z')			
		};

    // Save instance to element    
    $(this.element).data('transformable', this);
    
    return this;
  },
	
	perspective: function()
	{
		if (arguments.length > 0)
		{
	    this._perspective += arguments[0];    
	    $(this.element).parent()[0].style.webkitPerspective = this._perspective;
		}

		return this._perspective;
	},
	
  startRotate: function()
  {
    var axis = arguments[0] ? arguments[0].toUpperCase() : 'X';
    
		this.rotating(axis, true);
		
    var t = this,
    x = this.props.rotateX,
    y = this.props.rotateY,
    z = this.props.rotateZ;		
    
		// One refresh timer for all 3 axis
    this._intervalId = (function()
    {
      return setInterval(function() {
        t.rotate( t.props.rotateX, t.props.rotateY, t.props.rotateZ );
        
				for (var axis in t._rotators)
				{
					if (t._rotators[axis].rotating())
					{
	          // Prevent from going out of bounds
	          if (t.props['rotate'+axis] == 360) t.props['rotate'+axis] = 0;
	          
	          t.props['rotate'+axis] += t._rotateIncrement;						
					}
				}

      }, t._refreshInterval);
    })();
  },
  stopRotate: function()
  {
		var axis = arguments[0] ? arguments[0].toUpperCase() : 'X';
		
		this.rotating(axis, false);
    clearTimeout(this._intervalId);
  },
	
	// getter/setter for 'rotating' state
	rotating: function(axis)
	{
		// No args; return true if rotating on any axis
		if (arguments.length < 1)
		{
			for (var i=0,l=this._rotators.length; i<l; i++)
			{
				if (this._rotators[i].rotating()) return true;
			}
			
			return false;
		}
		
		// Start or stop rotation if passing a second param
		if (arguments.length > 1)
		{
			this._rotators[axis].rotating(arguments[1]);
		}
		
		return this._rotators[axis].rotating();
	},
	
  // set rotation params and trigger css transformation
  rotate: function(x,y,z)
  {
    this.props.rotateX = x;
    this.props.rotateY = y;
    this.props.rotateZ = z;
    this.transform();
  },
	
	scale: function(axis, increment)
	{
		this.props['scale'+axis] += increment;
		
		// If it's rotating, transformation will be applied automatically
		if (!this.rotating()) this.transform();
	},
	
	skew: function(axis, increment)
  {
    this.props['skew'+axis] += increment;
    
    // If it's rotating, transformation will be applied automatically
    if (!this.rotating()) this.transform();
  },
		
  // apply the css transformation
  transform: function()
  {
    this.element.style.webkitTransform = this.transformCSS();
  },
  
  // generate value for the -webkit-transform CSS property
  transformCSS: function()
  {
    var str = 'rotateX(' + this.props.rotateX + 'deg) '
    +' rotateY(' + this.props.rotateY + 'deg)'
    +' rotateZ(' + this.props.rotateZ + 'deg)'
		+' scaleX(' + this.props.scaleX+')'
		+' scaleY(' + this.props.scaleY+')'
		+' skewX(' + this.props.skewX+'deg)'
    +' skewY(' + this.props.skewY+'deg)'
		;
		//console.log("transformCSS: "+ str)
		return str;
  }
});


// jQuery wrapper
$.fn.transformable = function()
{
  return this.each(function()
  {
    return new Webkit.Transformable(this);
  });
};