1 /*jslint 
  2 bitwise: false,
  3 browser: true,
  4 newcap: false,
  5 nomen: false,
  6 debug: true,
  7 forin: true,
  8 plusplus: false,
  9 undef: true,
 10 white: false,
 11 onevar: false 
 12  */
 13 var sc;
 14  
 15 /**
 16  * determines if a string contains the given screen name prefixed with a @
 17  * this is mainly used for determining if a message should be considered a 'mention'
 18  * @param {string} str  the string to check
 19  * @param {string} sn   the screen name to look for
 20  * @return {boolean} 
 21  * @member sc.helpers
 22 
*/
 23 sc.helpers.containsScreenName = function(str, sn) {
 24 	
 25 	var re = new RegExp('(?:\\s|\\b|^[[:alnum:]]|^)@('+sn+')(?:\\s|\\b|$)', 'gi');
 26 	if ( re.test(str) ) {
 27 		return true;
 28 	}
 29 	return false;
 30 	
 31 };
 32 
 33 sc.helpers.extractScreenNames = function(str, tpl) {
 34     // var re_uname = /(^|\s|\(\[|,|\.|\()@([a-zA-Z0-9_]+)([^a-zA-Z0-9_]|$)/gi;
 35 	var re_uname = /(?:^|\s|\(\[|,|\.|\()@([a-zA-Z0-9_]+)/gi;
 36 	var usernames = [];
 37 	var ms = [];
 38 	while (ms = re_uname.exec(str))
 39 	{
 40 		/*
 41 			sometimes we can end up with a null instead of a blank string,
 42 			so we need to force the issue in javascript.
 43 		*/
 44 		for (var x=0; x<ms.length; x++) {
 45 			if (!ms[x]) {
 46 				ms[x] = '';
 47 			}
 48 		}
 49 		
 50 		if(ms[1] != ''){
 51 			usernames.push(ms[1]);
 52 		}
 53 	}
 54 	return usernames;
 55 };
 56 
 57 /**
 58  * find URLs within the given string 
 59  * @member sc.helpers
 60 
*/
 61 sc.helpers.extractURLs = function(str) {
 62 	// var wwwlinks = /(^|\s)((https?|ftp)\:\/\/)?([a-z0-9+!*(),;?&=\$_.-]+(\:[a-z0-9+!*(),;?&=\$_.-]+)?@)?([✪a-z0-9-.]*)\.([a-z]{2,3})(\:[0-9]{2,5})?(\/([a-z0-9+\$_-]\.?)+)*\/?(\?[a-z+&\$_.-][a-z0-9;:@&%=+\/\$_.-]*)?(#[a-z_.-][a-z0-9+\$_.-]*)?(\s|$)/gi;
 63 	var wwwlinks = /(^|\s|\(|:)(((http(s?):\/\/)|(www\.))([\w✪]+[^\s\)<]+))/gi;
 64 		
 65 	var ms = [];
 66 	var URLs = [];
 67 	while ( (ms = wwwlinks.exec(str)) !== null ) {
 68 		for (var x=0; x<ms.length; x++) {
 69 			if (!ms[x]) {
 70 				ms[x] = '';
 71 			}
 72 		}
 73 		var last = ms[7].charAt(ms[7].length - 1);
 74 		if (last.search(/[\.,;\?]/) !== -1) { // if ends in common punctuation, strip
 75 			ms[7] = ms[7].slice(0,-1);
 76 		}
 77 		URLs.push(ms[3]+ms[7]);
 78 	}
 79 	return URLs;
 80 };
 81 
 82 /**
 83  * given as string and a mapping object, replace multiple values in the string (or vice versa)
 84  * map should be of format
 85  * {
 86  * 	'searchforme':'replacewithme',
 87  * 	'searchforme2':'replacewithme2',
 88  * 	'searchforme3':'replacewithme3'
 89  * }
 90  * @param {string} str
 91  * @param {object} map
 92  * @return {string}
 93  * @member sc.helpers
 94 
*/
 95 sc.helpers.replaceMultiple = function(str, map) {
 96 	for (var key in map) {
 97 		str = str.replace(key, map[key]);
 98 	}
 99 	return str;
100 };
101 
102 
103 /**
104  * This is a port of the CodeIgniter helper "autolink" to javascript.
105  * It finds and links both web addresses and email addresses. It will ignore
106  * links within HTML (as attributes or between tag pairs)
107  * 
108  * @param {string} str
109  * @param {string} type  'email', 'url', or 'both' (default is 'both')
110  * @param {boolean} extra_code  a string that will be inserted verbatim into <a> tag
111  * @param {integer} maxlen  the maximum length the link description can be (the string inside the <a></a> tag)
112  * @return {string}
113  * @member sc.helpers
114 
*/
115 sc.helpers.autolink = function(str, type, extra_code, maxlen) {
116 	if (!type) {
117 		type = 'both';
118 	}
119 
120 	var re_nohttpurl = /((^|\s)(www\.)?([a-zA-Z_\-]+\.)(com|net|org|uk)($|\s))/gi;
121 
122 	var re_noemail = /(^|[\s\(:。])((http(s?):\/\/)|(www\.))([\w✪]+[^\s\)<]+)/gi;
123 	var re_nourl   = /(^|\s|\()([a-zA-Z0-9_\.\-\+]+)@([a-zA-Z0-9\-]+)\.([a-zA-Z0-9\-\.]*)([^\s\)<]+)/gi;
124 	
125 	var x, ms, period = '';
126 
127 	if (type !== 'email')
128 	{	
129 		while ((ms = re_nohttpurl.exec(str))) { // look for URLs without a preceding "http://"
130 			/*
131 				sometimes we can end up with a null instead of a blank string,
132 				so we need to force the issue in javascript.
133 			*/
134 			for (x=0; x<ms.length; x++) {
135 				if (!ms[x]) {
136 					ms[x] = '';
137 				}
138 			}
139 
140 			if (extra_code) {
141 				extra_code = ' '+extra_code;
142 			} else {
143 				extra_code = '';
144 			}
145 			
146 			var desc = ms[3]+ms[4]+ms[5];
147 
148 			if (maxlen && maxlen > 0 && desc.length > maxlen) {
149 				desc = desc.substr(0, maxlen)+'...';
150 			}
151 
152 			var newstr = ms[2]+'<a href="http://'+ms[3]+ms[4]+ms[5]+'"'+extra_code+'>'+desc+'</a>'+ms[6];
153 			sch.error(newstr);
154 			str = str.replace(ms[0], newstr);
155 		}
156 		
157 		
158 		while ((ms = re_noemail.exec(str))) {
159 			
160 			/*
161 				sometimes we can end up with a null instead of a blank string,
162 				so we need to force the issue in javascript.
163 			*/
164 			for (x=0; x<ms.length; x++) {
165 				if (!ms[x]) {
166 					ms[x] = '';
167 				}
168 			}
169 
170 			if (extra_code) {
171 				extra_code = ' '+extra_code;
172 			} else {
173 				extra_code = '';
174 			}
175 			
176 			/*
177 				if the last character is one of . , ; ?, we strip it off and
178 				stick it on the end of newstr below as "period"
179 			*/
180 			var last = ms[6].charAt(ms[6].length - 1);
181 			if (last.search(/[\.,;\?]/) !== -1) {
182 				ms[6] = ms[6].slice(0,-1);
183 				period = last;
184 			}
185 
186 
187 			var desc = ms[5]+ms[6];
188 
189 			if (maxlen && maxlen > 0 && desc.length > maxlen) {
190 				desc = desc.substr(0, maxlen)+'...';
191 			}
192 			
193 			
194 			var newstr = ms[1]+'<a href="http'+ms[4]+'://'+ms[5]+ms[6]+'"'+extra_code+'>'+desc+'</a>'+period;
195 			str = str.replace(ms[0], newstr);
196 		}
197 	}
198 
199 	if (type !== 'url')
200 	{
201 		while ((ms = re_nourl.exec(str)))
202 		{
203 			period = '';
204 			if ( /\./.test(ms[5]) ) {
205 				period = '.';
206 				ms[5] = ms[5].slice(0, -1);
207 			}
208 			
209 			/*
210 				sometimes we can end up with a null instead of a blank string,
211 				so we need to force the issue in javascript.
212 			*/
213 			for (x=0; x<ms.length; x++) {
214 				if (!ms[x]) {
215 					ms[x] = '';
216 				}
217 			}
218 			str = str.replace(ms[0], ms[1]+'<a href="mailto:'+ms[2]+'@'+ms[3]+'.'+ms[4]+ms[5]+'">'+ms[2]+'@'+ms[3]+'.'+ms[4]+ms[5]+'</a>'+period);
219 		}
220 	}
221 
222 	return str;
223 
224 
225 };
226 
227 /**
228  * turns twitter style username refs ('@username') into links
229  * by default, the template used is <a href="http://twitter.com/#username#">@#username#<a/>
230  * pass the second param to give it a custom template
231  * 
232  * @param {string} str
233  * @param {string} tpl  default is '<a href="http://twitter.com/#username#">@#username#</a>'
234  * @return {string}
235  * @member sc.helpers
236 
*/
237 sc.helpers.autolinkTwitterScreenname = function(str, tpl) {
238 	if (!tpl) {
239 		tpl = '<a href="http://twitter.com/#username#">@#username#</a>';
240 	}
241 	
242 	var re_uname = /(^|\s|\(\[|,|\.|\()@([a-zA-Z0-9_]+)([^a-zA-Z0-9_]|$)/gi;
243 	
244 	var ms = [];
245 	while (ms = re_uname.exec(str))
246 	{
247 		
248 		/*
249 			sometimes we can end up with a null instead of a blank string,
250 			so we need to force the issue in javascript.
251 		*/
252 		for (var x=0; x<ms.length; x++) {
253 			if (!ms[x]) {
254 				ms[x] = '';
255 			}
256 		}
257 		
258 		var repl_tpl = tpl.replace(/#username#/gi, ms[2]);
259 		str = str.replace(ms[0], ms[1]+repl_tpl+ms[3]);
260 
261 	}
262 	return str;
263 };
264 
265 
266 
267 /**
268  * turns twitter style hashtags ('#hashtag') into links
269  * by default, the template used is <a href="http://search.twitter.com/search?q=#hashtag_enc#">##hashtag#<a/>
270  * pass the second param to give it a custom template
271  * 
272  * @param {string} str
273  * @param {string} tpl  default is '<a href="http://search.twitter.com/search?q=#hashtag_enc#">##hashtag#<a/>'
274  * @return {string}
275  * @member sc.helpers
276 
*/
277 sc.helpers.autolinkTwitterHashtag = function(str, tpl) {
278 	if (!tpl) {
279 		tpl = '<a href="http://search.twitter.com/search?q=#hashtag_enc#">##hashtag#</a>';
280 	}
281 	
282 	var re_hashtag = /(^|\s|\()#([a-zA-Z0-9\-_\.+:=]{1,}\w)([^a-zA-Z0-9\-_+]|$)/gi;
283 	
284 	var ms = [];
285 	while (ms = re_hashtag.exec(str))
286 	{
287 		
288 		/*
289 			sometimes we can end up with a null instead of a blank string,
290 			so we need to force the issue in javascript.
291 		*/
292 		for (var x=0; x<ms.length; x++) {
293 			if (!ms[x]) {
294 				ms[x] = '';
295 			}
296 		}
297 		
298 		var repl_tpl = tpl.replace(/#hashtag#/gi, ms[2]);
299 		repl_tpl = repl_tpl.replace(/#hashtag_enc#/gi, encodeURIComponent(ms[2]));
300 		str = str.replace(ms[0], ms[1]+repl_tpl+ms[3]);
301 
302 	}
303 	return str;
304 };
305 
306 
307 
308 /**
309  * Applies autolink, autolinkTwitterScreenname, autolinkTwitterHashtag
310  * 
311  * @param {string} str
312  * @param {oobject} opts
313  * 
314  * Opts structure:
315  *  {
316  *  	'autolink': {
317  *  		'type'      :'both', (email, url, or both)
318  *  		'extra_code':'',
319  *  		'maxlen'    :20
320  *  	},
321  *  	'screenname': {
322  *  		'tpl':'' // should contain macro '#username#'
323  *  	},
324  *  	'hashtag': {
325  *  		'tpl':'' // should contain macros '#hashtag#' and '#hashtag_enc#'
326  *  	}
327  *  }
328  * @member sc.helpers
329 
*/
330 sc.helpers.makeClickable = function(str, opts) {
331 	var autolink_type, autolink_extra_code, autolink_maxlen, screenname_tpl, hashtag_tpl;
332 	
333 	if (!opts) {
334 		opts = {};
335 	}
336 	
337 	if (opts.autolink) {
338 		var autolink_type       = opts.autolink.type || null;
339 		var autolink_extra_code = opts.autolink.extra_code || null;
340 		var autolink_maxlen     = opts.autolink.maxlen || null;
341 	}
342 	
343 	if (opts.screenname) {
344 		var screenname_tpl      = opts.screenname.tpl || null;
345 	}
346 	
347 	if (opts.hashtag) {
348 		var hashtag_tpl         = opts.hashtag.tpl || null;
349 	}
350 	
351 	str = sc.helpers.autolink(str, autolink_type, autolink_extra_code, autolink_maxlen);
352 	str = sc.helpers.autolinkTwitterScreenname(str, screenname_tpl);
353 	str = sc.helpers.autolinkTwitterHashtag(str, hashtag_tpl);
354 	return str;
355 };
356 
357 
358 
359 /**
360  * Simple html tag remover
361  * @param {string} str
362  * @return {string}
363  * @member sc.helpers
364 
*/
365 sc.helpers.stripTags = function(str) {
366 	var re = /<[^>]*>/gim;
367 	str = str.replace(re, '');
368 	return str;
369 };
370 
371 
372 /**
373  * Converts the following entities into regular chars: < > " '
374  * @member sc.helpers
375 
*/
376 sc.helpers.fromHTMLSpecialChars = function(str) {
377 	str = str.replace(/</gi, '<');
378 	sc.helpers.dump(str);
379 	str = str.replace(/>/gi, '>');
380 	sc.helpers.dump(str);
381 	str = str.replace(/"/gi, '"');
382 	sc.helpers.dump(str);
383 	str = str.replace(/'/gi, '\'');
384 	sc.helpers.dump(str);
385 	str = str.replace(/&/gi, '&');
386 	sc.helpers.dump(str);
387 	return str;
388 };
389 
390 
391 sc.helpers.escape_html = function(string) {
392 	return sc.helpers.htmlspecialchars(string, 'ENT_QUOTES');
393 };
394 
395 
396 sc.helpers.htmlspecialchars = function(string, quote_style) {
397     // http://kevin.vanzonneveld.net
398     // +   original by: Mirek Slugen
399     // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
400     // +   bugfixed by: Nathan
401     // +   bugfixed by: Arno
402     // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
403     // -    depends on: get_html_translation_table
404     // *     example 1: htmlspecialchars("<a href='test'>Test</a>", 'ENT_QUOTES');
405     // *     returns 1: '<a href='test'>Test</a>'
406 
407     var histogram = {}, symbol = '', tmp_str = '', i = 0;
408     tmp_str = string.toString();
409 
410     if (false === (histogram = sc.helpers._get_html_translation_table('HTML_SPECIALCHARS', quote_style))) {
411         return false;
412     }
413 
414 	// first, do &
415 	tmp_str = tmp_str.split('&').join(histogram['&']);
416 	
417 	// then do the rest
418     for (symbol in histogram) {
419 		if (symbol != '&') {
420 			entity = histogram[symbol];
421 	        tmp_str = tmp_str.split(symbol).join(entity);
422 		}
423     }
424 
425     return tmp_str;
426 };
427 
428 
429 
430 sc.helpers.htmlentities = function(string, quote_style) {
431     // http://kevin.vanzonneveld.net
432     // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
433     // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
434     // +   improved by: nobbler
435     // +    tweaked by: Jack
436     // +   bugfixed by: Onno Marsman
437     // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
438     // -    depends on: get_html_translation_table
439     // *     example 1: htmlentities('Kevin & van Zonneveld');
440     // *     returns 1: 'Kevin & van Zonneveld'
441     // *     example 2: htmlentities("foo'bar","ENT_QUOTES");
442     // *     returns 2: 'foo'bar'
443  
444     var histogram = {}, symbol = '', tmp_str = '', entity = '';
445     tmp_str = string.toString();
446     
447     if (false === (histogram = sc.helpers._get_html_translation_table('HTML_ENTITIES', quote_style))) {
448         return false;
449     }
450     
451     for (symbol in histogram) {
452         entity = histogram[symbol];
453         tmp_str = tmp_str.split(symbol).join(entity);
454     }
455     
456     return tmp_str;
457 };
458 
459 sc.helpers._get_html_translation_table = function(table, quote_style) {
460     // http://kevin.vanzonneveld.net
461     // +   original by: Philip Peterson
462     // +    revised by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
463     // +   bugfixed by: noname
464     // +   bugfixed by: Alex
465     // +   bugfixed by: Marco
466     // +   bugfixed by: madipta
467     // %          note: It has been decided that we're not going to add global
468     // %          note: dependencies to php.js. Meaning the constants are not
469     // %          note: real constants, but strings instead. integers are also supported if someone
470     // %          note: chooses to create the constants themselves.
471     // %          note: Table from http://www.the-art-of-web.com/html/character-codes/
472     // *     example 1: get_html_translation_table('HTML_SPECIALCHARS');
473     // *     returns 1: {'"': '"', '&': '&', '<': '<', '>': '>'}
474     
475     var entities = {}, histogram = {}, decimal = 0, symbol = '';
476     var constMappingTable = {}, constMappingQuoteStyle = {};
477     var useTable = {}, useQuoteStyle = {};
478     
479     useTable      = (table ? table.toUpperCase() : 'HTML_SPECIALCHARS');
480     useQuoteStyle = (quote_style ? quote_style.toUpperCase() : 'ENT_COMPAT');
481     
482     // Translate arguments
483     constMappingTable[0]      = 'HTML_SPECIALCHARS';
484     constMappingTable[1]      = 'HTML_ENTITIES';
485     constMappingQuoteStyle[0] = 'ENT_NOQUOTES';
486     constMappingQuoteStyle[2] = 'ENT_COMPAT';
487     constMappingQuoteStyle[3] = 'ENT_QUOTES';
488     
489     // Map numbers to strings for compatibilty with PHP constants
490     if (!isNaN(useTable)) {
491         useTable = constMappingTable[useTable];
492     }
493     if (!isNaN(useQuoteStyle)) {
494         useQuoteStyle = constMappingQuoteStyle[useQuoteStyle];
495     }
496  
497     if (useTable === 'HTML_SPECIALCHARS') {
498         // ascii decimals for better compatibility
499         entities['38'] = '&';
500         if (useQuoteStyle !== 'ENT_NOQUOTES') {
501             entities['34'] = '"';
502         }
503         if (useQuoteStyle === 'ENT_QUOTES') {
504             entities['39'] = ''';
505         }
506         entities['60'] = '<';
507         entities['62'] = '>';
508     } else if (useTable === 'HTML_ENTITIES') {
509         // ascii decimals for better compatibility
510       entities['38']  = '&';
511         if (useQuoteStyle !== 'ENT_NOQUOTES') {
512             entities['34'] = '"';
513         }
514         if (useQuoteStyle === 'ENT_QUOTES') {
515             entities['39'] = ''';
516         }
517       entities['60']  = '<';
518       entities['62']  = '>';
519       entities['160'] = ' ';
520       entities['161'] = '¡';
521       entities['162'] = '¢';
522       entities['163'] = '£';
523       entities['164'] = '¤';
524       entities['165'] = '¥';
525       entities['166'] = '¦';
526       entities['167'] = '§';
527       entities['168'] = '¨';
528       entities['169'] = '©';
529       entities['170'] = 'ª';
530       entities['171'] = '«';
531       entities['172'] = '¬';
532       entities['173'] = '­';
533       entities['174'] = '®';
534       entities['175'] = '¯';
535       entities['176'] = '°';
536       entities['177'] = '±';
537       entities['178'] = '²';
538       entities['179'] = '³';
539       entities['180'] = '´';
540       entities['181'] = 'µ';
541       entities['182'] = '¶';
542       entities['183'] = '·';
543       entities['184'] = '¸';
544       entities['185'] = '¹';
545       entities['186'] = 'º';
546       entities['187'] = '»';
547       entities['188'] = '¼';
548       entities['189'] = '½';
549       entities['190'] = '¾';
550       entities['191'] = '¿';
551       entities['192'] = 'À';
552       entities['193'] = 'Á';
553       entities['194'] = 'Â';
554       entities['195'] = 'Ã';
555       entities['196'] = 'Ä';
556       entities['197'] = 'Å';
557       entities['198'] = 'Æ';
558       entities['199'] = 'Ç';
559       entities['200'] = 'È';
560       entities['201'] = 'É';
561       entities['202'] = 'Ê';
562       entities['203'] = 'Ë';
563       entities['204'] = 'Ì';
564       entities['205'] = 'Í';
565       entities['206'] = 'Î';
566       entities['207'] = 'Ï';
567       entities['208'] = 'Ð';
568       entities['209'] = 'Ñ';
569       entities['210'] = 'Ò';
570       entities['211'] = 'Ó';
571       entities['212'] = 'Ô';
572       entities['213'] = 'Õ';
573       entities['214'] = 'Ö';
574       entities['215'] = '×';
575       entities['216'] = 'Ø';
576       entities['217'] = 'Ù';
577       entities['218'] = 'Ú';
578       entities['219'] = 'Û';
579       entities['220'] = 'Ü';
580       entities['221'] = 'Ý';
581       entities['222'] = 'Þ';
582       entities['223'] = 'ß';
583       entities['224'] = 'à';
584       entities['225'] = 'á';
585       entities['226'] = 'â';
586       entities['227'] = 'ã';
587       entities['228'] = 'ä';
588       entities['229'] = 'å';
589       entities['230'] = 'æ';
590       entities['231'] = 'ç';
591       entities['232'] = 'è';
592       entities['233'] = 'é';
593       entities['234'] = 'ê';
594       entities['235'] = 'ë';
595       entities['236'] = 'ì';
596       entities['237'] = 'í';
597       entities['238'] = 'î';
598       entities['239'] = 'ï';
599       entities['240'] = 'ð';
600       entities['241'] = 'ñ';
601       entities['242'] = 'ò';
602       entities['243'] = 'ó';
603       entities['244'] = 'ô';
604       entities['245'] = 'õ';
605       entities['246'] = 'ö';
606       entities['247'] = '÷';
607       entities['248'] = 'ø';
608       entities['249'] = 'ù';
609       entities['250'] = 'ú';
610       entities['251'] = 'û';
611       entities['252'] = 'ü';
612       entities['253'] = 'ý';
613       entities['254'] = 'þ';
614       entities['255'] = 'ÿ';
615     } else {
616         throw Error("Table: "+useTable+' not supported');
617     }
618     
619     // ascii decimals to real symbols
620     for (decimal in entities) {
621         symbol = String.fromCharCode(decimal);
622         histogram[symbol] = entities[decimal];
623     }
624     
625     return histogram;
626 };
627 
628 
629 
630 
631 /**
632 *
633 *  UTF-8 data encode / decode
634 *  http://www.webtoolkit.info/
635 *  @namespace
636 **/
637 sc.helpers.Utf8 = {
638  
639 	/** @function public method for url encoding */
640 	encode : function (string) {
641 		string = string.replace(/\r\n/g,"\n");
642 		var utftext = "";
643  
644 		for (var n = 0; n < string.length; n++) {
645  
646 			var c = string.charCodeAt(n);
647  
648 			if (c < 128) {
649 				utftext += String.fromCharCode(c);
650 			}
651 			else if((c > 127) && (c < 2048)) {
652 				utftext += String.fromCharCode((c >> 6) | 192);
653 				utftext += String.fromCharCode((c & 63) | 128);
654 			}
655 			else {
656 				utftext += String.fromCharCode((c >> 12) | 224);
657 				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
658 				utftext += String.fromCharCode((c & 63) | 128);
659 			}
660  
661 		}
662  
663 		return utftext;
664 	},
665  
666 	/** @function public method for url decoding */
667 	decode : function (utftext) {
668 		var string = "";
669 		var i = 0;
670 		var c = 0, c1 = 0, c2 = 0, c3 = 0;
671  
672 		while ( i < utftext.length ) {
673  
674 			c = utftext.charCodeAt(i);
675  
676 			if (c < 128) {
677 				string += String.fromCharCode(c);
678 				i++;
679 			}
680 			else if((c > 191) && (c < 224)) {
681 				c2 = utftext.charCodeAt(i+1);
682 				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
683 				i += 2;
684 			}
685 			else {
686 				c2 = utftext.charCodeAt(i+1);
687 				c3 = utftext.charCodeAt(i+2);
688 				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
689 				i += 3;
690 			}
691  
692 		}
693  
694 		return string;
695 	}
696  
697 };
698 
699 
700 
701 
702 
703 /**
704 *
705 *  Javascript trim, ltrim, rtrim
706 *  http://www.webtoolkit.info/
707 *
708 **/
709  
710 sc.helpers.trim = function (str, chars) {
711 	return sc.helpers.ltrim(sc.helpers.rtrim(str, chars), chars);
712 };
713  
714 sc.helpers.ltrim = function (str, chars) {
715 	chars = chars || "\\s";
716 	return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
717 };
718  
719 sc.helpers.rtrim = function (str, chars) {
720 	chars = chars || "\\s";
721 	return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
722 };
723 
724 
725 /**
726  * @param {string} input the input string
727  * @param {number} pad_length the length to pad the string
728  * @param {string} pad_string the string to pad with
729  * @param {string} pad_type STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH. Default is STR_PAD_RIGHT 
730  * @member sc.helpers
731 
*/
732 sc.helpers.pad = function (input, pad_length, pad_string, pad_type) {
733     // http://kevin.vanzonneveld.net
734     // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
735     // + namespaced by: Michael White (http://getsprink.com)
736     // +      input by: Marco van Oort
737     // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
738     // *     example 1: str_pad('Kevin van Zonneveld', 30, '-=', 'STR_PAD_LEFT');
739     // *     returns 1: '-=-=-=-=-=-Kevin van Zonneveld'
740     // *     example 2: str_pad('Kevin van Zonneveld', 30, '-', 'STR_PAD_BOTH');
741     // *     returns 2: '------Kevin van Zonneveld-----'
742 
743     var half = '', pad_to_go;
744 
745     var str_pad_repeater = function (s, len) {
746         var collect = '', i;
747 
748         while (collect.length < len) {collect += s;}
749         collect = collect.substr(0,len);
750 
751         return collect;
752     };
753 
754     input += '';
755     pad_string = pad_string !== undefined ? pad_string : ' ';
756     
757     if (pad_type != 'STR_PAD_LEFT' && pad_type != 'STR_PAD_RIGHT' && pad_type != 'STR_PAD_BOTH') { pad_type = 'STR_PAD_RIGHT'; }
758     if ((pad_to_go = pad_length - input.length) > 0) {
759         if (pad_type == 'STR_PAD_LEFT') { input = str_pad_repeater(pad_string, pad_to_go) + input; }
760         else if (pad_type == 'STR_PAD_RIGHT') { input = input + str_pad_repeater(pad_string, pad_to_go); }
761         else if (pad_type == 'STR_PAD_BOTH') {
762             half = str_pad_repeater(pad_string, Math.ceil(pad_to_go/2));
763             input = half + input + half;
764             input = input.substr(0, pad_length);
765         }
766     }
767 
768     return input;
769 };
770 
771 
772 /**
773  * @param {string} str the string in which we're converting linebreaks
774  * @param {string} [breaktag] the tag used to break up lines. defaults to <br>
775  * @returns {string} the string with linebreaks converted to breaktags
776  * @member sc.helpers
777 
*/
778 sc.helpers.nl2br = function(str, breaktag) {
779 	
780 	breaktag = breaktag || '<br>';
781 	
782 	str = str.replace(/(\r\n|\n\r|\r|\n)/g, breaktag+'$1');
783 	return str;
784 };