/**
 *  Protify jQuery Plugin
 *  version 0.3
 *  
 * Copyright (c) 2009 Josh Powell
 * Licensed under the MIT license.
 * 
 *  * Date: 2009-02-04 11:45:50 (Wed, 04 Feb 2009)
 *  
 */
(function ($) {
  var $break = { };
  var arrayFunc = {
    _each: function(iterator) {
      for (var i = 0, length = this.length; i < length; i++) {
        iterator(this[i]);
      }
    },
  
    all: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var result = true;
      this.each(function(value, index) {
        result = result && !!iterator.call(context, value, index);
        if (!result) { throw $break; }
      });
      return result;
    },
  
    any: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var result = false;
      this.each(function(value, index) {
        if (result = !!iterator.call(context, value, index)) {
          throw $break;
        }
      });
      return result;
    },
  
    clear: function() {
      this.length = 0;
      return this;
    },
  
    clone: function() {
      return $.protify([].concat(this));
    },

    collect: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var results = $.protify([]);
      this.each(function(value, index) {
        results.push(iterator.call(context, value, index));
      });
      return results;
    },
  
    detect: function(iterator, context) {
      var result;
      this.each(function(value, index) {
        if (iterator.call(context, value, index)) {
          result = value;
          throw $break;
        }
      });
      return result;
    },
    
    compact: function() {
      return $.protify(this.select(function(value) {
        return value !== null;
      }));
    },

    each: function(iterator, context) {
      context = context || this;
      var index = 0;
      try {
        this._each(function(value) {
          iterator.call(context, value, index++);
        });
      } catch (e) {
        if (e != $break) { throw e; }
      }
      return this;
    },
    
    eachSlice: function(number, iterator, context) {
      var index = -number, slices = [], array = this.toArray();
      if (number < 1) { return array; }
      while ((index += number) < array.length) {
        slices.push(array.slice(index, index+number));
      }
      return $.protify($.protify(slices).collect(iterator, context));
    },
    
    extended : function() {
      return true;
    },

    findAll: function(iterator, context) {
      var results = $.protify([]);
      this.each(function(value, index) {
        if (iterator.call(context, value, index)) {
          results.push(value);
        }
      });
      return results;
    },
  
    flatten: function() {
      return this.inject([], function(array, value) {
        $.protify(value);
        return $.protify(array.concat($.isArray(value) ?
          value.flatten() : [value]));
      });
    },
    
    first: function() {
      return this[0];
    },
  
    grep: function(filter, iterator, context) {
      iterator = iterator || function(x) { return x; };
      var results = $.protify([]);
      if (typeof filter === 'string') {
        filter = new RegExp(filter);
      }
  
      this.each(function(value, index) {
        if (filter.test(value)) {
          results.push(iterator.call(context, value, index));
        }
      });
      return results;
    },
  
    include: function(object) {
        if ($.isFunction(this.indexOf)) {
            if (this.indexOf(object) != -1) { 
              return true; 
            }
        }
  
      var found = false;
      this.each(function(value) {
        if (value == object) {
          found = true;
          throw $break;
        }
      });
      return found;
    },
    
    indexOf: function(item, i) {
        i || (i = 0);
        var length = this.length;
        if (i < 0) i = length + i;
        for (; i < length; i++)
          if (this[i] === item) return i;
        return -1;
    },
  
    inGroupsOf: function(number, fillWith) {
      fillWith = fillWith ? null : fillWith;
      return this.eachSlice(number, function(slice) {
        while(slice.length < number) { slice.push(fillWith); }
        return slice;
      });
    },
  
    inject: function(memo, iterator, context) {
      this.each(function(value, index) {
        memo = iterator.call(context, memo, value, index);
      });
      return memo;
    },
    
    inspect: function() {
      return '[' + this.map($.inspect).join(', ') + ']';
    },
  
    intersect: function(array) {
      $.protify(array);
      return this.uniq().findAll(function(item) {
        return array.detect(function(value) { return item === value; });
      });
    },
  
    invoke: function(method) {
      var args = $.makeArray(arguments).slice(1);
      return this.map(function(value) {
        return value[method].apply(value, args);
      });
    },
  
    last: function() {
      return this[this.length - 1];
    },

    lastIndexOf : function(item, i) {
        i = isNaN(i) ? this.length : (i < 0 ? this.length + i : i) + 1;
        var n = $.protify(this.slice(0, i).reverse()).indexOf(item);
        return (n < 0) ? n : i - n - 1;
    },
    max: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var result;
      this.each(function(value, index) {
        value = iterator.call(context, value, index);
        if (result == null || value >= result) {
          result = value;
        }
      });
      return result;
    },
  
    min: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var result;
      this.each(function(value, index) {
        value = iterator.call(context, value, index);
        if (result == null || value < result) {
          result = value;
        }
      });
      return result;
    },
  
    partition: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var trues = [], falses = [];
      this.each(function(value, index) {
        (iterator.call(context, value, index) ? trues : falses).push(value);
      });
      return [trues, falses];
    },
  
    pluck: function(property) {
      var results = $.protify([]);
      this.each(function(value) {
        results.push(value[property]);
      });
      return results;
    },
    
    purge: function () {
      return [].concat(this);
    },
  
    reduce: function() {
      return this.length > 1 ? this : this[0];
    },
  
    reject: function(iterator, context) {
      var results = $.protify([]);
      this.each(function(value, index) {
        if (!iterator.call(context, value, index)) {
          results.push(value);
        }
      });
      return results;
    },
  
    size: function() {
      return this.length;
    },
  
    sortBy: function(iterator, context) {
      return this.map(function(value, index) {
        return {
          value: value,
          criteria: iterator.call(context, value, index)
        };
      }).sort(function(left, right) {
        var a = left.criteria, b = right.criteria;
        return a < b ? -1 : a > b ? 1 : 0;
      }).pluck('value');
    },
  
    toArray: function() {
      return $.protify(this.map());
    },
    
//  toJSON: function() {
//  var results = [];
//  this.each(function(object) {
//    var value = Object.toJSON(object);
//    if (!Object.isUndefined(value)) results.push(value);
//  });
//  return '[' + results.join(', ') + ']';
//},

    // added by RL for TMC coding
    // sum of all array element
    sumALL: function(iterator, context) {
      iterator = iterator || function(x) { return x; };
      var result = 0;
      this.each(function(value, index) {
        value = iterator.call(context, value, index);
        result += parseFloat(value);
      });
      return result;
    }, 
    
    uniq: function(sorted) {
      return $.protify(this.inject([], function(array, value, index) {
        $.protify(array, true);
        if (0 === index || (sorted ? array.last() != value : !array.include(value))) {
          array.push(value);
        }
        return array;
      }));
    },
    
    without: function() {
      var values = $.protify($.makeArray(arguments));
      return $.protify(this.select(function(value) {
        return !values.include(value);
      }));
    },
  
    zip: function() {
      var iterator = function(x) { return x; }, args = $.protify($.makeArray(arguments));
      if ($.isFunction(args.last())) {
        iterator = args.pop();
      }
  
      var collections = $.protify([this].concat(args)).map();
      return this.map(function(value, index) {
        return iterator(collections.pluck(index));
      });
    }
  };
  
  $.extend(arrayFunc, {
    map:     arrayFunc.collect,
    find:    arrayFunc.detect,
    select:  arrayFunc.findAll,
    filter:  arrayFunc.findAll,
    member:  arrayFunc.include,
    entries: arrayFunc.toArray,
    every:   arrayFunc.all,
    some:    arrayFunc.any
  });  
  
  $.protify = function(target, permanent) {
    if (permanent) {
      $.extend(target, arrayFunc);
      return target;
    }
    target = $.extend(target.slice(), arrayFunc);
    return target;
  };
  
})(jQuery);