blob: d2d6e969df92abb6aca0be9cea4f4980f1dc0f5a [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// This file relies on the fact that the following declaration has been made
30// in runtime.js:
31// const $String = global.String;
32// const $NaN = 0/0;
33
34
35// Set the String function and constructor.
36%SetCode($String, function(x) {
37 var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
38 if (%_IsConstructCall()) {
39 %_SetValueOf(this, value);
40 } else {
41 return value;
42 }
43});
44
45%FunctionSetPrototype($String, new $String());
46
47// ECMA-262 section 15.5.4.2
48function StringToString() {
49 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this))
50 throw new $TypeError('String.prototype.toString is not generic');
51 return %_ValueOf(this);
52}
53
54
55// ECMA-262 section 15.5.4.3
56function StringValueOf() {
57 if (!IS_STRING(this) && !IS_STRING_WRAPPER(this))
58 throw new $TypeError('String.prototype.valueOf is not generic');
59 return %_ValueOf(this);
60}
61
62
63// ECMA-262, section 15.5.4.4
64function StringCharAt(pos) {
65 var char_code = %_FastCharCodeAt(this, pos);
66 if (!%_IsSmi(char_code)) {
67 var subject = ToString(this);
68 var index = TO_INTEGER(pos);
69 if (index >= subject.length || index < 0) return "";
70 char_code = %StringCharCodeAt(subject, index);
71 }
72 return %CharFromCode(char_code);
73}
74
75
76// ECMA-262 section 15.5.4.5
77function StringCharCodeAt(pos) {
78 var fast_answer = %_FastCharCodeAt(this, pos);
79 if (%_IsSmi(fast_answer)) {
80 return fast_answer;
81 }
82 var subject = ToString(this);
83 var index = TO_INTEGER(pos);
84 return %StringCharCodeAt(subject, index);
85}
86
87
88// ECMA-262, section 15.5.4.6
89function StringConcat() {
90 var len = %_ArgumentsLength();
91 var parts = new $Array(len + 1);
92 parts[0] = ToString(this);
93 for (var i = 0; i < len; i++)
94 parts[i + 1] = ToString(%_Arguments(i));
95 return parts.join('');
96}
97
98// Match ES3 and Safari
99%FunctionSetLength(StringConcat, 1);
100
101
102// ECMA-262 section 15.5.4.7
103function StringIndexOf(searchString /* position */) { // length == 1
104 var subject_str = ToString(this);
105 var pattern_str = ToString(searchString);
106 var subject_str_len = subject_str.length;
107 var pattern_str_len = pattern_str.length;
108 var index = 0;
109 if (%_ArgumentsLength() > 1) {
110 var arg1 = %_Arguments(1); // position
111 index = TO_INTEGER(arg1);
112 }
113 if (index < 0) index = 0;
114 if (index > subject_str_len) index = subject_str_len;
115 if (pattern_str_len + index > subject_str_len) return -1;
116 return %StringIndexOf(subject_str, pattern_str, index);
117}
118
119
120// ECMA-262 section 15.5.4.8
121function StringLastIndexOf(searchString /* position */) { // length == 1
122 var sub = ToString(this);
123 var subLength = sub.length;
124 var pat = ToString(searchString);
125 var patLength = pat.length;
126 var index = subLength - patLength;
127 if (%_ArgumentsLength() > 1) {
128 var position = ToNumber(%_Arguments(1));
129 if (!$isNaN(position)) {
130 position = TO_INTEGER(position);
131 if (position < 0) {
132 position = 0;
133 }
134 if (position + patLength < subLength) {
135 index = position
136 }
137 }
138 }
139 if (index < 0) {
140 return -1;
141 }
142 return %StringLastIndexOf(sub, pat, index);
143}
144
145
146// ECMA-262 section 15.5.4.9
147//
148// This function is implementation specific. For now, we do not
149// do anything locale specific.
150function StringLocaleCompare(other) {
151 if (%_ArgumentsLength() === 0) return 0;
152
153 var this_str = ToString(this);
154 var other_str = ToString(other);
155 return %StringLocaleCompare(this_str, other_str);
156}
157
158
159// ECMA-262 section 15.5.4.10
160function StringMatch(regexp) {
161 if (!IS_REGEXP(regexp)) regexp = new ORIGINAL_REGEXP(regexp);
162 var subject = ToString(this);
163
164 if (!regexp.global) return regexp.exec(subject);
165 %_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
166 // lastMatchInfo is defined in regexp-delay.js.
167 return %StringMatch(subject, regexp, lastMatchInfo);
168}
169
170
171// SubString is an internal function that returns the sub string of 'string'.
172// If resulting string is of length 1, we use the one character cache
173// otherwise we call the runtime system.
174function SubString(string, start, end) {
175 // Use the one character string cache.
176 if (start + 1 == end) {
177 var char_code = %_FastCharCodeAt(string, start);
178 if (!%_IsSmi(char_code)) {
179 char_code = %StringCharCodeAt(string, start);
180 }
181 return %CharFromCode(char_code);
182 }
183 return %StringSlice(string, start, end);
184}
185
186
187// This has the same size as the lastMatchInfo array, and can be used for
188// functions that expect that structure to be returned. It is used when the
189// needle is a string rather than a regexp. In this case we can't update
190// lastMatchArray without erroneously affecting the properties on the global
191// RegExp object.
192var reusableMatchInfo = [2, "", "", -1, -1];
193
194
195// ECMA-262, section 15.5.4.11
196function StringReplace(search, replace) {
197 var subject = ToString(this);
198
199 // Delegate to one of the regular expression variants if necessary.
200 if (IS_REGEXP(search)) {
201 %_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
202 if (IS_FUNCTION(replace)) {
203 return StringReplaceRegExpWithFunction(subject, search, replace);
204 } else {
205 return StringReplaceRegExp(subject, search, replace);
206 }
207 }
208
209 // Convert the search argument to a string and search for it.
210 search = ToString(search);
211 var start = %StringIndexOf(subject, search, 0);
212 if (start < 0) return subject;
213 var end = start + search.length;
214
215 var builder = new ReplaceResultBuilder(subject);
216 // prefix
217 builder.addSpecialSlice(0, start);
218
219 // Compute the string to replace with.
220 if (IS_FUNCTION(replace)) {
221 builder.add(replace.call(null, search, start, subject));
222 } else {
223 reusableMatchInfo[CAPTURE0] = start;
224 reusableMatchInfo[CAPTURE1] = end;
225 ExpandReplacement(ToString(replace), subject, reusableMatchInfo, builder);
226 }
227
228 // suffix
229 builder.addSpecialSlice(end, subject.length);
230
231 return builder.generate();
232}
233
234
235// Helper function for regular expressions in String.prototype.replace.
236function StringReplaceRegExp(subject, regexp, replace) {
237 replace = ToString(replace);
238 return %StringReplaceRegExpWithString(subject,
239 regexp,
240 replace,
241 lastMatchInfo);
242};
243
244
245// Expand the $-expressions in the string and return a new string with
246// the result.
247function ExpandReplacement(string, subject, matchInfo, builder) {
248 var next = %StringIndexOf(string, '$', 0);
249 if (next < 0) {
250 builder.add(string);
251 return;
252 }
253
254 // Compute the number of captures; see ECMA-262, 15.5.4.11, p. 102.
255 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1; // Includes the match.
256
257 if (next > 0) builder.add(SubString(string, 0, next));
258 var length = string.length;
259
260 while (true) {
261 var expansion = '$';
262 var position = next + 1;
263 if (position < length) {
264 var peek = %_FastCharCodeAt(string, position);
265 if (!%_IsSmi(peek)) {
266 peek = %StringCharCodeAt(string, position);
267 }
268 if (peek == 36) { // $$
269 ++position;
270 builder.add('$');
271 } else if (peek == 38) { // $& - match
272 ++position;
273 builder.addSpecialSlice(matchInfo[CAPTURE0],
274 matchInfo[CAPTURE1]);
275 } else if (peek == 96) { // $` - prefix
276 ++position;
277 builder.addSpecialSlice(0, matchInfo[CAPTURE0]);
278 } else if (peek == 39) { // $' - suffix
279 ++position;
280 builder.addSpecialSlice(matchInfo[CAPTURE1], subject.length);
281 } else if (peek >= 48 && peek <= 57) { // $n, 0 <= n <= 9
282 ++position;
283 var n = peek - 48;
284 if (position < length) {
285 peek = %_FastCharCodeAt(string, position);
286 if (!%_IsSmi(peek)) {
287 peek = %StringCharCodeAt(string, position);
288 }
289 // $nn, 01 <= nn <= 99
290 if (n != 0 && peek == 48 || peek >= 49 && peek <= 57) {
291 var nn = n * 10 + (peek - 48);
292 if (nn < m) {
293 // If the two digit capture reference is within range of
294 // the captures, we use it instead of the single digit
295 // one. Otherwise, we fall back to using the single
296 // digit reference. This matches the behavior of
297 // SpiderMonkey.
298 ++position;
299 n = nn;
300 }
301 }
302 }
303 if (0 < n && n < m) {
304 addCaptureString(builder, matchInfo, n);
305 } else {
306 // Because of the captures range check in the parsing of two
307 // digit capture references, we can only enter here when a
308 // single digit capture reference is outside the range of
309 // captures.
310 builder.add('$');
311 --position;
312 }
313 } else {
314 builder.add('$');
315 }
316 } else {
317 builder.add('$');
318 }
319
320 // Go the the next $ in the string.
321 next = %StringIndexOf(string, '$', position);
322
323 // Return if there are no more $ characters in the string. If we
324 // haven't reached the end, we need to append the suffix.
325 if (next < 0) {
326 if (position < length) {
327 builder.add(SubString(string, position, length));
328 }
329 return;
330 }
331
332 // Append substring between the previous and the next $ character.
333 builder.add(SubString(string, position, next));
334 }
335};
336
337
338// Compute the string of a given regular expression capture.
339function CaptureString(string, lastCaptureInfo, index) {
340 // Scale the index.
341 var scaled = index << 1;
342 // Compute start and end.
343 var start = lastCaptureInfo[CAPTURE(scaled)];
344 var end = lastCaptureInfo[CAPTURE(scaled + 1)];
345 // If either start or end is missing return undefined.
346 if (start < 0 || end < 0) return;
347 return SubString(string, start, end);
348};
349
350
351// Add the string of a given regular expression capture to the
352// ReplaceResultBuilder
353function addCaptureString(builder, matchInfo, index) {
354 // Scale the index.
355 var scaled = index << 1;
356 // Compute start and end.
357 var start = matchInfo[CAPTURE(scaled)];
358 var end = matchInfo[CAPTURE(scaled + 1)];
359 // If either start or end is missing return.
360 if (start < 0 || end <= start) return;
361 builder.addSpecialSlice(start, end);
362};
363
364
365// Helper function for replacing regular expressions with the result of a
366// function application in String.prototype.replace. The function application
367// must be interleaved with the regexp matching (contrary to ECMA-262
368// 15.5.4.11) to mimic SpiderMonkey and KJS behavior when the function uses
369// the static properties of the RegExp constructor. Example:
370// 'abcd'.replace(/(.)/g, function() { return RegExp.$1; }
371// should be 'abcd' and not 'dddd' (or anything else).
372function StringReplaceRegExpWithFunction(subject, regexp, replace) {
373 var matchInfo = DoRegExpExec(regexp, subject, 0);
374 if (IS_NULL(matchInfo)) return subject;
375
376 var result = new ReplaceResultBuilder(subject);
377 // There's at least one match. If the regexp is global, we have to loop
378 // over all matches. The loop is not in C++ code here like the one in
379 // RegExp.prototype.exec, because of the interleaved function application.
380 // Unfortunately, that means this code is nearly duplicated, here and in
381 // jsregexp.cc.
382 if (regexp.global) {
383 var previous = 0;
384 do {
385 result.addSpecialSlice(previous, matchInfo[CAPTURE0]);
386 var startOfMatch = matchInfo[CAPTURE0];
387 previous = matchInfo[CAPTURE1];
388 result.add(ApplyReplacementFunction(replace, matchInfo, subject));
389 // Can't use matchInfo any more from here, since the function could
390 // overwrite it.
391 // Continue with the next match.
392 // Increment previous if we matched an empty string, as per ECMA-262
393 // 15.5.4.10.
394 if (previous == startOfMatch) {
395 // Add the skipped character to the output, if any.
396 if (previous < subject.length) {
397 result.addSpecialSlice(previous, previous + 1);
398 }
399 previous++;
400 }
401
402 // Per ECMA-262 15.10.6.2, if the previous index is greater than the
403 // string length, there is no match
404 matchInfo = (previous > subject.length)
405 ? null
406 : DoRegExpExec(regexp, subject, previous);
407 } while (!IS_NULL(matchInfo));
408
409 // Tack on the final right substring after the last match, if necessary.
410 if (previous < subject.length) {
411 result.addSpecialSlice(previous, subject.length);
412 }
413 } else { // Not a global regexp, no need to loop.
414 result.addSpecialSlice(0, matchInfo[CAPTURE0]);
415 var endOfMatch = matchInfo[CAPTURE1];
416 result.add(ApplyReplacementFunction(replace, matchInfo, subject));
417 // Can't use matchInfo any more from here, since the function could
418 // overwrite it.
419 result.addSpecialSlice(endOfMatch, subject.length);
420 }
421
422 return result.generate();
423}
424
425
426// Helper function to apply a string replacement function once.
427function ApplyReplacementFunction(replace, matchInfo, subject) {
428 // Compute the parameter list consisting of the match, captures, index,
429 // and subject for the replace function invocation.
430 var index = matchInfo[CAPTURE0];
431 // The number of captures plus one for the match.
432 var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
433 if (m == 1) {
434 var s = CaptureString(subject, matchInfo, 0);
435 // Don't call directly to avoid exposing the built-in global object.
436 return replace.call(null, s, index, subject);
437 }
438 var parameters = $Array(m + 2);
439 for (var j = 0; j < m; j++) {
440 parameters[j] = CaptureString(subject, matchInfo, j);
441 }
442 parameters[j] = index;
443 parameters[j + 1] = subject;
444 return replace.apply(null, parameters);
445}
446
447
448// ECMA-262 section 15.5.4.12
449function StringSearch(re) {
450 var regexp = new ORIGINAL_REGEXP(re);
451 var s = ToString(this);
452 var last_idx = regexp.lastIndex; // keep old lastIndex
453 regexp.lastIndex = 0; // ignore re.global property
454 var result = regexp.exec(s);
455 regexp.lastIndex = last_idx; // restore lastIndex
456 if (result == null)
457 return -1;
458 else
459 return result.index;
460}
461
462
463// ECMA-262 section 15.5.4.13
464function StringSlice(start, end) {
465 var s = ToString(this);
466 var s_len = s.length;
467 var start_i = TO_INTEGER(start);
468 var end_i = s_len;
469 if (end !== void 0)
470 end_i = TO_INTEGER(end);
471
472 if (start_i < 0) {
473 start_i += s_len;
474 if (start_i < 0)
475 start_i = 0;
476 } else {
477 if (start_i > s_len)
478 start_i = s_len;
479 }
480
481 if (end_i < 0) {
482 end_i += s_len;
483 if (end_i < 0)
484 end_i = 0;
485 } else {
486 if (end_i > s_len)
487 end_i = s_len;
488 }
489
490 var num_c = end_i - start_i;
491 if (num_c < 0)
492 num_c = 0;
493
494 return SubString(s, start_i, start_i + num_c);
495}
496
497
498// ECMA-262 section 15.5.4.14
499function StringSplit(separator, limit) {
500 var subject = ToString(this);
501 limit = (limit === void 0) ? 0xffffffff : ToUint32(limit);
502 if (limit === 0) return [];
503
504 // ECMA-262 says that if separator is undefined, the result should
505 // be an array of size 1 containing the entire string. SpiderMonkey
506 // and KJS have this behaviour only when no separator is given. If
507 // undefined is explicitly given, they convert it to a string and
508 // use that. We do as SpiderMonkey and KJS.
509 if (%_ArgumentsLength() === 0) {
510 return [subject];
511 }
512
513 var length = subject.length;
514 if (IS_REGEXP(separator)) {
515 %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
516 } else {
517 separator = ToString(separator);
518 // If the separator string is empty then return the elements in the subject.
519 if (separator.length == 0) {
520 var result = $Array(length);
521 for (var i = 0; i < length; i++) result[i] = subject[i];
522 return result;
523 }
524 }
525
526 if (length === 0) {
527 if (splitMatch(separator, subject, 0, 0) != null) return [];
528 return [subject];
529 }
530
531 var currentIndex = 0;
532 var startIndex = 0;
533 var result = [];
534
535 while (true) {
536
537 if (startIndex === length) {
538 result[result.length] = subject.slice(currentIndex, length);
539 return result;
540 }
541
542 var matchInfo = splitMatch(separator, subject, currentIndex, startIndex);
543
544 if (IS_NULL(matchInfo)) {
545 result[result.length] = subject.slice(currentIndex, length);
546 return result;
547 }
548
549 var endIndex = matchInfo[CAPTURE1];
550
551 // We ignore a zero-length match at the currentIndex.
552 if (startIndex === endIndex && endIndex === currentIndex) {
553 startIndex++;
554 continue;
555 }
556
557 result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]);
558 if (result.length === limit) return result;
559
560 for (var i = 2; i < NUMBER_OF_CAPTURES(matchInfo); i += 2) {
561 var start = matchInfo[CAPTURE(i)];
562 var end = matchInfo[CAPTURE(i + 1)];
563 if (start != -1 && end != -1) {
564 result[result.length] = SubString(subject, start, end);
565 } else {
566 result[result.length] = void 0;
567 }
568 if (result.length === limit) return result;
569 }
570
571 startIndex = currentIndex = endIndex;
572 }
573}
574
575
576// ECMA-262 section 15.5.4.14
577// Helper function used by split. This version returns the matchInfo
578// instead of allocating a new array with basically the same information.
579function splitMatch(separator, subject, current_index, start_index) {
580 if (IS_REGEXP(separator)) {
581 var matchInfo = DoRegExpExec(separator, subject, start_index);
582 if (matchInfo == null) return null;
583 // Section 15.5.4.14 paragraph two says that we do not allow zero length
584 // matches at the end of the string.
585 if (matchInfo[CAPTURE0] === subject.length) return null;
586 return matchInfo;
587 }
588
589 var separatorIndex = subject.indexOf(separator, start_index);
590 if (separatorIndex === -1) return null;
591
592 reusableMatchInfo[CAPTURE0] = separatorIndex;
593 reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
594 return reusableMatchInfo;
595};
596
597
598// ECMA-262 section 15.5.4.15
599function StringSubstring(start, end) {
600 var s = ToString(this);
601 var s_len = s.length;
602 var start_i = TO_INTEGER(start);
603 var end_i = s_len;
604 if (!IS_UNDEFINED(end))
605 end_i = TO_INTEGER(end);
606
607 if (start_i < 0) start_i = 0;
608 if (start_i > s_len) start_i = s_len;
609 if (end_i < 0) end_i = 0;
610 if (end_i > s_len) end_i = s_len;
611
612 if (start_i > end_i) {
613 var tmp = end_i;
614 end_i = start_i;
615 start_i = tmp;
616 }
617
618 return SubString(s, start_i, end_i);
619}
620
621
622// This is not a part of ECMA-262.
623function StringSubstr(start, n) {
624 var s = ToString(this);
625 var len;
626
627 // Correct n: If not given, set to string length; if explicitly
628 // set to undefined, zero, or negative, returns empty string.
629 if (n === void 0) {
630 len = s.length;
631 } else {
632 len = TO_INTEGER(n);
633 if (len <= 0) return '';
634 }
635
636 // Correct start: If not given (or undefined), set to zero; otherwise
637 // convert to integer and handle negative case.
638 if (start === void 0) {
639 start = 0;
640 } else {
641 start = TO_INTEGER(start);
642 // If positive, and greater than or equal to the string length,
643 // return empty string.
644 if (start >= s.length) return '';
645 // If negative and absolute value is larger than the string length,
646 // use zero.
647 if (start < 0) {
648 start += s.length;
649 if (start < 0) start = 0;
650 }
651 }
652
653 var end = start + len;
654 if (end > s.length) end = s.length;
655
656 return SubString(s, start, end);
657}
658
659
660// ECMA-262, 15.5.4.16
661function StringToLowerCase() {
662 return %StringToLowerCase(ToString(this));
663}
664
665
666// ECMA-262, 15.5.4.17
667function StringToLocaleLowerCase() {
668 return %StringToLowerCase(ToString(this));
669}
670
671
672// ECMA-262, 15.5.4.18
673function StringToUpperCase() {
674 return %StringToUpperCase(ToString(this));
675}
676
677
678// ECMA-262, 15.5.4.19
679function StringToLocaleUpperCase() {
680 return %StringToUpperCase(ToString(this));
681}
682
Steve Block3ce2e202009-11-05 08:53:23 +0000683// ES5, 15.5.4.20
684function StringTrim() {
685 return %StringTrim(ToString(this), true, true);
686}
687
688function StringTrimLeft() {
689 return %StringTrim(ToString(this), true, false);
690}
691
692function StringTrimRight() {
693 return %StringTrim(ToString(this), false, true);
694}
Steve Blocka7e24c12009-10-30 11:49:00 +0000695
696// ECMA-262, section 15.5.3.2
697function StringFromCharCode(code) {
698 var n = %_ArgumentsLength();
699 if (n == 1) return %CharFromCode(ToNumber(code) & 0xffff)
700
701 // NOTE: This is not super-efficient, but it is necessary because we
702 // want to avoid converting to numbers from within the virtual
703 // machine. Maybe we can find another way of doing this?
704 var codes = new $Array(n);
705 for (var i = 0; i < n; i++) codes[i] = ToNumber(%_Arguments(i));
706 return %StringFromCharCodeArray(codes);
707}
708
709
710// Helper function for very basic XSS protection.
711function HtmlEscape(str) {
712 return ToString(str).replace(/</g, "&lt;")
713 .replace(/>/g, "&gt;")
714 .replace(/"/g, "&quot;")
715 .replace(/'/g, "&#039;");
716};
717
718
719// Compatibility support for KJS.
720// Tested by mozilla/js/tests/js1_5/Regress/regress-276103.js.
721function StringLink(s) {
722 return "<a href=\"" + HtmlEscape(s) + "\">" + this + "</a>";
723}
724
725
726function StringAnchor(name) {
727 return "<a name=\"" + HtmlEscape(name) + "\">" + this + "</a>";
728}
729
730
731function StringFontcolor(color) {
732 return "<font color=\"" + HtmlEscape(color) + "\">" + this + "</font>";
733}
734
735
736function StringFontsize(size) {
737 return "<font size=\"" + HtmlEscape(size) + "\">" + this + "</font>";
738}
739
740
741function StringBig() {
742 return "<big>" + this + "</big>";
743}
744
745
746function StringBlink() {
747 return "<blink>" + this + "</blink>";
748}
749
750
751function StringBold() {
752 return "<b>" + this + "</b>";
753}
754
755
756function StringFixed() {
757 return "<tt>" + this + "</tt>";
758}
759
760
761function StringItalics() {
762 return "<i>" + this + "</i>";
763}
764
765
766function StringSmall() {
767 return "<small>" + this + "</small>";
768}
769
770
771function StringStrike() {
772 return "<strike>" + this + "</strike>";
773}
774
775
776function StringSub() {
777 return "<sub>" + this + "</sub>";
778}
779
780
781function StringSup() {
782 return "<sup>" + this + "</sup>";
783}
784
785
786// StringBuilder support.
787
788function StringBuilder() {
789 this.elements = new $Array();
790}
791
792
793function ReplaceResultBuilder(str) {
794 this.elements = new $Array();
795 this.special_string = str;
796}
797
798
799ReplaceResultBuilder.prototype.add =
800StringBuilder.prototype.add = function(str) {
801 if (!IS_STRING(str)) str = ToString(str);
802 if (str.length > 0) {
803 var elements = this.elements;
804 elements[elements.length] = str;
805 }
806}
807
808
809ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) {
810 var len = end - start;
811 if (len == 0) return;
812 var elements = this.elements;
813 if (start >= 0 && len >= 0 && start < 0x80000 && len < 0x800) {
814 elements[elements.length] = (start << 11) + len;
815 } else {
816 elements[elements.length] = SubString(this.special_string, start, end);
817 }
818}
819
820
821StringBuilder.prototype.generate = function() {
822 return %StringBuilderConcat(this.elements, "");
823}
824
825
826ReplaceResultBuilder.prototype.generate = function() {
827 return %StringBuilderConcat(this.elements, this.special_string);
828}
829
830
831function StringToJSON(key) {
832 return CheckJSONPrimitive(this.valueOf());
833}
834
835
836// -------------------------------------------------------------------
837
838function SetupString() {
839 // Setup the constructor property on the String prototype object.
840 %SetProperty($String.prototype, "constructor", $String, DONT_ENUM);
841
842
843 // Setup the non-enumerable functions on the String object.
844 InstallFunctions($String, DONT_ENUM, $Array(
845 "fromCharCode", StringFromCharCode
846 ));
847
848
849 // Setup the non-enumerable functions on the String prototype object.
850 InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array(
851 "valueOf", StringValueOf,
852 "toString", StringToString,
853 "charAt", StringCharAt,
854 "charCodeAt", StringCharCodeAt,
855 "concat", StringConcat,
856 "indexOf", StringIndexOf,
857 "lastIndexOf", StringLastIndexOf,
858 "localeCompare", StringLocaleCompare,
859 "match", StringMatch,
860 "replace", StringReplace,
861 "search", StringSearch,
862 "slice", StringSlice,
863 "split", StringSplit,
864 "substring", StringSubstring,
865 "substr", StringSubstr,
866 "toLowerCase", StringToLowerCase,
867 "toLocaleLowerCase", StringToLocaleLowerCase,
868 "toUpperCase", StringToUpperCase,
869 "toLocaleUpperCase", StringToLocaleUpperCase,
Steve Block3ce2e202009-11-05 08:53:23 +0000870 "trim", StringTrim,
871 "trimLeft", StringTrimLeft,
872 "trimRight", StringTrimRight,
Steve Blocka7e24c12009-10-30 11:49:00 +0000873 "link", StringLink,
874 "anchor", StringAnchor,
875 "fontcolor", StringFontcolor,
876 "fontsize", StringFontsize,
877 "big", StringBig,
878 "blink", StringBlink,
879 "bold", StringBold,
880 "fixed", StringFixed,
881 "italics", StringItalics,
882 "small", StringSmall,
883 "strike", StringStrike,
884 "sub", StringSub,
885 "sup", StringSup,
886 "toJSON", StringToJSON
887 ));
888}
889
890
891SetupString();