shell bypass 403
/*===========================================================================*\
* Requires
\*===========================================================================*/
var JClass = require('jclass');
/*===========================================================================*\
* HashArray
\*===========================================================================*/
var HashArray = JClass._extend({
//-----------------------------------
// Constructor
//-----------------------------------
init: function(keyFields, callback, options) {
keyFields = keyFields instanceof Array ? keyFields : [keyFields];
this._map = {};
this._list = [];
this.callback = callback;
this.keyFields = keyFields;
this.isHashArray = true;
this.options = options || {
ignoreDuplicates: false
};
if (callback) {
callback('construct');
}
},
//-----------------------------------
// add()
//-----------------------------------
addOne: function (obj) {
var needsDupCheck = false;
for (var key in this.keyFields) {
key = this.keyFields[key];
var inst = this.objectAt(obj, key);
if (inst) {
if (this.has(inst)) {
if (this.options.ignoreDuplicates)
return;
if (this._map[inst].indexOf(obj) != -1) {
// Cannot add the same item twice
needsDupCheck = true;
continue;
}
this._map[inst].push(obj);
}
else this._map[inst] = [obj];
}
}
if (!needsDupCheck || this._list.indexOf(obj) == -1)
this._list.push(obj);
},
add: function() {
for (var i = 0; i < arguments.length; i++) {
this.addOne(arguments[i]);
}
if (this.callback) {
this.callback('add', Array.prototype.slice.call(arguments, 0));
}
return this;
},
addAll: function (arr) {
if (arr.length < 100)
this.add.apply(this, arr);
else {
for (var i = 0; i < arr.length; i++)
this.add(arr[i]);
}
return this;
},
addMap: function(key, obj) {
this._map[key] = obj;
if (this.callback) {
this.callback('addMap', {
key: key,
obj: obj
});
}
return this;
},
//-----------------------------------
// Intersection, union, etc.
//-----------------------------------
/**
* Returns a new HashArray that contains the intersection between this (A) and the hasharray passed in (B). Returns A ^ B.
*/
intersection: function (other) {
var self = this;
if (!other || !other.isHashArray)
throw Error('Cannot HashArray.intersection() on a non-hasharray object. You passed in: ', other);
var ret = this.clone(null, true),
allItems = this.clone(null, true).addAll(this.all.concat(other.all));
allItems.all.forEach(function (item) {
if (self.collides(item) && other.collides(item))
ret.add(item);
});
return ret;
},
/**
* Returns a new HashArray that contains the complement (difference) between this hash array (A) and the hasharray passed in (B). Returns A - B.
*/
complement: function (other) {
var self = this;
if (!other || !other.isHashArray)
throw Error('Cannot HashArray.complement() on a non-hasharray object. You passed in: ', other);
var ret = this.clone(null, true);
this.all.forEach(function (item) {
if (!other.collides(item))
ret.add(item);
});
return ret;
},
//-----------------------------------
// Retrieval
//-----------------------------------
get: function(key) {
if (!this.has(key))
return;
return (!(this._map[key] instanceof Array) || this._map[key].length != 1) ? this._map[key] : this._map[key][0];
},
getAll: function(keys) {
keys = keys instanceof Array ? keys : [keys];
if (keys[0] == '*')
return this.all;
var res = new HashArray(this.keyFields);
for (var key in keys)
res.add.apply(res, this.getAsArray(keys[key]));
return res.all;
},
getAsArray: function(key) {
return this._map[key] || [];
},
getUniqueRandomIntegers: function (count, min, max) {
var res = [], map = {};
count = Math.min(Math.max(max - min, 1), count);
while (res.length < count)
{
var r = Math.floor(min + (Math.random() * (max + 1)));
if (map[r]) continue;
map[r] = true;
res.push(r);
}
return res;
},
sample: function (count, keys) {
// http://en.wikipedia.org/wiki/Image_(mathematics)
var image = this.all,
ixs = {},
res = [];
if (keys)
image = this.getAll(keys);
var rand = this.getUniqueRandomIntegers(count, 0, image.length - 1);
for (var i = 0; i < rand.length; i++)
res.push(image[rand[i]]);
return res;
},
//-----------------------------------
// Peeking
//-----------------------------------
has: function(key) {
return this._map.hasOwnProperty(key);
},
collides: function (item) {
for (var k in this.keyFields)
if (this.has(this.objectAt(item, this.keyFields[k])))
return true;
return false;
},
hasMultiple: function(key) {
return this._map[key] instanceof Array;
},
//-----------------------------------
// Removal
//-----------------------------------
removeByKey: function() {
var removed = [];
for (var i = 0; i < arguments.length; i++) {
var key = arguments[i];
var items = this._map[key].concat();
if (items) {
removed = removed.concat(items);
for (var j in items) {
var item = items[j];
for (var ix in this.keyFields) {
var key2 = this.objectAt(item, this.keyFields[ix]);
if (key2 && this.has(key2)) {
var ix = this._map[key2].indexOf(item);
if (ix != -1) {
this._map[key2].splice(ix, 1);
}
if (this._map[key2].length == 0)
delete this._map[key2];
}
}
this._list.splice(this._list.indexOf(item), 1);
}
}
delete this._map[key];
}
if (this.callback) {
this.callback('removeByKey', removed);
}
return this;
},
remove: function() {
for (var i = 0; i < arguments.length; i++) {
var item = arguments[i];
for (var ix in this.keyFields) {
var key = this.objectAt(item, this.keyFields[ix]);
if (key) {
var ix = this._map[key].indexOf(item);
if (ix != -1)
this._map[key].splice(ix, 1);
else
throw new Error('HashArray: attempting to remove an object that was never added!' + key);
if (this._map[key].length == 0)
delete this._map[key];
}
}
var ix = this._list.indexOf(item);
if (ix != -1)
this._list.splice(ix, 1);
else
throw new Error('HashArray: attempting to remove an object that was never added!' + key);
}
if (this.callback) {
this.callback('remove', arguments);
}
return this;
},
removeAll: function() {
var old = this._list.concat();
this._map = {};
this._list = [];
if (this.callback) {
this.callback('remove', old);
}
return this;
},
//-----------------------------------
// Utility
//-----------------------------------
objectAt: function(obj, path) {
if (typeof path === 'string') {
return obj[path];
}
var dup = path.concat();
// else assume array.
while (dup.length && obj) {
obj = obj[dup.shift()];
}
return obj;
},
//-----------------------------------
// Iteration
//-----------------------------------
forEach: function(keys, callback) {
keys = keys instanceof Array ? keys : [keys];
var objs = this.getAll(keys);
objs.forEach(callback);
return this;
},
forEachDeep: function(keys, key, callback) {
keys = keys instanceof Array ? keys : [keys];
var self = this,
objs = this.getAll(keys);
objs.forEach(function (item) {
callback(self.objectAt(item, key), item);
});
return this;
},
//-----------------------------------
// Cloning
//-----------------------------------
clone: function(callback, ignoreItems) {
var n = new HashArray(this.keyFields.concat(), callback ? callback : this.callback);
if (!ignoreItems)
n.add.apply(n, this.all.concat());
return n;
},
//-----------------------------------
// Mathematical
//-----------------------------------
sum: function(keys, key, weightKey) {
var self = this,
ret = 0;
this.forEachDeep(keys, key, function (value, item) {
if (weightKey !== undefined)
value *= self.objectAt(item, weightKey);
ret += value;
});
return ret;
},
average: function(keys, key, weightKey) {
var ret = 0,
tot = 0,
weightsTotal = 0,
self = this;
if (weightKey !== undefined)
this.forEachDeep(keys, weightKey, function (value) {
weightsTotal += value;
})
this.forEachDeep(keys, key, function (value, item) {
if (weightKey !== undefined)
value *= (self.objectAt(item, weightKey) / weightsTotal);
ret += value;
tot++;
});
return weightKey !== undefined ? ret : ret / tot;
},
//-----------------------------------
// Filtering
//-----------------------------------
filter: function (keys, callbackOrKey) {
var self = this;
var callback = (typeof(callbackOrKey) == 'function') ? callbackOrKey : defaultCallback;
var ha = new HashArray(this.keyFields);
ha.addAll(this.getAll(keys).filter(callback));
return ha;
function defaultCallback(item) {
var val = self.objectAt(item, callbackOrKey);
return val !== undefined && val !== false;
}
}
});
//-----------------------------------
// Operators
//-----------------------------------
Object.defineProperty(HashArray.prototype, 'all', {
get: function () {
return this._list;
}
});
Object.defineProperty(HashArray.prototype, 'map', {
get: function () {
return this._map;
}
});
module.exports = HashArray;
//-----------------------------------
// Browser
//-----------------------------------
if (typeof window !== 'undefined')
window.HashArray = HashArray;