1 /**
  2  * The SpazFilterChain is intended to create a chain of filters for processing some input.
  3  * There are no restrictions on the type of input, but all filter functions must expect
  4  * the same type of input, and return the same type of output
  5  * 
  6  * All filter functions must be synchronous -- they need to take input and return the
  7  * modified version
  8  * 
  9  * @constructor 
 10  */
 11 var SpazFilterChain = function (opts) {
 12 	
 13 	opts = sch.defaults({
 14 		filters:null
 15 	}, opts);
 16 
 17 	this._filters = [];
 18 	
 19 	/*
 20 		if we have filters, process them
 21 	*/
 22 	if (opts.filters) {
 23 		for (var i=0; i < opts.filters.length; i++) {
 24 			this.addFilter(opts.filters[i].label, opts.filters[i].func);
 25 		}
 26 	}
 27 };
 28 
 29 /**
 30  * add a filter to the chain
 31  * @param {string} label the label for this filter. REQUIRED
 32  * @param {function} func the filter function. REQUIRED
 33  */
 34 SpazFilterChain.prototype.addFilter = function(label, func, position) {
 35 	var filter_obj = {
 36 		'label':label,
 37 		'func':func
 38 	};
 39 	
 40 	if (position) {
 41 		this._filters.splice(position, 0, filter_obj);
 42 	} else {
 43 		this._filters.push(filter_obj);
 44 	}
 45 	
 46 	sch.debug('added filter "'+label+'"');
 47 };
 48 
 49 /**
 50  * remove a filter from the chain 
 51  */
 52 SpazFilterChain.prototype.removeFilter = function(label) {
 53 	
 54 	var i = this.getFilterIndex(label);
 55 	var removed = this._filters.splice(i,1);
 56 	sch.debug('removed filter "'+label+'": '+removed);
 57 };
 58 
 59 
 60 /**
 61  * removes all filters in the chain 
 62  */
 63 SpazFilterChain.prototype.nukeFilters = function() {
 64 	this._filters = [];
 65 	sch.debug("filters nuked");
 66 };
 67 
 68 
 69 /**
 70  * move the identified filter to the front of the chain
 71  * @param {string} label the filter's label
 72  */
 73 SpazFilterChain.prototype.makeFilterFirst = function(label) {
 74 	var i = this.getFilterIndex(label);
 75 	if (i !== 0) { // don't move it if it's already there
 76 		var removed = this._filters.splice(i,1);
 77 		this._filters.unshift(removed);
 78 	}
 79 };
 80 
 81 
 82 /**
 83  * takes a filter label and moves that filter to last in the chain
 84  * @param {string} label the label for a filter in the chain 
 85  */
 86 SpazFilterChain.prototype.makeFilterLast = function(label) {
 87 	var i = this.getFilterIndex(label);
 88 	if (i !== (this._filters.langth - 1)) { // don't move it if it's already there
 89 		var removed = this._filters.splice(i,1);
 90 		this._filters.push(removed);
 91 	}
 92 };
 93 
 94 
 95 /**
 96  * Returns an array of all the labels of filters in the chain
 97  * @returns {array} 
 98  */
 99 SpazFilterChain.prototype.getFilterList = function() {
100 	var filter_list = [];
101 	for (var i=0; i < this._filters.length; i++) {
102 		filter_list.push(this._filters[i].label);
103 	}
104 	return filter_list;
105 };
106 
107 
108 /**
109  * takes input and processes it through each filter in the chain, returning the final result
110  * @param {Mixed} input The input
111  * @returns {Mixed} the output 
112  */
113 SpazFilterChain.prototype.process = function(input) {
114 	var filter_obj;
115 	for (var i=0; i < this._filters.length; i++) {
116 		filter_obj = this._filters[i];
117 		sch.debug('Calling filter '+filter_obj.label);
118 		input = filter_obj.func(input);
119 	}
120 	return input;
121 };
122 
123 /**
124  * like process, but takes an array and processes each item through the filter chain
125  * @param {Array} input_array the input array
126  * @returns {Array} the processed array
127  */
128 SpazFilterChain.prototype.processArray = function(input_array) {
129 	var filter_obj;
130 	
131 	for (var i=0; i < input_array.length; i++) {
132 		for (var k=0; k < this._filters.length; k++) {
133 			filter_obj = this._filters[k];
134 			sch.debug('Calling filter '+filter_obj.label);
135 			input_array[i] = filter_obj.func(input_array[i]);
136 			if (input_array[i] === null) {
137 				break;
138 			}
139 		}
140 	}
141 	
142 	// remove stuff set to null, so we can use filters that remove items by returning null;
143 	input_array = _.compact(input_array);
144 	return input_array;
145 };
146 
147 /**
148  * find the array index of a given filter
149  * @param {string} label the label for a filter in the chain
150  * @returns {Number|Boolean} the position of the filter, or FALSE if not found 
151  */
152 SpazFilterChain.prototype.getFilterIndex = function(label) {
153 	for (var i=0; i < this._filters.length; i++) {
154 		if (this._filters[i].label === label) {
155 			return i;
156 		}
157 	}
158 	return false;
159 };