Files
atom/src/stdlib/underscore-extensions.coffee
2013-03-12 10:38:05 -07:00

184 lines
4.8 KiB
CoffeeScript

_ = require 'underscore'
_.mixin
remove: (array, element) ->
index = array.indexOf(element)
array.splice(index, 1) if index >= 0
sum: (array) ->
sum = 0
sum += elt for elt in array
sum
adviseBefore: (object, methodName, advice) ->
original = object[methodName]
object[methodName] = (args...) ->
unless advice.apply(this, args) == false
original.apply(this, args)
escapeRegExp: (string) ->
# Referring to the table here:
# https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/regexp
# these characters should be escaped
# \ ^ $ * + ? . ( ) | { } [ ]
# These characters only have special meaning inside of brackets
# they do not need to be escaped, but they MAY be escaped
# without any adverse effects (to the best of my knowledge and casual testing)
# : ! , =
# my test "~!@#$%^&*(){}[]`/=?+\|-_;:'\",<.>".match(/[\#]/g)
specials = [
# order matters for these
"-"
"["
"]"
# order doesn't matter for any of these
"/"
"{"
"}"
"("
")"
"*"
"+"
"?"
"."
"\\"
"^"
"$"
"|"]
# I choose to escape every character with '\'
# even though only some strictly require it when inside of []
regex = RegExp('[' + specials.join('\\') + ']', 'g')
string.replace(regex, "\\$&");
humanizeEventName: (eventName) ->
if /:/.test(eventName)
[namespace, name] = eventName.split(':')
return "#{@humanizeEventName(namespace)}: #{@humanizeEventName(name)}"
words = eventName.split('-')
words.map(_.capitalize).join(' ')
capitalize: (word) ->
word[0].toUpperCase() + word[1..]
pluralize: (count=0, singular, plural=singular+'s') ->
if count is 1
"#{count} #{singular}"
else
"#{count} #{plural}"
camelize: (string) ->
string.replace /[_-]+(\w)/g, (m) -> m[1].toUpperCase()
dasherize: (string) ->
string = string[0].toLowerCase() + string[1..]
string.replace /([A-Z])|(_)/g, (m, letter, underscore) ->
if letter
"-" + letter.toLowerCase()
else
"-"
underscore: (string) ->
string = string[0].toLowerCase() + string[1..]
string.replace /([A-Z])|(-)/g, (m, letter, dash) ->
if letter
"_" + letter.toLowerCase()
else
"_"
losslessInvert: (hash) ->
inverted = {}
for key, value of hash
inverted[value] ?= []
inverted[value].push(key)
inverted
multiplyString: (string, n) ->
new Array(1 + n).join(string)
nextTick: (fn) ->
unless @messageChannel
@pendingNextTickFns = []
@messageChannel = new MessageChannel
@messageChannel.port1.onmessage = =>
fn() while fn = @pendingNextTickFns.shift()
@pendingNextTickFns.push(fn)
@messageChannel.port2.postMessage(0)
endsWith: (string, suffix) ->
string.indexOf(suffix, string.length - suffix.length) isnt -1
valueForKeyPath: (object, keyPath) ->
keys = keyPath.split('.')
for key in keys
object = object[key]
return unless object?
object
setValueForKeyPath: (object, keyPath, value) ->
keys = keyPath.split('.')
while keys.length > 1
key = keys.shift()
object[key] ?= {}
object = object[key]
object[keys.shift()] = value
compactObject: (object) ->
newObject = {}
for key, value of object
newObject[key] = value if value?
newObject
originalIsEqual = _.isEqual
extendedIsEqual = (a, b, aStack=[], bStack=[]) ->
return originalIsEqual(a, b) if a is b
return originalIsEqual(a, b) if _.isFunction(a) or _.isFunction(b)
return a.isEqual(b) if _.isFunction(a?.isEqual)
return b.isEqual(a) if _.isFunction(b?.isEqual)
stackIndex = aStack.length
while stackIndex--
return bStack[stackIndex] is b if aStack[stackIndex] is a
aStack.push(a)
bStack.push(b)
equal = false
if _.isArray(a) and _.isArray(b) and a.length is b.length
equal = true
for aElement, i in a
unless extendedIsEqual(aElement, b[i], aStack, bStack)
equal = false
break
else if _.isObject(a) and _.isObject(b)
aCtor = a.constructor
bCtor = b.constructor
aCtorValid = _.isFunction(aCtor) and aCtor instanceof aCtor
bCtorValid = _.isFunction(bCtor) and bCtor instanceof bCtor
if aCtor isnt bCtor and not (aCtorValid and bCtorValid)
equal = false
else
aKeyCount = 0
equal = true
for key, aValue of a
continue unless _.has(a, key)
aKeyCount++
unless _.has(b, key) and extendedIsEqual(aValue, b[key], aStack, bStack)
equal = false
break
if equal
bKeyCount = 0
for key, bValue of b
bKeyCount++ if _.has(b, key)
equal = aKeyCount is bKeyCount
else
equal = originalIsEqual(a, b)
aStack.pop()
bStack.pop()
equal
_.isEqual = (a, b) -> extendedIsEqual(a, b)