Show:
/**
 * @module proact-arrays
 */

/**
 * Defines a set of special listeners used to trak {{#crossLink "ProAct.Array"}}{{/crossLink}} changes and updating dependent {{#crossLink "ProAct.Array"}}{{/crossLink}}s in an optimal way.
 *
 * @class Listeners
 * @namespace ProAct.Array
 * @static
 */
ProAct.Array.Listeners = P.A.L = pArrayLs = {

  /**
   * Checks the validity of an event.
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Event} event
   *      The event to check.
   * @throws {Error}
   *      If the event is not {{#crossLink "ProAct.Event.Types/array:property"}}{{/crossLink}}
   */
  check: function(event) {
    if (event.type !== P.E.Types.array) {
      throw Error('Not implemented for non array events');
    }
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} method is another {{#crossLink "ProAct.Array"}}{{/crossLink}}, dependent on the <i>original</i> one.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var b = a.concat(7, 9); // b is [1, 3, 5, 7, 9]
   *  </pre>
   *  The new array - <b>b</b> is dependent on <b>a</b>, so if for example we push something to <b>a</b>, <b>b</b> should be updated:
   *  <pre>
   *    a.push(11); // b authomatically should become [1, 3, 5, 11, 7, 9]
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>transformed</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, when the <i>original</i> changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Array} transformed
   *      The array created as a result of invoking {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>transformed</i> {{#crossLink "ProAct.Array"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  leftConcat: function (transformed, original, args) {
    return function (event) {
      pArrayLs.check(event);
      var op    = event.args[0],
          ind   = event.args[1],
          ov    = event.args[2],
          nv    = event.args[3],
          argln = args.length,
          nvs, toAdd;
      if (op === pArrayOps.set) {
        transformed[ind] = nv;
      } else if (op === pArrayOps.add) {
        nvs = slice.call(nv, 0);
        if (ind === 0) {
          pArrayProto.unshift.apply(transformed, nvs);
        } else {
          pArrayProto.splice.apply(transformed, [transformed._array.length - argln, 0].concat(nvs));
        }
      } else if (op === pArrayOps.remove) {
        if (ind === 0) {
          pArrayProto.shift.call(transformed, ov);
        } else {
          pArrayProto.splice.apply(transformed, [transformed._array.length - argln - 1, 1]);
        }
      } else if (op === pArrayOps.setLength) {
        nvs = ov -nv;
        if (nvs > 0) {
          pArrayProto.splice.apply(transformed, [nv, nvs]);
        } else {
          toAdd = [ov, 0];
          toAdd.length = 2 - nvs;
          pArrayProto.splice.apply(transformed, toAdd);
        }
      } else if (op === pArrayOps.reverse || op === pArrayOps.sort) {
        nvs = transformed._array;
        if (P.AU.isProArray(args)) {
          toAdd = args._array;
        } else {
          toAdd = args;
        }
        transformed._array.length = 0;
        push.apply(transformed._array, concat.apply(original._array, toAdd));
        transformed.core.updateByDiff(nvs);
      } else if (op === pArrayOps.splice) {
        pArrayProto.splice.apply(transformed, [ind, ov.length].concat(nv));
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} is invoked with argument, another {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} method is another {{#crossLink "ProAct.Array"}}{{/crossLink}},
   *  dependent on both the <i>original</i> and the passed as an argument one.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var x = new ProAct.Array(7, 9);
   *    var b = a.concat(x); // b is [1, 3, 5, 7, 9]
   *  </pre>
   *  The new array - <b>b</b> is dependent on <b>x</b>, so if for example we push something to <b>x</b>, <b>b</b> should be updated:
   *  <pre>
   *    x.push(13); // b authomatically should become [1, 3, 5, 7, 9, 13]
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>transformed</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, when the <i>original</i> changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Array} transformed
   *      The array created as a result of invoking {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}} was invoked.
   * @param {ProAct.Array} right
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} passed as an argument to {{#crossLink "ProAct.Array/concat:method"}}{{/crossLink}}.
   * @return {Function}
   *      A listener for events from the <i>right</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>transformed</i> {{#crossLink "ProAct.Array"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  rightConcat: function (transformed, original, right) {
    return function (event) {
      pArrayLs.check(event);
      var op    = event.args[0],
          ind   = event.args[1],
          ov    = event.args[2],
          nv    = event.args[3],
          oln   = original._array.length,
          nvs;
      if (op === pArrayOps.set) {
        transformed[oln + ind] = nv;
      } else if (op === pArrayOps.add) {
        if (ind === 0) {
          pArrayProto.splice.apply(transformed, [oln, 0].concat(nv));
        } else {
          pArrayProto.push.apply(transformed, nv);
        }
      } else if (op === pArrayOps.remove) {
        if (ind === 0) {
          pArrayProto.splice.call(transformed, oln, 1);
        } else {
          pArrayProto.pop.call(transformed, ov);
        }
      } else if (op === pArrayOps.setLength) {
        transformed.length = oln + nv;
      } else if (op === pArrayOps.reverse || op === pArrayOps.sort) {
        nvs = transformed._array;
        transformed._array.length = 0;
        push.apply(transformed._array, concat.apply(original._array, right._array));
        transformed.core.updateByDiff(nvs);
      } else if (op === pArrayOps.splice) {
        pArrayProto.splice.apply(transformed, [ind + oln, ov.length].concat(nv));
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.pevery(function (el) {
   *      return el % 2 === 1;
   *    }); // val.v is true.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we push something to <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.push(2); // val.v authomatically should become false.
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/pevery:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  every: function (val, original, args) {
    var fun = args[0], thisArg = args[1];
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          ev;
      if (op === pArrayOps.set) {
        ev = fun.call(thisArg, nv);
        if (val.valueOf() === true && !ev) {
          val.v = false;
        } else if (val.valueOf() === false && ev) {
          val.v = every.apply(original._array, args);
        }
      } else if (op === pArrayOps.add) {
        if (val.valueOf() === true) {
          val.v = every.call(nv, fun, thisArg);
        }
      } else if (op === pArrayOps.remove) {
        if (val.valueOf() === false && !fun.call(thisArg, ov)) {
          val.v = every.apply(original._array, args);
        }
      } else if (op === pArrayOps.setLength) {
        if (val.valueOf() === false) {
          val.v = every.apply(original._array, args);
        }
      } else if (op === pArrayOps.splice) {
        if (val.valueOf() === true) {
          val.v = every.call(nv, fun, thisArg);
        } else if (every.call(nv, fun, thisArg) && !every.call(ov, fun, thisArg)) {
          val.v = every.apply(original._array, args);
        }
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.psome(function (el) {
   *      return el % 2 === 0;
   *    }); // val.v is false.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we push something to <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.push(2); // val.v authomatically should become true
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/psome:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  some: function (val, original, args) {
    var fun = args[0], thisArg = args[1];
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          sv;
      if (op === pArrayOps.set) {
        sv = fun.call(thisArg, nv);
        if (val.valueOf() === false && sv) {
          val.v = true;
        } else if (val.valueOf() === true && !sv) {
          val.v = some.apply(original._array, args);
        }
      } else if (op === pArrayOps.add) {
        if (val.valueOf() === false) {
          val.v = some.call(nv, fun, thisArg);
        }
      } else if (op === pArrayOps.remove) {
        if (val.valueOf() === true && fun.call(thisArg, ov)) {
          val.v = some.apply(original._array, args);
        }
      } else if (op === pArrayOps.setLength) {
        if (val.valueOf() === true) {
          val.v = some.apply(original._array, args);
        }
      } else if (op === pArrayOps.splice) {
        if (val.valueOf() === false) {
          val.v = some.call(nv, fun, thisArg);
        } else if (some.call(ov, fun, thisArg) && !some.call(nv, fun, thisArg)) {
          val.v = some.apply(original._array, args);
        }
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}} method is another {{#crossLink "ProAct.Array"}}{{/crossLink}}, dependent on the <i>original</i> one.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var b = a.filter(function (el) {
   *      return el % 2 === 0;
   *    }); // b is []
   *  </pre>
   *  The new array - <b>b</b> is dependent on <b>a</b>, so if for example we unshift something to <b>a</b>, <b>b</b> should be updated:
   *  <pre>
   *    a.unshift(4); // b authomatically should become [4]
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>filtered</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, when the <i>original</i> changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Array} filtered
   *      The array created as a result of invoking {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/filter:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>filtered</i> {{#crossLink "ProAct.Array"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  filter: function (filtered, original, args) {
    var fun = args[0], thisArg = args[1];
    return function (event) {
      if (P.U.isFunction(event)) {
        args[0] = fun = event;
        pArray.reFilter(original, filtered, args);
        return;
      }

      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          napply, oapply, oarr,
          nvs, fnvs, j, ln, diff;

      if (op === pArrayOps.set) {
        napply = fun.call(thisArg, nv);
        oapply = fun.call(thisArg, ov);

        if (oapply === true || napply === true) {
          pArray.reFilter(original, filtered, args);
        }
      } else if (op === pArrayOps.add) {
        fnvs = [];
        nvs = slice.call(nv, 0);
        ln = nvs.length;
        if (ind === 0) {
          j = ln - 1;
          while(j >= 0) {
            if (fun.apply(thisArg, [nvs[j], j, original._array])) {
              fnvs.unshift(nvs[j]);
            }
            j--;
          }

          if (fnvs.length) {
            pArrayProto.unshift.apply(filtered, fnvs);
          }
        } else {
          j = 0;
          while(j < ln) {
            if (fun.apply(thisArg, [nvs[j], original._array.length - (ln - j), original._array])) {
              fnvs.push(nvs[j]);
            }
            j++;
          }

          if (fnvs.length) {
            pArrayProto.push.apply(filtered, fnvs);
          }
        }
      } else if (op === pArrayOps.remove) {
        if (fun.apply(thisArg, [ov, ind, original._array])) {
          if (ind === 0) {
            filtered.shift();
          } else {
            filtered.pop();
          }
        }
      } else if (op === pArrayOps.setLength) {
        pArray.reFilter(original, filtered, args);
      } else if (op === pArrayOps.reverse) {
        filtered.reverse();
      } else if (op === pArrayOps.sort) {
        pArrayProto.sort.apply(filtered, nv);
      } else if (op === pArrayOps.splice) {
        pArray.reFilter(original, filtered, args);
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}} method is another {{#crossLink "ProAct.Array"}}{{/crossLink}}, dependent on the <i>original</i> one.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var b = a.map(function (el) {
   *      return el * el;
   *    }); // b is [1, 9, 25]
   *  </pre>
   *  The new array - <b>b</b> is dependent on <b>a</b>, so if for example we pop from <b>a</b>, <b>b</b> should be updated:
   *  <pre>
   *    a.pop(); // b authomatically should become [1, 9]
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>mapped</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, when the <i>original</i> changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Array} mapped
   *      The array created as a result of invoking {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/map:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>mapped</i> {{#crossLink "ProAct.Array"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  map: function (mapped, original, args) {
    var fun = args[0], thisArg = args[1];
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          nvs, j, ln, mnvs;
      if (op === pArrayOps.set) {
        mapped[ind] = fun.call(thisArg, nv);
      } else if (op === pArrayOps.add) {
        mnvs = [];
        nvs = slice.call(nv, 0);
        ln = nvs.length;
        if (ind === 0) {
          j = ln - 1;
          while(j >= 0) {
            mnvs[j] = fun.apply(thisArg, [nvs[j], j, original._array]);
            j--;
          }

          pArrayProto.unshift.apply(mapped, mnvs);
        } else {
          j = 0;
          while(j < ln) {
            mnvs[j] = fun.apply(thisArg, [nvs[j], original._array.length - (ln - j), original._array]);
            j++;
          }

          pArrayProto.push.apply(mapped, mnvs);
        }
      } else if (op === pArrayOps.remove) {
        if (ind === 0) {
          mapped.shift();
        } else {
          mapped.pop();
        }
      } else if (op === pArrayOps.setLength) {
        mapped.length = nv;
      } else if (op === pArrayOps.reverse) {
        mapped.reverse();
      } else if (op === pArrayOps.sort) {
        pArrayProto.sort.apply(mapped, nv);
      } else if (op === pArrayOps.splice) {
        mnvs = [];
        j = 0;
        while (j < nv.length) {
          mnvs[j] = fun.apply(thisArg, [nv[j], (j + ind), original._array]);
          j++;
        }

        pArrayProto.splice.apply(mapped, [
          ind,
          ov.length
        ].concat(mnvs));
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.preduce(function (pel, el) {
   *      return pel + el;
   *    }, 0); // val.v is 0 + 1 + 3 + 5 = 9.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we shift from <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.shift(); // val.v authomatically should become 0 + 3 + 5 = 8.
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/preduce:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  reduce: function (val, original, args) {
    var oldLn = original._array.length, fun = args[0];
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3];
      if ((op === pArrayOps.add && ind !== 0) ||
         (op === pArrayOps.splice && ind >= oldLn && ov.length === 0)) {
        val.v = reduce.apply(nv, [fun, val.valueOf()]);
      } else {
        val.v = reduce.apply(original._array, args);
      }
      oldLn = original._array.length;
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.preduceRight(function (pel, el) {
   *      return pel + el;
   *    }, 0); // val.v is 0 + 5 + 3 + 1 = 9.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we splice <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.splice(1, 2, 4, 5); // val.v authomatically should become 0 + 5 + 4 + 1 = 10.
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/preduceRight:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  reduceRight: function (val, original, args) {
    var fun = args[0];
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3];
      if ((op === pArrayOps.add && ind === 0) ||
         (op === pArrayOps.splice && ind === 0 && ov.length === 0)) {
        val.v = reduceRight.apply(nv, [fun, val.valueOf()]);
      } else {
        val.v = reduceRight.apply(original._array, args);
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.pindexOf(5); // val.v is 2.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we reverse <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.reverse(); // val.v authomatically should become 0.
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/pindexOf:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  indexOf: function (val, original, args) {
    var what = args[0], fromIndex = args[1], hasFrom = !!fromIndex;
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          v = val.valueOf(),
          nvi, i;

      if (op === pArrayOps.set) {
        if (ov === what) {
          val.v = indexOf.apply(original._array, args);
        } else if (nv === what && (ind < v || v === -1) && (!hasFrom || ind >= fromIndex)) {
          val.v = ind;
        }
      } else if (op === pArrayOps.add) {
        nvi = nv.indexOf(what);
        if (ind === 0) {
          if (nvi !== -1 && (!hasFrom || ind >= fromIndex)) {
            val.v = nvi;
          } else if (v !== -1) {
            val.v = v + nv.length;
          }
        } else if (v === -1 &&  (!hasFrom || ind >= fromIndex)) {
          if (nvi !== -1) {
            val.v = ind;
          }
        }
      } else if (op === pArrayOps.remove) {
        if (v !== -1) {
          if (ind === 0) {
            if (ov === what && !hasFrom) {
              val.v = indexOf.apply(original._array, args);
            } else {
              val.v = v - 1;
            }
          } else if (what === ov) {
            val.v = -1;
          }
        }
      } else if (op === pArrayOps.setLength && nv <= v) {
        val.v = -1;
      } else if (op === pArrayOps.reverse || op === pArrayOps.sort) {
        val.v = indexOf.apply(original._array, args);
      } else if (op === pArrayOps.splice) {
        nvi = nv.indexOf(what);
        i = nvi + ind;
        if (ind <= v) {
          if (nvi !== -1 && i < v && (!hasFrom || fromIndex <= i)) {
            val.v = i;
          } else if (nv.length !== ov.length && ov.indexOf(what) === -1) {
            v = v + (nv.length - ov.length);
            if (!hasFrom || v >= fromIndex) {
              val.v = v;
            } else {
              val.v = indexOf.apply(original._array, args);
            }
          } else {
            val.v = indexOf.apply(original._array, args);
          }
        } else if (v === -1 && nvi !== -1) {
          val.v = i;
        }
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}} method is a {{#crossLink "ProAct.Property"}}{{/crossLink}}, dependent on the <i>original</i> array.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([5, 4, 5, 3]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var val = a.plastIndexOf(5); // val.v is 2.
   *  </pre>
   *  The new value - <b>val</b> is dependent on <b>a</b>, so if for example we sort <b>a</b>, <b>val</b> should be updated:
   *  <pre>
   *    a.sort(); // val.v authomatically should become 3.
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}}, when the <i>original</i> array changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Property} val
   *      The result of invoking {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/plastIndexOf:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>val</i> {{#crossLink "ProAct.Property"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  lastIndexOf: function (val, original, args) {
    var what = args[0], fromIndex = args[1], hasFrom = !!fromIndex;
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          v = val.valueOf(),
          nvi, i;

      if (op === pArrayOps.set) {
        if (ov === what) {
          val.v = lastIndexOf.apply(original._array, args);
        } else if (nv === what && (ind > v || v === -1) && (!hasFrom || ind <= fromIndex)) {
          val.v = ind;
        }
      } else if (op === pArrayOps.add) {
        nvi = nv.indexOf(what);
        if (ind === 0) {
          if (nvi !== -1 && v === -1 && (!hasFrom || ind <= fromIndex)) {
            val.v = nvi;
          } else if (v !== -1) {
            val.v = v + nv.length;
          }
        } else if (nvi !== -1 && (!hasFrom || (ind + nvi) <= fromIndex)) {
          val.v = ind + nvi;
        }
      } else if (op === pArrayOps.remove) {
        if (v !== -1) {
          if (ind === 0) {
            val.v = v - 1;
          } else if (what === ov) {
            val.v = lastIndexOf.apply(original._array, args);
          }
        }
      } else if (op === pArrayOps.splice || op === pArrayOps.reverse || op === pArrayOps.sort || (op === pArrayOps.setLength && nv < ov)) {
        val.v = lastIndexOf.apply(original._array, args);
      }
    };
  },

  /**
   * Generates a listener that can be attached to an {{#crossLink "ProAct.Array"}}{{/crossLink}} on which
   * the method {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}} is invoked.
   * <p>
   *  The result of the {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}} method is another {{#crossLink "ProAct.Array"}}{{/crossLink}}, dependent on the <i>original</i> one.
   * </p>
   * <p>
   *  For example if the original was:
   *  <pre>
   *    var a = new ProAct.Array([1, 3, 5]);
   *  </pre>
   *  and we invoked {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}} on it like this:
   *  <pre>
   *    var b = a.slice(1); // b is [3, 5]
   *  </pre>
   *  The new array - <b>b</b> is dependent on <b>a</b>, so if for example we push to <b>a</b>, <b>b</b> should be updated:
   *  <pre>
   *    a.push(32); // b authomatically should become [3, 5, 32]
   *  </pre>
   * </p>
   * <p>
   *  The generated listener by this method does this - updates the <i>sliced</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, when the <i>original</i> changes
   *  and it does it in an optimal way.
   * </p>
   *
   * @for ProAct.Array.Listeners
   * @static
   * @param {ProAct.Array} sliced
   *      The array created as a result of invoking {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}} on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}.
   * @param {ProAct.Array} original
   *      The {{#crossLink "ProAct.Array"}}{{/crossLink}} on which {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}} was invoked.
   * @param {Array} args
   *      The arguments passed to {{#crossLink "ProAct.Array/slice:method"}}{{/crossLink}}, when it was invoked on the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}
   * @return {Function}
   *      A listener for events from the <i>original</i> {{#crossLink "ProAct.Array"}}{{/crossLink}}, updating the <i>sliced</i> {{#crossLink "ProAct.Array"}}{{/crossLink}} on
   *      every new event, if it is necessary.
   */
  slice: function (sliced, original, args) {
    var s = args[0], e = args[1], hasEnd = !!e;
    return function (event) {
      pArrayLs.check(event);
      var op  = event.args[0],
          ind = event.args[1],
          ov  = event.args[2],
          nv  = event.args[3],
          osl;
      if (op === pArrayOps.set) {
        if (ind >= s && (!hasEnd || ind < e)) {
          sliced[ind - s] = nv;
        }
      } else {
        osl = sliced._array;
        sliced._array.length = 0;
        push.apply(sliced._array, slice.apply(original._array, args));
        sliced.core.updateByDiff(osl);
      }
    };
  }
};