coffee-script 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/coffee-script.gemspec +4 -3
- data/examples/code.coffee +17 -17
- data/examples/poignant.coffee +45 -12
- data/examples/potion.coffee +11 -11
- data/examples/underscore.coffee +124 -115
- data/{lib/coffee_script → extras}/CoffeeScript.tmbundle/Preferences/CoffeeScript.tmPreferences +0 -0
- data/{lib/coffee_script → extras}/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +11 -26
- data/{lib/coffee_script → extras}/CoffeeScript.tmbundle/info.plist +0 -0
- data/extras/EXTRAS +20 -0
- data/extras/coffee.vim +111 -0
- data/lib/coffee-script.rb +1 -1
- data/lib/coffee_script/command_line.rb +6 -4
- data/lib/coffee_script/grammar.y +26 -17
- data/lib/coffee_script/lexer.rb +31 -13
- data/lib/coffee_script/narwhal/coffee-script.coffee +6 -6
- data/lib/coffee_script/narwhal/lib/coffee-script.js +1 -1
- data/lib/coffee_script/narwhal/loader.coffee +3 -3
- data/lib/coffee_script/nodes.rb +48 -32
- data/lib/coffee_script/parse_error.rb +5 -5
- data/lib/coffee_script/parser.rb +1267 -1234
- data/lib/coffee_script/rewriter.rb +80 -10
- data/package.json +1 -1
- metadata +7 -7
- data/examples/documents.coffee +0 -72
- data/examples/syntax_errors.coffee +0 -20
data/coffee-script.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'coffee-script'
|
3
|
-
s.version = '0.
|
4
|
-
s.date = '2010-1-
|
3
|
+
s.version = '0.3.0' # Keep version in sync with coffee-script.rb
|
4
|
+
s.date = '2010-1-26'
|
5
5
|
|
6
6
|
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
7
7
|
s.summary = "The CoffeeScript Compiler"
|
@@ -22,5 +22,6 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.require_paths = ['lib']
|
23
23
|
s.executables = ['coffee']
|
24
24
|
|
25
|
-
s.files = Dir['bin/*', 'examples/*', '
|
25
|
+
s.files = Dir['bin/*', 'examples/*', 'extras/**/*', 'lib/**/*',
|
26
|
+
'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
|
26
27
|
end
|
data/examples/code.coffee
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
# Functions:
|
2
|
-
square: x
|
2
|
+
square: (x) -> x * x
|
3
3
|
|
4
|
-
sum: x, y
|
4
|
+
sum: (x, y) -> x + y
|
5
5
|
|
6
|
-
odd: x
|
6
|
+
odd: (x) -> x % 2 isnt 0
|
7
7
|
|
8
|
-
even: x
|
8
|
+
even: (x) -> x % 2 is 0
|
9
9
|
|
10
|
-
run_loop:
|
11
|
-
fire_events(e
|
10
|
+
run_loop: ->
|
11
|
+
fire_events((e) -> e.stopPropagation())
|
12
12
|
listen()
|
13
13
|
wait()
|
14
14
|
|
@@ -22,14 +22,14 @@ spaced_out_multiline_object: {
|
|
22
22
|
three: new Idea()
|
23
23
|
|
24
24
|
inner_obj: {
|
25
|
-
freedom:
|
25
|
+
freedom: -> _.freedom()
|
26
26
|
}
|
27
27
|
}
|
28
28
|
|
29
29
|
# Arrays:
|
30
30
|
stooges: [{moe: 45}, {curly: 43}, {larry: 46}]
|
31
31
|
|
32
|
-
exponents: [(x
|
32
|
+
exponents: [(x) -> x, (x) -> x * x, (x) -> x * x * x]
|
33
33
|
|
34
34
|
empty: []
|
35
35
|
|
@@ -54,7 +54,7 @@ decoration: medal_of_honor if war_hero
|
|
54
54
|
go_to_sleep() unless coffee
|
55
55
|
|
56
56
|
# Returning early:
|
57
|
-
race:
|
57
|
+
race: ->
|
58
58
|
run()
|
59
59
|
walk()
|
60
60
|
crawl()
|
@@ -103,7 +103,7 @@ while true
|
|
103
103
|
|
104
104
|
# Lexical scoping.
|
105
105
|
v_1: 5
|
106
|
-
change_a_and_set_b:
|
106
|
+
change_a_and_set_b: ->
|
107
107
|
v_1: 10
|
108
108
|
v_2: 15
|
109
109
|
v_2: 20
|
@@ -128,7 +128,7 @@ activity: switch day
|
|
128
128
|
else go_to_work()
|
129
129
|
|
130
130
|
# Semicolons can optionally be used instead of newlines.
|
131
|
-
wednesday:
|
131
|
+
wednesday: -> eat_breakfast(); go_to_work(); eat_dinner()
|
132
132
|
|
133
133
|
# Array slice literals.
|
134
134
|
zero_to_nine: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
@@ -140,19 +140,19 @@ sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna
|
|
140
140
|
aliquam erat volutpat. Ut wisi enim ad."
|
141
141
|
|
142
142
|
# Inheritance and calling super.
|
143
|
-
Animal:
|
144
|
-
Animal::move: meters
|
143
|
+
Animal: ->
|
144
|
+
Animal::move: (meters) ->
|
145
145
|
alert(this.name + " moved " + meters + "m.")
|
146
146
|
|
147
|
-
Snake: name
|
147
|
+
Snake: (name) -> this.name: name
|
148
148
|
Snake extends Animal
|
149
|
-
Snake::move:
|
149
|
+
Snake::move: ->
|
150
150
|
alert('Slithering...')
|
151
151
|
super(5)
|
152
152
|
|
153
|
-
Horse: name
|
153
|
+
Horse: (name) -> this.name: name
|
154
154
|
Horse extends Animal
|
155
|
-
Horse::move:
|
155
|
+
Horse::move: ->
|
156
156
|
alert('Galloping...')
|
157
157
|
super(45)
|
158
158
|
|
data/examples/poignant.coffee
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
# ['toast', 'cheese', 'wine'].each { |food| print food.capitalize }
|
4
4
|
|
5
|
-
['toast', 'wine', 'cheese'].each(food
|
5
|
+
['toast', 'wine', 'cheese'].each (food) -> print(food.capitalize())
|
6
6
|
|
7
7
|
|
8
8
|
|
@@ -14,10 +14,43 @@
|
|
14
14
|
# end
|
15
15
|
|
16
16
|
LotteryTicket: {
|
17
|
-
get_picks:
|
18
|
-
set_picks: nums
|
19
|
-
get_purchase:
|
20
|
-
set_purchase: amount
|
17
|
+
get_picks: -> this.picks
|
18
|
+
set_picks: (nums) -> this.picks: nums
|
19
|
+
get_purchase: -> this.purchase
|
20
|
+
set_purchase: (amount) -> this.purchase: amount
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
# class << LotteryDraw
|
26
|
+
# def play
|
27
|
+
# result = LotteryTicket.new_random
|
28
|
+
# winners = {}
|
29
|
+
# @@tickets.each do |buyer, ticket_list|
|
30
|
+
# ticket_list.each do |ticket|
|
31
|
+
# score = ticket.score( result )
|
32
|
+
# next if score.zero?
|
33
|
+
# winners[buyer] ||= []
|
34
|
+
# winners[buyer] << [ ticket, score ]
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
# @@tickets.clear
|
38
|
+
# winners
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
|
42
|
+
LotteryDraw: {
|
43
|
+
play: ->
|
44
|
+
result: LotteryTicket.new_random()
|
45
|
+
winners: {}
|
46
|
+
this.tickets.each (buyer, ticket_list) ->
|
47
|
+
ticket_list.each (ticket) ->
|
48
|
+
score: ticket.score(result)
|
49
|
+
return if score is 0
|
50
|
+
winners[buyer] ||= []
|
51
|
+
winners[buyer].push([ticket, score])
|
52
|
+
this.tickets: {}
|
53
|
+
winners
|
21
54
|
}
|
22
55
|
|
23
56
|
|
@@ -32,8 +65,8 @@ LotteryTicket: {
|
|
32
65
|
# end
|
33
66
|
|
34
67
|
WishScanner: {
|
35
|
-
scan_for_a_wish:
|
36
|
-
wish: this.read().detect(thought
|
68
|
+
scan_for_a_wish: ->
|
69
|
+
wish: this.read().detect((thought) -> thought.index('wish: ') is 0)
|
37
70
|
wish.replace('wish: ', '')
|
38
71
|
}
|
39
72
|
|
@@ -78,7 +111,7 @@ WishScanner: {
|
|
78
111
|
Creature : {
|
79
112
|
|
80
113
|
# This method applies a hit taken during a fight.
|
81
|
-
hit: damage
|
114
|
+
hit: (damage) ->
|
82
115
|
p_up: Math.rand(this.charisma)
|
83
116
|
if p_up % 9 is 7
|
84
117
|
this.life += p_up / 4
|
@@ -87,7 +120,7 @@ Creature : {
|
|
87
120
|
if this.life <= 0 then puts("[" + this.name + " has died.]")
|
88
121
|
|
89
122
|
# This method takes one turn in a fight.
|
90
|
-
fight: enemy, weapon
|
123
|
+
fight: (enemy, weapon) ->
|
91
124
|
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
|
92
125
|
|
93
126
|
# Attack the opponent.
|
@@ -123,12 +156,12 @@ Creature : {
|
|
123
156
|
# Get evil idea and swap in code words
|
124
157
|
print("Enter your new idea: ")
|
125
158
|
idea: gets()
|
126
|
-
code_words.each(real, code
|
159
|
+
code_words.each((real, code) -> idea.replace(real, code))
|
127
160
|
|
128
161
|
# Save the jibberish to a new file
|
129
162
|
print("File encoded. Please enter a name for this idea: ")
|
130
163
|
idea_name: gets().strip()
|
131
|
-
File.open("idea-" + idea_name + '.txt', 'w', file
|
164
|
+
File.open("idea-" + idea_name + '.txt', 'w', (file) -> file.write(idea))
|
132
165
|
|
133
166
|
|
134
167
|
|
@@ -144,7 +177,7 @@ File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
|
|
144
177
|
# end
|
145
178
|
# end
|
146
179
|
|
147
|
-
wipe_mutterings_from: sentence
|
180
|
+
wipe_mutterings_from: (sentence) ->
|
148
181
|
throw new Error("cannot wipe mutterings") unless sentence.indexOf
|
149
182
|
while sentence.indexOf('(') >= 0
|
150
183
|
open: sentence.indexOf('(') - 1
|
data/examples/potion.coffee
CHANGED
@@ -8,7 +8,7 @@ print("Odelay!") for i in [1..5]
|
|
8
8
|
# add = (x, y): x + y.
|
9
9
|
# add(2, 4) string print
|
10
10
|
|
11
|
-
add: x, y
|
11
|
+
add: (x, y) -> x + y
|
12
12
|
print(add(2, 4))
|
13
13
|
|
14
14
|
|
@@ -31,7 +31,7 @@ print({language: 'Potion', pointless: true}['language'])
|
|
31
31
|
# minus = (x, y): x - y.
|
32
32
|
# minus (y=10, x=6)
|
33
33
|
|
34
|
-
minus: x, y
|
34
|
+
minus: (x, y) -> x - y
|
35
35
|
minus(6, 10)
|
36
36
|
|
37
37
|
|
@@ -53,8 +53,8 @@ for key, val of {dog: 'canine', cat: 'feline', fox: 'vulpine'}
|
|
53
53
|
# Person print = ():
|
54
54
|
# ('My name is ', /name, '.') join print.
|
55
55
|
|
56
|
-
Person:
|
57
|
-
Person::print:
|
56
|
+
Person: ->
|
57
|
+
Person::print: ->
|
58
58
|
print('My name is ' + this.name + '.')
|
59
59
|
|
60
60
|
|
@@ -71,9 +71,9 @@ print(p.name)
|
|
71
71
|
#
|
72
72
|
# Policeman ('Constable') print
|
73
73
|
|
74
|
-
Policeman: rank
|
74
|
+
Policeman: (rank) -> this.rank: rank
|
75
75
|
Policeman extends Person
|
76
|
-
Policeman::print:
|
76
|
+
Policeman::print: ->
|
77
77
|
print('My name is ' + this.name + " and I'm a " + this.rank + '.')
|
78
78
|
|
79
79
|
print(new Policeman('Constable'))
|
@@ -115,13 +115,13 @@ table: {
|
|
115
115
|
# String length = (): 10.
|
116
116
|
|
117
117
|
# this foul business...
|
118
|
-
String::length:
|
118
|
+
String::length: -> 10
|
119
119
|
|
120
120
|
|
121
121
|
# block = :
|
122
122
|
# 'potion' print.
|
123
123
|
|
124
|
-
block:
|
124
|
+
block: ->
|
125
125
|
print('potion')
|
126
126
|
|
127
127
|
|
@@ -178,7 +178,7 @@ if (3).gender?
|
|
178
178
|
# HomePage get = (url):
|
179
179
|
# session = url query ? at ('session').
|
180
180
|
|
181
|
-
HomePage::get: url
|
181
|
+
HomePage::get: (url) ->
|
182
182
|
session: url.query.session if url.query?
|
183
183
|
|
184
184
|
|
@@ -187,7 +187,7 @@ HomePage::get: url =>
|
|
187
187
|
# b /left = BTree ()
|
188
188
|
# b /right = BTree ()
|
189
189
|
|
190
|
-
BTree:
|
190
|
+
BTree: ->
|
191
191
|
b: new BTree()
|
192
192
|
b.left: new BTree()
|
193
193
|
b.right: new BTree()
|
@@ -199,7 +199,7 @@ b.right: new BTree()
|
|
199
199
|
# if (b ? /left):
|
200
200
|
# 'left path found!' print.
|
201
201
|
|
202
|
-
BTree:
|
202
|
+
BTree: ->
|
203
203
|
b: new BTree()
|
204
204
|
|
205
205
|
print('left path found!') if b.left?
|
data/examples/underscore.coffee
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
# Underscore.coffee
|
3
|
-
# (c)
|
3
|
+
# (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
|
4
4
|
# Underscore is freely distributable under the terms of the MIT license.
|
5
5
|
# Portions of Underscore are inspired by or borrowed from Prototype.js,
|
6
6
|
# Oliver Steele's Functional, and John Resig's Micro-Templating.
|
@@ -21,7 +21,7 @@
|
|
21
21
|
# If Underscore is called as a function, it returns a wrapped object that
|
22
22
|
# can be used OO-style. This wrapper holds altered versions of all the
|
23
23
|
# underscore functions. Wrapped objects may be chained.
|
24
|
-
wrapper: obj
|
24
|
+
wrapper: (obj) ->
|
25
25
|
this._wrapped: obj
|
26
26
|
this
|
27
27
|
|
@@ -31,7 +31,7 @@
|
|
31
31
|
|
32
32
|
|
33
33
|
# Create a safe reference to the Underscore object forreference below.
|
34
|
-
_: root._: obj
|
34
|
+
_: root._: (obj) -> new wrapper(obj)
|
35
35
|
|
36
36
|
|
37
37
|
# Export the Underscore object for CommonJS.
|
@@ -47,14 +47,14 @@
|
|
47
47
|
|
48
48
|
|
49
49
|
# Current version.
|
50
|
-
_.VERSION: '0.5.
|
50
|
+
_.VERSION: '0.5.7'
|
51
51
|
|
52
52
|
|
53
53
|
# ------------------------ Collection Functions: ---------------------------
|
54
54
|
|
55
55
|
# The cornerstone, an each implementation.
|
56
56
|
# Handles objects implementing forEach, arrays, and raw objects.
|
57
|
-
_.each: obj, iterator, context
|
57
|
+
_.each: (obj, iterator, context) ->
|
58
58
|
index: 0
|
59
59
|
try
|
60
60
|
return obj.forEach(iterator, context) if obj.forEach
|
@@ -68,36 +68,36 @@
|
|
68
68
|
|
69
69
|
# Return the results of applying the iterator to each element. Use JavaScript
|
70
70
|
# 1.6's version of map, if possible.
|
71
|
-
_.map: obj, iterator, context
|
71
|
+
_.map: (obj, iterator, context) ->
|
72
72
|
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
73
73
|
results: []
|
74
|
-
_.each
|
74
|
+
_.each obj, (value, index, list) ->
|
75
75
|
results.push(iterator.call(context, value, index, list))
|
76
76
|
results
|
77
77
|
|
78
78
|
|
79
79
|
# Reduce builds up a single result from a list of values. Also known as
|
80
80
|
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
|
81
|
-
_.reduce: obj, memo, iterator, context
|
81
|
+
_.reduce: (obj, memo, iterator, context) ->
|
82
82
|
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
|
83
|
-
_.each
|
83
|
+
_.each obj, (value, index, list) ->
|
84
84
|
memo: iterator.call(context, memo, value, index, list)
|
85
85
|
memo
|
86
86
|
|
87
87
|
|
88
88
|
# The right-associative version of reduce, also known as foldr. Uses
|
89
89
|
# JavaScript 1.8's version of reduceRight, if available.
|
90
|
-
_.reduceRight: obj, memo, iterator, context
|
90
|
+
_.reduceRight: (obj, memo, iterator, context) ->
|
91
91
|
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
|
92
|
-
_.each
|
92
|
+
_.each _.clone(_.toArray(obj)).reverse(), (value, index) ->
|
93
93
|
memo: iterator.call(context, memo, value, index, obj)
|
94
94
|
memo
|
95
95
|
|
96
96
|
|
97
97
|
# Return the first value which passes a truth test.
|
98
|
-
_.detect: obj, iterator, context
|
98
|
+
_.detect: (obj, iterator, context) ->
|
99
99
|
result: null
|
100
|
-
_.each
|
100
|
+
_.each obj, (value, index, list) ->
|
101
101
|
if iterator.call(context, value, index, list)
|
102
102
|
result: value
|
103
103
|
_.breakLoop()
|
@@ -106,47 +106,47 @@
|
|
106
106
|
|
107
107
|
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
108
108
|
# filter(), if it exists.
|
109
|
-
_.select: obj, iterator, context
|
109
|
+
_.select: (obj, iterator, context) ->
|
110
110
|
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
|
111
111
|
results: []
|
112
|
-
_.each
|
112
|
+
_.each obj, (value, index, list) ->
|
113
113
|
results.push(value) if iterator.call(context, value, index, list)
|
114
114
|
results
|
115
115
|
|
116
116
|
|
117
117
|
# Return all the elements for which a truth test fails.
|
118
|
-
_.reject: obj, iterator, context
|
118
|
+
_.reject: (obj, iterator, context) ->
|
119
119
|
results: []
|
120
|
-
_.each
|
120
|
+
_.each obj, (value, index, list) ->
|
121
121
|
results.push(value) if not iterator.call(context, value, index, list)
|
122
122
|
results
|
123
123
|
|
124
124
|
|
125
125
|
# Determine whether all of the elements match a truth test. Delegate to
|
126
126
|
# JavaScript 1.6's every(), if it is present.
|
127
|
-
_.all: obj, iterator, context
|
127
|
+
_.all: (obj, iterator, context) ->
|
128
128
|
iterator ||= _.identity
|
129
129
|
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
|
130
130
|
result: true
|
131
|
-
_.each
|
131
|
+
_.each obj, (value, index, list) ->
|
132
132
|
_.breakLoop() unless (result: result and iterator.call(context, value, index, list))
|
133
133
|
result
|
134
134
|
|
135
135
|
|
136
136
|
# Determine if at least one element in the object matches a truth test. Use
|
137
137
|
# JavaScript 1.6's some(), if it exists.
|
138
|
-
_.any: obj, iterator, context
|
138
|
+
_.any: (obj, iterator, context) ->
|
139
139
|
iterator ||= _.identity
|
140
140
|
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
|
141
141
|
result: false
|
142
|
-
_.each
|
142
|
+
_.each obj, (value, index, list) ->
|
143
143
|
_.breakLoop() if (result: iterator.call(context, value, index, list))
|
144
144
|
result
|
145
145
|
|
146
146
|
|
147
147
|
# Determine if a given value is included in the array or object,
|
148
148
|
# based on '==='.
|
149
|
-
_.include: obj, target
|
149
|
+
_.include: (obj, target) ->
|
150
150
|
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
|
151
151
|
for key, val of obj
|
152
152
|
return true if val is target
|
@@ -154,49 +154,49 @@
|
|
154
154
|
|
155
155
|
|
156
156
|
# Invoke a method with arguments on every item in a collection.
|
157
|
-
_.invoke: obj, method
|
157
|
+
_.invoke: (obj, method) ->
|
158
158
|
args: _.rest(arguments, 2)
|
159
159
|
(if method then val[method] else val).apply(val, args) for val in obj
|
160
160
|
|
161
161
|
|
162
162
|
# Convenience version of a common use case of map: fetching a property.
|
163
|
-
_.pluck: obj, key
|
164
|
-
_.map(obj, (val
|
163
|
+
_.pluck: (obj, key) ->
|
164
|
+
_.map(obj, ((val) -> val[key]))
|
165
165
|
|
166
166
|
|
167
167
|
# Return the maximum item or (item-based computation).
|
168
|
-
_.max: obj, iterator, context
|
168
|
+
_.max: (obj, iterator, context) ->
|
169
169
|
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
|
170
170
|
result: {computed: -Infinity}
|
171
|
-
_.each
|
171
|
+
_.each obj, (value, index, list) ->
|
172
172
|
computed: if iterator then iterator.call(context, value, index, list) else value
|
173
173
|
computed >= result.computed and (result: {value: value, computed: computed})
|
174
174
|
result.value
|
175
175
|
|
176
176
|
|
177
177
|
# Return the minimum element (or element-based computation).
|
178
|
-
_.min: obj, iterator, context
|
178
|
+
_.min: (obj, iterator, context) ->
|
179
179
|
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
|
180
180
|
result: {computed: Infinity}
|
181
|
-
_.each
|
181
|
+
_.each obj, (value, index, list) ->
|
182
182
|
computed: if iterator then iterator.call(context, value, index, list) else value
|
183
183
|
computed < result.computed and (result: {value: value, computed: computed})
|
184
184
|
result.value
|
185
185
|
|
186
186
|
|
187
187
|
# Sort the object's values by a criteria produced by an iterator.
|
188
|
-
_.sortBy: obj, iterator, context
|
189
|
-
_.pluck(((_.map
|
188
|
+
_.sortBy: (obj, iterator, context) ->
|
189
|
+
_.pluck(((_.map obj, (value, index, list) ->
|
190
190
|
{value: value, criteria: iterator.call(context, value, index, list)}
|
191
|
-
).sort(
|
191
|
+
).sort((left, right) ->
|
192
192
|
a: left.criteria; b: right.criteria
|
193
193
|
if a < b then -1 else if a > b then 1 else 0
|
194
|
-
), 'value')
|
194
|
+
)), 'value')
|
195
195
|
|
196
196
|
|
197
197
|
# Use a comparator function to figure out at what index an object should
|
198
198
|
# be inserted so as to maintain order. Uses binary search.
|
199
|
-
_.sortedIndex: array, obj, iterator
|
199
|
+
_.sortedIndex: (array, obj, iterator) ->
|
200
200
|
iterator ||= _.identity
|
201
201
|
low: 0; high: array.length
|
202
202
|
while low < high
|
@@ -206,7 +206,7 @@
|
|
206
206
|
|
207
207
|
|
208
208
|
# Convert anything iterable into a real, live array.
|
209
|
-
_.toArray: iterable
|
209
|
+
_.toArray: (iterable) ->
|
210
210
|
return [] if (!iterable)
|
211
211
|
return iterable.toArray() if (iterable.toArray)
|
212
212
|
return iterable if (_.isArray(iterable))
|
@@ -215,7 +215,7 @@
|
|
215
215
|
|
216
216
|
|
217
217
|
# Return the number of elements in an object.
|
218
|
-
_.size: obj
|
218
|
+
_.size: (obj) -> _.toArray(obj).length
|
219
219
|
|
220
220
|
|
221
221
|
# -------------------------- Array Functions: ------------------------------
|
@@ -223,7 +223,7 @@
|
|
223
223
|
# Get the first element of an array. Passing "n" will return the first N
|
224
224
|
# values in the array. Aliased as "head". The "guard" check allows it to work
|
225
225
|
# with _.map.
|
226
|
-
_.first: array, n, guard
|
226
|
+
_.first: (array, n, guard) ->
|
227
227
|
if n and not guard then slice.call(array, 0, n) else array[0]
|
228
228
|
|
229
229
|
|
@@ -231,35 +231,35 @@
|
|
231
231
|
# Especially useful on the arguments object. Passing an "index" will return
|
232
232
|
# the rest of the values in the array from that index onward. The "guard"
|
233
233
|
# check allows it to work with _.map.
|
234
|
-
_.rest: array, index, guard
|
234
|
+
_.rest: (array, index, guard) ->
|
235
235
|
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
236
236
|
|
237
237
|
|
238
238
|
# Get the last element of an array.
|
239
|
-
_.last: array
|
239
|
+
_.last: (array) -> array[array.length - 1]
|
240
240
|
|
241
241
|
|
242
242
|
# Trim out all falsy values from an array.
|
243
|
-
_.compact: array
|
243
|
+
_.compact: (array) -> array[i] for i in [0...array.length] when array[i]
|
244
244
|
|
245
245
|
|
246
246
|
# Return a completely flattened version of an array.
|
247
|
-
_.flatten: array
|
248
|
-
_.reduce
|
247
|
+
_.flatten: (array) ->
|
248
|
+
_.reduce array, [], (memo, value) ->
|
249
249
|
return memo.concat(_.flatten(value)) if _.isArray(value)
|
250
250
|
memo.push(value)
|
251
251
|
memo
|
252
252
|
|
253
253
|
|
254
254
|
# Return a version of the array that does not contain the specified value(s).
|
255
|
-
_.without: array
|
255
|
+
_.without: (array) ->
|
256
256
|
values: _.rest(arguments)
|
257
257
|
val for val in _.toArray(array) when not _.include(values, val)
|
258
258
|
|
259
259
|
|
260
260
|
# Produce a duplicate-free version of the array. If the array has already
|
261
261
|
# been sorted, you have the option of using a faster algorithm.
|
262
|
-
_.uniq: array, isSorted
|
262
|
+
_.uniq: (array, isSorted) ->
|
263
263
|
memo: []
|
264
264
|
for el, i in _.toArray(array)
|
265
265
|
memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
|
@@ -268,28 +268,27 @@
|
|
268
268
|
|
269
269
|
# Produce an array that contains every item shared between all the
|
270
270
|
# passed-in arrays.
|
271
|
-
_.intersect: array
|
271
|
+
_.intersect: (array) ->
|
272
272
|
rest: _.rest(arguments)
|
273
|
-
_.select
|
274
|
-
_.all
|
273
|
+
_.select _.uniq(array), (item) ->
|
274
|
+
_.all rest, (other) ->
|
275
275
|
_.indexOf(other, item) >= 0
|
276
276
|
|
277
277
|
|
278
278
|
# Zip together multiple lists into a single array -- elements that share
|
279
279
|
# an index go together.
|
280
|
-
_.zip:
|
281
|
-
|
282
|
-
length: _.max(_.pluck(args, 'length'))
|
280
|
+
_.zip: ->
|
281
|
+
length: _.max(_.pluck(arguments, 'length'))
|
283
282
|
results: new Array(length)
|
284
283
|
for i in [0...length]
|
285
|
-
results[i]: _.pluck(
|
284
|
+
results[i]: _.pluck(arguments, String(i))
|
286
285
|
results
|
287
286
|
|
288
287
|
|
289
288
|
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
|
290
289
|
# we need this function. Return the position of the first occurence of an
|
291
290
|
# item in an array, or -1 if the item is not included in the array.
|
292
|
-
_.indexOf: array, item
|
291
|
+
_.indexOf: (array, item) ->
|
293
292
|
return array.indexOf(item) if array.indexOf
|
294
293
|
i: 0; l: array.length
|
295
294
|
while l - i
|
@@ -299,7 +298,7 @@
|
|
299
298
|
|
300
299
|
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
|
301
300
|
# if possible.
|
302
|
-
_.lastIndexOf: array, item
|
301
|
+
_.lastIndexOf: (array, item) ->
|
303
302
|
return array.lastIndexOf(item) if array.lastIndexOf
|
304
303
|
i: array.length
|
305
304
|
while i
|
@@ -310,8 +309,8 @@
|
|
310
309
|
# Generate an integer Array containing an arithmetic progression. A port of
|
311
310
|
# the native Python range() function. See:
|
312
311
|
# http://docs.python.org/library/functions.html#range
|
313
|
-
_.range: start, stop, step
|
314
|
-
a:
|
312
|
+
_.range: (start, stop, step) ->
|
313
|
+
a: arguments
|
315
314
|
solo: a.length <= 1
|
316
315
|
i: start: if solo then 0 else a[0];
|
317
316
|
stop: if solo then a[0] else a[1];
|
@@ -331,45 +330,45 @@
|
|
331
330
|
|
332
331
|
# Create a function bound to a given object (assigning 'this', and arguments,
|
333
332
|
# optionally). Binding with arguments is also known as 'curry'.
|
334
|
-
_.bind: func, obj
|
333
|
+
_.bind: (func, obj) ->
|
335
334
|
args: _.rest(arguments, 2)
|
336
|
-
|
335
|
+
-> func.apply(obj or root, args.concat(arguments))
|
337
336
|
|
338
337
|
|
339
338
|
# Bind all of an object's methods to that object. Useful for ensuring that
|
340
339
|
# all callbacks defined on an object belong to it.
|
341
|
-
_.bindAll: obj
|
340
|
+
_.bindAll: (obj) ->
|
342
341
|
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
343
|
-
_.each(funcs, (f
|
342
|
+
_.each(funcs, (f) -> obj[f]: _.bind(obj[f], obj))
|
344
343
|
obj
|
345
344
|
|
346
345
|
|
347
346
|
# Delays a function for the given number of milliseconds, and then calls
|
348
347
|
# it with the arguments supplied.
|
349
|
-
_.delay: func, wait
|
348
|
+
_.delay: (func, wait) ->
|
350
349
|
args: _.rest(arguments, 2)
|
351
|
-
setTimeout((
|
350
|
+
setTimeout((-> func.apply(func, args)), wait)
|
352
351
|
|
353
352
|
|
354
353
|
# Defers a function, scheduling it to run after the current call stack has
|
355
354
|
# cleared.
|
356
|
-
_.defer: func
|
355
|
+
_.defer: (func) ->
|
357
356
|
_.delay.apply(_, [func, 1].concat(_.rest(arguments)))
|
358
357
|
|
359
358
|
|
360
359
|
# Returns the first function passed as an argument to the second,
|
361
360
|
# allowing you to adjust arguments, run code before and after, and
|
362
361
|
# conditionally execute the original function.
|
363
|
-
_.wrap: func, wrapper
|
364
|
-
|
362
|
+
_.wrap: (func, wrapper) ->
|
363
|
+
-> wrapper.apply(wrapper, [func].concat(arguments))
|
365
364
|
|
366
365
|
|
367
366
|
# Returns a function that is the composition of a list of functions, each
|
368
367
|
# consuming the return value of the function that follows.
|
369
|
-
_.compose:
|
370
|
-
funcs:
|
371
|
-
|
372
|
-
args:
|
368
|
+
_.compose: ->
|
369
|
+
funcs: arguments
|
370
|
+
->
|
371
|
+
args: arguments
|
373
372
|
for i in [(funcs.length - 1)..0]
|
374
373
|
args: [funcs[i].apply(this, args)]
|
375
374
|
args[0]
|
@@ -378,43 +377,43 @@
|
|
378
377
|
# ------------------------- Object Functions: ----------------------------
|
379
378
|
|
380
379
|
# Retrieve the names of an object's properties.
|
381
|
-
_.keys: obj
|
380
|
+
_.keys: (obj) ->
|
382
381
|
return _.range(0, obj.length) if _.isArray(obj)
|
383
382
|
key for key, val of obj
|
384
383
|
|
385
384
|
|
386
385
|
# Retrieve the values of an object's properties.
|
387
|
-
_.values: obj
|
386
|
+
_.values: (obj) ->
|
388
387
|
_.map(obj, _.identity)
|
389
388
|
|
390
389
|
|
391
390
|
# Return a sorted list of the function names available in Underscore.
|
392
|
-
_.functions: obj
|
393
|
-
_.select(_.keys(obj), key
|
391
|
+
_.functions: (obj) ->
|
392
|
+
_.select(_.keys(obj), (key) -> _.isFunction(obj[key])).sort()
|
394
393
|
|
395
394
|
|
396
395
|
# Extend a given object with all of the properties in a source object.
|
397
|
-
_.extend: destination, source
|
396
|
+
_.extend: (destination, source) ->
|
398
397
|
for key, val of source
|
399
398
|
destination[key]: val
|
400
399
|
destination
|
401
400
|
|
402
401
|
|
403
402
|
# Create a (shallow-cloned) duplicate of an object.
|
404
|
-
_.clone: obj
|
403
|
+
_.clone: (obj) ->
|
405
404
|
return obj.slice(0) if _.isArray(obj)
|
406
405
|
_.extend({}, obj)
|
407
406
|
|
408
407
|
|
409
408
|
# Invokes interceptor with the obj, and then returns obj.
|
410
409
|
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
411
|
-
_.tap: obj, interceptor
|
410
|
+
_.tap: (obj, interceptor) ->
|
412
411
|
interceptor(obj)
|
413
412
|
obj
|
414
413
|
|
415
414
|
|
416
415
|
# Perform a deep comparison to check if two objects are equal.
|
417
|
-
_.isEqual: a, b
|
416
|
+
_.isEqual: (a, b) ->
|
418
417
|
# Check object identity.
|
419
418
|
return true if a is b
|
420
419
|
# Different types?
|
@@ -450,93 +449,104 @@
|
|
450
449
|
|
451
450
|
|
452
451
|
# Is a given array or object empty?
|
453
|
-
_.isEmpty: obj
|
452
|
+
_.isEmpty: (obj) -> _.keys(obj).length is 0
|
454
453
|
|
455
454
|
|
456
455
|
# Is a given value a DOM element?
|
457
|
-
_.isElement: obj
|
456
|
+
_.isElement: (obj) -> obj and obj.nodeType is 1
|
458
457
|
|
459
458
|
|
460
459
|
# Is a given value an array?
|
461
|
-
_.isArray: obj
|
460
|
+
_.isArray: (obj) -> !!(obj and obj.concat and obj.unshift)
|
462
461
|
|
463
462
|
|
464
463
|
# Is a given variable an arguments object?
|
465
|
-
_.isArguments: obj
|
464
|
+
_.isArguments: (obj) -> obj and _.isNumber(obj.length) and not obj.concat and
|
465
|
+
not obj.substr and not obj.apply and not propertyIsEnumerable.call(obj, 'length')
|
466
466
|
|
467
467
|
|
468
468
|
# Is the given value a function?
|
469
|
-
_.isFunction: obj
|
469
|
+
_.isFunction: (obj) -> !!(obj and obj.constructor and obj.call and obj.apply)
|
470
470
|
|
471
471
|
|
472
472
|
# Is the given value a string?
|
473
|
-
_.isString: obj
|
473
|
+
_.isString: (obj) -> !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
474
474
|
|
475
475
|
|
476
476
|
# Is a given value a number?
|
477
|
-
_.isNumber: obj
|
477
|
+
_.isNumber: (obj) -> (obj is +obj) or toString.call(obj) is '[object Number]'
|
478
478
|
|
479
479
|
|
480
480
|
# Is a given value a Date?
|
481
|
-
_.isDate: obj
|
481
|
+
_.isDate: (obj) -> !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
482
482
|
|
483
483
|
|
484
484
|
# Is the given value a regular expression?
|
485
|
-
_.isRegExp: obj
|
485
|
+
_.isRegExp: (obj) -> !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
|
486
486
|
|
487
487
|
|
488
488
|
# Is the given value NaN -- this one is interesting. NaN != NaN, and
|
489
489
|
# isNaN(undefined) == true, so we make sure it's a number first.
|
490
|
-
_.isNaN: obj
|
490
|
+
_.isNaN: (obj) -> _.isNumber(obj) and window.isNaN(obj)
|
491
491
|
|
492
492
|
|
493
493
|
# Is a given value equal to null?
|
494
|
-
_.isNull: obj
|
494
|
+
_.isNull: (obj) -> obj is null
|
495
495
|
|
496
496
|
|
497
497
|
# Is a given variable undefined?
|
498
|
-
_.isUndefined: obj
|
498
|
+
_.isUndefined: (obj) -> typeof obj is 'undefined'
|
499
499
|
|
500
500
|
|
501
501
|
# -------------------------- Utility Functions: --------------------------
|
502
502
|
|
503
503
|
# Run Underscore.js in noConflict mode, returning the '_' variable to its
|
504
504
|
# previous owner. Returns a reference to the Underscore object.
|
505
|
-
_.noConflict:
|
505
|
+
_.noConflict: ->
|
506
506
|
root._: previousUnderscore
|
507
507
|
this
|
508
508
|
|
509
509
|
|
510
510
|
# Keep the identity function around for default iterators.
|
511
|
-
_.identity: value
|
511
|
+
_.identity: (value) -> value
|
512
512
|
|
513
513
|
|
514
514
|
# Break out of the middle of an iteration.
|
515
|
-
_.breakLoop:
|
515
|
+
_.breakLoop: -> throw breaker
|
516
516
|
|
517
517
|
|
518
518
|
# Generate a unique integer id (unique within the entire client session).
|
519
519
|
# Useful for temporary DOM ids.
|
520
520
|
idCounter: 0
|
521
|
-
_.uniqueId: prefix
|
521
|
+
_.uniqueId: (prefix) ->
|
522
522
|
(prefix or '') + idCounter++
|
523
523
|
|
524
524
|
|
525
|
+
# By default, Underscore uses ERB-style template delimiters, change the
|
526
|
+
# following template settings to use alternative delimiters.
|
527
|
+
_.templateSettings: {
|
528
|
+
start: '<%'
|
529
|
+
end: '%>'
|
530
|
+
interpolate: /<%=(.+?)%>/g
|
531
|
+
}
|
532
|
+
|
533
|
+
|
525
534
|
# JavaScript templating a-la ERB, pilfered from John Resig's
|
526
535
|
# "Secrets of the JavaScript Ninja", page 83.
|
527
|
-
|
528
|
-
|
536
|
+
# Single-quotea fix from Rick Strahl's version.
|
537
|
+
_.template: (str, data) ->
|
538
|
+
c: _.templateSettings
|
539
|
+
fn: new Function 'obj',
|
529
540
|
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
530
541
|
'with(obj){p.push(\'' +
|
531
|
-
str.
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
"');}return p.join('');")`
|
542
|
+
str.replace(/[\r\t\n]/g, " ")
|
543
|
+
.replace(new RegExp("'(?=[^"+c.end[0]+"]*"+c.end+")","g"),"\t")
|
544
|
+
.split("'").join("\\'")
|
545
|
+
.split("\t").join("'")
|
546
|
+
.replace(c.interpolate, "',$1,'")
|
547
|
+
.split(c.start).join("');")
|
548
|
+
.split(c.end).join("p.push('") +
|
549
|
+
"');}return p.join('');"
|
540
550
|
if data then fn(data) else fn
|
541
551
|
|
542
552
|
|
@@ -556,39 +566,38 @@
|
|
556
566
|
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
|
557
567
|
|
558
568
|
# Helper function to continue chaining intermediate results.
|
559
|
-
result: obj, chain
|
569
|
+
result: (obj, chain) ->
|
560
570
|
if chain then _(obj).chain() else obj
|
561
571
|
|
562
572
|
|
563
573
|
# Add all of the Underscore functions to the wrapper object.
|
564
|
-
_.each
|
574
|
+
_.each _.functions(_), (name) ->
|
565
575
|
method: _[name]
|
566
|
-
wrapper.prototype[name]:
|
567
|
-
|
568
|
-
|
569
|
-
result(method.apply(_, args), this._chain)
|
576
|
+
wrapper.prototype[name]: ->
|
577
|
+
unshift.call(arguments, this._wrapped)
|
578
|
+
result(method.apply(_, arguments), this._chain)
|
570
579
|
|
571
580
|
|
572
581
|
# Add all mutator Array functions to the wrapper.
|
573
|
-
_.each
|
582
|
+
_.each ['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], (name) ->
|
574
583
|
method: Array.prototype[name]
|
575
|
-
wrapper.prototype[name]:
|
584
|
+
wrapper.prototype[name]: ->
|
576
585
|
method.apply(this._wrapped, arguments)
|
577
586
|
result(this._wrapped, this._chain)
|
578
587
|
|
579
588
|
|
580
589
|
# Add all accessor Array functions to the wrapper.
|
581
|
-
_.each
|
590
|
+
_.each ['concat', 'join', 'slice'], (name) ->
|
582
591
|
method: Array.prototype[name]
|
583
|
-
wrapper.prototype[name]:
|
592
|
+
wrapper.prototype[name]: ->
|
584
593
|
result(method.apply(this._wrapped, arguments), this._chain)
|
585
594
|
|
586
595
|
|
587
596
|
# Start chaining a wrapped Underscore object.
|
588
|
-
wrapper::chain:
|
597
|
+
wrapper::chain: ->
|
589
598
|
this._chain: true
|
590
599
|
this
|
591
600
|
|
592
601
|
|
593
602
|
# Extracts the result from a wrapped and chained object.
|
594
|
-
wrapper::value:
|
603
|
+
wrapper::value: -> this._wrapped
|