1 /*jslint 
  2 browser: true,
  3 nomen: false,
  4 debug: true,
  5 forin: true,
  6 undef: true,
  7 white: false,
  8 onevar: false 
  9  */
 10 var sc, jQuery;
 11 
 12 /**
 13  * A library to do url shortening 
 14  */
 15 
 16 /**
 17  * Constants to refer to services 
 18  */
 19 /**
 20  * @constant 
 21  */
 22 var SPAZCORE_SHORTURL_SERVICE_SHORTIE = 'short.ie';
 23 /**
 24  * @constant 
 25  */
 26 var SPAZCORE_SHORTURL_SERVICE_ISGD	  = 'is.gd';
 27 /**
 28  * @constant 
 29  */
 30 var SPAZCORE_SHORTURL_SERVICE_BITLY	  = 'bit.ly';
 31 /**
 32  * @constant 
 33  */
 34 var SPAZCORE_SHORTURL_SERVICE_JMP     = 'j.mp';
 35 
 36 /**
 37  * @constant 
 38  */
 39 var SPAZCORE_EXPANDABLE_DOMAINS = [
 40 	"bit.ly",
 41 	"cli.gs",
 42 	"digg.com",
 43 	"fb.me",
 44 	"is.gd",
 45 	"j.mp",
 46 	"kl.am",
 47 	"su.pr",
 48 	"tinyurl.com",
 49 	"goo.gl",
 50 	"307.to",
 51 	"adjix.com",
 52 	"b23.ru",
 53 	"bacn.me",
 54 	"bloat.me",
 55 	"budurl.com",
 56 	"clipurl.us",
 57 	"cort.as",
 58 	"dwarfurl.com",
 59 	"ff.im",
 60 	"fff.to",
 61 	"href.in",
 62 	"idek.net",
 63 	"korta.nu",
 64 	"lin.cr",
 65 	"livesi.de",
 66 	"ln-s.net",
 67 	"loopt.us",
 68 	"lost.in",
 69 	"memurl.com",
 70 	"merky.de",
 71 	"migre.me",
 72 	"moourl.com",
 73 	"nanourl.se",
 74 	"om.ly",
 75 	"ow.ly",
 76 	"peaurl.com",
 77 	"ping.fm",
 78 	"piurl.com",
 79 	"plurl.me",
 80 	"pnt.me",
 81 	"poprl.com",
 82 	"post.ly",
 83 	"rde.me",
 84 	"reallytinyurl.com",
 85 	"redir.ec",
 86 	"retwt.me",
 87 	"rubyurl.com",
 88 	"short.ie",
 89 	"short.to",
 90 	"smallr.com",
 91 	"sn.im",
 92 	"sn.vc",
 93 	"snipr.com",
 94 	"snipurl.com",
 95 	"snurl.com",
 96 	"tiny.cc",
 97 	"tinysong.com",
 98 	"togoto.us",
 99 	"tr.im",
100 	"tra.kz",
101 	"trg.li",
102 	"twurl.cc",
103 	"twurl.nl",
104 	"u.mavrev.com",
105 	"u.nu",
106 	"ur1.ca",
107 	"url.az",
108 	"url.ie",
109 	"urlx.ie",
110 	"w34.us",
111 	"xrl.us",
112 	"yep.it",
113 	"zi.ma",
114 	"zurl.ws",
115 	"chilp.it",
116 	"notlong.com",
117 	"qlnk.net",
118 	"trim.li",
119 	"url4.eu"
120 ];
121 
122 
123 /**
124  * events raised here 
125  */
126 if (!sc.events) { sc.events = {}; }
127 sc.events.newShortURLSuccess	= 'newShortURLSuccess';
128 sc.events.newShortURLFailure	= 'newShortURLFailure';
129 sc.events.newExpandURLSuccess   = 'recoverLongURLSuccess';
130 sc.events.newExpandURLFailure   = 'recoverLongURLFailure';
131 
132 
133 /**
134  * Constructor
135  * @param {string} service	the name of a service. Preferrably one of the SPAZCORE_SHORTURL_SERVICE_* constants
136  * @class SpazShortURL
137  * @constructor
138  */
139 function SpazShortURL(service) {
140 	
141 	this.api = this.getAPIObj(service);
142 	
143 	
144 	this.expanded_cache = {};
145 	
146 }
147 
148 SpazShortURL.prototype.getAPIObj = function(service) {
149 	
150 	var apis = {};
151 	
152 	apis[SPAZCORE_SHORTURL_SERVICE_BITLY] = {
153 		'url'	  : 'http://bit.ly/api',
154 		'getData' : function(longurl, opts){
155 			
156 			/*
157 				use the api if we're doing multiple URLs
158 			*/
159 			if (sc.helpers.isArray(longurl)) {
160 				apis[SPAZCORE_SHORTURL_SERVICE_BITLY].processing_multiple = true;
161 				apis[SPAZCORE_SHORTURL_SERVICE_BITLY].url = 'http://api.bit.ly/shorten';
162 				opts.longUrl = longurl;
163 				return opts;
164 			} else {
165 				apis[SPAZCORE_SHORTURL_SERVICE_BITLY].processing_multiple = false;
166 				return { 'url':longurl };				
167 			}
168 		},
169 		'processResult' : function(data) {
170 			if (apis[SPAZCORE_SHORTURL_SERVICE_BITLY].processing_multiple === true) {
171 				var result = sc.helpers.deJSON(data);
172 				var rs = {};
173 				for (var i in result.results) {
174 					rs[i] = result.results[i].shortUrl;
175 				}
176 				return rs;
177 			} else {
178 				return data;
179 			}
180 		}
181 		
182 	};
183 		
184 	apis[SPAZCORE_SHORTURL_SERVICE_JMP] = {
185 		'url'	  : 'http://j.mp/api',
186 		'getData' : function(longurl, opts){
187 			
188 			/*
189 				use the api if we're doing multiple URLs
190 			*/
191 			if (sc.helpers.isArray(longurl)) {
192 				apis[SPAZCORE_SHORTURL_SERVICE_JMP].processing_multiple = true;
193 				apis[SPAZCORE_SHORTURL_SERVICE_JMP].url = 'http://api.j.mp/shorten';
194 				opts.longUrl = longurl;
195 				return opts;
196 			} else {
197 				apis[SPAZCORE_SHORTURL_SERVICE_JMP].processing_multiple = false;
198 				return { 'url':longurl };				
199 			}
200 		},
201 		'processResult' : function(data) {
202 			if (apis[SPAZCORE_SHORTURL_SERVICE_JMP].processing_multiple === true) {
203 				var result = sc.helpers.deJSON(data);
204 				var rs = {};
205 				for (var i in result.results) {
206 					rs[i] = result.results[i].shortUrl;
207 				}
208 				return rs;
209 			} else {
210 				return data;
211 			}
212 		}
213 		
214 	};
215 		
216 	apis[SPAZCORE_SHORTURL_SERVICE_SHORTIE] = {
217 		'url'	  : 'http://short.ie/api?',
218 		'getData' : function(longurl, opts){
219 			
220 			if (longurl.match(/ /gi)) {
221 				longurl = longurl.replace(/ /gi, '%20');
222 			}
223 			
224 			var shortie = {
225 				orig: longurl,
226 				url:  longurl,
227 				email:	 '',
228 				'private': 'false',
229 				format:	 'rest'
230 			};
231 			return shortie;
232 		}
233 	};
234 		
235 	apis[SPAZCORE_SHORTURL_SERVICE_ISGD] = {
236 		'url'	  : 'http://is.gd/api.php',
237 		'getData' : function(longurl, opts) {
238 			return { 'longurl':longurl };
239 		}
240 	};
241 	
242 	return apis[service];
243 };
244 
245 
246 /**
247  * shortens a URL by making an ajax call
248  * @param {string} longurl
249  * @param {object} opts   right now opts.event_target (a DOMelement) and opts.apiopts (passed to api's getData() call) are supported
250  */
251 SpazShortURL.prototype.shorten = function(longurl, opts) {
252 	
253 	var shortener = this;
254 	
255 	if (!opts) { opts = {}; }
256 
257 	/*
258 		set defaults if needed
259 	*/
260 	opts.event_target = opts.event_target || document;
261 	opts.apiopts	  = opts.apiopts	  || null;
262 	
263 	/*
264 		we call getData now in case it needs to override anything
265 	*/
266 	var apidata = this.api.getData(longurl, opts.apiopts);
267 
268 	if (sc.helpers.getMojoURL) {
269 		this.api.url = sc.helpers.getMojoURL(this.api.url);
270 	}
271 		
272 
273 	var xhr = jQuery.ajax({
274 		'traditional':true, // so we don't use square brackets on arrays in data. Bit.ly doesn't like it
275 		'dataType':'text',
276 		complete:function(xhr, rstr) {
277 		},
278 		'error':function(xhr, msg, exc) {
279 			sc.helpers.dump(shortener.api.url + ' error:'+msg);
280 			
281 			var errobj = {'url':shortener.api.url, 'xhr':null, 'msg':null};
282 			
283 			if (xhr) {
284 				errobj.xhr = xhr;
285 				sc.helpers.error("Error:"+xhr.status+" from "+ shortener.api.url);
286 			} else {
287 				sc.helpers.error("Error:Unknown from "+ shortener.api.url);
288 				errobj.msg = 'Unknown Error';
289 			}
290 			shortener._onShortenResponseFailure(errobj, opts.event_target);
291 		},
292 		success:function(data) {
293 			// var shorturl = trim(data);
294 			var return_data = {};
295 			if (shortener.api.processResult) {
296 				return_data = shortener.api.processResult(data);
297 			} else {
298 				return_data = {
299 					'shorturl':data,
300 					'longurl' :longurl
301 				};
302 			}
303 			sch.error(return_data);
304 			shortener._onShortenResponseSuccess(return_data, opts.event_target);
305 		},
306 		'type':"POST",
307 		'url' :this.api.url,
308 		'data':apidata
309 	});
310 
311 };
312 
313 SpazShortURL.prototype._onShortenResponseSuccess = function(data, target) {
314 	sc.helpers.triggerCustomEvent(sc.events.newShortURLSuccess, target, data);
315 };
316 SpazShortURL.prototype._onShortenResponseFailure = function(errobj, target) {
317 	sc.helpers.triggerCustomEvent(sc.events.newShortURLFailure, target, errobj);
318 };
319 
320 /**
321  * @TODO 
322  */
323 SpazShortURL.prototype.expand = function(shorturl, opts) {
324 	
325 	var shortener = this;
326 	var longurl;
327 	
328 	if (!opts) {
329 		opts = {};
330 	}
331 	
332 	opts.event_target = opts.event_target || document;
333 	
334 	/*
335 		Do a lookup in the cache first
336 	*/
337 	if ( (longurl = this.getExpandedURLFromCache()) ) {
338 		shortener._onExpandResponseSuccess({
339 				'shorturl':shorturl,
340 				'longurl' :longurl
341 			},
342 			opts.event_target
343 		);
344 		return;
345 	}
346 	
347 	/*
348 		if not cached, do query to look it up
349 	*/
350 	var xhr = jQuery.ajax({
351     	'dataType':'text',
352 		complete:function(xhr, rstr) {
353 		},
354 		'error':function(xhr, msg, exc) {
355 			sc.helpers.dump(this.url + ' error:'+msg);
356 			
357 			var errobj = {'url':this.url, 'xhr':null, 'msg':null};
358 			
359 			if (xhr) {
360 				errobj.xhr = xhr;
361 				sc.helpers.dump("Error:"+xhr.status+" from "+ this.url);
362 			} else {
363 				sc.helpers.dump("Error:Unknown from "+ this.url);
364 				errobj.msg = 'Unknown Error';
365 			}
366 			shortener._onExpandResponseFailure(errobj, opts.event_target);
367 		},
368 		success:function(data) {
369 			// var shorturl = trim(data);
370 			data = sc.helpers.deJSON(data);
371 			var longurl = data[shorturl];
372 			
373 			/*
374 				save it to cache
375 			*/
376 			shortener.saveExpandedURLToCache(shorturl, longurl);
377 			
378 			shortener._onExpandResponseSuccess({
379 					'shorturl':shorturl,
380 					'longurl' :longurl
381 				},
382 				opts.event_target
383 			);
384 		},
385 		beforeSend:function(xhr) {},
386 		type:"GET",
387 		url :'http://longurlplease.appspot.com/api/v1.1',
388 		data:{ 'q':shorturl }
389 	});
390 };
391 
392 /**
393  * @TODO 
394  */
395 SpazShortURL.prototype._onExpandResponseSuccess = function(data, target) {
396 	sc.helpers.triggerCustomEvent(sc.events.newExpandURLSuccess, target, data);
397 };
398 
399 /**
400  * @TODO 
401  */
402 SpazShortURL.prototype._onExpandResponseFailure = function(errobj, target) {
403 	sc.helpers.triggerCustomEvent(sc.events.newExpandURLFailure, target, errobj);
404 };
405 
406 
407 SpazShortURL.prototype.findExpandableURLs = function(str) {
408 	var x, i, matches = [], re_matches, key, thisdomain, thisregex, regexes = [];
409 	
410 	for (i=0; i < SPAZCORE_EXPANDABLE_DOMAINS.length; i++) {
411 		thisdomain = SPAZCORE_EXPANDABLE_DOMAINS[i];
412 		if (thisdomain == 'ff.im') {
413 			regexes.push(new RegExp("http://"+thisdomain+"/(-?[a-zA-Z0-9]+)", "gi"));
414 		} else {
415 			regexes.push(new RegExp("http://"+thisdomain+"/([a-zA-Z0-9]+)", "gi"));
416 		}
417 		
418 	};
419 	
420 	for (i=0; i < regexes.length; i++) {
421 		thisregex = regexes[i];
422 		sch.dump("looking for "+thisregex+ " in '"+str+"'");
423 		while( (re_matches = thisregex.exec(sch.trim(str))) != null) {
424 			matches.push(re_matches[0]);
425 		}		
426 	};
427 	
428 	sch.dump(matches);
429 	
430 	if (matches.length > 0) {
431 		return matches;
432 	} else {
433 		return null;
434 	}
435 
436 };
437 
438 
439 SpazShortURL.prototype.expandURLs = function(urls, target) {
440 	for (var i=0; i < urls.length; i++) {
441 		var thisurl = urls[i];
442 		sch.dump('expanding '+thisurl);
443 		this.expand(thisurl, { 'event_target':target });
444 	};
445 };
446 
447 
448 
449 /**
450  * @param {string} str  the string to replace the URLs in
451  * @param {string} shorturl 
452  * @param {string} longurl 
453  */
454 SpazShortURL.prototype.replaceExpandableURL = function(str, shorturl, longurl) {
455 	str = str.replace(shorturl, longurl, 'gi');
456 	/*
457 		we also expand the non-http://-prefixed versions. Wonder if this is a bad idea, though -- seems
458 		possible we could have unexpected consqeuences with this
459 	*/
460 	str = str.replace(shorturl.replace('http://', ''), longurl.replace('http://', ''), 'gi');
461 	return str;
462 };
463 
464 
465 
466 SpazShortURL.prototype.getExpandedURLFromCache = function(shortURL) {
467 	return this.expanded_cache[shortURL];
468 };
469 
470 SpazShortURL.prototype.saveExpandedURLToCache  = function(shortURL, longURL) {
471 	this.expanded_cache[shortURL] = longURL;
472 };