1 /**
  2  * Util Object
  3  * 
  4  * Various object and string manipulation functions
  5  * Note: these are based on similar phpjs functions: http://phpjs.org
  6  */
  7 (function(){
  8 
  9 	"use strict";
 10 
 11 	var 
 12 	reverse_key_sort =  function(o)
 13 	{
 14 		//Define some variables
 15 		var keys = [],
 16 			num_keys = 0,
 17 			new_o = {},
 18 			i;
 19 	
 20 		//Extract the keys
 21 		keys = u.object_keys(o);
 22 		
 23 		//Sort the keys
 24 		keys.sort(function (b, a) {		
 25 			var aFloat = parseFloat(a),
 26 				bFloat = parseFloat(b),
 27 				aNumeric = aFloat + '' === a,
 28 				bNumeric = bFloat + '' === b;
 29 
 30 			if (aNumeric && bNumeric) 
 31 			{
 32 				return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0;
 33 			} 
 34 			else if (aNumeric && !bNumeric) 
 35 			{
 36 				return 1;
 37 			} 
 38 			else if (!aNumeric && bNumeric) 
 39 			{
 40 				return -1;
 41 			}
 42 			
 43 			return a > b ? 1 : a < b ? -1 : 0;
 44 		});
 45 		
 46 		// cache object/array size
 47 		num_keys = keys.length;
 48 		
 49 		// Recreate the object/array
 50 		for(i=0; i < num_keys; i++)
 51 		{
 52 			new_o[keys[i]] = o[keys[i]];
 53 		}
 54 		
 55 		return new_o;
 56 	},
 57 	
 58 	/**
 59 	 * String and object manipulation utilities
 60 	 *	
 61 	 * @namespace
 62 	 * @name util
 63 	 * @memberOf $_
 64 	 */
 65 	u = {
 66 		/**
 67 		 * Retrieve the keys, or member names of an object
 68 		 * 
 69 		 * @name object_keys
 70 		 * @memberOf $_.util
 71 		 * @function
 72 		 * @param object
 73 		 * @return array
 74 		 * @type array
 75 		 */
 76 		object_keys: function(o)
 77 		{
 78 			var keys = [],
 79 				k;
 80 				
 81 			for(k in o)
 82 			{
 83 				if(o.hasOwnProperty(k))
 84 				{
 85 					keys.push(k);
 86 				}
 87 			}
 88 			
 89 			return keys;
 90 		},
 91 		/**
 92 		 * Retrieves the values of an object, and returns
 93 		 * them as an array
 94 		 *
 95 		 * @name object_values
 96 		 * @memberOf $_.util
 97 		 * @function
 98 		 * @param object
 99 		 * @return array
100 		 * @type array
101 		 */
102 		object_values: function(o)
103 		{
104 			var vals = [],
105 				prop;
106 			
107 			for(prop in o)
108 			{
109 				vals.push(o[prop]);
110 			}
111 			
112 			return vals;
113 		},
114 		/**
115 		 * Creates an object, with the property names of the first array, 
116 		 * and the values of the second. If objects are passed, the values 
117 		 * of the object are used. If the arrays or objects passed are 
118 		 * not the same size, the function will return false.
119 		 * 
120 		 * @name array_combine
121 		 * @memberOf $_.util
122 		 * @function
123 		 * @param array/object keys
124 		 * @param array/object vals
125 		 * @return object
126 		 * @type object
127 		 */
128 		array_combine: function(keys, vals)
129 		{
130 			var new_object = {},
131 				num_keys,
132 				i = 0;
133 				
134 			// Extract the keys or values if needed
135 			if($_.type(keys) !== "array")
136 			{
137 				keys = this.object_values(keys);
138 			}
139 			if($_.type(vals) !== "array")
140 			{
141 				vals = this.object_values(vals);
142 			}
143 			
144 			// cache the number of keys
145 			num_keys = keys.length;
146 			
147 			if(num_keys !== vals.length)
148 			{
149 				console.log("Object combine requires two arrays of the same size");
150 				return false;
151 			}
152 			
153 			// Create and return the new object
154 			for(i = 0; i < num_keys; i++)
155 			{
156 				new_object[keys[i]] = vals[i];
157 			}
158 			
159 			return new_object;
160 		},
161 		/**
162 		 * Combines two or more objects/arrays. If the keys are numeric, the outputted
163 		 * object will have re-indexed keys. If a key/value pair exists in both objects,
164 		 * indentical values will be droped, but if a key exists with a different value, 
165 		 * with the same key, the value in the second array will replace the value in the
166 		 * first
167 		 * 
168 		 * @name object_merge
169 		 * @memberOf $_.util
170 		 * @function
171 		 * @param object [as many as you wish to combine]
172 		 * @type object
173 		 * @return object
174 		 */
175 		object_merge: function()
176 		{
177 			var args = Array.prototype.slice.call(arguments),
178 				arg_len = args.length,
179 				new_obj = {},
180 				arg,
181 				iarg_len = 0,
182 				i,
183 				j,
184 				x,
185 				is_array = true;
186 				
187 			// Check for an array in the arguments
188 			for(i=0; i < arg_len; i++)
189 			{
190 				if($_.type(args[i]) !== "array")
191 				{
192 					is_array = false;
193 					break;
194 				}
195 			}
196 			
197 			// If all the arguments are javascript arrays
198 			if(is_array)
199 			{
200 				new_obj = [];
201 				// Let javascript do all the work!
202 				for(i=0; i< arg_len; i++)
203 				{
204 					new_obj = new_obj.contact(args[i]);
205 				}
206 				
207 				// Return early
208 				return new_obj;
209 			}
210 			
211 			// No, there's at least one object
212 			for(i=0, x=0; i < arg_len; i++)
213 			{
214 				arg = args[i];
215 				
216 				// If the argument is an array, add the array items as
217 				// numeric object properties
218 				if ($_.type(arg) == "array")
219 				{
220 					for (j=0, iarg_len= arg.length; j < iarg_len; j++)
221 					{
222 						new_obj[x++] = arg[j];
223 					}
224 				}
225 				else
226 				{
227 					for (j in arg)
228 					{
229 						if(arg.hasOwnProperty(j))
230 						{
231 							// If the key is numeric, add the property with
232 							// a numeric key
233 							if(parseInt(j, 10) + '' === j)
234 							{
235 								new_obj[x++] = arg[j];
236 							}
237 							else
238 							{
239 								new_obj[j] = arg[j];
240 							} 
241 						}
242 					}
243 				} 
244 			}
245 			
246 			return new_obj;
247 		},
248 		/**
249 		 * Replaces sections of strings in a greedy fashion, 
250 		 * starting with the longest replace pairs first. Accepts
251 		 * one replace pair as two parameters, or an object, with
252 		 * from => to replacements as key/value pairs
253 		 *
254 		 * @name str_trans
255 		 * @memberOf $_.util
256 		 * @function
257 		 * @param string input_string
258 		 * @param mixed from (string)/replace pairs (object)
259 		 * @param [string]
260 		 * @return string
261 		 * @type string
262 		 */
263 		str_trans: function(str, from, to)
264 		{
265 			var froms = [],
266 				tos = [],
267 				ret = '',
268 				match = false,
269 				from_len = 0,
270 				str_len = 0,
271 				to_len = 0,
272 				to_is_str = '',
273 				from_is_str = '',
274 				strx = '',
275 				strw = '',
276 				stry = '',
277 				from_strx = '',
278 				new_str = '',
279 				f,
280 				i,
281 				j;
282 				
283 			//Replace pairs? add them to the internal arrays
284 			if(typeof from === 'object')
285 			{
286 				// Sort the keys in descending order for better
287 				// replacement functionality
288 				from = reverse_key_sort(from);
289 				
290 				for(f in from)
291 				{
292 					if(from.hasOwnProperty(f))
293 					{
294 						froms.push(f);
295 						tos.push(from[f]);
296 					}
297 				}
298 				
299 				from = froms;
300 				to = tos;
301 			}
302 			
303 			//Go through the string, and replace characters as needed
304 			str_len = str.length;
305 			from_len = from.length;
306 			to_len = to.length;
307 			to_is_str = typeof to === 'string';
308 			from_is_str = typeof from === 'string';
309 			
310 			for(i=0; i < str_len; i++)
311 			{
312 				match = false;
313 				if(from_is_str)
314 				{
315 					strw = str.charAt(i-1);
316 					strx = str.charAt(i);
317 					stry = str.charAt(i+1);
318 					for(j=0; j < from_len; j++)
319 					{
320 						if(strx == from.charAt(j))
321 						{
322 							match = true;
323 							break;
324 						}
325 					}
326 				}
327 				else
328 				{
329 					for(j=0; j < from_len; j++)
330 					{
331 						if(str.substr(i, from[j].length) == from[j])
332 						{
333 							match = true;
334 							
335 							//Go past the current match
336 							i = (i + from[j].length) -1;
337 							break;
338 
339 						}
340 					}
341 				}
342 
343 				if(match)
344 				{
345 					new_str += (to_is_str) ? to.charAt(j) : to[j];
346 				}
347 				else
348 				{
349 					new_str += str.charAt(i);
350 				}
351 			}
352 
353 			return new_str;
354 				
355 		}
356 		
357 	};
358 
359 	//Add it to the $_ object
360 	$_.ext('util', u);
361 }());
362 
363