# Arrays are ordered collections, indexed by integers starting at `0`.
# Indexes may be negative, where `-1` represents the last item in the
# array, `-2` the last but one, etc. Arrays may be constructed by using a
# method like {Array.[]}, or by using an array literal:
#
#     Array[1, 2, 3, 4, 5]   # => [1, 2, 3, 4, 5]
#     ['a', 'b', 'c', 'd']   # => ["a", "b", "c", "d"]
#
# Implementation details
# ----------------------
#
# Ruby arrays are toll-free bridged to native javascript arrays, meaning
# that anywhere that a ruby array is required, a normal javascript array
# may be passed instead. The {Array} class infact makes use of a lot of
# the standard javascript functions on array prototypes to make its
# functionality as fast as possible.
#
# Due to the fact that arrays may be constructed in a javascript
# environment, and then passed through to a ruby method, Opal cannot
# guarantee that an array will not have a bad value. Bad values are
# those which ruby cannot send messages to, and therefore is an object
# that will raise an error when it is accessed by methods in {Array}, or
# any other object accessing an arrays elements. Bad values from
# javascript include the native `true`, `false`, `null` and `undefined`
# values from javascript, as well as any object literal.
#
# Ruby compatibility
# ------------------
#
# As instances of {Array} are actually javascript arrays, they can
# perform all the same functionality as Rubyspec defines arrays should.
# While not 100% of methods are currently implemented, the missing
# methods are being added quickly. All implemented methods are listed in
# this file. Any method that is only partially implemented also contains
# a list of restrictions in its description.
#
# The main area of partialy implemented methods are the enumerating
# methods like {#each}, {#each\_index} and {#reverse\_each}. Rubyspec
# defines that they should return an Enumerator if no block is passed to
# that method. Currently this does not happen, and `self` is returned
# with no side effects.
#
# Custom subclasses of {Array} may also be defined, and this is
# implemented in {.allocate}, when the array is created using {.new}.
# Internally a native javascript array is still used, but its class and
# method table are swizzled.
#
# Finally the {Array} class does not include the Enumerable module. Its
# methods are mostly implemented directly on the Array class. The
# Enumerable module will be added shortly, and the relevant methods
# moved back into that module.
class Array

  # Returns a new array populated with the given objects.
  #
  # @example
  #
  #     Array['a', 'b', 'c']    # => ['a', 'b', 'c']
  #
  # **FIXME** should support custom subclasses
  #
  # @param [Object] objs
  # @return [Array]
  def self.[](*objs)
    objs
  end

  # **FIXME** should support custom subclasses
  def self.allocate
    []
  end

  def initialize(*objs)
    `for (var i = 0, length = objs.length; i < length; i++) {
      self.push(objs[i]);
    }

    return self;`
  end

  # Returns a formatted, printable version of the array. {#inspect} is called
  # on each of the elements and appended to the string.
  #
  # @return [String] string representation of the receiver
  def inspect
    `var description = [];

    for (var i = 0, length = self.length; i < length; i++) {
      description.push(#{`self[i]`.inspect});
    }

    return '[' + description.join(', ') + ']';`
  end

  # Returns a simple string version of the array. {#to_s} is applied to each
  # of the child elements with no seperator.
  def to_s
    `var description = [];

    for (var i = 0, length = self.length; i < length; i++) {
      description.push(#{`self[i]`.to_s});
    }

    return description.join('');`
  end

  # Append - pushes the given object onto the end of this array. This 
  # expression returns the array itself, so several appends may be chained
  # together.
  #
  # @example
  #
  #     [1, 2] << "c" << "d" << [3, 4]
  #     # => [1, 2, "c", "d", [3, 4]]
  #
  # @param [Object] obj the object to append
  # @return [Array] returns the receiver
  def <<(obj)
    `self.push(obj);`
    self
  end

  # Returns the number of elements in `self`. May be zero.
  #
  # @example
  #
  #     [1, 2, 3, 4, 5].length
  #     # => 5
  #
  # @return [Numeric] length
  def length
    `return self.length;`
  end

  def size
    `return self.length;`
  end

  # Yields the block once for each element in `self`, passing that element as
  # a parameter.
  #
  # If no block is given, an enumerator is returned instead.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.each { |x| puts x }
  #     # => 'a'
  #     # => 'b'
  #     # => 'c'
  #
  # **TODO** needs to return enumerator for no block.
  #
  # @return [Array] returns the receiver
  def each
    `for (var i = 0, length = self.length; i < length; i++) {
      try {
        #{yield `self[i]`};
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Similar to {#each}, but also passes in the current element index to the
  # block.
  def each_with_index
    `for (var i = 0, length = self.length; i < length; i++) {
      try {
        #{yield `self[i]`, `i`};
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Same as {#each}, but passes the index of the element instead of the 
  # element itself.
  #
  # If no block given, an enumerator is returned instead.
  #
  # **TODO** enumerator functionality not yet implemented.
  #
  # @example
  #
  #     a = [1, 2, 3]
  #     a.each_index { |x| puts x }
  #     # => 0
  #     # => 1
  #     # => 2
  #
  # @return [Array] returns receiver
  def each_index
    `for (var i = 0, length = self.length; i < length; i++) {
      try {
        #{yield `i`};
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Append - pushes the given object(s) onto the end of this array. This
  # expression returns the array itself, so several appends may be chained
  # together.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.push 'd', 'e', 'f'
  #     # => ['a', 'b', 'c', 'd', 'e', 'f'
  #
  # @param [Object] obj the object(s) to push onto the array
  # @return [Array] returns the receiver
  def push(*objs)
    `for (var i = 0, length = objs.length; i < length; i++) {
      self.push(objs[i]);
    }

    return self;`
  end

  # Returns the index of the first object in `self` such that it is `==` to
  # `obj`. If a block is given instead of an argument, returns first object for
  # which the block is true. Returns `nil` if no match is found. See also
  # {#rindex}.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.index('a')              # => 0
  #     a.index('z')              # => nil
  #     a.index { |x| x == 'b' }  # => 1
  #
  # @param [Object] obj the object to look for
  # @return [Numeric, nil] result
  def index(obj)
    `for (var i = 0, length = self.length; i < length; i++) {
      if (#{`self[i]` == obj}.$r) {
        return i;
      }
    }

    return nil;`
  end

  # Concatenation - returns a new array built by concatenating the two arrays
  # together to produce a third array.
  #
  # @example
  #
  #     [1, 2, 3] + [4, 5]
  #     # => [1, 2, 3, 4, 5]
  #
  # @param [Array] other the array to concat with
  # @return [Array] returns new concatenated array
  def +(other)
    `return self.concat(other);`
  end

  # Difference. Creates a new array that is a copy of the original array,
  # removing any items that also appear in `other`.
  #
  # @example
  #
  #   [1, 2, 3, 3, 4, 4, 5] - [1, 2, 4]
  #   # => [3, 3, 5]
  #
  # @param [Array] other array to use for difference
  # @return [Array] new array
  def -(other)
    raise "Array#- not yet implemented"
  end

  # Equality. Two arrays are equal if they contain the same number of elements
  # and if each element is equal to (according to {BasicObject#==} the
  # corresponding element in the second array.
  #
  # @example
  #
  #     ['a', 'c'] == ['a', 'c', 7]       # => false
  #     ['a', 'c', '7'] == ['a', 'c', 7]  # => true
  #     ['a', 'c', 7] == ['a', 'd', 'f']  # => false
  #
  # @param [Array] other array to compare self with
  # @return [Boolean] if the arrays are equal
  def ==(other)
    `if (self.$hash() == other.$hash()) return Qtrue;
    if (self.length != other.length) return Qfalse;

    for (var i = 0; i < self.length; i++) {
      if (!#{`self[i]` == `other[i]`}.$r) {
        return Qfalse;
      }
    }

    return Qtrue;`
  end

  # Searches through an array whose elements are also arrays, comparing `obj`
  # with their first element of each contained array using {BasicObject#==}.
  # Returns the first contained array that matches (that is, the first
  # associated array) or `nil` if no match is found. See also {#rassoc}.
  #
  # @example
  #
  #     s1 = ['colors', 'red', 'blue', 'green']
  #     s2 = ['letters', 'a', 'b', 'c']
  #     s3 = 'foo'
  #     a = [s1, s2, s3]
  #
  #     a.assoc 'letters'               # => ['letters', 'a', 'b', 'c']
  #     a.assoc 'foo'                   # => nil
  def assoc(obj)
    `var arg;

    for (var i = 0; i < self.length; i++) {
      arg = self[i];

      if (arg.length && #{`arg[0]` == obj}.$r) {
        return arg;
      }
    }

    return nil;`
  end

  # Returns the element at `index`. A negative index counts from the end of the
  # receiver. Returns `nil` if the given index is out of range. See also {#[]}.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd', 'e']
  #     a.at 0          # => 'a'
  #     a.at -1         # => 'e'
  #     a.at 324        # => nil
  #
  # @param [Numeric] index the index to get
  # @return [Object, nil] returns nil or the result
  def at(idx)
    `if (idx < 0) idx += self.length;

    if (idx < 0 || idx >= self.length) return nil;
    return self[idx];`
  end

  # Removes all elements from the receiver.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd', 'e']
  #     a.clear   # => []
  #
  # @return [Array] returns the receiver
  def clear
    `self.splice(0);
    return self;`
  end

  # Yields the block, passing in successive elements from the receiver, 
  # returning an array containing those elements for which the block returns a
  # true value.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.select { |x| x > 4 }
  #     # => [5, 6]
  #
  # @return [Array] returns a new array of selected elements
  def select
    `var result = [], arg;

    for (var i = 0; i < self.length; i++) {
      try {
        arg = self[i];
        if (#{yield `arg`}.$r) {
          result.push(arg);
        }
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return result;`
  end

  # Yields the block once for each element of the receiver. Creates a new array
  # containing the values returned by the block. See also `Enumerable#collect`.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd']
  #     a.collect { |x| x + '!' }     # => ['a!', 'b!', 'c!', 'd!']
  #     a                             # => ['a', 'b', 'c', 'd']
  #
  # @return [Array] new array
  def collect
    `var result = [];

    for (var i = 0; i < self.length; i++) {
      try {
        result.push(#{yield `self[i]`});
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return result;`
  end

  # alias_method 'map', 'collect'

  # Yields the block once for each element of `self`, replacing the element with
  # the value returned by the block. See also `Enumerable#collect`.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd']
  #     a.collect { |x| x + '!' }
  #     # => ['a!', 'b!', 'c!', 'd!']
  #     a
  #     # => ['a!', 'b!', 'c!', 'd!']
  #
  # @return [Array] returns the receiver
  def collect!
    `for (var i = 0; i < self.length; i++) {
      try {
        self[i] = #{yield `self[i]`};
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Duplicate.
  def dup
    `return self.slice(0);`
  end

  # Returns a copy of the receiver with all nil elements removed
  #
  # @example
  #
  #     ['a', nil, 'b', nil, 'c', nil].compact
  #     # => ['a', 'b', 'c']
  #
  # @return [Array] new Array
  def compact
    `var result = [], length = self.length;

    for (var i = 0; i < length; i++) {
      if (self[i] != nil) {
        result.push(self[i]);
      }
    }

    return result;`
  end

  # Removes nil elements from the receiver. Returns nil if no changes were made,
  # otherwise returns self.
  #
  # @example
  #
  #     ['a', nil, 'b', nil, 'c'].compact!
  #     # => ['a', 'b', 'c']
  #
  #     ['a', 'b', 'c'].compact!
  #     # => nil
  #
  # @return [Array, nil] returns either the receiver or nil
  def compact!
    `var length = self.length;

    for (var i = 0; i < length; i++) {
      if (self[i] == nil) {
        self.splice(i, 1);
        i--;
      }
    }

    return length == self.length ? nil : self;`
  end

  # Appends the elements of `other` to `self`.
  #
  # @example
  #
  #     ['a', 'b'].concat ['c', 'd']
  #     # => ['a', 'b', 'c', 'd']
  #
  # @param [Array] other array to concat
  # @return [Array] returns the receiver
  def concat(other)
    `var length = other.length;

    for (var i = 0; i < length; i++) {
      self.push(other[i]);
    }

    return self;`
  end

  # Returns the number of elements. If an argument is given, counts the number
  # of elements which equals to `obj`. If a block is given, counts the number of
  # elements yielding a true value.
  #
  # @example
  #
  #     ary = [1, 2, 4, 2]
  #     ary.count     # => 4
  #     ary.count(2)  # =>2
  #
  # @param [Object] obj object to check
  # @return [Numeric] count or count of obj
  def count(obj)
    `if (obj != undefined) {
      var total = 0;

      for (var i = 0; i < self.length; i++) {
        if (#{`self[i]` == obj}.$r) {
          total++;
        }
      }

      return total;
    } else {
      return self.length;
    }`
  end

  # Deletes items from `self` that are equal to `obj`. If any items are found,
  # returns `obj`. If the item is not found, returns `nil`. If the optional code
  # block is given, returns the result of block if the item is not found.
  #
  # @example
  #
  #     a = ['a', 'b', 'b', 'b', 'c']
  #
  #     a.delete 'b'
  #     # => 'b'
  #     a
  #     # => ['a', 'c']
  #
  #     a.delete 'z'
  #     # => nil
  #
  # @param [Object] obj object to delete
  # @return [Object, nil] returns obj or nil
  def delete(obj)
    `var length = self.length;

    for (var i = 0; i < self.length; i++) {
      if (#{`self[i]` == obj}.$r) {
        self.splice(i, 1);
        i--;
      }
    }

    return length == self.length ? nil : obj;`
  end

  # Deletes the element at the specified index, returning that element, or nil
  # if the index is out of range. 
  #
  # @example
  #
  #     a = ['ant', 'bat', 'cat', 'dog']
  #     a.delete_at 2
  #     # => 'cat'
  #     a
  #     # => ['ant', 'bat', 'dog']
  #     a.delete_at 99
  #     # => nil
  #
  # @param [Numeric] idx the index to delete
  # @return [Object, nil] returns the deleted object or nil
  def delete_at(idx)
    `if (idx < 0) idx += self.length;
    if (idx < 0 || idx >= self.length) return nil;
    var res = self[idx];
    self.splice(idx, 1);
    return self;`
  end

  # Deletes every element of `self` for which `block` evaluates to true.
  #
  # @example
  #
  #     a = [1, 2, 3]
  #     a.delete_if { |x| x >= 2 }
  #     # => [1]
  #
  # @return [Array] returns amended receiver
  def delete_if
    `for (var i = 0; i < self.length; i++) {
      try {
        if (#{yield `self[i]`}.$r) {
          self.splice(i, 1);
          i--;
        }
      } catch (e) {
        switch(e.$keyword) {
          case 2:
            return e.$value;
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Drop first `n` elements from receiver, and returns remaining elements in
  # array.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.drop 3
  #     # => [4, 5, 6]
  #
  # @param [Number] n number of elements to drop
  # @return [Array] returns new array
  def drop(n)
    `if (n > self.length) return [];
    return self.slice(n);`
  end

  # Drop elements up to, but not including, the first element for which the
  # block returns nil or false, and returns an array containing the remaining
  # elements.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.drop_while { |i| i < 3 }
  #     # => [3, 4, 5, 6]
  #
  # @return [Array] returns a new array
  def drop_while
    `for (var i = 0; i < self.length; i++) {
      if (!#{yield `self[i]`}.$r) {
        return self.slice(i);
      }
    }

    return [];`
  end

  # Returns `true` if the receiver contains no elements, `false` otherwise.
  #
  # @example
  #
  #     [].empty?
  #     # => true
  #
  # @return [false, true] empty or not
  def empty?
    `return self.length == 0 ? Qtrue : Qfalse;`
  end

  # Tries to return the element as position `index`. If the index lies outside
  # the array, the first form throws an IndexError exception, the second form
  # returns `default`, and the third form returns the value of invoking the
  # block, passing in the index. Negative values of `index` count from the end
  # of the array.
  #
  # @example First form
  #
  #     a = [11, 22, 33, 44]
  #     a.fetch 1
  #     # => 22
  #     a.fetch -1
  #     # => 44
  #
  # @example Second form
  #
  #     a.fetch 4, 'cat'
  #     # => 'cat'
  #
  # @example Third form
  #
  #     a.fetch 4 { |i| i * i }
  #     # => 16
  #
  # @param [Numeric] idx
  # @param [Object] defaults
  # @return [Object] returns result
  def fetch(idx, defaults)
    `var original = idx;

    if (idx < 0) idx += self.length;
    if (idx < 0 || idx >= self.length) {
      if (defaults == undefined)
        return rb_raise("Index Error: Array#fetch");
      else if (__block__)
        return #{yield `original`};
      else
        return defaults;
    }

    return self[idx];`
  end

  # Returns the first element, or the first `n` elements, of the array. If the
  # array is empty, the first form returns `nil`, and the second form returns an
  # empty array.
  #
  # @example
  #
  #     a = ['q', 'r', 's', 't']
  #     a.first
  #     # => q
  #     a.first 2
  #     # => ['q', 'r']
  #
  # @param [Numeric] count number of elements
  # @return [Object, Array] object or array of objects
  def first(count = nil)
    `if (count == nil) {
      if (self.length == 0) return nil;
      return self[0];
    }
    return self.slice(0, count);`
  end

  # Returns a new array that is a one-dimensional flattening of this array
  # (recursively). That is, for evey element that is an array, extract its
  # elements into the new array. If the optional `level` argument determines the
  # level of recursion to flatten.
  #
  # @example
  #
  #     s = [1, 2, 3]
  #     # => [a, 2, 3]
  #     t = [4, 5, 6, [7, 8]]
  #     # => [4, 5, 6, [7, 8]]
  #     a = [s, t, 9, 10]
  #     # => [[1, 2, 3], [4, 5, 6, [7, 8]], 9, 10]
  #     a.flatten
  #     # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  #     a = [1, 2, [3, [4, 5]]]
  #     a.flatten 1
  #     # => [1, 2, 3, [4, 5]]
  #
  # @param [Numeric] level the level to flatten
  # @return [Array] returns new array
  def flatten(level = nil)
    `var result = [], item;

    for (var i = 0; i < self.length; i++) {
      item = self[i];

      if (item.hasOwnProperty('length')) {
        if (level == nil)
          result = result.concat(#{`item`.flatten});
        else if (level == 0)
          result.push(item);
        else
          result = result.concat(#{`item`.flatten `level - 1`});
      } else {
        result.push(item);
      }
    }

    return result;`
  end

  # Flattens the receiver in place. Returns `nil` if no modifications were made.
  # If the optional level argument determines the level of recursion to flatten.
  #
  # @example
  #
  #     a = [1, 2, [3, [4, 5]]]
  #     a.flatten!
  #     # => [1, 2, 3, 4, 5]
  #     a.flatten!
  #     # => nil
  #     a
  #     # => [1, 2, 3, 4, 5]
  #
  # @param [Number] level to flatten to
  # @return [Array] returns the receiver
  def flatten!(level = nil)
    `var length = self.length;
    var result = #{self.flatten level};
    self.splice(0);

    for (var i = 0; i < result.length; i++) {
      self.push(result[i]);
    }

    if (self.length == length)
      return nil;

    return self;`
  end

  # Returns true if the given object is present in `self`, false otherwise.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.include? 'b'
  #     # => true
  #     a.include? 'z'
  #     # => false
  def include?(member)
    `for (var i = 0; i < self.length; i++) {
      if (#{`self[i]` == member}.$r) {
        return #{true};
      }
    }

    return #{false};`
  end

  # Replaces the contents of `self` with the contents of `other`, truncating or
  # expanding if necessary.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd', 'e']
  #     a.replace ['x', 'y', 'z']
  #     # => ['x', 'y', 'z']
  #     a
  #     # => ['x', 'y', 'z']
  #
  # @param [Array] other array to replace contents with
  # @return [Array] returns the receiver
  def replace(other)
    `self.splice(0);

    for (var i = 0; i < other.length; i++) {
      self.push(other[i]);
    }

    return self;`
  end

  # Inserts the given values before the element with the given index (which may
  # be negative).
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd']
  #     a.insert 2, 99
  #     # => ['a', 'b', 99, 'c', 'd']
  #     a.insert -2, 1, 2, 3
  #     # => ['a', 'b', 99, 'c', 1, 2, 3, 'd']
  #
  # @param [Numeric] idx the index for insertion
  # @param [Object] objs objects to insert
  # @return [Array] returns the receiver
  def insert(idx, *objs)
    `if (idx < 0) idx += self.length;

    if (idx < 0 || idx >= self.length)
      rb_raise("IndexError: out of range");

    self.splice.apply(self, [idx, 0].concat(objs));
    return self;`
  end

  # Returns a string created by converting each element of the array to a string
  # seperated by `sep`.
  #
  # @example
  #
  #     ['a', 'b', 'c'].join
  #     # => 'abc'
  #     ['a', 'b', 'c'].join '-'
  #     # => 'a-b-c'
  #
  # @param [String] sep the separator
  # @return [String] joined string
  def join(sep = '')
    `var result = [];

    for (var i = 0; i < self.length; i++) {
      result.push(#{`self[i]`.to_s});
    }

    return result.join(sep);`
  end

  # Deletes every element of `self` for which the block evaluates to false.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.keep_if { |x| x < 4 }
  #     # => [1, 2, 3]
  #
  # @return [Array] returns the receiver
  def keep_if
    `for (var i = 0; i < self.length; i++) {
      if (!#{yield `self[i]`}.$r) {
        self.splice(i, 1);
        i--;
      }
    }

    return self;`
  end

  # Return the last element(s) of `self`. If the array is empty, the first form
  # returns `nil`.
  #
  # @example
  #
  #     a = ['w', 'x', 'y', 'z']
  #     a.last
  #     # => 'z'
  #     a.last 2
  #     # => ['y', 'z']
  #
  # @param [Number] count the number of items to get
  # @return [Object, Array] result
  def last(count = nil)
    `if (count == nil) {
      if (self.length == 0) return nil;
      return self[self.length - 1];
    } else {
      if (count > self.length) count = self.length;
      return self.slice(self.length - count, self.length);
    }`
  end

  # Removes the last element from `self` and returns it, or `nil` if the array
  # is empty. If a count is given, returns an array of the last `count`
  # elements (or less).
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd']
  #     a.pop
  #     # => 'd'
  #     a.pop 2
  #     # => 'b', 'c'
  #     a
  #     # => ['a']
  #
  # @param [Numeric] count number to pop
  # @return [Array] returns popped items
  def pop(count = nil)
    `if (count == nil) {
      if (self.length) return self.pop();
      return nil;
    } else {
      return self.splice(self.length - count, self.length);
    }`
  end

  # Searches through the array whose elements are also arrays. Compares `obj`
  # with the second element of each contained array using `==`. Returns the
  # first contained array that matches.
  #
  # @example
  #
  #     a = [[1, 'one'], [2, 'two'], [3, 'three'], ['ii', 'two']]
  #     a.rassoc 'two'
  #     # => [2, 'two']
  #     a.rassoc 'four'
  #     # => nil
  #
  # @param [Object] obj object to search for
  # @return [Object, nil] result or nil
  def rassoc(obj)
    `var test;

    for (var i = 0; i < self.length; i++) {
      test = self[i];
      if (test.hasOwnProperty('length') && test[1] != undefined) {
        console.log("trying " + i);
        if (#{`test[1]` == obj}.$r) return test;
      }
    }

    return nil;`
  end

  # Returns a new array containing the items in `self` for which the block is
  # not true. See also `#delete_if`.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.reject { |x| x > 3 }
  #     # => [1, 2, 3]
  #     a
  #     # => [1, 2, 3, 4, 5, 6]
  #
  # @return [Array] returns the receiver
  def reject
    `var result = [];

    for (var i = 0; i < self.length; i++) {
      if (!#{yield `self[i]`}.$r) {
        result.push(self[i]);
      }
    }

    return result;`
  end

  # Equivalent to `#delete_if!`, deleting elements from self for which the block
  # evaluates to true, but returns nil if no changes were made.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.reject! { |x| x > 3 }
  #     # => [1, 2, 3]
  #     a.reject! { |x| x > 3 }
  #     # => nil
  #     a
  #     # => [1, 2, 3]
  #
  # @return [Array] returns receiver
  def reject!
    `var length = self.length;

    for (var i = 0; i < self.length; i++) {
      if (#{yield `self[i]`}.$r) {
        self.splice(i, 1);
        i--;
      }
    }

    return self.length == length ? nil : self;`
  end

  # Returns a new array containing the receiver's elements in reverse order.
  #
  # @example
  #
  #     ['a', 'b', 'c'].reverse
  #     # => ['c', 'b', 'a']
  #     [1].reverse
  #     # => [1]
  #
  # @return [Array] return new array
  def reverse
    `var result = [];

    for (var i = self.length - 1; i >= 0; i--) {
      result.push(self[i]);
    }

    return result;`
  end

  # Reverses the receiver in place.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.reverse!
  #     # => ['c', 'b', 'a']
  #     a
  #     # => ['c', 'b', 'a']
  #
  # @return [Array] returns the receiver
  def reverse!
    `var length = self.length / 2, tmp;

    for (var i = 0; i < length; i++) {
      tmp = self[i];
      self[i] = self[self.length - (i + 1)];
      self[self.length - (i + 1)] = tmp;
    }

    return self;`
  end

  # Same as {#each}, but traverses the receiver in reverse order
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.reverse_each { |x| puts x }
  #     # => 'c'
  #     # => 'b'
  #     # => 'a'
  #
  # @return [Array] returns the receiver
  def reverse_each
    `for (var i = self.length - 1; i >= 0; i--) {
      try {
        #{yield `self[i]`};
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e['@exit_value'];
          default:
            throw e;
        }
      }
    }

    return self;`
  end

  # Returns the index of the last object in self that is == to object. If a 
  # block is given instead of an argument, returns the first object for which
  # block is true, starting from the last object. Returns `nil` if no match is
  # found.
  #
  # @example
  #
  #     a = ['a', 'b', 'b', 'b', 'c']
  #     a.rindex 'b'
  #     # => 3
  #     a.rindex 'z'
  #     # => nil
  #     a.rindex { |x| x == 'b' }
  #     # => 3
  #
  # @return [Object, nil] returns result or nil
  def rindex(obj = `undefined`)
    `if (obj != undefined) {
      for (var i = self.length - 1; i >=0; i--) {
        if (#{`self[i]` == obj}.$r) {
          return i;
        }
      }
    } else if (true || __block__) {
      rb_raise("array#rindex needs to do block action");
    }

    return nil;`
  end

  # Invokes the block passing in successive elements from `self`, deleting the
  # elements for which the block returns a false value. It returns `self` if
  # changes were made, otherwise it returns `nil`.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.select! { |x| x > 4 }
  #     # => [5, 6]
  #     a.select! { |x| x > 4 }
  #     # => nil
  #     a
  #     # => [5, 6]
  #
  # @return [Array] returns receiver
  def select!
    `var length = self.length;

    for (var i = 0; i < self.length; i++) {
      if (!#{yield `self[i]`}.$r) {
        self.splice(i, 1);
        i--;
      }
    }

    return self.length == length ? nil : self;`
  end

  # Returns the first element of `self` and removes it (shifting all other
  # elements down by one). Returns `nil` if the array is empty.
  #
  # If a number `n` is given, returns an array of the first n elements (or 
  # less), just like `#slice` does.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.shift
  #     # => 'a'
  #     a
  #     # => ['b', 'c']
  #     a = ['a', 'b', 'c']
  #     a.shift 2
  #     # => ['a', 'b']
  #     a
  #     # => ['c']
  #
  # @param [Numeric] count elements to shift
  # @return [Array] result
  def shift(count = nil)
    `if (count != nil)
      return self.splice(0, count);

    if (self.length) 
      return self.shift();

    return nil;`
  end

  # Deletes the element(s) given by an `index` (optionally with a length) or
  # by a range. Returns the deleted object(s), or `nil` if the index is out of
  # range.
  #
  # @example
  #
  #     a = ['a', 'b', 'c']
  #     a.slice! 1
  #     # => 'b'
  #     a
  #     # => ['a', 'c']
  #     a.slice! -1
  #     # => 'c'
  #     a
  #     # => ['a']
  #     a.slice! 100
  #     # => nil
  #
  # **TODO** does not yet work with ranges
  #
  # @param [Range, Number] index to begin with
  # @param [Number] length last index
  # @return [Array, nil] result
  def slice!(index, length = nil)
    `var size = self.length;

    if (index < 0) index += size;

    if (index >= size || index < 0) return nil;

    if (length != nil) {
      if (length <= 0 || length > self.length) return nil;
      return self.splice(index, index + length);
    } else {
      return self.splice(index, 1)[0];
    }`
  end

  # Returns first `count` elements from ary.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.take 3
  #     # => [1, 2, 3]
  #
  # @return [Array] array of elements
  def take(count)
    `return self.slice(0, count);`
  end


  # Passes elements to the block until the block returns a false value, then
  # stops iterating and returns an array of all prior elements.
  #
  # @example
  #
  #     a = [1, 2, 3, 4, 5, 6]
  #     a.take_while { |i| i < 3 }
  #     # => [1, 2]
  #
  # @return [Array] new array with elements
  def take_while
    `var result = [];

    for (var i = 0; i < self.length; i++) {
      try {
        if (#{yield `self[i]`}.$r) {
          result.push(self[i]);
        } else {
          break;
        }
      } catch (e) {
        switch (e.$keyword) {
          case 2:
            return e['@exit_value'];

          default:
            throw e;
        }
      }
    }

    return result;`
  end

  # Returns the receiver.
  #
  # @example
  #
  #     a = [1, 2, 3]
  #     a.to_a
  #     # => [1, 2, 3]
  #
  # @return [Array] returns the receiver
  def to_a
    self
  end

  # Returns a new array by removing duplicate values in `self`.
  #
  # @example
  #
  #     a = ['a', 'a', 'b', 'b', 'c']
  #     a.uniq
  #     # => ['a', 'b', 'c']
  #     a
  #     # => ['a', 'a', 'b', 'b', 'c']
  #
  # @return [Array]
  def uniq
    `var result = [], seen = [];

    for (var i = 0; i < self.length; i++) {
      var test = self[i], hash = test.$hash();
      if (seen.indexOf(hash) == -1) {
        seen.push(hash);
        result.push(test);
      }
    }

    return result;`
  end

  # Removes duplicate elements from `self`. Returns `nil` if no changes are
  # made (that is, no duplicates are found).
  #
  # @example
  #
  #     a = ['a', 'a', 'b', 'b', 'c']
  #     a.uniq!
  #     # => ['a', 'b', 'c']
  #     a.uniq!
  #     # => nil
  #
  # @return [Array] returns receiver
  def uniq!
    `var seen = [], length = self.length;

    for (var i = 0; i < self.length; i++) {
      var test = self[i], hash = test.$hash();
      if (seen.indexOf(hash) == -1) {
        seen.push(hash);
      } else {
        self.splice(i, 1);
        i--;
      }
    }

    return self.length == length ? nil : self;`
  end

  # Prepends objects to the front of `self`, moving other elements upwards.
  #
  # @example
  #
  #     a = ['b', 'c', 'd']
  #     a.unshift 'a'
  #     # => ['a', 'b', 'c', 'd']
  #     a.unshift 1, 2
  #     # => [1, 2, 'a', 'b', 'c', 'd']
  #
  # @param [Object] objs objects to add
  # @return [Array] returns the receiver
  def unshift(*objs)
    `for (var i = objs.length - 1; i >= 0; i--) {
      self.unshift(objs[i]);
    }

    return self;`
  end

  # Set intersection - Returns a new array containing elements common to the
  # two arrays, with no duplicates.
  #
  # @example
  #
  #     [1, 1, 3, 5] & [1, 2, 3]
  #     # => [1, 3]
  #
  # @param [Array] other second array to intersect
  # @return [Array] new intersected array
  def &(other)
    `var result = [], seen = [];

    for (var i = 0; i < self.length; i++) {
      var test = self[i], hash = test.$hash();

      if (seen.indexOf(hash) == -1) {
        for (var j = 0; j < other.length; j++) {
          var test_b = other[j], hash_b = test_b.$hash();

          if ((hash == hash_b) && seen.indexOf(hash) == -1) {
            seen.push(hash);
            result.push(test);
          }
        }
      }
    }

    return result;`
  end

  # Repitition - When given a string argument, acts the same as {#join}.
  # Otherwise, returns a new array build by concatenating the `num` copies of
  # self.
  #
  # @example With Number
  #
  #     [1, 2, 3] * 3
  #     # => [1, 2, 3, 1, 2, 3, 1, 2, 3]
  #
  # @example With String
  #
  #     [1, 2, 3] * ','
  #     # => '1,2,3'
  #
  # @param [String, Number] num string or number used to join or concat
  # @return [String, Array] depending on argument
  def *(arg)
    `if (typeof arg == 'string') {
      return #{self.join `arg`};
    } else {
      var result = [];
      for (var i = 0; i < parseInt(arg); i++) {
        result = result.concat(self);
      }

      return result;
    }`
  end

  # Element Reference - Returns the element at `index`, or returns a subarray
  # at index and counting for length elements, or returns a subarray if index
  # is a range. Negative indecies count backward from the end of the array (-1
  # is the last element). Returns `nil` if the index (or starting index) are
  # out of range.
  #
  # @example
  #
  #     a = ['a', 'b', 'c', 'd', 'e']
  #     a[2] + a[0] + a[1]              # => 'cab'
  #     a[6]                            # => nil
  #     a[1, 2]                         # => ['b', 'c']
  #     a[1..3]                         # => ['b', 'c', 'd']
  #     a[4..7]                         # => ['e']
  #     a[6..10]                        # => nil
  #     a[-3, 3]                        # => ['c', 'd', 'e']
  #     a[5]                            # => nil
  #     a[5, 1]                         # => []
  #     a[5..10]                        # => []
  #
  # **TODO** does not yet work with ranges
  #
  # @param [Range, Numeric] index to begin
  # @param [Numeric] length last index
  # @return [Array, Object, nil] result
  def [](index, length = `undefined`)
    `var size = self.length;

    if (index < 0) index += size;

    if (index >= size || index < 0) return nil;

    if (length != undefined) {
      if (length <= 0) return [];
      return self.slice(index, index + length);
    } else {
      return self[index];
    }`
  end

  # Element reference setting.
  #
  # **TODO** need to expand functionlaity.
  def []=(index, value)
    `return self[index] = value;`
  end
end