35 CPCaseInsensitiveSearch = 1;
47 CPBackwardsSearch = 4;
66 var CPStringUIDs =
new CFMutableDictionary(),
68 CPStringRegexSpecialCharacters = [
69 '/',
'.',
'*',
'+',
'?',
'|',
'$',
'^',
70 '(',
')',
'[',
']',
'{',
'}',
'\\'
72 CPStringRegexEscapeExpression =
new RegExp(
"(\\" + CPStringRegexSpecialCharacters.join(
"|\\") +
")",
'g'),
73 CPStringRegexTrimWhitespace =
new RegExp(
"(^\\s+|\\s+$)",
'g');
109 return [[
self alloc] init];
116 + (id)stringWithHash:(
unsigned)aHash
118 var hashString = parseInt(aHash, 10).toString(16);
119 return "000000".substring(0, MAX(6 - hashString.length, 0)) + hashString;
128 + (id)stringWithString:(
CPString)aString
132 reason:"stringWithString: the string can't be 'nil'"];
134 return [[
self alloc] initWithString:aString];
142 - (id)initWithString:(
CPString)aString
145 return String(aString);
147 var result =
new String(aString);
149 result.isa = [
self class];
159 - (id)initWithFormat:(
CPString)format, ...
163 reason:"initWithFormat: the format can't be 'nil'"];
165 self = ObjectiveJ.sprintf.apply(
this, Array.prototype.slice.call(arguments, 2));
175 + (id)stringWithFormat:(
CPString)format, ...
179 reason:"initWithFormat: the format can't be 'nil'"];
181 return ObjectiveJ.sprintf.apply(
this, Array.prototype.slice.call(arguments, 2));
204 - (
CPString)characterAtIndex:(CPUInteger)anIndex
206 return self.charAt(anIndex);
222 return self + ObjectiveJ.sprintf.apply(this, Array.prototype.slice.call(arguments, 2));
232 return self + aString;
247 - (
CPString)stringByPaddingToLength:(
unsigned)aLength withString:(
CPString)aString startingAtIndex:(CPUInteger)anIndex
249 if (
self.length == aLength)
252 if (aLength <
self.length)
253 return self.substr(0, aLength);
256 substring = aString.substring(anIndex),
257 difference = aLength - self.length;
259 while ((difference -= substring.length) >= 0)
262 if (-difference < substring.length)
263 string += substring.substring(0, -difference);
280 - (CPArray)componentsSeparatedByString:(
CPString)aString
282 return self.split(aString);
290 - (
CPString)substringFromIndex:(
unsigned)anIndex
292 return self.substr(anIndex);
300 - (
CPString)substringWithRange:(CPRange)aRange
302 if (aRange.location < 0 ||
CPMaxRange(aRange) >
self.length)
305 return self.substr(aRange.location, aRange.length);
315 - (
CPString)substringToIndex:(
unsigned)anIndex
317 if (anIndex >
self.length)
320 return self.substring(0, anIndex);
331 - (CPRange)rangeOfString:(
CPString)aString
333 return [
self rangeOfString:aString options:0];
353 - (CPRange)rangeOfString:(
CPString)aString options:(
int)aMask
355 return [
self rangeOfString:aString options:aMask range:nil];
377 - (CPRange)rangeOfString:(
CPString)aString options:(
int)aMask range:(CPrange)aRange
383 var
string = (aRange == nil) ?
self : [
self substringWithRange:aRange],
386 if (aMask & CPCaseInsensitiveSearch)
388 string =
string.toLowerCase();
389 aString = aString.toLowerCase();
392 if (aMask & CPBackwardsSearch)
394 location =
string.lastIndexOf(aString);
395 if (aMask & CPAnchoredSearch && location + aString.length !=
string.length)
398 else if (aMask & CPAnchoredSearch)
401 location =
string.indexOf(aString);
406 return CPMakeRange(location + (aRange ? aRange.location : 0), aString.length);
411 - (
CPString)stringByEscapingRegexControlCharacters
413 return self.replace(CPStringRegexEscapeExpression,
"\\$1");
425 return self.replace(
new RegExp([target stringByEscapingRegexControlCharacters],
"g"), replacement);
437 - (
CPString)stringByReplacingOccurrencesOfString:(
CPString)target withString:(
CPString)replacement options:(
int)options range:(CPRange)searchRange
439 var start =
self.substring(0, searchRange.location),
440 stringSegmentToSearch =
self.substr(searchRange.location, searchRange.length),
441 end =
self.substring(searchRange.location + searchRange.length,
self.length),
442 target = [target stringByEscapingRegexControlCharacters],
445 if (options & CPCaseInsensitiveSearch)
446 regExp =
new RegExp(target,
"gi");
448 regExp =
new RegExp(target,
"g");
450 return start + '' + stringSegmentToSearch.replace(regExp, replacement) + '' + end;
460 - (
CPString)stringByReplacingCharactersInRange:(CPRange)range withString:(
CPString)replacement
462 return '' + self.substring(0, range.location) + replacement + self.substring(range.location + range.length, self.length);
468 - (
CPString)stringByTrimmingWhitespace
470 return self.replace(CPStringRegexTrimWhitespace,
"");
480 - (CPComparisonResult)compare:(
CPString)aString
482 return [
self compare:aString options:nil];
490 - (CPComparisonResult)caseInsensitiveCompare:(
CPString)aString
492 return [
self compare:aString options:CPCaseInsensitiveSearch];
504 - (CPComparisonResult)compare:(
CPString)aString options:(
int)aMask
509 if (aString === CPStringNull)
515 if (aMask & CPCaseInsensitiveSearch)
517 lhs = lhs.toLowerCase();
518 rhs = rhs.toLowerCase();
523 lhs = lhs.stripDiacritics();
524 rhs = rhs.stripDiacritics();
543 - (CPComparisonResult)compare:(
CPString)aString options:(
int)aMask range:(CPRange)range
545 var lhs = [
self substringWithRange:range],
548 return [lhs compare:rhs options:aMask];
558 return aString && aString !=
"" &&
self.indexOf(aString) == 0;
568 return aString && aString !=
"" &&
self.length >= aString.length &&
self.lastIndexOf(aString) == (
self.length - aString.length);
573 if (
self === anObject)
576 if (!anObject || ![anObject isKindOfClass:[
CPString class]])
579 return [
self isEqualToString:anObject];
586 - (BOOL)isEqualToString:(
CPString)aString
588 return self == String(aString);
596 var UID = CPStringUIDs.valueForKey(
self);
600 UID = objj_generateObjectUID();
601 CPStringUIDs.setValueForKey(
self, UID);
614 return [
self commonPrefixWithString: aString options: 0];
628 min = MIN([lhs length], [rhs length]);
630 if (aMask & CPCaseInsensitiveSearch)
632 lhs = [lhs lowercaseString];
633 rhs = [rhs lowercaseString];
636 for (; len < min; len++)
638 if ([lhs characterAtIndex:len] !== [rhs characterAtIndex:len])
642 return [
self substringToIndex:len];
650 var parts =
self.split(/\b/g),
652 count = parts.length;
654 for (; i < count; i++)
656 if (i == 0 || (/\s$/).test(parts[i - 1]))
657 parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1).toLowerCase();
659 parts[i] = parts[i].toLowerCase();
661 return parts.join(
"");
669 return self.toLowerCase();
677 return self.toUpperCase();
683 - (double)doubleValue
685 return parseFloat(
self, 10);
694 var replaceRegExp =
new RegExp(
"^\\s*[\\+,\\-]?0*");
695 return RegExp(
"^[Y,y,t,T,1-9]").test(
self.replace(replaceRegExp,
''));
703 return parseFloat(
self, 10);
711 return parseInt(
self, 10);
720 - (CPArray)pathComponents
722 if (
self.length === 0)
728 var result =
self.split(
'/');
730 if (result[0] ===
"")
733 var index = result.length - 1;
737 if (result[index] ===
"")
742 while (result[index] ===
"")
743 result.splice(index--, 1);
757 + (
CPString)pathWithComponents:(CPArray)components
759 var size = components.length,
763 firstIsSlash = false;
767 var component = components[i],
768 lenMinusOne = component.length - 1;
770 if (lenMinusOne >= 0 && (component !==
"/" || firstRound))
772 if (lenMinusOne > 0 && component.indexOf(
"/",lenMinusOne) === lenMinusOne)
773 component = component.substring(0, lenMinusOne);
777 if (component ===
"/")
781 else if (!firstIsSlash)
784 firstIsSlash =
false;
802 return self.substr(
self.lastIndexOf(
'.') + 1);
812 var components = [
self pathComponents],
813 lastIndex = components.length - 1,
814 lastComponent = components[lastIndex];
816 return lastIndex > 0 && lastComponent ===
"/" ? components[lastIndex - 1] : lastComponent;
827 var components = [
self pathComponents],
828 addComponents = aString && aString !==
"/" ? [aString pathComponents] : [];
830 return [
CPString pathWithComponents:components.concat(addComponents)];
842 if (ext.indexOf(
'/') >= 0 ||
self.length === 0 ||
self ===
"/")
845 var components = [
self pathComponents],
846 last = components.length - 1;
848 if (last > 0 && components[last] ===
"/")
849 components.splice(last--, 1);
851 components[last] = components[last] + "." + ext;
853 return [
CPString pathWithComponents:components];
862 - (
CPString)stringByDeletingLastPathComponent
864 if (
self.length === 0)
866 else if (
self ===
"/")
869 var components = [
self pathComponents],
870 last = components.length - 1;
872 if (components[last] ===
"/")
875 components.splice(last, components.length - last);
877 return [
CPString pathWithComponents:components];
886 - (
CPString)stringByDeletingPathExtension
888 var extension = [
self pathExtension];
890 if (extension ===
"")
892 else if (
self.lastIndexOf(
'.') < 1)
895 return self.substr(0, [
self length] - (extension.length + 1));
898 - (
CPString)stringByStandardizingPath
907 @implementation CPString (JSON)
912 + (
CPString)JSONFromObject:(JSObject)anObject
914 return JSON.stringify(anObject);
920 - (JSObject)objectFromJSON
922 return JSON.parse(
self);
928 @implementation CPString (UUID)
939 g += FLOOR(RAND() * 0xF).toString(0xF);
947 var diacritics = [[192,198],[224,230],[231,231],[232,235],[236,239],[242,246],[249,252]],
948 normalized = [65,97,99,101,105,111,117];
950 String.prototype.stripDiacritics =
function()
954 for (var indexSource = 0; indexSource < this.length; indexSource++)
956 var code = this.charCodeAt(indexSource);
958 for (var i = 0; i < diacritics.length; i++)
960 var drange = diacritics[i];
962 if (code >= drange[0] && code <= drange[drange.length - 1])
964 code = normalized[i];
969 output += String.fromCharCode(code);