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