 String::title = -> (word.capitalize() for word in @split(' ')).join(' ')
 
 # Test if the string is title and there is at least one cased character.
-String::istitle = -> return true if @toString() is @title() and isCaseable(@); return false
-
-# Return a copy of the string with leading whitespace removed, 
+String::istitle = -> return true if "#{@}" is @title() and isCaseable(@); return false
+
+# Return a copy of the string with leading whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
-String::lstrip = (s) -> ss = '\\s\\s*'; ss = "(#{s})+" if s?; @.replace(new RegExp("^#{ss}"), '')
-
-# Return a copy of the string with trailing whitespace removed, 
+String::lstrip = (s)-> ss = '\\s\\s*'; ss = "(#{s})+" if s?; @replace(new RegExp("^#{ss}"), '')
+
+# Return a copy of the string with trailing whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
-String::rstrip = (s) -> ss = '\\s*\\s'; ss = "(#{s})+" if s?; @.replace(new RegExp("#{ss}$"), '')
+String::rstrip = (s)-> ss = '\\s*\\s'; ss = "(#{s})+" if s?; @replace(new RegExp("#{ss}$"), '')
     #TODO: implement this without regex:
     #`re = new RegExp(ss); i = @length; continue while re.test(@charAt(--i)); @[0...i+1]`
 
-# Return a copy of the string with leading and trailing whitespace removed, 
+# Return a copy of the string with leading and trailing whitespace removed,
 # if characters are passed in thoes characters will be removed instead.
 # Uses the native `trim` if it's available and only whitespace is being stripped.
-String::strip = (s) -> return @trim() if not s and @trim; @lstrip(s).rstrip(s)
+String::strip = (s)-> return @trim() if not s and @trim; @lstrip(s).rstrip(s)
 
 # Return the number of non-overlapping occurrences of a string in the string.
 # Optional arguments start and end are interpreted as in slice notation.
-String::count = (c, s, e) -> @slice(s, e).match(new RegExp(c, 'g'))?.length or 0
+String::count = (c, s, e)-> @slice(s, e).match(new RegExp(c, 'g'))?.length or 0
 
 # Return the lowest index in the string where a string is found.
 # Optional arguments start and end are interpreted as in slice notation.
 # Return -1 if no match if found.
-String::find = (c, s, e) -> i = @slice(s, e).indexOf(c); return -1 if i is -1; i+(s or 0)
+String::find = (c, s, e)-> i = @slice(s, e).indexOf(c); return -1 if i is -1; i+(s or 0)
 
 # Test if all characters in the string are alphanumeric.
 # Use `A-Za-z` instead of `A-z` becuase `A-z` matches `-` for some reason...
-String::isalnum = -> @match(/[A-Za-z0-9]+/)?[0] is @toString()
+String::isalnum = -> @match(/[A-Za-z0-9]+/)?[0] is "#{@}"
 
 # Test if all characters in the string are alphabetic.
-String::isalpha = -> @match(/[A-Za-z]+/)?[0] is @toString()
+String::isalpha = -> @match(/[A-Za-z]+/)?[0] is "#{@}"
 
 # Test if all characters in the string are digits.
-String::isdigit = -> @match(/\d+/)?[0] is @toString()
+String::isdigit = -> @match(/\d+/)?[0] is "#{@}"
 
 # Test if all characters in the string are whitespace.
-String::isspace = -> @match(/\s+/)?[0] is @toString()
-
-# Return a string which is the concatenation of the strings in an array. 
+String::isspace = -> @match(/\s+/)?[0] is "#{@}"
+
+# Return a string which is the concatenation of the strings in an array.
 # The separator between elements is the string.
-String::join = (a) -> a.join(@)
-
-# Test if the string contains a string.
+String::join = (a)-> a.join(@)
+
+# Return S left-justified in a string of length width. Padding is
+# done using the specified fill character (default is a space).
+String::ljust = (w, c=' ')-> [c,t]=["#{c}","#{@}"]; return t unless w and c; t="#{t}#{c}" while(t.length < w); t
+
+# Return S right-justified in a string of length width. Padding is
+# done using the specified fill character (default is a space)
+String::rjust = (w, c=' ')-> [c,t]=["#{c}","#{@}"]; return t unless w and c; t="#{c}#{t}" while(t.length < w); t
+
+# Pad a numeric string S with zeros on the left, to fill a field
+# of the specified width.  The string S is never truncated.
+String::zfill = (w)-> "#{@}".rjust(w, 0)
+
+# Test if the string contains any of the passed in strings.
 # Python strings do not have a 'contains' function, but javascript doesn't have a way of saying `'foo' in 'foobar'`.
 # With this method the python `in` check would be written like this: `'foobar'.contains('foo')`
-String::contains = (s) -> @indexOf(s) isnt -1
+String::contains = (s...)-> (return true if @indexOf(i) isnt -1) for i in s; return false
+
+# C style string formatting
+# `'%d %s and %d %s'.cformat(2, 'cats', 6, 'dogs')` --> '2 cats and 6 dogs'
+# `'%.2f liters'.cformat(2.2483545345)` --> '2.25 liters'
+# `'%02d:%02d.%02d'.cformat(9, 36, 28)` --> 09:36.28
+String::cformat = (args...)->
+    return '' unless @
+    pad = (v,l,c)-> "#{v}".zfill(l, c)
+    @replace(/%(s|\.?[0-9]*f|0?[0-9]*d)/g, (match, type)->
+        arg = args.shift()
+        if fmatch = type.match(/(\.?)([0-9]*)f/)
+            arg = parseFloat(arg)
+            if fmatch[1] is '.' and fmatch[2]
+                return "#{arg.toFixed(fmatch[2])}"
+            else
+                return "#{arg}"
+        if imatch = type.match(/(0?)([0-9]*)d/)
+            arg = parseInt(arg, 10)
+            if imatch.length > 1 and imatch[2]
+                return "#{arg}".rjust(imatch[2], imatch[1] or ' ')
+            else
+                return "#{arg}"
+        return "#{arg}"
+    )
+
+# Return an integer hash of the string
+String::hash = ()->
+    hash = 0
+    for i in [0...@length]
+        hash= (hash<<5)-hash+@charCodeAt(i)
+        hash= hash & hash # Convert to 32bit integer
+    return hash
 
 # TODO: implement these
 
-# S.center(width[, fillchar]) -> string
-# 
+# S.center(width[, fillchar])-> string
+#
 # Return S centered in a string of length width. Padding is
 # done using the specified fill character (default is a space)
 # String::center = ->
 
-# S.decode([encoding[,errors]]) -> object
-# 
+# S.decode([encoding[,errors]])-> object
+#
 # Decodes S using the codec registered for encoding. encoding defaults
 # to the default encoding. errors may be given to set a different error
 # handling scheme. Default is 'strict' meaning that encoding errors raise

# With optional start, test the string beginning at that position.
# With optional end, stop comparing the string at that position.
# Suffix can also be an array of strings to try.
String::endswith = (c, s, e) -> 
    t = @slice(s, e)
    for a in makeArray(c)
        return true if endswith(t, a)

# able to handle UnicodeDecodeErrors.
# String::decode = ->

# S.encode([encoding[,errors]])-> object
#
# Encodes S using the codec registered for encoding. encoding defaults
# to the default encoding. errors may be given to set a different error
# handling scheme. Default is 'strict' meaning that encoding errors raise
