/**
* @copyright 2015 Tridium, Inc. All Rights Reserved.
* @author Gareth Johnson
*/
/**
* Defines {@link baja.FilterCursor}.
* @module baja/sys/structures/FilterCursor
*/
define([ "bajaScript/baja/sys/bajaUtils",
"bajaScript/baja/sys/inherit",
"bajaScript/baja/sys/structures/SyncCursor" ], function (
bajaUtils,
inherit,
SyncCursor) {
"use strict";
var subclass = inherit.subclass,
callSuper = inherit.callSuper,
strictArg = bajaUtils.strictArg;
/**
* A filtered cursor used for iteration.
*
* This Cursor is a generic Cursor used for iteration in a {@link baja.OrderedMap}.
*
* @class
* @alias baja.FilterCursor
* @extends baja.SyncCursor
* @see baja.OrderedMap
*
* @param {Object} context the context to bind to `this` in cursor operations.
* @param {baja.OrderedMap} orderedMap the ordered map to iterate over
*/
var FilterCursor = function FilterCursor(context, orderedMap) {
callSuper(FilterCursor, this, arguments);
this.$context = context;
this.$orderedMap = orderedMap;
this.$keys = orderedMap && orderedMap.getKeys();
this.$filter = null;
this.$index = -1;
};
subclass(FilterCursor, SyncCursor);
function filterNext(cursor) {
if (cursor.$index < cursor.$keys.length) {
++cursor.$index;
return cursor.$index !== cursor.$keys.length;
}
return false;
}
/**
* Advance cursor and return true if successful.
*
* @returns {Boolean}
*/
FilterCursor.prototype.next = function () {
var that = this;
if (!that.$filter) {
return filterNext(this);
}
// If a Constructor has been passed in then keep iterating
// until we find a matching element
do {
if (!filterNext(this)) {
return false;
}
} while (!that.$filter.call(that.$context, that.get()));
return true;
};
/**
* Return the current item. If this is a
* {@link module:baja/comp/SlotCursor SlotCursor}, this will return a
* {@link baja.Slot Slot}.
*
* @returns the cursor value (null if none available).
*/
FilterCursor.prototype.get = function () {
var x = this.$index,
keys = this.$keys;
if (x === -1 || x >= keys.length) {
return null;
}
return this.$orderedMap.get(keys[x]);
};
/**
* Return the current key.
*
* This is a private method and shouldn't be used by non-Tridium developers.
*
* @private
*
* @returns {String} the cursor key (null if none available).
*/
FilterCursor.prototype.getKey = function () {
var x = this.$index,
keys = this.$keys;
if (x === -1 || x >= keys.length) {
return null;
}
return keys[x];
};
/**
* Return the current index.
*
* This is a private method and shouldn't be used by non-Tridium developers.
*
* @private
*
* @returns {Number} the cursor index (null if none available).
*/
FilterCursor.prototype.getIndex = function () {
return this.$index;
};
/**
* Iterate through the Cursor and call a function on every item.
*
* When the function is called, `this` refers to the `context` that
* was passed in when the Cursor was created.
*
* @param {Function} func function called on every iteration with the 'value' being used as an argument.
* If this is a Slot Cursor the 'value' will be a Slot.
*/
FilterCursor.prototype.each = function (func) {
strictArg(func, Function);
var result;
while (this.next()) {
result = func.call(this.$context, this.get(), this.getIndex());
if (result) {
return result;
}
}
};
/**
* Return true if the Cursor is completely empty (regardless of iterative state).
*
* @returns {Boolean}
*/
FilterCursor.prototype.isEmpty = function () {
// Note the old cursor index
var oldX = this.$index;
// Set the cursor back to the start
this.$index = -1;
// See if we have any valid entries from the start
var res = this.next();
// Restore the old cursor index
this.$index = oldX;
// Return the result of our search
return !res;
};
/**
* Return an array of the cursor results (regardless of iterative state).
* If this is a Slot Cursor, this will be an array of Slots.
*
* @returns {Array}
*/
FilterCursor.prototype.toArray = function () {
// Note the old cursor index
var oldX = this.$index,
a = [];
this.$index = -1;
// Iterate through and fill up the array
while (this.next()) {
a.push(this.get());
}
// Restore the old cursor index
this.$index = oldX;
return a;
};
/**
* Return an Object Map of keys with their corresponding values.
* If this is a Slot Cursor, this will be a Map of Slot names with their
* corresponding Slots (regardless of iterative state).
*
* @returns {Object}
*/
FilterCursor.prototype.toMap = function () {
var slots = this.toArray(),
map = {},
s,
i;
for (i = 0; i < slots.length; ++i) {
s = slots[i];
map[s.getName()] = s;
}
return map;
};
/**
* Return the size of the cursor (regardless of iterative state).
*
* @returns {Number}
*/
FilterCursor.prototype.getSize = function () {
// Note the old cursor index
var oldX = this.$index,
count = 0;
this.$index = -1;
// Iterate through and fill up the array
while (this.next()) {
++count;
}
// Restore the old cursor index
this.$index = oldX;
return count;
};
/**
* Add a filter function to the Cursor.
*
* @param {Function} filter used to filter the results of the Cursor.
* When invoked, the first argument will be the item to filter (i.e. a Slot).
* This function must return a true value for the item to be kept.
* @returns {baja.FilterCursor} itself.
*/
FilterCursor.prototype.filter = function (filter) {
if (!this.$filter) {
this.$filter = filter;
} else {
var oldFilter = this.$filter;
// Merge the filter functions together
this.$filter = function (val) {
return oldFilter.call(this, val) && filter.call(this, val);
};
}
return this;
};
/**
* Return the first item in the cursor (regardless of iterative state).
*
* If this is being used as a Slot Cursor, the Slot will be returned.
*
* @returns first item found in the Cursor (or null if nothing found).
*/
FilterCursor.prototype.first = function () {
// Note the old cursor index
var oldX = this.$index,
val = null;
this.$index = -1;
// Iterate through and fill up the array
if (this.next()) {
val = this.get();
}
// Restore the old cursor index
this.$index = oldX;
return val;
};
/**
* Return the last item in the cursor (regardless of iterative state).
*
* If this is being used as a Slot Cursor, the Slot will be returned.
*
* @returns last item found in the Cursor (or null if nothing found).
*/
FilterCursor.prototype.last = function () {
// Note the old cursor index
var oldX = this.$index,
val = null,
index = this.$keys.length - 2;
while (val === null && index >= -1) {
this.$index = index;
if (this.next()) {
val = this.get();
}
index = index - 2;
}
// Restore the old cursor index
this.$index = oldX;
return val;
};
return FilterCursor;
});