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 };