coffee-script 0.1.6 → 0.2.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 +3 -3
- data/examples/code.coffee +43 -43
- data/examples/documents.coffee +17 -17
- data/examples/poignant.coffee +22 -22
- data/examples/underscore.coffee +457 -416
- data/lib/coffee-script.rb +2 -1
- data/lib/coffee_script/CoffeeScript.tmbundle/Syntaxes/CoffeeScript.tmLanguage +23 -4
- data/lib/coffee_script/command_line.rb +5 -4
- data/lib/coffee_script/grammar.y +99 -78
- data/lib/coffee_script/lexer.rb +91 -47
- data/lib/coffee_script/narwhal/coffee-script.coffee +16 -15
- data/lib/coffee_script/narwhal/{js → lib}/coffee-script.js +22 -17
- data/lib/coffee_script/narwhal/{js → lib/coffee-script}/loader.js +6 -4
- data/lib/coffee_script/narwhal/loader.coffee +3 -3
- data/lib/coffee_script/nodes.rb +307 -255
- data/lib/coffee_script/parse_error.rb +3 -2
- data/lib/coffee_script/parser.output +10284 -9773
- data/lib/coffee_script/parser.rb +1286 -1141
- data/lib/coffee_script/rewriter.rb +208 -0
- data/lib/coffee_script/scope.rb +15 -10
- data/package.json +9 -0
- metadata +6 -6
- data/lib/coffee_script/narwhal/js/launcher.js +0 -3
- data/lib/coffee_script/narwhal/launcher.coffee +0 -1
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 = '
|
3
|
+
s.version = '0.2.0' # Keep version in sync with coffee-script.rb
|
4
|
+
s.date = '2010-1-5'
|
5
5
|
|
6
6
|
s.homepage = "http://jashkenas.github.com/coffee-script/"
|
7
7
|
s.summary = "The CoffeeScript Compiler"
|
@@ -22,5 +22,5 @@ Gem::Specification.new do |s|
|
|
22
22
|
s.require_paths = ['lib']
|
23
23
|
s.executables = ['coffee']
|
24
24
|
|
25
|
-
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README']
|
25
|
+
s.files = Dir['bin/*', 'examples/*', 'lib/**/*', 'coffee-script.gemspec', 'LICENSE', 'README', 'package.json']
|
26
26
|
end
|
data/examples/code.coffee
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
# Functions:
|
2
|
-
square: x => x * x
|
2
|
+
square: x => x * x
|
3
3
|
|
4
|
-
sum: x, y => x + y
|
4
|
+
sum: x, y => x + y
|
5
5
|
|
6
|
-
odd: x => x % 2 is 0
|
6
|
+
odd: x => x % 2 is 0
|
7
7
|
|
8
|
-
even: x => x % 2 isnt 0
|
8
|
+
even: x => x % 2 isnt 0
|
9
9
|
|
10
10
|
run_loop: =>
|
11
|
-
fire_events(
|
11
|
+
fire_events(e => e.stopPropagation())
|
12
12
|
listen()
|
13
|
-
wait()
|
13
|
+
wait()
|
14
14
|
|
15
15
|
# Objects:
|
16
16
|
dense_object_literal: {one: 1, two: 2, three: 3}
|
@@ -22,14 +22,14 @@ spaced_out_multiline_object: {
|
|
22
22
|
three: new Idea()
|
23
23
|
|
24
24
|
inner_obj: {
|
25
|
-
freedom: => _.freedom()
|
25
|
+
freedom: => _.freedom()
|
26
26
|
}
|
27
27
|
}
|
28
28
|
|
29
29
|
# Arrays:
|
30
|
-
stooges
|
30
|
+
stooges: [{moe: 45}, {curly: 43}, {larry: 46}]
|
31
31
|
|
32
|
-
exponents
|
32
|
+
exponents: [(x => x), (x => x * x), (x => x * x * x)]
|
33
33
|
|
34
34
|
empty: []
|
35
35
|
|
@@ -45,9 +45,9 @@ if submarine.shields_up
|
|
45
45
|
else if submarine.sinking
|
46
46
|
abandon_ship()
|
47
47
|
else
|
48
|
-
run_away()
|
48
|
+
run_away()
|
49
49
|
|
50
|
-
eldest: if 25 > 21 then liz else marge
|
50
|
+
eldest: if 25 > 21 then liz else marge
|
51
51
|
|
52
52
|
decoration: medal_of_honor if war_hero
|
53
53
|
|
@@ -58,8 +58,8 @@ race: =>
|
|
58
58
|
run()
|
59
59
|
walk()
|
60
60
|
crawl()
|
61
|
-
if tired then return sleep()
|
62
|
-
race()
|
61
|
+
if tired then return sleep()
|
62
|
+
race()
|
63
63
|
|
64
64
|
# Conditional assignment:
|
65
65
|
good ||= evil
|
@@ -81,54 +81,54 @@ try
|
|
81
81
|
dogs_and_cats_living_together()
|
82
82
|
throw "up"
|
83
83
|
catch error
|
84
|
-
print(
|
84
|
+
print(error)
|
85
85
|
finally
|
86
|
-
clean_up()
|
86
|
+
clean_up()
|
87
87
|
|
88
|
-
try all_hell_breaks_loose() catch error print(error) finally clean_up()
|
88
|
+
try all_hell_breaks_loose() catch error then print(error) finally clean_up()
|
89
89
|
|
90
90
|
# While loops, break and continue.
|
91
91
|
while demand > supply
|
92
92
|
sell()
|
93
|
-
restock()
|
93
|
+
restock()
|
94
94
|
|
95
|
-
while supply > demand then buy()
|
95
|
+
while supply > demand then buy()
|
96
96
|
|
97
97
|
while true
|
98
98
|
break if broken
|
99
|
-
continue if continuing
|
99
|
+
continue if continuing
|
100
100
|
|
101
101
|
# Unary operators.
|
102
102
|
!!true
|
103
103
|
|
104
104
|
# Lexical scoping.
|
105
|
-
|
105
|
+
v_1: 5
|
106
106
|
change_a_and_set_b: =>
|
107
|
-
|
108
|
-
|
109
|
-
|
107
|
+
v_1: 10
|
108
|
+
v_2: 15
|
109
|
+
v_2: 20
|
110
110
|
|
111
111
|
# Array comprehensions.
|
112
|
-
supper: food.capitalize() for food in ['toast', 'cheese', 'wine']
|
112
|
+
supper: food.capitalize() for food in ['toast', 'cheese', 'wine']
|
113
113
|
|
114
|
-
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade']
|
114
|
+
drink(bottle) for bottle, i in ['soda', 'wine', 'lemonade'] when even(i)
|
115
115
|
|
116
116
|
# Switch statements ("else" serves as a default).
|
117
117
|
activity: switch day
|
118
|
-
when "Tuesday" then eat_breakfast()
|
119
|
-
when "Sunday" then go_to_church()
|
120
|
-
when "Saturday" then go_to_the_park()
|
121
|
-
when "Wednesday"
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
else go_to_work()
|
118
|
+
when "Tuesday" then eat_breakfast()
|
119
|
+
when "Sunday" then go_to_church()
|
120
|
+
when "Saturday" then go_to_the_park()
|
121
|
+
when "Wednesday"
|
122
|
+
if day is bingo_day
|
123
|
+
go_to_bingo()
|
124
|
+
else
|
125
|
+
eat_breakfast()
|
126
|
+
go_to_work()
|
127
|
+
eat_dinner()
|
128
|
+
else go_to_work()
|
129
129
|
|
130
130
|
# Semicolons can optionally be used instead of newlines.
|
131
|
-
wednesday: => eat_breakfast(); go_to_work(); eat_dinner()
|
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,21 +140,21 @@ 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: =>
|
143
|
+
Animal: =>
|
144
144
|
Animal.prototype.move: meters =>
|
145
|
-
alert(this.name + " moved " + meters + "m.")
|
145
|
+
alert(this.name + " moved " + meters + "m.")
|
146
146
|
|
147
|
-
Snake: name => this.name: name
|
147
|
+
Snake: name => this.name: name
|
148
148
|
Snake extends Animal
|
149
149
|
Snake.prototype.move: =>
|
150
150
|
alert('Slithering...')
|
151
|
-
super(5)
|
151
|
+
super(5)
|
152
152
|
|
153
|
-
Horse: name => this.name: name
|
153
|
+
Horse: name => this.name: name
|
154
154
|
Horse extends Animal
|
155
155
|
Horse.prototype.move: =>
|
156
156
|
alert('Galloping...')
|
157
|
-
super(45)
|
157
|
+
super(45)
|
158
158
|
|
159
159
|
sam: new Snake("Sammy the Snake")
|
160
160
|
tom: new Horse("Tommy the Horse")
|
data/examples/documents.coffee
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Document Model
|
2
2
|
dc.model.Document: dc.Model.extend({
|
3
3
|
|
4
|
-
constructor: attributes => this.base(attributes)
|
4
|
+
constructor: attributes => this.base(attributes)
|
5
5
|
|
6
6
|
# For display, show either the highlighted search results, or the summary,
|
7
7
|
# if no highlights are available.
|
@@ -9,22 +9,22 @@ dc.model.Document: dc.Model.extend({
|
|
9
9
|
# version of the summary has all runs of whitespace squeezed out.
|
10
10
|
displaySummary: =>
|
11
11
|
text: this.get('highlight') or this.get('summary') or ''
|
12
|
-
text and text.replace(/\s+/g, ' ')
|
12
|
+
text and text.replace(/\s+/g, ' ')
|
13
13
|
|
14
14
|
# Return a list of the document's metadata. Think about caching this on the
|
15
15
|
# document by binding to Metadata, instead of on-the-fly.
|
16
16
|
metadata: =>
|
17
17
|
docId: this.id
|
18
|
-
_.select(Metadata.models()
|
19
|
-
|
20
|
-
instance
|
18
|
+
_.select(Metadata.models(), (meta =>
|
19
|
+
_.any(meta.get('instances'), instance =>
|
20
|
+
instance.document_id is docId)))
|
21
21
|
|
22
22
|
bookmark: pageNumber =>
|
23
23
|
bookmark: new dc.model.Bookmark({title: this.get('title'), page_number: pageNumber, document_id: this.id})
|
24
|
-
Bookmarks.create(bookmark)
|
24
|
+
Bookmarks.create(bookmark)
|
25
25
|
|
26
26
|
# Inspect.
|
27
|
-
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
|
27
|
+
toString: => 'Document ' + this.id + ' "' + this.get('title') + '"'
|
28
28
|
|
29
29
|
})
|
30
30
|
|
@@ -37,31 +37,31 @@ dc.model.DocumentSet: dc.model.RESTfulSet.extend({
|
|
37
37
|
|
38
38
|
constructor: options =>
|
39
39
|
this.base(options)
|
40
|
-
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
|
40
|
+
_.bindAll(this, 'downloadSelectedViewers', 'downloadSelectedPDF', 'downloadSelectedFullText')
|
41
41
|
|
42
|
-
selected: => _.select(this.models(), m => m.get('selected')
|
42
|
+
selected: => _.select(this.models(), m => m.get('selected'))
|
43
43
|
|
44
|
-
selectedIds: => _.pluck(this.selected(), 'id')
|
44
|
+
selectedIds: => _.pluck(this.selected(), 'id')
|
45
45
|
|
46
|
-
countSelected: => this.selected().length
|
46
|
+
countSelected: => this.selected().length
|
47
47
|
|
48
48
|
downloadSelectedViewers: =>
|
49
|
-
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip')
|
49
|
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_viewer.zip')
|
50
50
|
|
51
51
|
downloadSelectedPDF: =>
|
52
|
-
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url'))
|
53
|
-
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
|
52
|
+
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('pdf_url'))
|
53
|
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_pdfs.zip')
|
54
54
|
|
55
55
|
downloadSelectedFullText: =>
|
56
|
-
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url'))
|
57
|
-
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
|
56
|
+
if this.countSelected() <= 1 then return window.open(this.selected()[0].get('full_text_url'))
|
57
|
+
dc.app.download('/download/' + this.selectedIds().join('/') + '/document_text.zip')
|
58
58
|
|
59
59
|
# We override "_onModelEvent" to fire selection changed events when documents
|
60
60
|
# change their selected state.
|
61
61
|
_onModelEvent: e, model =>
|
62
62
|
this.base(e, model)
|
63
63
|
fire: e == dc.Model.CHANGED and model.hasChanged('selected')
|
64
|
-
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
|
64
|
+
if fire then _.defer(_(this.fire).bind(this, this.SELECTION_CHANGED, this))
|
65
65
|
|
66
66
|
})
|
67
67
|
|
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(
|
5
|
+
['toast', 'wine', 'cheese'].each(food => print(food.capitalize()))
|
6
6
|
|
7
7
|
|
8
8
|
|
@@ -14,10 +14,10 @@
|
|
14
14
|
# end
|
15
15
|
|
16
16
|
LotteryTicket: {
|
17
|
-
get_picks: => this.picks
|
18
|
-
set_picks: nums => this.picks: nums
|
19
|
-
get_purchase: => this.purchase
|
20
|
-
set_purchase: amount => this.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
21
|
}
|
22
22
|
|
23
23
|
|
@@ -33,8 +33,8 @@ LotteryTicket: {
|
|
33
33
|
|
34
34
|
WishScanner: {
|
35
35
|
scan_for_a_wish: =>
|
36
|
-
wish: this.read().detect(
|
37
|
-
wish.replace('wish: ', '')
|
36
|
+
wish: this.read().detect(thought => thought.index('wish: ') is 0)
|
37
|
+
wish.replace('wish: ', '')
|
38
38
|
}
|
39
39
|
|
40
40
|
|
@@ -79,28 +79,28 @@ Creature : {
|
|
79
79
|
|
80
80
|
# This method applies a hit taken during a fight.
|
81
81
|
hit: damage =>
|
82
|
-
p_up: Math.rand(
|
82
|
+
p_up: Math.rand(this.charisma)
|
83
83
|
if p_up % 9 is 7
|
84
84
|
this.life += p_up / 4
|
85
|
-
puts(
|
85
|
+
puts("[" + this.name + " magick powers up " + p_up + "!]")
|
86
86
|
this.life -= damage
|
87
|
-
if this.life <= 0 then puts(
|
87
|
+
if this.life <= 0 then puts("[" + this.name + " has died.]")
|
88
88
|
|
89
89
|
# This method takes one turn in a fight.
|
90
90
|
fight: enemy, weapon =>
|
91
|
-
if this.life <= 0 then return puts(
|
91
|
+
if this.life <= 0 then return puts("[" + this.name + "is too dead to fight!]")
|
92
92
|
|
93
93
|
# Attack the opponent.
|
94
|
-
your_hit: Math.rand(
|
95
|
-
puts(
|
96
|
-
enemy.hit(
|
94
|
+
your_hit: Math.rand(this.strength + weapon)
|
95
|
+
puts("[You hit with " + your_hit + "points of damage!]")
|
96
|
+
enemy.hit(your_hit)
|
97
97
|
|
98
98
|
# Retaliation.
|
99
|
-
puts(
|
99
|
+
puts(enemy)
|
100
100
|
if enemy.life > 0
|
101
|
-
enemy_hit: Math.rand(
|
102
|
-
puts(
|
103
|
-
this.hit(
|
101
|
+
enemy_hit: Math.rand(enemy.strength + enemy.weapon)
|
102
|
+
puts("[Your enemy hit with " + enemy_hit + "points of damage!]")
|
103
|
+
this.hit(enemy_hit)
|
104
104
|
|
105
105
|
}
|
106
106
|
|
@@ -123,12 +123,12 @@ Creature : {
|
|
123
123
|
# Get evil idea and swap in code words
|
124
124
|
print("Enter your new idea: ")
|
125
125
|
idea: gets()
|
126
|
-
code_words.each(
|
126
|
+
code_words.each(real, code => idea.replace(real, code))
|
127
127
|
|
128
128
|
# Save the jibberish to a new file
|
129
129
|
print("File encoded. Please enter a name for this idea: ")
|
130
130
|
idea_name: gets().strip()
|
131
|
-
File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea)
|
131
|
+
File.open("idea-" + idea_name + '.txt', 'w', file => file.write(idea))
|
132
132
|
|
133
133
|
|
134
134
|
|
@@ -149,5 +149,5 @@ wipe_mutterings_from: sentence =>
|
|
149
149
|
while sentence.indexOf('(') >= 0
|
150
150
|
open: sentence.indexOf('(') - 1
|
151
151
|
close: sentence.indexOf(')') + 1
|
152
|
-
sentence: sentence[0..open] + sentence[close..sentence.length]
|
153
|
-
sentence
|
152
|
+
sentence: sentence[0..open] + sentence[close..sentence.length]
|
153
|
+
sentence
|
data/examples/underscore.coffee
CHANGED
@@ -1,107 +1,126 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
#
|
56
|
-
#
|
57
|
-
_.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
_.
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
1
|
+
|
2
|
+
# Underscore.coffee
|
3
|
+
# (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
|
4
|
+
# Underscore is freely distributable under the terms of the MIT license.
|
5
|
+
# Portions of Underscore are inspired by or borrowed from Prototype.js,
|
6
|
+
# Oliver Steele's Functional, and John Resig's Micro-Templating.
|
7
|
+
# For all details and documentation:
|
8
|
+
# http://documentcloud.github.com/underscore/
|
9
|
+
|
10
|
+
|
11
|
+
# ------------------------- Baseline setup ---------------------------------
|
12
|
+
|
13
|
+
# Establish the root object, "window" in the browser, or "global" on the server.
|
14
|
+
root: this
|
15
|
+
|
16
|
+
|
17
|
+
# Save the previous value of the "_" variable.
|
18
|
+
previousUnderscore: root._
|
19
|
+
|
20
|
+
|
21
|
+
# If Underscore is called as a function, it returns a wrapped object that
|
22
|
+
# can be used OO-style. This wrapper holds altered versions of all the
|
23
|
+
# underscore functions. Wrapped objects may be chained.
|
24
|
+
wrapper: obj =>
|
25
|
+
this._wrapped: obj
|
26
|
+
this
|
27
|
+
|
28
|
+
|
29
|
+
# Establish the object that gets thrown to break out of a loop iteration.
|
30
|
+
breaker: if typeof(StopIteration) is 'undefined' then '__break__' else StopIteration
|
31
|
+
|
32
|
+
|
33
|
+
# Create a safe reference to the Underscore object for reference below.
|
34
|
+
_: root._: obj => new wrapper(obj)
|
35
|
+
|
36
|
+
|
37
|
+
# Export the Underscore object for CommonJS.
|
38
|
+
if typeof(exports) != 'undefined' then exports._: _
|
39
|
+
|
40
|
+
|
41
|
+
# Create quick reference variables for speed access to core prototypes.
|
42
|
+
slice: Array.prototype.slice
|
43
|
+
unshift: Array.prototype.unshift
|
44
|
+
toString: Object.prototype.toString
|
45
|
+
hasOwnProperty: Object.prototype.hasOwnProperty
|
46
|
+
propertyIsEnumerable: Object.prototype.propertyIsEnumerable
|
47
|
+
|
48
|
+
|
49
|
+
# Current version.
|
50
|
+
_.VERSION: '0.5.3'
|
51
|
+
|
52
|
+
|
53
|
+
# ------------------------ Collection Functions: ---------------------------
|
54
|
+
|
55
|
+
# The cornerstone, an each implementation.
|
56
|
+
# Handles objects implementing forEach, arrays, and raw objects.
|
57
|
+
_.each: obj, iterator, context =>
|
58
|
+
index: 0
|
59
|
+
try
|
60
|
+
return obj.forEach(iterator, context) if obj.forEach
|
61
|
+
if _.isArray(obj) or _.isArguments(obj)
|
62
|
+
return iterator.call(context, obj[i], i, obj) for i in [0...obj.length]
|
63
|
+
iterator.call(context, val, key, obj) for val, key in obj
|
64
|
+
catch e
|
65
|
+
throw e if e isnt breaker
|
66
|
+
obj
|
67
|
+
|
68
|
+
|
69
|
+
# Return the results of applying the iterator to each element. Use JavaScript
|
70
|
+
# 1.6's version of map, if possible.
|
71
|
+
_.map: obj, iterator, context =>
|
72
|
+
return obj.map(iterator, context) if (obj and _.isFunction(obj.map))
|
73
|
+
results: []
|
74
|
+
_.each(obj) value, index, list =>
|
75
|
+
results.push(iterator.call(context, value, index, list))
|
76
|
+
results
|
77
|
+
|
78
|
+
|
79
|
+
# Reduce builds up a single result from a list of values. Also known as
|
80
|
+
# inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
|
81
|
+
_.reduce: obj, memo, iterator, context =>
|
82
|
+
return obj.reduce(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduce))
|
83
|
+
_.each(obj) value, index, list =>
|
84
|
+
memo: iterator.call(context, memo, value, index, list)
|
85
|
+
memo
|
86
|
+
|
87
|
+
|
88
|
+
# The right-associative version of reduce, also known as foldr. Uses
|
89
|
+
# JavaScript 1.8's version of reduceRight, if available.
|
90
|
+
_.reduceRight: obj, memo, iterator, context =>
|
91
|
+
return obj.reduceRight(_.bind(iterator, context), memo) if (obj and _.isFunction(obj.reduceRight))
|
92
|
+
_.each(_.clone(_.toArray(obj)).reverse()) value, index =>
|
93
|
+
memo: iterator.call(context, memo, value, index, obj)
|
94
|
+
memo
|
95
|
+
|
80
96
|
|
81
97
|
# Return the first value which passes a truth test.
|
82
98
|
_.detect: obj, iterator, context =>
|
83
99
|
result: null
|
84
|
-
_.each(obj
|
100
|
+
_.each(obj) value, index, list =>
|
85
101
|
if iterator.call(context, value, index, list)
|
86
102
|
result: value
|
87
|
-
_.breakLoop()
|
88
|
-
result
|
103
|
+
_.breakLoop()
|
104
|
+
result
|
105
|
+
|
89
106
|
|
90
107
|
# Return all the elements that pass a truth test. Use JavaScript 1.6's
|
91
108
|
# filter(), if it exists.
|
92
109
|
_.select: obj, iterator, context =>
|
93
|
-
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
|
110
|
+
if obj and _.isFunction(obj.filter) then return obj.filter(iterator, context)
|
94
111
|
results: []
|
95
|
-
_.each(obj
|
96
|
-
results.push(value) if iterator.call(context, value, index, list)
|
97
|
-
results
|
112
|
+
_.each(obj) value, index, list =>
|
113
|
+
results.push(value) if iterator.call(context, value, index, list)
|
114
|
+
results
|
115
|
+
|
98
116
|
|
99
117
|
# Return all the elements for which a truth test fails.
|
100
118
|
_.reject: obj, iterator, context =>
|
101
119
|
results: []
|
102
|
-
_.each(obj
|
103
|
-
results.push(value) if not iterator.call(context, value, index, list)
|
104
|
-
results
|
120
|
+
_.each(obj) value, index, list =>
|
121
|
+
results.push(value) if not iterator.call(context, value, index, list)
|
122
|
+
results
|
123
|
+
|
105
124
|
|
106
125
|
# Determine whether all of the elements match a truth test. Delegate to
|
107
126
|
# JavaScript 1.6's every(), if it is present.
|
@@ -109,9 +128,10 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
109
128
|
iterator ||= _.identity
|
110
129
|
return obj.every(iterator, context) if obj and _.isFunction(obj.every)
|
111
130
|
result: true
|
112
|
-
_.each(obj
|
113
|
-
_.breakLoop() unless result: result and iterator.call(context, value, index, list)
|
114
|
-
result
|
131
|
+
_.each(obj) value, index, list =>
|
132
|
+
_.breakLoop() unless (result: result and iterator.call(context, value, index, list))
|
133
|
+
result
|
134
|
+
|
115
135
|
|
116
136
|
# Determine if at least one element in the object matches a truth test. Use
|
117
137
|
# JavaScript 1.6's some(), if it exists.
|
@@ -119,193 +139,193 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
119
139
|
iterator ||= _.identity
|
120
140
|
return obj.some(iterator, context) if obj and _.isFunction(obj.some)
|
121
141
|
result: false
|
122
|
-
_.each(obj
|
123
|
-
_.breakLoop() if (result: iterator.call(context, value, index, list))
|
124
|
-
result
|
142
|
+
_.each(obj) value, index, list =>
|
143
|
+
_.breakLoop() if (result: iterator.call(context, value, index, list))
|
144
|
+
result
|
145
|
+
|
125
146
|
|
126
147
|
# Determine if a given value is included in the array or object,
|
127
148
|
# based on '==='.
|
128
149
|
_.include: obj, target =>
|
129
150
|
return _.indexOf(obj, target) isnt -1 if _.isArray(obj)
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
151
|
+
for val in obj
|
152
|
+
return true if val is target
|
153
|
+
false
|
154
|
+
|
134
155
|
|
135
156
|
# Invoke a method with arguments on every item in a collection.
|
136
157
|
_.invoke: obj, method =>
|
137
158
|
args: _.rest(arguments, 2)
|
138
|
-
|
139
|
-
|
159
|
+
(if method then val[method] else val).apply(val, args) for val in obj
|
160
|
+
|
140
161
|
|
141
162
|
# Convenience version of a common use case of map: fetching a property.
|
142
163
|
_.pluck: obj, key =>
|
143
|
-
_.map(obj, (
|
164
|
+
_.map(obj, (val => val[key]))
|
165
|
+
|
144
166
|
|
145
167
|
# Return the maximum item or (item-based computation).
|
146
168
|
_.max: obj, iterator, context =>
|
147
|
-
return Math.max.apply(Math, obj) if
|
169
|
+
return Math.max.apply(Math, obj) if not iterator and _.isArray(obj)
|
148
170
|
result: {computed: -Infinity}
|
149
|
-
_.each(obj
|
150
|
-
computed: if iterator then iterator.call(context, value, index, list) else value
|
151
|
-
computed >= result.computed and (result: {value: value, computed: computed})
|
152
|
-
result.value
|
153
|
-
|
154
|
-
|
155
|
-
#
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
#
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
#
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
#
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
#
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
#
|
200
|
-
|
201
|
-
#
|
202
|
-
#
|
203
|
-
#
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
211
|
-
#
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
#
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
#
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
#
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
#
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
#
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
#
|
248
|
-
#
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
#
|
257
|
-
#
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
#
|
279
|
-
#
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
#
|
289
|
-
#
|
290
|
-
#
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
# }
|
308
|
-
# };
|
171
|
+
_.each(obj) value, index, list =>
|
172
|
+
computed: if iterator then iterator.call(context, value, index, list) else value
|
173
|
+
computed >= result.computed and (result: {value: value, computed: computed})
|
174
|
+
result.value
|
175
|
+
|
176
|
+
|
177
|
+
# Return the minimum element (or element-based computation).
|
178
|
+
_.min: obj, iterator, context =>
|
179
|
+
return Math.min.apply(Math, obj) if not iterator and _.isArray(obj)
|
180
|
+
result: {computed: Infinity}
|
181
|
+
_.each(obj) value, index, list =>
|
182
|
+
computed: if iterator then iterator.call(context, value, index, list) else value
|
183
|
+
computed < result.computed and (result: {value: value, computed: computed})
|
184
|
+
result.value
|
185
|
+
|
186
|
+
|
187
|
+
# Sort the object's values by a criteria produced by an iterator.
|
188
|
+
_.sortBy: obj, iterator, context =>
|
189
|
+
_.pluck(((_.map(obj) value, index, list =>
|
190
|
+
{value: value, criteria: iterator.call(context, value, index, list)}
|
191
|
+
).sort() left, right =>
|
192
|
+
a: left.criteria; b: right.criteria
|
193
|
+
if a < b then -1 else if a > b then 1 else 0
|
194
|
+
), 'value')
|
195
|
+
|
196
|
+
|
197
|
+
# Use a comparator function to figure out at what index an object should
|
198
|
+
# be inserted so as to maintain order. Uses binary search.
|
199
|
+
_.sortedIndex: array, obj, iterator =>
|
200
|
+
iterator ||= _.identity
|
201
|
+
low: 0; high: array.length
|
202
|
+
while low < high
|
203
|
+
mid: (low + high) >> 1
|
204
|
+
if iterator(array[mid]) < iterator(obj) then low: mid + 1 else high: mid
|
205
|
+
low
|
206
|
+
|
207
|
+
|
208
|
+
# Convert anything iterable into a real, live array.
|
209
|
+
_.toArray: iterable =>
|
210
|
+
return [] if (!iterable)
|
211
|
+
return iterable.toArray() if (iterable.toArray)
|
212
|
+
return iterable if (_.isArray(iterable))
|
213
|
+
return slice.call(iterable) if (_.isArguments(iterable))
|
214
|
+
_.values(iterable)
|
215
|
+
|
216
|
+
|
217
|
+
# Return the number of elements in an object.
|
218
|
+
_.size: obj => _.toArray(obj).length
|
219
|
+
|
220
|
+
|
221
|
+
# -------------------------- Array Functions: ------------------------------
|
222
|
+
|
223
|
+
# Get the first element of an array. Passing "n" will return the first N
|
224
|
+
# values in the array. Aliased as "head". The "guard" check allows it to work
|
225
|
+
# with _.map.
|
226
|
+
_.first: array, n, guard =>
|
227
|
+
if n and not guard then slice.call(array, 0, n) else array[0]
|
228
|
+
|
229
|
+
|
230
|
+
# Returns everything but the first entry of the array. Aliased as "tail".
|
231
|
+
# Especially useful on the arguments object. Passing an "index" will return
|
232
|
+
# the rest of the values in the array from that index onward. The "guard"
|
233
|
+
# check allows it to work with _.map.
|
234
|
+
_.rest: array, index, guard =>
|
235
|
+
slice.call(array, if _.isUndefined(index) or guard then 1 else index)
|
236
|
+
|
237
|
+
|
238
|
+
# Get the last element of an array.
|
239
|
+
_.last: array => array[array.length - 1]
|
240
|
+
|
241
|
+
|
242
|
+
# Trim out all falsy values from an array.
|
243
|
+
_.compact: array => array[i] for i in [0...array.length] when array[i]
|
244
|
+
|
245
|
+
|
246
|
+
# Return a completely flattened version of an array.
|
247
|
+
_.flatten: array =>
|
248
|
+
_.reduce(array, []) memo, value =>
|
249
|
+
return memo.concat(_.flatten(value)) if _.isArray(value)
|
250
|
+
memo.push(value)
|
251
|
+
memo
|
252
|
+
|
253
|
+
|
254
|
+
# Return a version of the array that does not contain the specified value(s).
|
255
|
+
_.without: array =>
|
256
|
+
values: _.rest(arguments)
|
257
|
+
val for val in _.toArray(array) when not _.include(values, val)
|
258
|
+
|
259
|
+
|
260
|
+
# Produce a duplicate-free version of the array. If the array has already
|
261
|
+
# been sorted, you have the option of using a faster algorithm.
|
262
|
+
_.uniq: array, isSorted =>
|
263
|
+
memo: []
|
264
|
+
for el, i in _.toArray(array)
|
265
|
+
memo.push(el) if i is 0 || (if isSorted is true then _.last(memo) isnt el else not _.include(memo, el))
|
266
|
+
memo
|
267
|
+
|
268
|
+
|
269
|
+
# Produce an array that contains every item shared between all the
|
270
|
+
# passed-in arrays.
|
271
|
+
_.intersect: array =>
|
272
|
+
rest: _.rest(arguments)
|
273
|
+
_.select(_.uniq(array)) item =>
|
274
|
+
_.all(rest) other =>
|
275
|
+
_.indexOf(other, item) >= 0
|
276
|
+
|
277
|
+
|
278
|
+
# Zip together multiple lists into a single array -- elements that share
|
279
|
+
# an index go together.
|
280
|
+
_.zip: =>
|
281
|
+
args: _.toArray(arguments)
|
282
|
+
length: _.max(_.pluck(args, 'length'))
|
283
|
+
results: new Array(length)
|
284
|
+
for i in [0...length]
|
285
|
+
results[i]: _.pluck(args, String(i))
|
286
|
+
results
|
287
|
+
|
288
|
+
|
289
|
+
# If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
|
290
|
+
# we need this function. Return the position of the first occurence of an
|
291
|
+
# item in an array, or -1 if the item is not included in the array.
|
292
|
+
_.indexOf: array, item =>
|
293
|
+
return array.indexOf(item) if array.indexOf
|
294
|
+
i: 0; l: array.length
|
295
|
+
while l - i
|
296
|
+
if array[i] is item then return i else i++
|
297
|
+
-1
|
298
|
+
|
299
|
+
|
300
|
+
# Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
|
301
|
+
# if possible.
|
302
|
+
_.lastIndexOf: array, item =>
|
303
|
+
return array.lastIndexOf(item) if array.lastIndexOf
|
304
|
+
i: array.length
|
305
|
+
while i
|
306
|
+
if array[i] is item then return i else i--
|
307
|
+
-1
|
308
|
+
|
309
|
+
|
310
|
+
# Generate an integer Array containing an arithmetic progression. A port of
|
311
|
+
# the native Python range() function. See:
|
312
|
+
# http://docs.python.org/library/functions.html#range
|
313
|
+
_.range: start, stop, step =>
|
314
|
+
a: _.toArray(arguments)
|
315
|
+
solo: a.length <= 1
|
316
|
+
i: start: if solo then 0 else a[0];
|
317
|
+
stop: if solo then a[0] else a[1];
|
318
|
+
step: a[2] or 1
|
319
|
+
len: Math.ceil((stop - start) / step)
|
320
|
+
return [] if len <= 0
|
321
|
+
range: new Array(len)
|
322
|
+
idx: 0
|
323
|
+
while true
|
324
|
+
return range if (if step > 0 then i - stop else stop - i) >= 0
|
325
|
+
range[idx]: i
|
326
|
+
idx++
|
327
|
+
i+= step
|
328
|
+
|
309
329
|
|
310
330
|
# ----------------------- Function Functions: -----------------------------
|
311
331
|
|
@@ -313,34 +333,36 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
313
333
|
# optionally). Binding with arguments is also known as 'curry'.
|
314
334
|
_.bind: func, obj =>
|
315
335
|
args: _.rest(arguments, 2)
|
316
|
-
=> func.apply(obj or root, args.concat(_.toArray(arguments)))
|
317
|
-
|
318
|
-
|
319
|
-
#
|
320
|
-
#
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
#
|
328
|
-
#
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
336
|
+
=> func.apply(obj or root, args.concat(_.toArray(arguments)))
|
337
|
+
|
338
|
+
|
339
|
+
# Bind all of an object's methods to that object. Useful for ensuring that
|
340
|
+
# all callbacks defined on an object belong to it.
|
341
|
+
_.bindAll: obj =>
|
342
|
+
funcs: if arguments.length > 1 then _.rest(arguments) else _.functions(obj)
|
343
|
+
_.each(funcs, (f => obj[f]: _.bind(obj[f], obj)))
|
344
|
+
obj
|
345
|
+
|
346
|
+
|
347
|
+
# Delays a function for the given number of milliseconds, and then calls
|
348
|
+
# it with the arguments supplied.
|
349
|
+
_.delay: func, wait =>
|
350
|
+
args: _.rest(arguments, 2)
|
351
|
+
setTimeout((=> func.apply(func, args)), wait)
|
352
|
+
|
333
353
|
|
334
354
|
# Defers a function, scheduling it to run after the current call stack has
|
335
355
|
# cleared.
|
336
356
|
_.defer: func =>
|
337
|
-
_.delay.apply(_, [func, 1].concat(_.rest(arguments)))
|
357
|
+
_.delay.apply(_, [func, 1].concat(_.rest(arguments)))
|
358
|
+
|
338
359
|
|
339
360
|
# Returns the first function passed as an argument to the second,
|
340
361
|
# allowing you to adjust arguments, run code before and after, and
|
341
362
|
# conditionally execute the original function.
|
342
363
|
_.wrap: func, wrapper =>
|
343
|
-
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
|
364
|
+
=> wrapper.apply(wrapper, [func].concat(_.toArray(arguments)))
|
365
|
+
|
344
366
|
|
345
367
|
# Returns a function that is the composition of a list of functions, each
|
346
368
|
# consuming the return value of the function that follows.
|
@@ -348,40 +370,48 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
348
370
|
funcs: _.toArray(arguments)
|
349
371
|
=>
|
350
372
|
args: _.toArray(arguments)
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
#
|
357
|
-
|
358
|
-
#
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
#
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
#
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
#
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
#
|
382
|
-
|
383
|
-
|
384
|
-
|
373
|
+
for i in [(funcs.length - 1)..0]
|
374
|
+
args: [funcs[i].apply(this, args)]
|
375
|
+
args[0]
|
376
|
+
|
377
|
+
|
378
|
+
# ------------------------- Object Functions: ----------------------------
|
379
|
+
|
380
|
+
# Retrieve the names of an object's properties.
|
381
|
+
_.keys: obj =>
|
382
|
+
return _.range(0, obj.length) if _.isArray(obj)
|
383
|
+
key for val, key in obj
|
384
|
+
|
385
|
+
|
386
|
+
# Retrieve the values of an object's properties.
|
387
|
+
_.values: obj =>
|
388
|
+
_.map(obj, _.identity)
|
389
|
+
|
390
|
+
|
391
|
+
# Return a sorted list of the function names available in Underscore.
|
392
|
+
_.functions: obj =>
|
393
|
+
_.select(_.keys(obj), key => _.isFunction(obj[key])).sort()
|
394
|
+
|
395
|
+
|
396
|
+
# Extend a given object with all of the properties in a source object.
|
397
|
+
_.extend: destination, source =>
|
398
|
+
for val, key in source
|
399
|
+
destination[key]: val
|
400
|
+
destination
|
401
|
+
|
402
|
+
|
403
|
+
# Create a (shallow-cloned) duplicate of an object.
|
404
|
+
_.clone: obj =>
|
405
|
+
return obj.slice(0) if _.isArray(obj)
|
406
|
+
_.extend({}, obj)
|
407
|
+
|
408
|
+
|
409
|
+
# Invokes interceptor with the obj, and then returns obj.
|
410
|
+
# 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 =>
|
412
|
+
interceptor(obj)
|
413
|
+
obj
|
414
|
+
|
385
415
|
|
386
416
|
# Perform a deep comparison to check if two objects are equal.
|
387
417
|
_.isEqual: a, b =>
|
@@ -402,10 +432,10 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
402
432
|
return true if _.isNaN(a) and _.isNaN(b)
|
403
433
|
# Compare regular expressions.
|
404
434
|
if _.isRegExp(a) and _.isRegExp(b)
|
405
|
-
return a.source is b.source and
|
406
|
-
a.global is b.global and
|
407
|
-
a.ignoreCase is b.ignoreCase and
|
408
|
-
a.multiline is b.multiline
|
435
|
+
return a.source is b.source and
|
436
|
+
a.global is b.global and
|
437
|
+
a.ignoreCase is b.ignoreCase and
|
438
|
+
a.multiline is b.multiline
|
409
439
|
# If a is not an object by this point, we can't handle it.
|
410
440
|
return false if atype isnt 'object'
|
411
441
|
# Check for different array lengths before comparing contents.
|
@@ -416,42 +446,57 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
416
446
|
return false if aKeys.length isnt bKeys.length
|
417
447
|
# Recursive comparison of contents.
|
418
448
|
# for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
|
419
|
-
return true
|
449
|
+
return true
|
450
|
+
|
420
451
|
|
421
452
|
# Is a given array or object empty?
|
422
|
-
_.isEmpty:
|
453
|
+
_.isEmpty: obj => _.keys(obj).length is 0
|
454
|
+
|
423
455
|
|
424
456
|
# Is a given value a DOM element?
|
425
|
-
_.isElement:
|
457
|
+
_.isElement: obj => obj and obj.nodeType is 1
|
458
|
+
|
459
|
+
|
460
|
+
# Is a given value an array?
|
461
|
+
_.isArray: obj => !!(obj and obj.concat and obj.unshift)
|
462
|
+
|
426
463
|
|
427
464
|
# Is a given variable an arguments object?
|
428
|
-
_.isArguments:
|
465
|
+
_.isArguments: obj => obj and _.isNumber(obj.length) and !_.isArray(obj) and !propertyIsEnumerable.call(obj, 'length')
|
466
|
+
|
467
|
+
|
468
|
+
# Is the given value a function?
|
469
|
+
_.isFunction: obj => !!(obj and obj.constructor and obj.call and obj.apply)
|
470
|
+
|
471
|
+
|
472
|
+
# Is the given value a string?
|
473
|
+
_.isString: obj => !!(obj is '' or (obj and obj.charCodeAt and obj.substr))
|
474
|
+
|
475
|
+
|
476
|
+
# Is a given value a number?
|
477
|
+
_.isNumber: obj => toString.call(obj) is '[object Number]'
|
478
|
+
|
479
|
+
|
480
|
+
# Is a given value a Date?
|
481
|
+
_.isDate: obj => !!(obj and obj.getTimezoneOffset and obj.setUTCFullYear)
|
482
|
+
|
483
|
+
|
484
|
+
# Is the given value a regular expression?
|
485
|
+
_.isRegExp: obj => !!(obj and obj.exec and (obj.ignoreCase or obj.ignoreCase is false))
|
486
|
+
|
429
487
|
|
430
488
|
# Is the given value NaN -- this one is interesting. NaN != NaN, and
|
431
489
|
# isNaN(undefined) == true, so we make sure it's a number first.
|
432
|
-
_.isNaN:
|
490
|
+
_.isNaN: obj => _.isNumber(obj) and window.isNaN(obj)
|
491
|
+
|
433
492
|
|
434
493
|
# Is a given value equal to null?
|
435
|
-
_.isNull:
|
494
|
+
_.isNull: obj => obj is null
|
495
|
+
|
436
496
|
|
437
497
|
# Is a given variable undefined?
|
438
|
-
_.isUndefined:
|
498
|
+
_.isUndefined: obj => typeof obj is 'undefined'
|
439
499
|
|
440
|
-
# Invokes interceptor with the obj, and then returns obj.
|
441
|
-
# The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
|
442
|
-
_.tap: obj, interceptor =>
|
443
|
-
interceptor(obj)
|
444
|
-
obj.
|
445
|
-
|
446
|
-
# # Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
|
447
|
-
# # functions based on their toString identifiers.
|
448
|
-
# var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
|
449
|
-
# for (var i=0, l=types.length; i<l; i++) {
|
450
|
-
# (function() {
|
451
|
-
# var identifier = '[object ' + types[i] + ']';
|
452
|
-
# _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
|
453
|
-
# })();
|
454
|
-
# }
|
455
500
|
|
456
501
|
# -------------------------- Utility Functions: --------------------------
|
457
502
|
|
@@ -459,39 +504,41 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
459
504
|
# previous owner. Returns a reference to the Underscore object.
|
460
505
|
_.noConflict: =>
|
461
506
|
root._: previousUnderscore
|
462
|
-
this
|
507
|
+
this
|
508
|
+
|
463
509
|
|
464
510
|
# Keep the identity function around for default iterators.
|
465
|
-
_.identity: value => value
|
511
|
+
_.identity: value => value
|
512
|
+
|
466
513
|
|
467
514
|
# Break out of the middle of an iteration.
|
468
|
-
_.breakLoop: => throw breaker
|
469
|
-
|
470
|
-
|
471
|
-
#
|
472
|
-
#
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
#
|
479
|
-
#
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
515
|
+
_.breakLoop: => throw breaker
|
516
|
+
|
517
|
+
|
518
|
+
# Generate a unique integer id (unique within the entire client session).
|
519
|
+
# Useful for temporary DOM ids.
|
520
|
+
idCounter: 0
|
521
|
+
_.uniqueId: prefix =>
|
522
|
+
(prefix or '') + idCounter++
|
523
|
+
|
524
|
+
|
525
|
+
# JavaScript templating a-la ERB, pilfered from John Resig's
|
526
|
+
# "Secrets of the JavaScript Ninja", page 83.
|
527
|
+
_.template: str, data =>
|
528
|
+
`var fn = new Function('obj',
|
529
|
+
'var p=[],print=function(){p.push.apply(p,arguments);};' +
|
530
|
+
'with(obj){p.push(\'' +
|
531
|
+
str.
|
532
|
+
replace(/[\r\t\n]/g, " ").
|
533
|
+
split("<%").join("\t").
|
534
|
+
replace(/((^|%>)[^\t]*)'/g, "$1\r").
|
535
|
+
replace(/\t=(.*?)%>/g, "',$1,'").
|
536
|
+
split("\t").join("');").
|
537
|
+
split("%>").join("p.push('").
|
538
|
+
split("\r").join("\\'") +
|
539
|
+
"');}return p.join('');")`
|
540
|
+
if data then fn(data) else fn
|
541
|
+
|
495
542
|
|
496
543
|
# ------------------------------- Aliases ----------------------------------
|
497
544
|
|
@@ -505,48 +552,42 @@ _.reduceRight: obj, memo, iterator, context =>
|
|
505
552
|
_.tail: _.rest
|
506
553
|
_.methods: _.functions
|
507
554
|
|
508
|
-
|
509
|
-
#
|
510
|
-
|
511
|
-
#
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
#
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
#
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
#
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
#
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
#
|
546
|
-
|
547
|
-
# # Extracts the result from a wrapped and chained object.
|
548
|
-
# wrapper.prototype.value = function() {
|
549
|
-
# return this._wrapped;
|
550
|
-
# };
|
551
|
-
#
|
552
|
-
# ()
|
555
|
+
|
556
|
+
# /*------------------------ Setup the OOP Wrapper: --------------------------*/
|
557
|
+
|
558
|
+
# Helper function to continue chaining intermediate results.
|
559
|
+
result: obj, chain =>
|
560
|
+
if chain then _(obj).chain() else obj
|
561
|
+
|
562
|
+
|
563
|
+
# Add all of the Underscore functions to the wrapper object.
|
564
|
+
_.each(_.functions(_)) name =>
|
565
|
+
method: _[name]
|
566
|
+
wrapper.prototype[name]: =>
|
567
|
+
unshift.call(arguments, this._wrapped)
|
568
|
+
result(method.apply(_, arguments), this._chain)
|
569
|
+
|
570
|
+
|
571
|
+
# Add all mutator Array functions to the wrapper.
|
572
|
+
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift']) name =>
|
573
|
+
method: Array.prototype[name]
|
574
|
+
wrapper.prototype[name]: =>
|
575
|
+
method.apply(this._wrapped, arguments)
|
576
|
+
result(this._wrapped, this._chain)
|
577
|
+
|
578
|
+
|
579
|
+
# Add all accessor Array functions to the wrapper.
|
580
|
+
_.each(['concat', 'join', 'slice']) name =>
|
581
|
+
method: Array.prototype[name]
|
582
|
+
wrapper.prototype[name]: =>
|
583
|
+
result(method.apply(this._wrapped, arguments), this._chain)
|
584
|
+
|
585
|
+
|
586
|
+
# Start chaining a wrapped Underscore object.
|
587
|
+
wrapper.prototype.chain: =>
|
588
|
+
this._chain: true
|
589
|
+
this
|
590
|
+
|
591
|
+
|
592
|
+
# Extracts the result from a wrapped and chained object.
|
593
|
+
wrapper.prototype.value: => this._wrapped
|