/* {{{ */
$(function() {
    $("button, input:submit, input:button, a", ".buttons").button();
    $("a", ".buttons").click(function() {
        if (!$(this).hasClass("force")) {
            return false;
        }
    });
});
/* }}} */
/** {{{
 *  Array jQuery Plugin
 *  
 *  All comments taken from the prototype web site:
 *  http://prototypejs.org/api/
 *  
 *  @browser  ie6, ie7, firefox 2, ff3, chrome, safari, opera
 *  @compatibility
 *  
 *  Include a bunch of functions to make handling of arrays simpler. Right now, they are
 *  all taken from Prototypes array and enumberables functions
 *  
 *  TODO: What to do if what is passed in is not an array?
 *  TODO: actually test in above browsers
 *  
 */
(function ($) {
  $.array = {
      
    K : function(x) {
      return x
    },
      
    /**
     *  Determines whether all the elements are boolean-equivalent to true, either directly 
     *  or through computation by the provided iterator.
     *
     *  @returns Boolean.
     */
    all: function (arr, fn) {
      arr = $.makeArray(arr);
      fn = fn || this.K;
      for (var a = 0, isAll = true; a < arr.length; a++) {
        c(arr[a]);
        c(typeof arr[a]);
        if (!fn.call(this, arr[0])) {
          isAll = false;
        }
      }
      return isAll;
    },
    
    /**
     *  Determines whether at least one element is boolean-equivalent to true, either 
     *  directly or through computation by the provided iterator.
     *
     *  @returns Boolean.
     */   
    any: function (arr, fn) {
      fn = fn || this.K;
      for (var a = 0; a < arr.length; a++) {
        if (fn.call(this, arr[a])) {
          return true;
        }
      }
      return false;
    },
    
    /**
     *  Returns the results of applying the iterator to each element. Aliased as map.
     *
     *  @returns Array.
     */ 
    collect: function (arr, fn) {
      return this.map(arr, fn);
    },
    
    /**
     *  Clears the array (makes it empty).
     *
     *  @returns empty Array.
     */     
    clear: function (arr) {
      arr.length = 0;
      return arr;
    },
    
    /**
     *  Returns a duplicate of the array, leaving the original array intact.
     *
     *  @returns Array.
     */     
    clone: function (arr) {
      return arr.slice();
    },
    
    /**
     *  Returns a new version of the array, without any null/undefined values.
     *
     *  @returns Array.
     */  
    compact: function (arr) {
      return $.map(arr, function (n) {
        if (n !== null && n !== undefined) {
          return n;
        }
      }); 
    },
    
    /**
     *  Finds the first element for which the iterator returns true. Aliased by the find method.
     *
     *  @returns firstElement | undefined.
     */  
    detect: function (arr, fn) {
      for (var a = 0; a < arr.length; a++) {
        if (fn.call(this, arr[a])) {
          return arr[a];
        }
      }
      return undefined;
    },
    
    /**
     *  It lets you iterate over all the elements in a generic fashion, then returns the array, 
     *  thereby allowing chain-calling.
     *
     *  @returns Array.
     */  
    each: function (arr, fn, context) {
      for (var a = 0; a < arr.length; a++) {
        fn.call(context, arr[a], a);
      }
      return arr;
    },
    
    /**
     *  Groups items in chunks based on a given size, with last chunk being possibly smaller.
     *
     *  @returns Array.
     */  
    eachSlice: function (arr, num, fn, context) {
      var newArray = [];
      var tempArray = [];
      for (var a = 1; a < arr.length + 1; a++) {
        if (fn == undefined) {
          tempArray.push(arr[a - 1]);
        } else {
          tempArray.push(fn.call(context, arr[a - 1]));
        }
        if (a % num === 0) {
          newArray.push(tempArray);
          tempArray.length = 0;
        }
      }
      if (tempArray.length > 0) {
        newArray.push(tempArray);
      }
      return newArray;
    },
    
    /**
     *  alias for jQuery.makeArray()
     *
     *  @returns Array.
     */  
    entries: function (arr) {
      return $.makeArray(arr);
    },
    
    /**
     *  Finds the first element for which the iterator returns true. 
     *  Convenience alias for detect, but constitutes the preferred (more readable) syntax.
     *
     *  @returns firstElement | undefined
     */  
    find: function (arr, fn) {
      return this.detect(arr, fn);
    },
    
    /**
     *  Returns all the elements for which the iterator returned true. Aliased as select.
     *
     *  @returns Array.
     */  
    findAll: function (arr, fn, context) {
      var newArray = [];
      for (var a = 0; a < arr.length; a++) {
        if (fn.call(context, arr[a])) {
          newArray.push(arr[a]);
        }
      }
      return newArray;
    },
    
    /**
     *  Returns the first item in the array, or undefined if the array is empty.
     *
     *  @returns value | undefined
     */  
    first: function (arr) {
      return arr[0];
    },
    
    /**
     *  Returns a ÒflatÓ (one-dimensional) version of the array. Nested arrays are 
     *  recursively injected Òinline.Ó This can prove very useful when handling the 
     *  results of a recursive collection algorithm, for instance.
     *
     *  @returns array
     */  
    flatten: function (arr) {
      return $.array.inject(arr, [], function (arr, val) {
        return arr.concat($.isArray(val) ?
          $.array.flatten(val) : [val]);
      });
    },
    
    /**
     *  Clones an existing array or creates a new one from an array-like collection.
     *  This is an alias for the jQuery.makeArray.
     *
     *  @returns array
     */  
    from: function (arr) {
      return $.makeArray(arr);
    },
    
    /**
     *  Returns all the elements that match the filter. If an iterator function is provided, 
     *  it is used to produce the returned value for each selected element.
     *
     *  @returns array
     */ 
    grep: function (arr, filter, fn, context) {
      fn = fn || function (x) { 
        return x;
      };
      var results = [];

      $.each(arr, function (index, value) {
        if (this.match(filter)) {
          results.push(fn.call(context, value, index));
        }
      });
      return results;
    },
    
    /**
     *  Returns the position of the first occurrence of the argument within the array. If the 
     *  argument doesnÕt exist in the array, returns -1.
     *
     *  @returns number
     */ 
    indexOf: function (arr, val) {
      return $.inArray(val, arr); 
    },
    
    /**
     *  Determines whether a given object is in the array or not, based on the == comparison 
     *  operator. Aliased as member.
     *
     *  @returns boolean | undefined
     */ 
    include: function (arr, val) {
      if (arr && arr.constructor == Array) {  
        for (var a = 0;a < arr.length; a++) {
          if (arr[a] == val) {
            return true;
          }
        }          
        return false;
      } else {
        return undefined;
      }
    },
    
    /**
     *  Groups items in fixed-size chunks, using a specific value to fill up the last chunk if necessary.
     *  Unlike Prototype, there is an optional 4th parameter for a filter function
     *
     *  @returns Array
     */ 
    inGroupsOf: function (arr, num, filler, fn) {
      var newArray = [];
      var tempArray = [];
      for (var a = 1; a < arr.length + 1; a++) {
        if (fn == undefined) {
          tempArray.push(arr[a - 1]);
        } else {
          tempArray.push(fn.call(this, arr[a - 1]));
        }
        if (a % num === 0) {
          newArray.push(tempArray);
          tempArray = [];
        }
      }
      if (tempArray.length > 0) {
        for (var a = tempArray.length; a < num; a++) {
          tempArray.push(filler ? filler : null);
        }
        newArray.push(tempArray);
      }
      return newArray;
    },
    
    /**
     *  Incrementally builds a result value based on the successive results 
     *  of the iterator. This can be used for array construction, numerical 
     *  sums/averages, etc.
     *
     *  @returns Array
     */ 
    inject: function (arr, acc, fn, context) {
      for (var a = 0; a < arr.length; a++) {
        acc = fn.call(context, acc, arr[a], a);
      }
      return acc;
    },
    
    /**
     *  Invokes the same method, with the same arguments, for all items in 
     *  a collection. Returns the results of the method calls.
     *
     *  @returns Array
     *  TODO: Complicated method because prototype adds methods to the classes
     *  so it's easy to call them with value[method].apply(value, args)
     */ 
    invoke : function(arr, method) {
      var args = $.makeArray(arguments).slice(2);
      return this.map(arr, function(value) {
        if (eval('$.' + method)) {
          return eval('$.' + method).apply(value, args);
        } else if (eval('$(value).' + method) && arr.constructor!=Array) {
          eval('$(value).' + method + '.apply($(value),args)')
        } else if (arr[method]) {
          arr[method].apply(value, args);
        }
        return arr; 
      });
    },
    
    invoke2: function (arr, fn, context) {
      for (var a = 0; a < arr.length; a++) {
        fn.call(context, arr[a]);
      }
      return arr;
    },
    
    /**
     *  Returns true if arr is an array, false otherwise.
     *
     *  @returns Array
     */ 
    isArray: function (arr) {
      return $.isArray(arr);
    },
    
    /**
     *  Returns the last item in the array, or undefined if the array is empty.
     *
     *  @returns value | undefined
     */ 
    last: function (arr) {
      return arr[arr.length - 1];
    },
    
    /**
     *  Returns the results of applying the iterator to each element. Convenience alias for jQuery.map.
     *  Prototype fills unreturned values with undefined, this does not.
     *
     *  @returns value | undefined
     */ 
    map: function (arr, fn) {
      return $.map(arr, fn);
    },
    
    /**
     *  Returns the maximum element (or element-based computation), or undefined if the enumeration is 
     *  empty. Elements are either compared directly, or by first applying the iterator and comparing 
     *  returned values.
     *
     *  @returns value | undefined
     */ 
    max: function (arr, fn, context) {
      fn = fn || this.K;
      var max = null;
      for (var a = 0; a < arr.length; a++) {
        if (max === null || fn.call(context,arr[a]) >= fn.call(context,max)) {
          max = arr[a];
        }
      }
      return max;
    },
    
    /**
     *  Determines whether a given object is in the array or not, 
     *  based on the == comparison operator. Convenience alias for include.
     *
     *  @returns boolean
     */ 
    member: function (arr, val) {
      return this.include(arr, val);
    },
    
    /**
     *  Returns the minimum element (or element-based computation), or undefined if 
     *  the enumeration is empty. Elements are either compared directly, or by first 
     *  applying the iterator and comparing returned values.
     *
     *  @returns value
     */ 
    min: function (arr, fn, context) {
      fn = fn || this.K;
      var min = null;
      for (var a = 0; a < arr.length; a++) {
        if (!isNaN(parseFloat(fn.call(context, arr[a]))) || typeof fn.call(context, arr[a]) === 'string') {
          if (min === null || fn.call(context, arr[a]) <= fn.call(context, min)) {
            min = arr[a];
          }
        } 
      }
      return min;
    },
    
    /**
     *  Partitions the elements in two groups: those regarded as true, and those 
     *  considered false. By default, regular JavaScript boolean equivalence is used, 
     *  but an iterator can be provided, that computes a boolean representation of the 
     *  elements.
     *
     *  @returns value
     */ 
    partition: function (arr, fn, context) {
      var newArr = [[], []];
      for (var a = 0; a < arr.length; a++) {
        if (fn.call(context, arr[a])) {
          newArr[0].push(arr[a]);
        } else {
          newArr[1].push(arr[a]);
        }
      }
      return newArr;
    },
    
    /**
     * Optimization for a common use-case of collect: fetching the same property for all 
     * the elements. Returns the property values.
     *
     *  @returns value
     */ 
    pluck: function (arr, val) {
      var newArr = [];
      for (var a = 0;a < arr.length; a++) {
        if (arr[a] != undefined && arr[a] != null) {
          newArr.push(arr[a][val]);
        }
      }
      return newArr;
    },
    
    /**
     * Optimization for a common use-case of collect: fetching the same property for all 
     * the elements. Returns the property values.
     * The Prototype method fills unreturned values with undefined, this does not.
     *
     *  @returns value
     */ 
    pluck2: function (arr, attr) {
      return $.map(arr, function (obj) {
        if (obj) {
          return eval('obj.' + attr);
        }
      });
    },
    
    /**
     * Reduces arrays: one-element arrays are turned into their unique element, while 
     * multiple-element arrays are returned untouched.
     *
     *  @returns single value
     */ 
    reduce: function (arr) {
      return arr.length >= 1 ? arr : arr[0];
    },
    
    /**
     * Returns all the elements for which the iterator returned false.
     *
     *  @returns array
     */ 
    reject: function (arr, fn, context) {
      var newArray = [];
      for (var a = 0; a < arr.length; a++) {
        if (!fn.call(context, arr[a])) {
          newArray.push(arr[a]);
        }
      }
      return newArray;
    },
    
    /**
     * Returns the reversed version of the array. By default, directly reverses the 
     * original. If inline is set to false, uses a clone of the original array.
     *
     *  @returns array
     *  TODO: implement inline functionality 
     */ 
    reverse: function (arr, inline) {
      arr = $.makeArray(arr);  //for DOM array
      return !inline ? [].concat(arr.slice().reverse()) : [].concat(arr.reverse());
    },
    
    /**
     * Alias for the findAll method.
     *
     *  @returns array
     */ 
    select: function (arr, fn) {
      return this.findAll(arr, fn);
    },
    
    /**
     * Returns the size of the array.
     *
     *  @returns number
     */ 
    size: function (arr) {
      return arr.length;
    },
    
    /**
     * Provides a custom-sorted view of the elements based on the criteria 
     * computed, for each element, by the iterator.
     *
     *  @returns Array
     */ 
    sortBy: function (arr, fn, context) {
      var newArray = [arr[0]];
      for (var a = 1; a < arr.length; a++) {
        if (fn.call(this, arr[a]) < fn.call(context, newArray[0])) {
          newArray.splice(0, 0, arr[a]);
        } else {
          for (var b = 0; b < newArray.length; b++) {
            if (fn.call(this, arr[a]) < fn.call(context, newArray[b])) {
              newArray.splice(b, 0, arr[a]);
              break;
            }
            if (b + 1 == newArray.length) {
              newArray.push(arr[a]);
              break;
            }
          }
        }
      }
      return newArray;
    },
    
    /**
     * Turns anything into a true array.  An alias for makeArray.
     *
     *  @returns Array
     */ 
    toArray: function (arr) {
      return $.makeArray(arr);
    },
    
    /**
     * Returns a JSON string.
     *
     *  @returns Array
     *  TODO: works for one level, not into and not for objects
     */ 
    toJSON: function (arr) {
      var jsonString = '[';      
      for (var a = 0; a < arr.length; a++) {
        switch (typeof arr[a]) {
        case 'string' :
          jsonString += '"' + arr[a] + '", ';
          break;
        case 'object':
          if (arr[a] === null) {
            jsonString += 'null, ';
          } else {
            jsonString += arr[a] + ', ';
          }
          break;
        case 'boolean':
          jsonString += arr[a] + ', ';
        }
      }
      jsonString = jsonString.substr(0, jsonString.length - 2);
      jsonString += ']';
      
      return jsonString;
    },
    
    /**
     * Produces a duplicate-free version of an array. If no duplicates 
     * are found, the original array is returned.
     *
     *  @returns Array
     */
    uniq: function (arr) {
      var tempArr = [];
      if (arr && arr.constructor == Array && arr.length > 0) {
        for (var a = 0; a < arr.length; a++) {
          if ($.inArray(arr[a], tempArr) == -1) {
            tempArr[tempArr.length] = arr[a];
          }
        }
      }
      return tempArr;
    },
    
    /**
     * Produces a new version of the array that does not contain any 
     * of the specified values.
     *
     *  @returns Array
     */
    without: function (arr, val) {
      if (arr && arr.constructor == Array) {  
        for (var a = 0; a < arr.length; a++) {
          if (arr[a] == val) {
            arr.splice(a--, 1);
          }
        }
      }
      return arr;  
    },
    
    /**
     * Zips together (think of the zip on a pair of trousers) 2+ sequences, providing 
     * an array of tuples. Each tuple contains one value per original sequence. Tuples 
     * can be converted to something else by applying the optional iterator on them.
     *
     *  @returns Array
     */
    zip: function () {
      var args = $.makeArray(arguments);
      if ($.isFunction(this.last(args))) {
        var fn = args.pop();
      }

      var newArr = [];
      for (var a = 0; a < args[0].length; a++) {
        var tempArr = [];
        for (var b = 0; b < args.length; b++) {
          tempArr.push(args[b][a]);
        }
        newArr.push(tempArr);
      }

      if (fn == undefined) {
        return newArr;
      }
      
      var arr2 = [];
      for (var a = 0; a < newArr.length; a++) {
        arr2.push(fn.call(this, newArr[a]));
      }
      return arr2;
    }
  };
})(jQuery);
/* }}} */

