/*
	$Id: core.js 66 2006-12-12 19:17:21Z oleg $
	$Rev: 66 $
	$Author: oleg $
	$URL: svn://alpha.spbnet.dom/internal/Spar/branches/golem/src/core.js $
	$Date: 2006-12-12 22:17:21 +0300 (Вт, 12 дек 2006) $
*/

if ('object' != typeof spar) var spar = {}

spar.declare = function (S, F, O) {

	if ('string' != typeof S)
        throw new TypeError('package name is not a string');

	function init (O, A) {
		var p = A.shift();
		if ('object' != typeof O[p]) O[p] = {};
		if (A.length) return init(O[p], A);
        if ('undefined' != typeof F) O[p] = F;

		return O[p];
	}
	
    init(O || window, S.split('.'));
}



String.prototype.ltrim = function () {
    return this.replace(/^(\s|\t)*/g, '');
}

String.prototype.rtrim = function () {
    return this.replace(/(\s|\t)*$/g, '');
}

String.prototype.trim = function () {
    return this.ltrim().rtrim();
}

/*
TODO
		* Better breaking closure handling (special property?)
	*/

/**
 * Allows a function/method to be executed periodically.
 *
 * You can avoid passing a whole Execution Settings Object by using
 * single Array (it's first item as target), Number (as interval) or
 * Function (as factor) value.
 *
 * Note, if the factor is a numeric value, execution occurs till factor's
 * module can be decremented. If it is a function, target and step are
 * passed into it every iteration.
 *
 * Examples: <pre>
 *
 *  function W (s) {
 *      document.body.innerHTML += (s+'<br>');
 *  }
 *
 *  var O = function () {
 *      var pvt = 4;
 *      var self = this;
 *      this.title = 'my title';
 *		this.prop = 5;
 *      this.methodA = function (arg) {
 *          W( [pvt, this.title, arg] );
 *      }
 *      this.methodB = function (arg) {
 *          W( [pvt, self.title, arg] );
 *      }
 *  }
 *
 *  var L = {
 *      title : 'my title',
 *      method : function (arg) {
 *          W( this.title )
 *      }
 *  }
 *
 *  function alone (arg) {
 *      W( [this.title, arg] );
 *  }
 *
 *  var o = new O;
 *  var a = [];
 *
 *  // default settings applied
 *  o.methodA.exec();
 *  o.methodB.exec(null, 'argument');
 *
 *  // configured execution
 *  o.methodB.exec({target:o, interval:500, factor:3});
 *  o.methodA.exec({target:o, factor:
 *      function (Target, Step) {
 *          return Step < Target.prop
 *      }
 *  });
 *
 *  // simple configured execution session with interval passed
 *  o.methodB.exec(500, 'argument');
 *
 *  // .. target passed
 *  o.methodA.exec([o]);
 *  o.methodA.exec([{title: 'generic title'}]);
 *
 *  // and filter
 *  o.methodB.exec(
 *      function (Target, Step) {
 *          return Step < 10 } );
 *
 *  // generic function
 *  (function (arg) { W([this.title, arg.i++]) }).exec([o], {i:0});
 *
 *  // single function simultaneity
 *  alone.exec([document], 'argument');
 *
 *  // arrays
 *  a.push.exec([a], 3);
 *  setTimeout(function() {W( a )}, 2000);
 *
 *  // static methods
 *  L.method.exec([L]);
 *
 * </pre>
 *
 * @prototype
 * @method  exec
 * @param   {Object | NULL} set A set of Execution settings. Valid keys are:
 *          interval (50) - Millisecond;
 *          target (null) - Variable to be passed as 'this' with the call;
 *          factor (null) - Expression to be evaluated each iteration; will
 *                          abort execution after evaluating to FALSE.
 * @param   {Any*} Any args to pass into the executed function/method.
 * @return  {Number} Execution session identifier.
 * @todo    onIteration (after) handler
 * @todo    onException handler
 * @todo    onAbort handler
 */
Function.prototype.exec = function (set) {

    var defaults = {
        interval : 50,
        target : null,
        factor : null
    };

    var factor = function () {
        return true;
    }

    var F = this;
    var iteration = 0;
    var id;

    // applying user defined settings to the defaults

    if (set instanceof Array)
        defaults.target = set.shift();

    else if (set instanceof Function)
        defaults.factor = set;

    else if ('number' == typeof set)
        defaults.interval = set;

    else if (set instanceof Object)
        for (var i in set)
            defaults[i] = set[i];

    else if (set != null)
        throw new TypeError('Settings is not an object or null');

    // clarify interval value

    if ('number' != typeof defaults.interval || defaults.interval <= 0)
        throw new TypeError('Settings: interval is not a positive number');
    
    else defaults.interval = Math.floor(defaults.interval);

    // customize factor handler
    
    if ('number' == typeof defaults.factor) {
        defaults.factor = Math.floor(Math.abs(defaults.factor));
        factor = function () {
            return defaults.factor--;
        }
    }
    else if ('function' == typeof defaults.factor) {
        factor = function (i) {
            return defaults.factor(defaults.target, iteration);
        }
    }
    else if (null != defaults.factor) {
        throw new TypeError('Settings: unsupported factor type');
    }


    var args = [];
    for (var p = 1; p < arguments.length; p++) {
        args.push(arguments[p]);
    }

    // callable environment
    var wrapper = function () {
        if (factor(++iteration))
            F.apply(defaults.target, args);
        else
            clearInterval(id);
    }

    return (id = setInterval(wrapper, defaults.interval));
}

/**
 * Aborts a function/method execution session
 *
 * Examples: <pre>
 *
 *  var ID = fn.exec();
 *  setTimeout( function(){ fn.abort(ID) }, 2000);
 *
 * </pre>
 *
 * @prototype
 * @method  abort
 * @param   {Number} id Execution session identifier.
 */
Function.prototype.abort = function (id) {
    clearInterval(id);
}

