root/apps/modules/trunk/core/String.js

Revision 9136, 18.3 kB (checked in by zumbrunn, 2 years ago)

fixed description of repeat method

  • Property cvs2svn:cvs-rev set to 1.6
  • Property svn:keywords set to Date Revision Author HeadURL Id
Line 
1 /*
2  * Helma License Notice
3  *
4  * The contents of this file are subject to the Helma License
5  * Version 2.0 (the "License"). You may not use this file except in
6  * compliance with the License. A copy of the License is available at
7  * http://adele.helma.org/download/helma/license.txt
8  *
9  * Copyright 1998-2006 Helma Software. All Rights Reserved.
10  *
11  * $RCSfile: String.js,v $
12  * $Author$
13  * $Revision$
14  * $Date$
15  */
16
17
18 String.ANUMPATTERN    = /[^a-zA-Z0-9]/;
19 String.APATTERN       = /[^a-zA-Z]/;
20 String.NUMPATTERN     = /[^0-9]/;
21 String.FILEPATTERN    = /[^a-zA-Z0-9-_\. ]/;
22 String.HEXPATTERN     = /[^a-fA-F0-9]/;
23 String.EMAILPATTERN   = /^.+@.+\.[a-zA-Z]+$/;
24 String.URLPATTERN     = /^([^:]*):\/\/+(?:([^\/]*):)?(?:([^\/]*)@)?([\w\-_.]*[^.])(\/[^?]*)?(?:\?(.*))?$/;
25 String.LEFT           = -1
26 String.BALANCE        = 0
27 String.RIGHT          = 1
28 String.ISOFORMAT      = "yyyy-MM-dd'T'HH:mm:ssZ";
29 String.SPACE          = " ";
30 String.EMPTY          = "";
31 String.NULL           = String.EMPTY; // to be deprecated?
32
33 /**
34  * @fileoverview Adds useful methods to the JavaScript String type.
35  * <br /><br />
36  * To use this optional module, its repository needs to be added to the
37  * application, for example by calling app.addRepository('modules/core/String.js')
38  */
39
40 /**
41  * checks if a date format pattern is correct
42  * @return Boolean true if the pattern is correct
43  */
44 String.prototype.isDateFormat = function() {
45     try {
46         new java.text.SimpleDateFormat(this);
47         return true;
48     } catch (err) {
49         return false;
50     }
51 };
52
53
54 /**
55  * parse a timestamp into a date object. This is used when users
56  * want to set createtime explicitly when creating/editing stories.
57  * @param String date format to be applied
58  * @param Object Java TimeZone Object (optional)
59  * @return Object contains the resulting date
60  */
61 String.prototype.toDate = function(format, timezone) {
62     var sdf = res.data._dateformat;
63     if (!sdf) {
64         sdf = new java.text.SimpleDateFormat(format);
65         res.data._dateformat = sdf;
66     } else if (format != sdf.toPattern())
67         sdf.applyPattern(format);
68     if (timezone && timezone != sdf.getTimeZone())
69         sdf.setTimeZone(timezone);
70     try {
71         return new Date(sdf.parse(this).getTime());
72     } catch (err) {
73         return null;
74     }
75 };
76
77
78 /**
79  * function checks if the string passed contains any characters that
80  * are forbidden in URLs and tries to create a java.net.URL from it
81  * FIXME: probably deprecated -> helma.Url
82  * @return Boolean
83  * @see helma.Url.PATTERN
84  */
85 String.prototype.isUrl = function() {
86     if (String.URLPATTERN.test(this))
87         return true;
88     try {
89         return new java.net.URL(this);
90     } catch (err) {
91         return false;
92     }
93     return true;
94 };
95
96
97 /**
98  * function checks if the string passed contains any characters
99  * that are forbidden in image- or filenames
100  * @return Boolean
101  */
102 String.prototype.isFileName = function() {
103     return !String.FILEPATTERN.test(this);
104 };
105
106
107 /**
108  * function cleans the string passed as argument from any characters
109  * that are forbidden or shouldn't be used in filenames
110  * @return Boolean
111  */
112 String.prototype.toFileName = function() {
113     return this.replace(new RegExp(String.FILEPATTERN.source, "g"), String.NULL);
114 };
115
116
117 /**
118  * function checks a string for a valid color value in hexadecimal format.
119  * it may also contain # as first character
120  *  @returns Boolean false, if string length (without #) > 6 or < 6 or
121  *              contains any character which is not a valid hex value
122  */
123 String.prototype.isHexColor = function() {
124     var str = this;
125     if (this.indexOf("#") == 0)
126         str = this.substring(1);
127     if (str.length != 6)
128         return false;
129     return !String.HEXPATTERN.test(str);
130 };
131
132
133 /**
134  * converts a string into a hexadecimal color
135  * representation (e.g. "ffcc33"). also knows how to
136  * convert a color string like "rgb (255, 204, 51)".
137  * @return String the resulting hex color (w/o "#")
138  */
139 String.prototype.toHexColor = function() {
140     if (this.startsWith("rgb")) {
141         res.push();
142         var col = this.replace(/[^0-9,]/g, String.NULL);
143         var parts = col.split(",");
144         for (var i in parts) {
145             var num = parseInt(parts[i], 10);
146             var hex = num.toString(16);
147             res.write(hex.pad("0", 2, String.LEFT));
148         }
149         return res.pop();
150     }
151     var col = this.replace(new RegExp(String.HEXPATTERN.source), String.NULL);
152     return col.toLowerCase().pad("0", 6, String.LEFT);
153 };
154
155
156 /**
157  * function returns true if the string contains
158  * only a-z and 0-9 (case insensitive!)
159  * @return Boolean true in case string is alpha, false otherwise
160  */
161 String.prototype.isAlphanumeric = function() {
162     if (!this.length)
163         return false;
164     return !String.ANUMPATTERN.test(this);
165 };
166
167
168 /**
169  * function cleans a string by throwing away all
170  * non-alphanumeric characters
171  * @return cleaned string
172  */
173 String.prototype.toAlphanumeric = function() {
174     return this.replace(new RegExp(String.ANUMPATTERN.source, "g"), String.NULL);
175 };
176
177
178 /**
179  * function returns true if the string contains
180  * only characters a-z
181  * @return Boolean true in case string is alpha, false otherwise
182  */
183 String.prototype.isAlpha = function() {
184     if (!this.length)
185         return false;
186     return !String.APATTERN.test(this);
187 };
188
189
190 /**
191  * function returns true if the string contains
192  * only 0-9
193  * @return Boolean true in case string is numeric, false otherwise
194  */
195 String.prototype.isNumeric = function() {
196     if (!this.length)
197         return false;
198     return !String.NUMPATTERN.test(this);
199 };
200
201
202 /**
203  * transforms the first n characters of a string to uppercase
204  * @param Number amount of characters to transform
205  * @return String the resulting string
206  */
207 String.prototype.capitalize = function(limit) {
208     if (limit == null)
209         limit = 1;
210     var head = this.substring(0, limit);
211     var tail = this.substring(limit, this.length);
212     return head.toUpperCase() + tail.toLowerCase();
213 };
214
215
216 /**
217  * transforms the first n characters of each
218  * word in a string to uppercase
219  * @return String the resulting string
220  */
221 String.prototype.titleize = function() {
222     var parts = this.split(" ");
223     res.push();
224     for (var i in parts) {
225         res.write(parts[i].capitalize());
226         if (i < parts.length-1)
227             res.write(" ");
228     }
229     return res.pop();
230 };
231
232
233 /**
234  * translates all characters of a string into HTML entities
235  * @return String translated result
236  */
237 String.prototype.entitize = function() {
238     res.push();
239     for (var i=0; i<this.length; i++) {
240         res.write("&#");
241         res.write(this.charCodeAt(i).toString());
242         res.write(";");
243     }
244     return res.pop();
245 };
246
247
248 /**
249  * breaks up a string into two parts called
250  * head and tail at the given position
251  * don't apply this to HTML, i.e. use stripTags() in advance
252  * @param Number number of charactrers or of segments separated by the delimiter
253  * @param String pre-/suffix to be pre-/appended to shortened string
254  * @param String delimiter
255  * @return Object containing head and tail properties
256  */
257 String.prototype.embody = function(limit, clipping, delimiter) {
258    if (typeof limit == "string")
259       limit = parseInt(limit, 10);
260    var result = {head: this, tail: String.NULL};
261    if (!limit || limit < 1)
262       return result;
263    if (!delimiter || delimiter == String.NULL)
264       result.head= this.substring(0, limit);
265    else {
266       var re = new RegExp ("(" + delimiter + "+)");
267       result.head = this.split(re, 2*limit - 1).join(String.NULL);
268    }
269    if (result.head != this) {
270       result.tail = this.substring(result.head.length).trim();
271       if (result.tail) {
272          if (clipping == null)
273             clipping = "...";
274          result.head = result.head.trim() + clipping;
275          result.tail = clipping + result.tail;
276       }
277    }
278    return result;
279 };
280
281
282 /**
283  * get the head of a string
284  * @see String.prototype.embody()
285  */
286 String.prototype.head = function(limit, clipping, delimiter) {
287     return this.embody(limit, clipping, delimiter).head;
288 };
289
290
291 /**
292  * get the tail of a string
293  * @see String.prototype.embody()
294  */
295 String.prototype.tail = function(limit, clipping, delimiter) {
296     return this.embody(limit, clipping, delimiter).tail;
297 };
298
299
300 /*
301  * set clip method out of compatibility/convenience reason
302  * FIXME: we eventually have to get rid of this one...
303  * @see String.prototype.head()
304  */
305 String.prototype.clip = String.prototype.head;
306
307
308 /**
309  * function inserts a string every number of characters
310  * @param Int number of characters after which insertion should take place
311  * @param String string to be inserted
312  * @param Boolean definitely insert at each interval position
313  * @return String resulting string
314  */
315 String.prototype.group = function(interval, str, ignoreWhiteSpace) {
316     if (!interval || interval < 1)
317         interval = 20;
318     if (!str || this.length < interval)
319         return this;
320     res.push();
321     for (var i=0; i<this.length; i=i+interval) {
322         var strPart = this.substring(i, i+interval);
323         res.write(strPart);
324         if (ignoreWhiteSpace == true ||
325             (strPart.length == interval && !/\s/g.test(strPart))) {
326             res.write(str);
327         }
328     }
329     return res.pop();
330 };
331
332
333 /**
334  * replace all linebreaks and optionally all w/br tags
335  * @param Boolean flag indicating if html tags should be replaced
336  * @param String replacement for the linebreaks / html tags
337  * @return String the unwrapped string
338  */
339 String.prototype.unwrap = function(removeTags, replacement) {
340     if (replacement == null)
341         replacement = String.NULL;
342     var str = this.replace(/[\n|\r]/g, replacement);
343     if (removeTags)
344         str = str.replace(/<[w]?br *\/?>/g, replacement);
345     return str;   
346 };
347
348
349 /**
350  * function calculates the md5 hash of a string
351  * @return String md5 hash of the string
352  */
353 String.prototype.md5 = function() {
354     return Packages.helma.util.MD5Encoder.encode(this);
355 };
356
357
358 /**
359  * function repeats a string the specified amount of times
360  * @param Int amount of repetitions
361  * @return String resulting string
362  */
363 String.prototype.repeat = function(multiplier) {
364     res.push();
365     for (var i=0; i<multiplier; i++)
366         res.write(this);
367     return res.pop();
368 };
369
370
371 /**
372  * function returns true if the string starts with
373  * the string passed as argument
374  * @param String string pattern to search for
375  * @return Boolean true in case it matches the beginning
376  *            of the string, false otherwise
377  */
378 String.prototype.startsWith = function(str, offset) {
379     var javaObj = new java.lang.String(this);
380     if (offset != null)
381         return javaObj.startsWith(str, offset);
382     return javaObj.startsWith(str);
383 };
384
385
386 /**
387  * function returns true if the string ends with
388  * the string passed as argument
389  * @param String string pattern to search for
390  * @return Boolean true in case it matches the end of
391  *            the string, false otherwise
392  */
393 String.prototype.endsWith = function(str) {
394     var javaObj = new java.lang.String(this);
395     return javaObj.endsWith(str);
396 };
397
398
399 /**
400  * fills a string with another string up to a desired length
401  * @param String the filling string
402  * @param Number the desired length of the resulting string
403  * @param Number the direction which the string will be padded in:
404  *                    -1: left    0: both (balance)  1: right
405  *                    (you can use the constants String.LEFT,
406  *                     String.BALANCE and String.RIGHT here as well.)
407  * @return String the resulting string
408  */
409 String.prototype.pad = function(str, len, mode) {
410     if (str  == null || len == null)
411         return this;
412     var diff = len - this.length;
413     if (diff == 0)
414         return this;
415     var left, right = 0;
416     if (mode == null || mode == String.RIGHT)
417         right = diff;
418     else if (mode == String.LEFT)
419         left = diff;
420     else if (mode == String.BALANCE) {
421         right = Math.round(diff / 2);
422         left = diff - right;
423     }
424     res.push();
425     for (var i=0; i<left; i++)
426         res.write(str);
427     res.write(this);
428     for (var i=0; i<right; i++)
429         res.write(str);
430     return res.pop();
431 };
432
433
434 /**
435  * function returns true if a string contains the string
436  * passed as argument
437  * @param String string to search for
438  * @param Int Position to start search
439  * @param Boolean
440  */
441 String.prototype.contains = function(str, fromIndex) {
442     if (this.indexOf(str, fromIndex ? fromIndex : 0) > -1)
443         return true;
444     return false;
445 };
446
447
448 /**
449  * function compares a string with the one passed as argument
450  * using diff
451  * @param String String to compare against String object value
452  * @param String Optional regular expression string to use for
453  *                 splitting. If not defined, newlines will be used.   
454  * @return Object Array containing one JS object for each line
455  *                     with the following properties:
456  *                     .num Line number
457  *                     .value String line if unchanged
458  *                     .deleted Obj Array containing deleted lines
459  *                     .inserted Obj Array containing added lines
460  */
461 String.prototype.diff = function(mod, separator) {
462     // if no separator use line separator
463     var regexp = (typeof(separator) == "undefined") ?
464         new RegExp("\r\n|\r|\n") :
465         new RegExp(separator);
466     // split both strings into arrays
467     var orig = this.split(regexp);
468     var mod = mod.split(regexp);
469     // create the Diff object
470     var diff = new Packages.helma.util.Diff(orig, mod);
471     // get the diff.
472     var d = diff.diff();
473     if (!d)
474         return null;
475
476     var max = Math.max(orig.length, mod.length);
477     var result = new Array();
478     for (var i=0;i<max;i++) {
479         var line = result[i];
480         if (!line) {
481             line = new Object();
482             line.num = (i+1);
483             result[i] = line;
484         }
485         if (d && i == d.line1) {
486             if (d.deleted) {
487                 var del = new Array();
488                 for (var j=d.line0; j<d.line0+d.deleted; j++)
489                     del[del.length] = orig[j];
490                 line.deleted = del;
491             }
492             if (d.inserted) {
493                 var ins = new Array();
494                 for (var j=d.line1; j<d.line1+d.inserted; j++)
495                     ins[ins.length] = mod[j];
496                 line.inserted = ins;
497             }
498             i = d.line1 + d.inserted -1;
499             d = d.link;
500         } else {
501             line.value = mod[i];
502         }
503     }
504     return result;
505 };
506
507
508 /**
509  * remove leading and trailing whitespace
510  */
511 String.prototype.trim = function () {
512     var s = new java.lang.String(this);
513     return String(s.trim());
514 };
515
516
517 /**
518  * returns true if the string looks like an e-mail
519  */
520 String.prototype.isEmail = function() {
521     return String.EMAILPATTERN.test(this);
522 };
523
524
525 /**
526  * returns the amount of occurences of one string in another
527  */
528 String.prototype.count = function(str) {
529     var count = 0;
530     var offset = 0;
531     while ((offset = this.indexOf(str, offset)) > -1) {
532         count += 1;
533         offset += 1;
534     }
535     return count;
536 };
537
538
539 /**
540  * returns the string encoded using the base64 algorithm
541  */
542 String.prototype.enbase64 = function() {
543     var bytes = new java.lang.String(this) . getBytes();
544     return new Packages.sun.misc.BASE64Encoder().encode(bytes);
545 };
546
547
548 /**
549  * returns the decoded string using the base64 algorithm
550  */
551 String.prototype.debase64 = function() {
552     var bytes = new Packages.sun.misc.BASE64Decoder().decodeBuffer(this);
553     return String(new java.lang.String(bytes));
554 };
555
556
557 // wrapper methods for string-related
558 // global helma functions
559
560 String.prototype.encode = function() {
561     return encode(this);
562 };
563
564 String.prototype.encodeXml = function() {
565     return encodeXml(this);
566 };
567
568 String.prototype.encodeForm = function() {
569     return encodeForm(this);
570 };
571
572 String.prototype.format = function() {
573     return format(this);
574 };
575
576 String.prototype.stripTags = function() {
577     return stripTags(this);
578 };
579
580 /**
581  * factory to create functions for sorting objects in an array
582  * @param String name of the field each object is compared with
583  * @param Number order (ascending or descending)
584  * @return Function ready for use in Array.prototype.sort
585  */
586 String.Sorter = function(field, order) {
587     if (!order)
588         order = 1;
589     var key = field + ":" + order;
590     if (!String.Sorter.cache[key]) {
591         String.Sorter.cache[key] = function(a, b) {
592             var str1 = String(a[field] || String.NULL).toLowerCase();
593             var str2 = String(b[field] || String.NULL).toLowerCase();
594             if (str1 > str2)
595                 return order * 1;
596             if (str1 < str2)
597                 return order * -1;
598             return 0;
599         };
600     }
601     return String.Sorter.cache[key];
602 };
603
604 String.Sorter.ASC = 1;
605 String.Sorter.DESC = -1;
606 String.Sorter.cache = {};
607
608
609 /**
610  * create a string from a bunch of substrings
611  * @param String one or more strings as arguments
612  * @return String the resulting string
613  */
614 String.compose = function() {
615     res.push();
616     for (var i=0; i<arguments.length; i++)
617         res.write(arguments[i]);
618     return res.pop();
619 };
620
621
622 /**
623  * creates a random string (numbers and chars)
624  * @param len length of key
625  * @param mode determines which letters to use. null or 0 = all letters;
626  *      1 = skip 0, 1, l and o which can easily be mixed with numbers;
627  *      2 = use numbers only
628  * @returns random string
629  */
630 String.random = function(len, mode) {
631     if (mode == 2) {
632         var x = Math.random() * Math.pow(10,len);
633         return Math.floor(x);
634     }
635     var keystr = String.NULL;
636     for (var i=0; i<len; i++) {
637         var x = Math.floor((Math.random() * 36));
638         if (mode == 1) {
639             // skip 0,1
640             x = (x<2) ? x + 2 : x;
641             // don't use the letters l (charCode 21+87) and o (24+87)
642             x = (x==21) ? 22 : x;
643             x = (x==24) ? 25 : x;
644         }
645         if (x<10) {
646             keystr += String(x);
647         }    else    {
648             keystr += String.fromCharCode(x+87);
649         }
650     }
651     return keystr;
652 };
653
654
655 /**
656  * append one string onto another and add some "glue"
657  * if none of the strings is empty or null.
658  * @param String the first string
659  * @param String the string to be appended onto the first one
660  * @param String the "glue" to be inserted between both strings
661  * @return String the resulting string
662  */
663 String.join = function(str1, str2, glue) {
664     if (glue == null)
665         glue = String.NULL;
666     if (str1 && str2)
667         return str1 + glue + str2;
668     else if (str2)
669         return str2;
670     return str1;
671 };
672
673
674 // prevent any newly added properties from being enumerated
675 for (var i in String)
676    String.dontEnum(i);
677 for (var i in String.prototype)
678    String.prototype.dontEnum(i);
Note: See TracBrowser for help on using the browser.