1 /*
  2  * 	Copyright (C) 2012-2013 DFKI GmbH
  3  * 	Deutsches Forschungszentrum fuer Kuenstliche Intelligenz
  4  * 	German Research Center for Artificial Intelligence
  5  * 	http://www.dfki.de
  6  * 
  7  * 	Permission is hereby granted, free of charge, to any person obtaining a 
  8  * 	copy of this software and associated documentation files (the 
  9  * 	"Software"), to deal in the Software without restriction, including 
 10  * 	without limitation the rights to use, copy, modify, merge, publish, 
 11  * 	distribute, sublicense, and/or sell copies of the Software, and to 
 12  * 	permit persons to whom the Software is furnished to do so, subject to 
 13  * 	the following conditions:
 14  * 
 15  * 	The above copyright notice and this permission notice shall be included 
 16  * 	in all copies or substantial portions of the Software.
 17  * 
 18  * 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
 19  * 	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
 20  * 	MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
 21  * 	IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 
 22  * 	CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 23  * 	TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
 24  * 	SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 25  */
 26 
 27 
 28 /*
 29  * JavaScript extensions for String type
 30  *
 31  */
 32 define (function () {
 33 	if( !String.prototype.startsWith ){
 34 		/**
 35 		 * Check if the String starts with token
 36 		 * @function
 37 		 */
 38 		String.prototype.startsWith = function (token, ignoreCase) {
 39 		    /// <summary>
 40 		    /// Check if the String starts with token
 41 		    /// </summary>
 42 		    /// <param name="token" type="String">
 43 		    ///     the token to check for
 44 		    /// </param>
 45 		    /// <param name="ignoreCase" type="Boolean">
 46 		    ///     (optional) if <code>true</code> checking will ignore case of characters.
 47 			///		Default is <code>false</code>.
 48 		    /// </param>
 49 		    /// <returns type="Boolean">
 50 		    ///     true, if the String starts with token, otherwise false.
 51 		    /// </returns>
 52 		    var str, isStarting = false;
 53 		    var isIgnoreCase = typeof ignoreCase !== 'undefined' && ignoreCase == true;
 54 		    if(isIgnoreCase){
 55 		    	token = token.toLowerCase();
 56 		    	str = this.toLowerCase();
 57 		    }
 58 		    else {
 59 		    	str = this;
 60 		    }
 61 		    
 62 		    isStarting = str.lastIndexOf(token, 0) === 0;
 63 
 64 		    return isStarting;
 65 		};
 66 		}//END: if( !String.prototype.startsWith
 67 		else {
 68 			
 69 			//if it already exists, then it is probably startsWith(str [, position])
 70 			String.prototype.startsWithOrig = String.prototype.startsWith;
 71 			
 72 			//"map" startsWith(str,number) to startsWith(str,boolean):
 73 			String.prototype.startsWith = function (token, startIndex, ignoreCase) {
 74 				if(typeof ignoreCase === 'undefined' && typeof startIndex === 'boolean' || startIndex === 'true'){
 75 					ignoreCase = startIndex;
 76 					startIndex = null;
 77 				}
 78 				var isIgnoreCase = typeof ignoreCase !== 'undefined' && ignoreCase == true;
 79 				
 80 				var str = this.toString(), other = token.toString();
 81 				if(isIgnoreCase){
 82 					str = str.toLowerCase();
 83 					other = other.toLowerCase();
 84 				}
 85 				
 86 				if(startIndex){
 87 					return str.startsWithOrig(other, startIndex);
 88 				}
 89 				else {
 90 					return str.startsWithOrig(other);
 91 				}
 92 			};
 93 		}
 94 
 95 
 96 
 97 		if( !String.prototype.endsWith ){
 98 		/**
 99 		 * Check if the String ends with token
100 		 * @function
101 		 */
102 		String.prototype.endsWith = function (token, ignoreCase) {
103 		    /// <summary>
104 		    /// Check if the String ends with token
105 		    /// </summary>
106 		    /// <param name="token" type="String">
107 		    ///     the token to check for
108 		    /// </param>
109 		    /// <param name="ignoreCase" type="Boolean">
110 		    ///     (optional) if <code>true</code> checking will ignore case of characters.
111 			///		Default is <code>false</code>.
112 		    /// </param>
113 		    /// <returns type="Boolean">
114 		    ///     true, if the String ends with token, otherwise false.
115 		    /// </returns>
116 		    var str, isEnding = false, pos;
117 		    var isIgnoreCase = typeof ignoreCase !== 'undefined' && ignoreCase == true;
118 		    if(isIgnoreCase){
119 		    	token = token.toLowerCase();
120 		    	str = this.toLowerCase();
121 		    }
122 		    else {
123 		    	str = this;
124 		    }
125 		    
126 		    pos = str.length - token.length;
127 		    //sanity check if the token is smaller than the String itself -> token cannot be a sub-string!
128 		    if(pos < 0){
129 		    	return false;
130 		    }
131 		    isEnding = str.indexOf(token, pos) === pos;
132 		    
133 		    return isEnding;
134 		};
135 		}//END: if( !String.prototype.endsWith
136 		else {
137 			
138 			//if it already exists, then it is probably endsWith(str [, position])
139 			String.prototype.endsWithOrig = String.prototype.endsWith;
140 			
141 			//"map" endsWith(str,number) to endsWith(str,boolean):
142 			String.prototype.endsWith = function (token, startIndex, ignoreCase) {
143 				if(typeof ignoreCase === 'undefined' && typeof startIndex === 'boolean' || startIndex === 'true'){
144 					ignoreCase = startIndex;
145 					startIndex = null;
146 				}
147 				var isIgnoreCase = typeof ignoreCase !== 'undefined' && ignoreCase == true;
148 				
149 				var str = this.toString(), other = token.toString();
150 				if(isIgnoreCase){
151 					str = str.toLowerCase();
152 					other = other.toLowerCase();
153 				}
154 				
155 				if(startIndex){
156 					return str.endsWithOrig(other, startIndex);
157 				}
158 				else {
159 					return str.endsWithOrig(other);
160 				}
161 			};
162 		}
163 
164 		if( !String.prototype.replaceAll ){
165 		/**
166 		* ReplaceAll by Fagner Brack (MIT License)
167 		*
168 		* Replaces all occurrences of a substring in a string
169 		* 
170 		* @function
171 		*/
172 		String.prototype.replaceAll = function (token, newToken, ignoreCase) {
173 		    /// <summary>
174 		    /// Replace all occurances of a String with a new String
175 		    /// </summary>
176 		    /// <param name="token">the String to replace (all its occurances)</param>
177 		    /// <param name="newToken">the new String for the replacement</param>
178 		    /// <param name="ignoreCase">
179 		    ///     if true, the String token is matched/searched for without
180 		    ///     taking case into account
181 		    /// </param>
182 		    /// <returns type="String">
183 		    ///     a new String in which all occurances of token are replaced by newToken.
184 		    ///     If token or newToken are not Strings, the unmodified String will be returned.
185 		    /// </returns>
186 		    var str, i = -1, _token;
187 		    if ((str = this.toString()) && typeof token === "string" && typeof newToken === "string") {
188 		        _token = ignoreCase === true ? token.toLowerCase() : undefined;
189 		        while ((i = (
190 		            _token !== undefined ?
191 		                str.toLowerCase().indexOf(
192 		                            _token,
193 		                            i >= 0 ? i + newToken.length : 0
194 		                ) : str.indexOf(
195 		                            token,
196 		                            i >= 0 ? i + newToken.length : 0
197 		                )
198 		        )) !== -1) {
199 		            str = str.substring(0, i)
200 		                    .concat(newToken)
201 		                    .concat(str.substring(i + token.length));
202 		        }
203 		    }
204 		    return str;
205 		};
206 		}//END: if( !String.prototype.replaceAll
207 
208 		/**
209 		 * Escape quotes, i.e. replace single quotes <code>'</code> with <code>\'</code>
210 		 * @function
211 		 */
212 		String.prototype.escapeQuotes = function () {
213 		    var str;
214 		    if (str = this.toString()) {
215 		        return str.replaceAll('\'', '\\\'', false);
216 		    } else if(str == ''){
217 		    	return str;
218 		    }
219 		    throw new Error('Error in String.escapeQuotes: This is not a string: '+ (typeof this));
220 		};
221 
222 		/**
223 		 * Escape double quotes, i.e. replace quotes <code>"</code> with <code>\"</code> 
224 		 */
225 		String.prototype.escapeDoubleQuotes = function () {
226 		    var str;
227 		    if (str = this.toString()) {
228 		        return str.replaceAll('"', '\\"', false);
229 		    } else if(str == ''){
230 		    	return str;
231 		    }
232 		    throw new Error('Error in String.escapeDoubleQuotes: This is not a string: '+ (typeof this));
233 		};
234 
235 		/**
236 		 * Un-escape quotes, i.e. replace escaped single quotes <code>\'</code> with <code>'</code> 
237 		 */
238 		String.prototype.unescapeQuotes = function () {
239 		    var str;
240 		    if (str = this.toString()) {
241 		        return str.replaceAll('\\\'', '\'', false);
242 		    } else if(str == ''){
243 		    	return str;
244 		    }
245 		    throw new Error('Error in String.unescapeQuotes: This is not a string: '+ (typeof this));
246 		};
247 
248 		/**
249 		 * Un-escape double quotes, i.e. replace escaped quotes <code>\"</code> with <code>"</code> 
250 		 */
251 		String.prototype.unescapeDoubleQuotes = function () {
252 		    var str;
253 		    if (str = this.toString()) {
254 		        return str.replaceAll('\\"', '"', false);
255 		    } else if(str == ''){
256 		    	return str;
257 		    }
258 		    throw new Error('Error in String.unescapeDoubleQuotes: This is not a string: '+ (typeof this));
259 		};
260 
261 		//TRIM function: only define, if the platform does not provide it already
262 		if (!String.prototype.trim) {
263 			
264 		   console.info('WARNING: No String.trim() function defined, extending String with custom trim() function...');
265 		   
266 		   String.prototype.trim = function(){
267 			   return this
268 			   			.replace(/^\s\s*/, '') //remove whitespace at start...
269 			   			.replace(/\s\s*$/, '');//... and whitespaces at the end of the String
270 		   };
271 		}
272 
273 
274 		if(String.prototype.htmlEncode == null){
275 
276 		/*jslint white: true, onevar: true, undef: true, nomen: true, eqeqeq: true,
277 			plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true */
278 		/**
279 		 * HTML-Encode the supplied input
280 		 * 
281 		 * Parameters:
282 		 *
283 		 * this/source {string}    
284 		 *                     The text to be encoded.
285 		 * 
286 		 * @param display {boolean}
287 		 *                     (optional)
288 		 *                     The output is intended for display.
289 		 *
290 		 *                     If true (or undefined):
291 		 *                     * Tabs will be expanded to the number of spaces 
292 		 *                       indicated by the 'tabs' argument.
293 		 *                     * Line breaks will be converted to <br />.
294 		 *
295 		 *                     If false:
296 		 *                     * Tabs and linebreaks get turned into &#____;
297 		 *                       entities just like all other control characters.
298 		 *
299 		 * @param tabs {number}
300 		 *                     (optional)
301 		 *                     The number of spaces to expand tabs to.  
302 		 *                     (Ignored if the 'display' parameter evaluates to false 
303 		 *                      or if tabs is not >= 0.)
304 		 *                      
305 		 *                     Default: undefined (do not replace tabs with spaces)
306 		 *
307 		 * version 2010-11-08 (modified: 2012-12-20)
308 		 */	
309 
310 			String.prototype.htmlEncode = function (display, tabs) {
311 				var i, s, ch, peek, line, result,
312 					next, endline, push,
313 					spaces, source = this;
314 				
315 				//'parse' parameters
316 				if(typeof display === 'number'){
317 					//use as tabs-parameter
318 					tabs = display;
319 					display = true;
320 				} else if(typeof display === 'undefined'){
321 					display = true;
322 				}
323 				if(typeof tabs === 'string'){
324 					tabs = parseInt(tabs,10);
325 				} else if(typeof tabs === 'number'){
326 					tabs = Math.floor(tabs);
327 				} else {
328 					tabs = -1;
329 				}
330 				
331 				// Stash the next character and advance the pointer
332 				next = function () {
333 					peek = source.charAt(i);
334 					i += 1;
335 				};
336 				
337 				// Start a new "line" of output, to be joined later by <br />
338 				endline = function () {
339 					line = line.join('');
340 					if (display) {
341 						// If a line starts or ends with a space, it evaporates in html
342 						// unless it's an nbsp.
343 						line = line.replace(/(^ )|( $)/g, ' ');
344 					}
345 					result.push(line);
346 					line = [];
347 				};
348 				
349 				// Push a character or its entity onto the current line
350 				push = function () {
351 					if (ch < ' ' || ch > '~') {
352 						line.push('&#' + ch.charCodeAt(0) + ';');
353 					} else {
354 						line.push(ch);
355 					}
356 				};
357 				
358 				
359 				result = [];
360 				line = [];
361 			
362 				i = 0;
363 				next();
364 				while (i <= source.length) { // less than or equal, because i is always one ahead
365 					ch = peek;
366 					next();
367 					
368 					// HTML special chars.
369 					switch (ch) {
370 					case '<':
371 						line.push('<');
372 						break;
373 					case '>':
374 						line.push('>');
375 						break;
376 					case '&':
377 						line.push('&');
378 						break;
379 					case '"':
380 						line.push('"');
381 						break;
382 					case "'":
383 						line.push(''');
384 						break;
385 					default:
386 						// If the output is intended for display,
387 						// then end lines on newlines, and replace tabs with spaces.
388 						if (display) {
389 							switch (ch) {
390 							case '\r':
391 								// If this \r is the beginning of a \r\n, skip over the \n part.
392 								if (peek === '\n') {
393 									next();
394 								}
395 								endline();
396 								break;
397 							case '\n':
398 								endline();
399 								break;
400 							case '\t':
401 								// expand tabs?
402 								if(tabs >= 0){
403 									spaces = tabs - (line.length % tabs);
404 									for (s = 0; s < spaces; s += 1) {
405 										line.push(' ');
406 									}
407 								} else{
408 									//otherwise: preserve tabs
409 									push('	');
410 								}
411 								break;
412 							default:
413 								// All other characters can be dealt with generically.
414 								push();
415 							}
416 						} else {
417 							// If the output is not for display,
418 							// then none of the characters need special treatment.
419 							push();
420 						}
421 					}
422 				}
423 				endline();
424 				
425 				// If you can't beat 'em, join 'em.
426 				result = result.join('<br />');
427 			
428 				if (display) {
429 					// Break up contiguous blocks of spaces with non-breaking spaces
430 					result = result.replace(/ {2}/g, '  ');
431 				}
432 				
433 				// tada!
434 				return result;
435 			};
436 
437 		}
438 });
439