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 				return false;
149 			}
150 
151 			// Create and return the new object
152 			for(i = 0; i < num_keys; i++)
153 			{
154 				new_object[keys[i]] = vals[i];
155 			}
156 
157 			return new_object;
158 		},
159 		/**
160 		 * Combines two or more objects/arrays. If the keys are numeric, the outputted
161 		 * object will have re-indexed keys. If a key/value pair exists in both objects,
162 		 * indentical values will be droped, but if a key exists with a different value,
163 		 * with the same key, the value in the second array will replace the value in the
164 		 * first
165 		 *
166 		 * @name object_merge
167 		 * @memberOf $_.util
168 		 * @function
169 		 * @param object [as many as you wish to combine]
170 		 * @type object
171 		 * @return object
172 		 */
173 		object_merge: function()
174 		{
175 			var args = Array.prototype.slice.call(arguments),
176 				arg_len = args.length,
177 				new_obj = {},
178 				arg,
179 				iarg_len = 0,
180 				i,
181 				j,
182 				x,
183 				is_array = true;
184 
185 			// Check for an array in the arguments
186 			for(i=0; i < arg_len; i++)
187 			{
188 				if($_.type(args[i]) !== "array")
189 				{
190 					is_array = false;
191 					break;
192 				}
193 			}
194 
195 			// If all the arguments are javascript arrays
196 			if(is_array)
197 			{
198 				new_obj = [];
199 				// Let javascript do all the work!
200 				for(i=0; i< arg_len; i++)
201 				{
202 					new_obj = new_obj.contact(args[i]);
203 				}
204 
205 				// Return early
206 				return new_obj;
207 			}
208 
209 			// No, there's at least one object
210 			for(i=0, x=0; i < arg_len; i++)
211 			{
212 				arg = args[i];
213 
214 				// If the argument is an array, add the array items as
215 				// numeric object properties
216 				if ($_.type(arg) == "array")
217 				{
218 					for (j=0, iarg_len= arg.length; j < iarg_len; j++)
219 					{
220 						new_obj[x++] = arg[j];
221 					}
222 				}
223 				else
224 				{
225 					for (j in arg)
226 					{
227 						if(arg.hasOwnProperty(j))
228 						{
229 							// If the key is numeric, add the property with
230 							// a numeric key
231 							if(parseInt(j, 10) + '' === j)
232 							{
233 								new_obj[x++] = arg[j];
234 							}
235 							else
236 							{
237 								new_obj[j] = arg[j];
238 							}
239 						}
240 					}
241 				}
242 			}
243 
244 			return new_obj;
245 		},
246 		/**
247 		 * Replaces sections of strings in a greedy fashion,
248 		 * starting with the longest replace pairs first. Accepts
249 		 * one replace pair as two parameters, or an object, with
250 		 * from => to replacements as key/value pairs
251 		 *
252 		 * @name str_trans
253 		 * @memberOf $_.util
254 		 * @function
255 		 * @param string input_string
256 		 * @param mixed from (string)/replace pairs (object)
257 		 * @param [string]
258 		 * @return string
259 		 * @type string
260 		 */
261 		str_trans: function(str, from, to)
262 		{
263 			var froms = [],
264 				tos = [],
265 				ret = '',
266 				match = false,
267 				from_len = 0,
268 				str_len = 0,
269 				to_len = 0,
270 				to_is_str = '',
271 				from_is_str = '',
272 				strx = '',
273 				strw = '',
274 				stry = '',
275 				from_strx = '',
276 				new_str = '',
277 				f,
278 				i,
279 				j;
280 
281 			//Replace pairs? add them to the internal arrays
282 			if(typeof from === 'object')
283 			{
284 				// Sort the keys in descending order for better
285 				// replacement functionality
286 				from = reverse_key_sort(from);
287 
288 				for(f in from)
289 				{
290 					if(from.hasOwnProperty(f))
291 					{
292 						froms.push(f);
293 						tos.push(from[f]);
294 					}
295 				}
296 
297 				from = froms;
298 				to = tos;
299 			}
300 
301 			//Go through the string, and replace characters as needed
302 			str_len = str.length;
303 			from_len = from.length;
304 			to_len = to.length;
305 			to_is_str = typeof to === 'string';
306 			from_is_str = typeof from === 'string';
307 
308 			for(i=0; i < str_len; i++)
309 			{
310 				match = false;
311 				if(from_is_str)
312 				{
313 					strw = str.charAt(i-1);
314 					strx = str.charAt(i);
315 					stry = str.charAt(i+1);
316 					for(j=0; j < from_len; j++)
317 					{
318 						if(strx == from.charAt(j))
319 						{
320 							match = true;
321 							break;
322 						}
323 					}
324 				}
325 				else
326 				{
327 					for(j=0; j < from_len; j++)
328 					{
329 						if(str.substr(i, from[j].length) == from[j])
330 						{
331 							match = true;
332 
333 							//Go past the current match
334 							i = (i + from[j].length) -1;
335 							break;
336 
337 						}
338 					}
339 				}
340 
341 				if(match)
342 				{
343 					new_str += (to_is_str) ? to.charAt(j) : to[j];
344 				}
345 				else
346 				{
347 					new_str += str.charAt(i);
348 				}
349 			}
350 
351 			return new_str;
352 
353 		}
354 
355 	};
356 
357 	//Add it to the $_ object
358 	$_.ext('util', u);
359 }());