mirror of
https://github.com/expressjs/express.git
synced 2026-02-27 03:07:33 +00:00
Compare commits
135 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c8dd169ad9 | ||
|
|
5495fbcd0c | ||
|
|
eb94667ec8 | ||
|
|
cf31355515 | ||
|
|
604c359a1c | ||
|
|
dad64b8a3b | ||
|
|
ffa432baa2 | ||
|
|
5c4a356348 | ||
|
|
ef949aec20 | ||
|
|
eaea3f188d | ||
|
|
818135789b | ||
|
|
2bbe573748 | ||
|
|
9efb15b267 | ||
|
|
5ce1306b75 | ||
|
|
75c85663ad | ||
|
|
8c1bcc4c47 | ||
|
|
200d09c7bd | ||
|
|
ef86474830 | ||
|
|
e2fee9b353 | ||
|
|
542cab1123 | ||
|
|
315c05034f | ||
|
|
148d34629b | ||
|
|
82891ea148 | ||
|
|
cd1aa92d07 | ||
|
|
2a4f3525d9 | ||
|
|
944f99abe0 | ||
|
|
e083321a9d | ||
|
|
a0a1a543e8 | ||
|
|
cf74bc655d | ||
|
|
0587278cda | ||
|
|
b333dccc2e | ||
|
|
a71201bd55 | ||
|
|
cec677062b | ||
|
|
327d5b0f88 | ||
|
|
20cf8f81a2 | ||
|
|
4d5a1b5f4d | ||
|
|
9d5a6f9412 | ||
|
|
7e92012415 | ||
|
|
6ee83ba8d0 | ||
|
|
70a99ade67 | ||
|
|
634503b8ba | ||
|
|
2a4cbb1174 | ||
|
|
ca7d0f3e34 | ||
|
|
9cb2645d1c | ||
|
|
acf0a652b7 | ||
|
|
2701dfd80b | ||
|
|
047499dcd1 | ||
|
|
fa37e5a35f | ||
|
|
4adbb4971f | ||
|
|
8ed99f6bb8 | ||
|
|
fbf547d127 | ||
|
|
f4b4c03cb5 | ||
|
|
6b9a079bbf | ||
|
|
7238afbf4b | ||
|
|
0186659469 | ||
|
|
50d14230f5 | ||
|
|
29fe71c8f7 | ||
|
|
5fe033cef1 | ||
|
|
6490f0c193 | ||
|
|
09ccd043f2 | ||
|
|
825e8b6f0c | ||
|
|
1726dfb3b3 | ||
|
|
8a7ac0a6af | ||
|
|
d66ce086d4 | ||
|
|
2bbf6cb8f9 | ||
|
|
4a52e641cf | ||
|
|
b4c90def81 | ||
|
|
50c0277e7f | ||
|
|
5a5f23fc88 | ||
|
|
ae8f9e1137 | ||
|
|
2b92e4053b | ||
|
|
8717e2e970 | ||
|
|
d0a9b0cb81 | ||
|
|
844eb5f7e9 | ||
|
|
41913fd8b9 | ||
|
|
2a5ccd8826 | ||
|
|
497402311f | ||
|
|
d52090619b | ||
|
|
9d0d6e412b | ||
|
|
9b5605a944 | ||
|
|
51f4c965b5 | ||
|
|
046bee8844 | ||
|
|
971739089a | ||
|
|
f962f34e53 | ||
|
|
34a88d029d | ||
|
|
d3b2e6057d | ||
|
|
12a58361b4 | ||
|
|
0085b0a33b | ||
|
|
bf9505a4ce | ||
|
|
4c94457380 | ||
|
|
af313e5e8c | ||
|
|
eb7b96efe1 | ||
|
|
5fe35bf97e | ||
|
|
12bc374ea9 | ||
|
|
68c57e08ae | ||
|
|
f73c3fc404 | ||
|
|
4d926d3840 | ||
|
|
817ff41b0d | ||
|
|
f1614a5946 | ||
|
|
17c1207d76 | ||
|
|
a92667fdad | ||
|
|
f03694e21e | ||
|
|
83fcf8b594 | ||
|
|
7afb895f1b | ||
|
|
98e566ad69 | ||
|
|
7d0470d285 | ||
|
|
f8879ac5d1 | ||
|
|
9f263e357b | ||
|
|
ae33e7b673 | ||
|
|
b766234b93 | ||
|
|
a2899a0acd | ||
|
|
43da20a6b3 | ||
|
|
25cf42250a | ||
|
|
6b6ec3f19f | ||
|
|
1a2e4342e2 | ||
|
|
1ce1abf216 | ||
|
|
704c933927 | ||
|
|
8207e7088f | ||
|
|
46fdf10c97 | ||
|
|
61b82480b0 | ||
|
|
4ce5c7650e | ||
|
|
965cbf63be | ||
|
|
d4d76cda58 | ||
|
|
f2d08aa2e3 | ||
|
|
a82b1b9270 | ||
|
|
c1617508c6 | ||
|
|
e125ae2c48 | ||
|
|
3f9cbbbffb | ||
|
|
6f7141a266 | ||
|
|
99e3130f3c | ||
|
|
774f96fcb9 | ||
|
|
17ab8e81aa | ||
|
|
b02b384f14 | ||
|
|
3ec970bb67 | ||
|
|
22e338fbd4 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@@ -7,3 +7,6 @@
|
||||
[submodule "lib/support/js-oo"]
|
||||
path = lib/support/js-oo
|
||||
url = git://github.com/visionmedia/js-oo.git
|
||||
[submodule "lib/support/sass"]
|
||||
path = lib/support/sass
|
||||
url = git://github.com/visionmedia/sass.js.git
|
||||
|
||||
31
History.md
31
History.md
@@ -1,4 +1,35 @@
|
||||
|
||||
0.2.0 / 2010-02-03
|
||||
==================
|
||||
|
||||
* Added parseParam() support for name[] etc. (allows for file inputs with "multiple" attr) Closes #180
|
||||
* Added Both Cache and Session option "reapInterval" may be "reapEvery". Closes #174
|
||||
* Added expiration support to cache api with reaper. Closes #133
|
||||
* Added cache Store.Memory#reap()
|
||||
* Added Cache; cache api now uses first class Cache instances
|
||||
* Added abstract session Store. Closes #172
|
||||
* Changed; cache Memory.Store#get() utilizing Collection
|
||||
* Renamed MemoryStore -> Store.Memory
|
||||
* Fixed use() of the same plugin several time will always use latest options. Closes #176
|
||||
|
||||
0.1.0 / 2010-02-03
|
||||
==================
|
||||
|
||||
* Changed; Hooks (before / after) pass request as arg as well as evaluated in their context
|
||||
* Updated node support to 0.1.27 Closes #169
|
||||
* Updated dirname(__filename) -> __dirname
|
||||
* Updated libxmljs support to v0.2.0
|
||||
* Added session support with memory store / reaping
|
||||
* Added quick uid() helper
|
||||
* Added multi-part upload support
|
||||
* Added Sass.js support / submodule
|
||||
* Added production env caching view contents and static files
|
||||
* Added static file caching. Closes #136
|
||||
* Added cache plugin with memory stores
|
||||
* Added support to StaticFile so that it works with non-textual files.
|
||||
* Removed dirname() helper
|
||||
* Removed several globals (now their modules must be required)
|
||||
|
||||
0.0.2 / 2010-01-10
|
||||
==================
|
||||
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,6 +1,8 @@
|
||||
|
||||
NODE = node
|
||||
|
||||
all: test
|
||||
|
||||
init:
|
||||
@git submodule init && git submodule update
|
||||
|
||||
@@ -13,8 +15,13 @@ test-independant: init
|
||||
test-dependant: init spec/support/libxmljs/libxmljs.node
|
||||
@$(NODE) spec/node.js dependant
|
||||
|
||||
app:
|
||||
@$(NODE) examples/app.js
|
||||
app: app-chat
|
||||
|
||||
app-chat:
|
||||
@$(NODE) examples/chat/app.js
|
||||
|
||||
app-upload:
|
||||
@$(NODE) examples/upload/app.js
|
||||
|
||||
benchmark:
|
||||
@$(NODE) benchmarks/collection.js
|
||||
|
||||
13
Readme.md
13
Readme.md
@@ -11,6 +11,7 @@
|
||||
|
||||
* Sexy DSL with robust sinatra-like routing
|
||||
* High performance
|
||||
* Session support
|
||||
* Mime helpers
|
||||
* Redirection helpers
|
||||
* Nested parameter parsing
|
||||
@@ -21,7 +22,7 @@
|
||||
* Light-weight JavaScript class implementation via js-oo
|
||||
* Collections and chainable iterators
|
||||
* ElementCollections / markup parsing via libxmljs and css selector traversal support via css2xpath
|
||||
* View support (ejs, haml, mustache)
|
||||
* View support (ejs, haml, sass, etc)
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -49,16 +50,15 @@ Or with the [gh](http://github.com/visionmedia/gh) utility:
|
||||
|
||||
## Examples
|
||||
|
||||
require.paths.unshift('lib')
|
||||
Below is a minimal app example when express is already within your load path.
|
||||
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(Redirect)
|
||||
set('root', dirname(__filename))
|
||||
enable('cache views')
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/hello', function(){
|
||||
@@ -104,7 +104,7 @@ Run individual suites:
|
||||
...
|
||||
|
||||
Express is currently being developed with node --version:
|
||||
v0.1.24-13-ge2abc5f
|
||||
v0.1.27
|
||||
|
||||
## More Information
|
||||
|
||||
@@ -117,6 +117,7 @@ Express is currently being developed with node --version:
|
||||
* TJ Holowaychuk (visionmedia) <tj@vision-media.ca>
|
||||
* Ciaran Jessup (ciaranj) <ciaranj@gmail.com>
|
||||
* Gareth Jones (csausdev) <gareth.jones@sensis.com.au>
|
||||
* Aaron Heckmann (aheckmann) <aaron.heckmann+github@gmail.com>
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -8,20 +8,37 @@ require('express')
|
||||
print = puts
|
||||
|
||||
engine = {
|
||||
ejs: require('support/ejs/ejs'),
|
||||
haml: require('support/haml/lib/haml')
|
||||
ejs: require('ejs'),
|
||||
haml: require('haml'),
|
||||
sass: require('sass')
|
||||
}
|
||||
|
||||
options = { locals: { article: { title: 'Foo', body: 'bar' }}}
|
||||
|
||||
ejs = ' \n\
|
||||
<h1><%= article.title %></h1> \n\
|
||||
<p><%= article.body %></p> \n\
|
||||
ejs = ' \n\
|
||||
<div id="primary"> \n\
|
||||
<div class="block first"> \n\
|
||||
<h1><%= article.title %></h1> \n\
|
||||
<p><%= article.body %></p> \n\
|
||||
</div> \n\
|
||||
</div> \n\
|
||||
'
|
||||
|
||||
haml = ' \n\
|
||||
%h1= article.title\n\
|
||||
%p= article.body \n\
|
||||
haml = ' \n\
|
||||
#primary \n\
|
||||
.block.first \n\
|
||||
%h1= article.title \n\
|
||||
%p= article.body \n\
|
||||
'
|
||||
|
||||
sass = ' \n\
|
||||
red: #ff0000 \n\
|
||||
body \n\
|
||||
ul \n\
|
||||
li \n\
|
||||
a \n\
|
||||
:color !red \n\
|
||||
:list-style none \n\
|
||||
'
|
||||
|
||||
suite('Template Engines', 1000, function(){
|
||||
@@ -32,4 +49,8 @@ suite('Template Engines', 1000, function(){
|
||||
benchmark('haml', function(){
|
||||
engine.haml.render(haml, options)
|
||||
})
|
||||
|
||||
benchmark('sass', function(){
|
||||
engine.sass.render(sass)
|
||||
})
|
||||
})
|
||||
@@ -4,15 +4,19 @@ require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
var fiveMinutes = 300000,
|
||||
oneMinute = 60000
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(CommonLogger)
|
||||
set('root', dirname(__filename))
|
||||
enable('cache views')
|
||||
use(Cookie)
|
||||
use(Cache, { lifetime: fiveMinutes, reapInterval: oneMinute })
|
||||
use(Session, { lifetime: fiveMinutes, reapInterval: oneMinute })
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
var messages = [],
|
||||
StaticFile = require('express/static').File
|
||||
utils = require('express/utils')
|
||||
|
||||
get('/', function(){
|
||||
this.redirect('/chat')
|
||||
@@ -21,13 +25,20 @@ get('/', function(){
|
||||
get('/chat', function(){
|
||||
this.render('chat.haml.html', {
|
||||
locals: {
|
||||
messages: messages
|
||||
title: 'Chat',
|
||||
messages: messages,
|
||||
name: this.session.name,
|
||||
usersOnline: Session.store.length()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
post('/chat', function(){
|
||||
messages.push(escape(this.param('message')).replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
|
||||
this.session.name = this.param('name')
|
||||
messages
|
||||
.push(utils.escape(this.param('name')) + ': ' + utils.escape(this.param('message'))
|
||||
.replace(/(http:\/\/[^\s]+)/g, '<a href="$1" target="express-chat">$1</a>')
|
||||
.replace(/:\)/g, '<img src="http://icons3.iconfinder.netdna-cdn.com/data/icons/ledicons/emoticon_smile.png">'))
|
||||
this.halt(200)
|
||||
})
|
||||
|
||||
@@ -44,7 +55,11 @@ get('/chat/messages', function(){
|
||||
})
|
||||
|
||||
get('/public/*', function(file){
|
||||
this.sendfile(dirname(__filename) + '/public/' + file)
|
||||
this.sendfile(__dirname + '/public/' + file)
|
||||
})
|
||||
|
||||
get('/*.css', function(file){
|
||||
this.render(file + '.sass.css', { layout: false })
|
||||
})
|
||||
|
||||
get('/error/view', function(){
|
||||
@@ -59,4 +74,8 @@ get('/simple', function(){
|
||||
return 'Hello :)'
|
||||
})
|
||||
|
||||
get('/favicon.ico', function(){
|
||||
this.halt()
|
||||
})
|
||||
|
||||
run()
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.2 KiB |
@@ -5,7 +5,7 @@ $(function(){
|
||||
var message = $('input[name=message]'),
|
||||
name = $('input[name=name]')
|
||||
if (message.val())
|
||||
$.post('/chat', { message: $.trim(name.val()) + ': ' + message.val() }, function(){
|
||||
$.post('/chat', { name: name.val(), message: message.val() }, function(){
|
||||
message.val('')
|
||||
})
|
||||
else
|
||||
@@ -5,6 +5,6 @@
|
||||
%li= msg
|
||||
%form{ method: 'post' }
|
||||
%input{ type: 'hidden', name: '_method', value: 'put' }
|
||||
%input{ type: 'text', name: 'name', value: 'guest' }
|
||||
%input{ type: 'text', name: 'name', value: name || 'guest' }
|
||||
%input{ type: 'text', name: 'message' }
|
||||
%input{ type: 'submit', value: 'Send' }
|
||||
11
examples/chat/views/layout.haml.html
Normal file
11
examples/chat/views/layout.haml.html
Normal file
@@ -0,0 +1,11 @@
|
||||
%html
|
||||
%head
|
||||
%title= title
|
||||
%script{ src: '/public/javascripts/jquery.js' }
|
||||
%script{ src: '/public/javascripts/app.js' }
|
||||
%link{ rel: 'stylesheet', href: '/style.css' }
|
||||
%body
|
||||
#wrapper= body
|
||||
#online
|
||||
Online:
|
||||
%strong= usersOnline
|
||||
81
examples/chat/views/style.sass.css
Normal file
81
examples/chat/views/style.sass.css
Normal file
@@ -0,0 +1,81 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
=text-stroke 1px rgba(255, 255, 255, 0.1)
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
|
||||
img.bubble
|
||||
:position absolute
|
||||
:top -25px
|
||||
:left 120px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
ul
|
||||
:margin 0
|
||||
:padding 0
|
||||
:max-height 300px
|
||||
:overflow-x hidden
|
||||
li
|
||||
:margin 5px 0
|
||||
:padding 3px 8px
|
||||
:list-style none
|
||||
:border 1px solid #eee
|
||||
=border-radius 3px
|
||||
=border-radius 3px
|
||||
li:hover
|
||||
:cursor pointer
|
||||
:color #2E2E2E
|
||||
|
||||
input[type=text]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
=border-radius 2px
|
||||
input[type=text]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
=border-radius 2px
|
||||
=box-shadow 0 1px 2px #ddd
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
a
|
||||
:color #1ABFF1
|
||||
=transition-property padding
|
||||
=transition-duration 0.15s
|
||||
a:hover
|
||||
:padding 0 5px
|
||||
a:hover:before
|
||||
:content 'visit: '
|
||||
|
||||
#online
|
||||
:font-size 12px
|
||||
@@ -1,84 +0,0 @@
|
||||
body {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 13px;
|
||||
text-align: center;
|
||||
-webkit-text-stroke: 1px rgba(255, 255, 255, 0.1);
|
||||
color: #555;
|
||||
}
|
||||
h1, h2 {
|
||||
margin: 0;
|
||||
font-size: 22px;
|
||||
color: #343434;
|
||||
}
|
||||
h1 {
|
||||
text-shadow: 1px 2px 2px #ddd;
|
||||
font-size: 60px;
|
||||
}
|
||||
img.bubble {
|
||||
position: absolute;
|
||||
top: -25px;
|
||||
left: 120px;
|
||||
}
|
||||
#wrapper {
|
||||
position: relative;
|
||||
margin: 100px auto;
|
||||
width: 500px;
|
||||
text-align: left;
|
||||
}
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul li {
|
||||
margin: 5px 0;
|
||||
padding: 3px 8px;
|
||||
list-style: none;
|
||||
border: 1px solid #eee;
|
||||
-webkit-border-radius: 3px;
|
||||
-mox-border-radius: 3px;
|
||||
-webkit-transition-property: color;
|
||||
-webkit-transition-duration: 0.1s;
|
||||
}
|
||||
ul li:hover {
|
||||
cursor: pointer;
|
||||
color: #2E2E2E;
|
||||
}
|
||||
ul {
|
||||
max-height: 300px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
input[type=text] {
|
||||
padding: 5px;
|
||||
border: 1px solid #ddd;
|
||||
outline: none;
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
}
|
||||
input[type=text]:focus {
|
||||
border-color: #00C3FF;
|
||||
}
|
||||
input[type=submit] {
|
||||
-webkit-border-radius: 2px;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-box-shadow: 0 1px 2px #ddd;
|
||||
-moz-box-shadow: 0 1px 2px #ddd;
|
||||
padding: 6px 10px;
|
||||
border: solid 1px #999;
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd));
|
||||
color: #333;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
text-shadow: 0px 1px 1px #fff;
|
||||
line-height: 1;
|
||||
}
|
||||
input[type=submit]:hover {
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4));
|
||||
}
|
||||
input[type=submit]:active {
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7));
|
||||
}
|
||||
input[name=name] {
|
||||
width: 80px;
|
||||
}
|
||||
36
examples/upload/app.js
Normal file
36
examples/upload/app.js
Normal file
@@ -0,0 +1,36 @@
|
||||
|
||||
require.paths.unshift('lib')
|
||||
require('express')
|
||||
require('express/plugins')
|
||||
|
||||
configure(function(){
|
||||
use(MethodOverride)
|
||||
use(ContentLength)
|
||||
use(CommonLogger)
|
||||
set('root', __dirname)
|
||||
})
|
||||
|
||||
get('/', function(){
|
||||
this.redirect('/upload')
|
||||
})
|
||||
|
||||
get('/upload', function(){
|
||||
this.render('upload.haml.html')
|
||||
})
|
||||
|
||||
post('/upload', function(){
|
||||
$(this.param('images')).each(function(image){
|
||||
puts('uploaded ' + image.filename + ' to ' + image.tempfile)
|
||||
})
|
||||
this.redirect('/upload')
|
||||
})
|
||||
|
||||
get('/public/*', function(file){
|
||||
this.sendfile(__dirname + '/public/' + file)
|
||||
})
|
||||
|
||||
get('/*.css', function(file){
|
||||
this.render(file + '.sass.css', { layout: false })
|
||||
})
|
||||
|
||||
run()
|
||||
0
examples/upload/public/javascripts/app.js
Normal file
0
examples/upload/public/javascripts/app.js
Normal file
19
examples/upload/public/javascripts/jquery.js
vendored
Normal file
19
examples/upload/public/javascripts/jquery.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -1,8 +1,8 @@
|
||||
%html
|
||||
%head
|
||||
%title Chat
|
||||
%title Upload
|
||||
%script{ src: '/public/javascripts/jquery.js' }
|
||||
%script{ src: '/public/javascripts/app.js' }
|
||||
%link{ rel: 'stylesheet', href: '/public/stylesheets/style.css' }
|
||||
%link{ rel: 'stylesheet', href: '/style.css' }
|
||||
%body
|
||||
#wrapper= body
|
||||
59
examples/upload/views/style.sass.css
Normal file
59
examples/upload/views/style.sass.css
Normal file
@@ -0,0 +1,59 @@
|
||||
body
|
||||
:font-family "Helvetica Neue", "Lucida Grande", "Arial"
|
||||
:font-size 13px
|
||||
:text-align center
|
||||
:-webkit-text-stroke 1px rgba(255, 255, 255, 0.1)
|
||||
:color #555
|
||||
|
||||
h1, h2
|
||||
:margin 0 0 15px 0
|
||||
:font-size 22px
|
||||
:color #343434
|
||||
h1
|
||||
:text-shadow 1px 2px 2px #ddd
|
||||
:font-size 60px
|
||||
h2
|
||||
:margin-top 15px
|
||||
|
||||
#wrapper
|
||||
:position relative
|
||||
:margin 100px auto
|
||||
:width 500px
|
||||
:text-align left
|
||||
|
||||
input[type=file]
|
||||
:padding 5px
|
||||
:border 1px solid #ddd
|
||||
:outline none
|
||||
:-webkit-border-radius 2px
|
||||
:-moz-border-radius 2px
|
||||
input[type=file]:focus
|
||||
:border-color #00C3FF
|
||||
|
||||
input[type=submit]
|
||||
:-webkit-border-radius 2px
|
||||
:-moz-border-radius 2px
|
||||
:-webkit-box-shadow 0 1px 2px #ddd
|
||||
:-moz-box-shadow 0 1px 2px #ddd
|
||||
:padding 6px 10px
|
||||
:border solid 1px #999
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#ddd))
|
||||
:color #333
|
||||
:text-decoration none
|
||||
:cursor pointer
|
||||
:display inline-block
|
||||
:text-align center
|
||||
:text-shadow 0px 1px 1px #fff
|
||||
:line-height 1
|
||||
input[type=submit]:hover
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#E6E4E4))
|
||||
input[type=submit]:active
|
||||
:background -webkit-gradient(linear, 0% 0%, 0% 100%, from(#fff), to(#c7c7c7))
|
||||
input[name=name]
|
||||
:width 80px
|
||||
|
||||
form
|
||||
.panel
|
||||
:float left
|
||||
:width 100%
|
||||
:margin-bottom 15px
|
||||
19
examples/upload/views/upload.haml.html
Normal file
19
examples/upload/views/upload.haml.html
Normal file
@@ -0,0 +1,19 @@
|
||||
%h1 Upload
|
||||
:if typeof images !== 'undefined'
|
||||
.images
|
||||
:each img in images
|
||||
%img{ src: img }
|
||||
|
||||
%h2 Singles
|
||||
%form{ method: 'post', enctype: 'multipart/form-data' }
|
||||
%input{ type: 'file', name: 'images[0]' }
|
||||
%input{ type: 'file', name: 'images[1]' }
|
||||
%input{ type: 'file', name: 'images[2]' }
|
||||
.panel
|
||||
%input{ type: 'submit', value: 'Upload' }
|
||||
|
||||
%h2 Multiple
|
||||
%form{ method: 'post', enctype: 'multipart/form-data' }
|
||||
%input{ type: 'file', name: 'images[]', multiple: 'multiple' }
|
||||
.panel
|
||||
%input{ type: 'submit', value: 'Upload' }
|
||||
@@ -1,3 +1,8 @@
|
||||
|
||||
require('support/js-oo/lib/oo')
|
||||
require('express/core')
|
||||
var path = require('path')
|
||||
require.paths.unshift(__dirname + '/support/js-oo/lib')
|
||||
require.paths.unshift(__dirname + '/support/ejs/lib')
|
||||
require.paths.unshift(__dirname + '/support/haml/lib')
|
||||
require.paths.unshift(__dirname + '/support/sass/lib')
|
||||
require('oo')
|
||||
require('express/core')
|
||||
@@ -29,8 +29,7 @@ function callback(fn) {
|
||||
if (fn === undefined) return
|
||||
if (fn instanceof Function) return fn
|
||||
if (fn.length < 4) return Function('a, b, c', 'return a ' + fn + ' b')
|
||||
if (property.test(fn) ||
|
||||
method.test(fn)) fn = 'a.' + fn
|
||||
if (property.test(fn) || method.test(fn)) fn = 'a.' + fn
|
||||
return Function('a, b, c', 'return ' + fn)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,17 @@ process.mixin(require('express/exceptions'))
|
||||
process.mixin(require('express/collection'))
|
||||
process.mixin(require('express/event'))
|
||||
process.mixin(require('express/request'))
|
||||
process.mixin(require('express/helpers'))
|
||||
process.mixin(require('express/plugin'))
|
||||
process.mixin(require('express/mime'))
|
||||
process.mixin(require('express/dsl'))
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var multipart = require('multipart'),
|
||||
File = require('file').File,
|
||||
utils = require('express/utils')
|
||||
|
||||
// --- Route
|
||||
|
||||
Route = Class({
|
||||
@@ -77,7 +83,7 @@ Route = Class({
|
||||
var self = this
|
||||
this.keys = []
|
||||
if (path instanceof RegExp) return path
|
||||
return new RegExp('^' + escapeRegexp(normalizePath(path), '.')
|
||||
return new RegExp('^' + utils.escapeRegexp(normalizePath(path), '.')
|
||||
.replace(/\*/g, '(.+)')
|
||||
.replace(/(\/|\\\.):(\w+)\?/g, function(_, c, key){
|
||||
self.keys.push(key)
|
||||
@@ -213,9 +219,51 @@ Server = Class({
|
||||
require('http')
|
||||
.createServer(function(request, response){
|
||||
request.body = ''
|
||||
request.setBodyEncoding('utf8')
|
||||
request.addListener('body', function(chunk){ request.body += chunk })
|
||||
request.addListener('complete', function(){ self.route(request, response) })
|
||||
request.setBodyEncoding('binary')
|
||||
if (request.headers['content-type'] &&
|
||||
request.headers['content-type'].indexOf('multipart/form-data') !== -1) {
|
||||
var stream = new multipart.Stream(request),
|
||||
promise = new process.Promise,
|
||||
pendingFiles = 0
|
||||
request.params = { post: {}}
|
||||
promise.timeout(set('upload timeout') || 5000)
|
||||
stream.addListener('part', function(part){
|
||||
if (part.filename) {
|
||||
var file = new File(part.tempfile = '/tmp/express-' + Number(new Date) + utils.uid(), 'w')
|
||||
++pendingFiles
|
||||
part.pos = 0
|
||||
part.addListener('body', function(chunk){
|
||||
file.write(chunk, part.pos, 'binary').addErrback(function(){
|
||||
promise.emitError.apply(promise, arguments)
|
||||
})
|
||||
part.pos += chunk.length
|
||||
})
|
||||
.addListener('complete', function(){
|
||||
file.close().addCallback(function(){
|
||||
if (!--pendingFiles)
|
||||
promise.emitSuccess()
|
||||
}).addErrback(function(){
|
||||
promise.emitError.apply(promise, arguments)
|
||||
})
|
||||
utils.mergeParam(part.name, part, request.params.post)
|
||||
})
|
||||
}
|
||||
else
|
||||
part.buf = '',
|
||||
part
|
||||
.addListener('body', function(chunk){ part.buf += chunk })
|
||||
.addListener('complete', function(){
|
||||
if (part.buf.length)
|
||||
utils.mergeParam(part.name, part.buf, request.params.post)
|
||||
})
|
||||
}).addListener('complete', function(){
|
||||
promise.addCallback(function(){ self.route(request, response) })
|
||||
})
|
||||
}
|
||||
else
|
||||
request
|
||||
.addListener('body', function(chunk){ request.body += chunk })
|
||||
.addListener('complete', function(){ self.route(request, response) })
|
||||
})
|
||||
.listen(this.port, this.host, this.backlog)
|
||||
puts('Express started at http://' + this.host + ':' + this.port + '/ in ' + Express.environment + ' mode')
|
||||
@@ -252,7 +300,7 @@ Server = Class({
|
||||
// --- Express
|
||||
|
||||
Express = {
|
||||
version: '0.0.2',
|
||||
version: '0.2.0',
|
||||
config: [],
|
||||
routes: [],
|
||||
plugins: [],
|
||||
@@ -264,6 +312,7 @@ Express = {
|
||||
|
||||
configure(function(){
|
||||
use(require('express/plugins/view').View)
|
||||
use(require('express/plugins/cache').Cache)
|
||||
use(require('express/plugins/redirect').Redirect)
|
||||
use(require('express/plugins/body-decoder').BodyDecoder)
|
||||
})
|
||||
@@ -276,3 +325,8 @@ configure('development', function(){
|
||||
configure('test', function(){
|
||||
enable('throw exceptions')
|
||||
})
|
||||
|
||||
configure('production', function(){
|
||||
enable('cache view contents')
|
||||
enable('cache static files')
|
||||
})
|
||||
|
||||
@@ -69,6 +69,10 @@ exports.disable = function(option) {
|
||||
|
||||
exports.run = function() {
|
||||
configure(Express.environment = process.ENV.EXPRESS_ENV || 'development')
|
||||
$(Express.plugins).each(function(plugin){
|
||||
if ('init' in plugin.klass)
|
||||
plugin.klass.init(plugin.options)
|
||||
})
|
||||
Express.server.run.apply(Express.server, arguments)
|
||||
}
|
||||
|
||||
@@ -103,7 +107,7 @@ exports.configure = function(environment, fn) {
|
||||
if (fn instanceof Function)
|
||||
return Express.config.push([environment, fn])
|
||||
if (typeof environment != 'string')
|
||||
throw 'environment require'
|
||||
throw new Error('environment required')
|
||||
for (var i = 0, len = Express.config.length; i < len; ++i)
|
||||
if (Express.config[i][0] == environment ||
|
||||
Express.config[i][0] == 'all')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Express - ElementCollection - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Load libxml support.
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var libxml = require('libxmljs')
|
||||
@@ -126,7 +126,7 @@ ElementCollection = Collection.extend({
|
||||
*/
|
||||
|
||||
prev: function() {
|
||||
return $([this.at(0).prev_sibling()])
|
||||
return $([this.at(0).prevSibling()])
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -137,7 +137,7 @@ ElementCollection = Collection.extend({
|
||||
*/
|
||||
|
||||
next: function() {
|
||||
return $([this.at(0).next_sibling()])
|
||||
return $([this.at(0).nextSibling()])
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,136 +1,278 @@
|
||||
|
||||
// Express - Mime - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var types = {
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('express/utils')
|
||||
|
||||
/**
|
||||
* Mime type lookup table.
|
||||
*/
|
||||
|
||||
exports.types = {
|
||||
'323' : 'text/h323',
|
||||
'3gp' : 'video/3gpp',
|
||||
'a' : 'application/octet-stream',
|
||||
'acx' : 'application/internet-property-stream',
|
||||
'ai' : 'application/postscript',
|
||||
'aif' : 'audio/x-aiff',
|
||||
'aifc' : 'audio/x-aiff',
|
||||
'aiff' : 'audio/x-aiff',
|
||||
'asc' : 'application/pgp-signature',
|
||||
'asf' : 'video/x-ms-asf',
|
||||
'asr' : 'video/x-ms-asf',
|
||||
'asm' : 'text/x-asm',
|
||||
'asx' : 'video/x-ms-asf',
|
||||
'atom' : 'application/atom+xml',
|
||||
'au' : 'audio/basic',
|
||||
'avi' : 'video/x-msvideo',
|
||||
'axs' : 'application/olescript',
|
||||
'bas' : 'text/plain',
|
||||
'bat' : 'application/x-msdownload',
|
||||
'bcpio' : 'application/x-bcpio',
|
||||
'bin' : 'application/octet-stream',
|
||||
'bmp' : 'image/bmp',
|
||||
'bz2' : 'application/x-bzip2',
|
||||
'c' : 'text/x-c',
|
||||
'cab' : 'application/vnd.ms-cab-compressed',
|
||||
'cat' : 'application/vnd.ms-pkiseccat',
|
||||
'cc' : 'text/x-c',
|
||||
'cdf' : 'application/x-netcdf',
|
||||
'cer' : 'application/x-x509-ca-cert',
|
||||
'cgm' : 'image/cgm',
|
||||
'chm' : 'application/vnd.ms-htmlhelp',
|
||||
'class' : 'application/octet-stream',
|
||||
'clp' : 'application/x-msclip',
|
||||
'cmx' : 'image/x-cmx',
|
||||
'cod' : 'image/cis-cod',
|
||||
'com' : 'application/x-msdownload',
|
||||
'conf' : 'text/plain',
|
||||
'cpio' : 'application/x-cpio',
|
||||
'cpp' : 'text/x-c',
|
||||
'cpt' : 'application/mac-compactpro',
|
||||
'crd' : 'application/x-mscardfile',
|
||||
'crl' : 'application/pkix-crl',
|
||||
'crt' : 'application/x-x509-ca-cert',
|
||||
'csh' : 'application/x-csh',
|
||||
'css' : 'text/css',
|
||||
'csv' : 'text/csv',
|
||||
'cxx' : 'text/x-c',
|
||||
'dcr' : 'application/x-director',
|
||||
'deb' : 'application/x-debian-package',
|
||||
'der' : 'application/x-x509-ca-cert',
|
||||
'diff' : 'text/x-diff',
|
||||
'dir' : 'application/x-director',
|
||||
'djv' : 'image/vnd.djvu',
|
||||
'djvu' : 'image/vnd.djvu',
|
||||
'dll' : 'application/x-msdownload',
|
||||
'dmg' : 'application/octet-stream',
|
||||
'dms' : 'application/octet-stream',
|
||||
'doc' : 'application/msword',
|
||||
'dot' : 'application/msword',
|
||||
'dtd' : 'application/xml-dtd',
|
||||
'dv' : 'video/x-dv',
|
||||
'dvi' : 'application/x-dvi',
|
||||
'dxr' : 'application/x-director',
|
||||
'ear' : 'application/java-archive',
|
||||
'eml' : 'message/rfc822',
|
||||
'eps' : 'application/postscript',
|
||||
'etx' : 'text/x-setext',
|
||||
'evy' : 'application/envoy',
|
||||
'exe' : 'application/x-msdownload',
|
||||
'ez' : 'application/andrew-inset',
|
||||
'f' : 'text/x-fortran',
|
||||
'f77' : 'text/x-fortran',
|
||||
'f90' : 'text/x-fortran',
|
||||
'fif' : 'application/fractals',
|
||||
'flr' : 'x-world/x-vrml',
|
||||
'flv' : 'video/x-flv',
|
||||
'for' : 'text/x-fortran',
|
||||
'gem' : 'application/octet-stream',
|
||||
'gemspec' : 'text/x-script.ruby',
|
||||
'gif' : 'image/gif',
|
||||
'gram' : 'application/srgs',
|
||||
'grxml' : 'application/srgs+xml',
|
||||
'gtar' : 'application/x-gtar',
|
||||
'gz' : 'application/x-gzip',
|
||||
'h' : 'text/x-c',
|
||||
'hdf' : 'application/x-hdf',
|
||||
'hh' : 'text/x-c',
|
||||
'hlp' : 'application/winhlp',
|
||||
'hqx' : 'application/mac-binhex40',
|
||||
'hta' : 'application/hta',
|
||||
'htc' : 'text/x-component',
|
||||
'htm' : 'text/html',
|
||||
'html' : 'text/html',
|
||||
'htt' : 'text/webviewhtml',
|
||||
'ice' : 'x-conference/x-cooltalk',
|
||||
'ico' : 'image/vnd.microsoft.icon',
|
||||
'ics' : 'text/calendar',
|
||||
'ief' : 'image/ief',
|
||||
'ifb' : 'text/calendar',
|
||||
'iges' : 'model/iges',
|
||||
'igs' : 'model/iges',
|
||||
'iii' : 'application/x-iphone',
|
||||
'ins' : 'application/x-internet-signup',
|
||||
'isp' : 'application/x-internet-signup',
|
||||
'iso' : 'application/octet-stream',
|
||||
'jar' : 'application/java-archive',
|
||||
'java' : 'text/x-java-source',
|
||||
'jfif' : 'image/pipeg',
|
||||
'jnlp' : 'application/x-java-jnlp-file',
|
||||
'jp2' : 'image/jp2',
|
||||
'jpe' : 'image/jpeg',
|
||||
'jpeg' : 'image/jpeg',
|
||||
'jpg' : 'image/jpeg',
|
||||
'js' : 'application/javascript',
|
||||
'json' : 'application/json',
|
||||
'kar' : 'audio/midi',
|
||||
'latex' : 'application/x-latex',
|
||||
'lha' : 'application/octet-stream',
|
||||
'lsf' : 'video/x-la-asf',
|
||||
'lsx' : 'video/x-la-asf',
|
||||
'lzh' : 'application/octet-stream',
|
||||
'log' : 'text/plain',
|
||||
'm13' : 'application/x-msmediaview',
|
||||
'm14' : 'application/x-msmediaview',
|
||||
'm3u' : 'audio/x-mpegurl',
|
||||
'm4a' : 'audio/mp4a-latm',
|
||||
'm4b' : 'audio/mp4a-latm',
|
||||
'm4p' : 'audio/mp4a-latm',
|
||||
'm4u' : 'video/vnd.mpegurl',
|
||||
'm4v' : 'video/mp4',
|
||||
'mac' : 'image/x-macpaint',
|
||||
'man' : 'text/troff',
|
||||
'mathml' : 'application/mathml+xml',
|
||||
'mbox' : 'application/mbox',
|
||||
'mdb' : 'application/x-msaccess',
|
||||
'mdoc' : 'text/troff',
|
||||
'me' : 'text/troff',
|
||||
'mesh' : 'model/mesh',
|
||||
'mht' : 'message/rfc822',
|
||||
'mhtml' : 'message/rfc822',
|
||||
'mid' : 'audio/midi',
|
||||
'midi' : 'audio/midi',
|
||||
'mif' : 'application/vnd.mif',
|
||||
'mime' : 'message/rfc822',
|
||||
'mml' : 'application/mathml+xml',
|
||||
'mng' : 'video/x-mng',
|
||||
'mny' : 'application/x-msmoney',
|
||||
'mov' : 'video/quicktime',
|
||||
'movie' : 'video/x-sgi-movie',
|
||||
'mp2' : 'video/mpeg',
|
||||
'mp3' : 'audio/mpeg',
|
||||
'mp4' : 'video/mp4',
|
||||
'mp4v' : 'video/mp4',
|
||||
'mpa' : 'video/mpeg',
|
||||
'mpe' : 'video/mpeg',
|
||||
'mpeg' : 'video/mpeg',
|
||||
'mpg' : 'video/mpeg',
|
||||
'mpga' : 'audio/mpeg',
|
||||
'mpp' : 'application/vnd.ms-project',
|
||||
'mpv2' : 'video/mpeg',
|
||||
'ms' : 'text/troff',
|
||||
'msh' : 'model/mesh',
|
||||
'msi' : 'application/x-msdownload',
|
||||
'mvb' : 'application/x-msmediaview',
|
||||
'mxu' : 'video/vnd.mpegurl',
|
||||
'nc' : 'application/x-netcdf',
|
||||
'nws' : 'message/rfc822',
|
||||
'oda' : 'application/oda',
|
||||
'odp' : 'application/vnd.oasis.opendocument.presentation',
|
||||
'ods' : 'application/vnd.oasis.opendocument.spreadsheet',
|
||||
'odt' : 'application/vnd.oasis.opendocument.text',
|
||||
'ogg' : 'application/ogg',
|
||||
'p' : 'text/x-pascal',
|
||||
'p10' : 'application/pkcs10',
|
||||
'p12' : 'application/x-pkcs12',
|
||||
'p7b' : 'application/x-pkcs7-certificates',
|
||||
'p7c' : 'application/x-pkcs7-mime',
|
||||
'p7m' : 'application/x-pkcs7-mime',
|
||||
'p7r' : 'application/x-pkcs7-certreqresp',
|
||||
'p7s' : 'application/x-pkcs7-signature',
|
||||
'pas' : 'text/x-pascal',
|
||||
'pbm' : 'image/x-portable-bitmap',
|
||||
'pct' : 'image/pict',
|
||||
'pdb' : 'chemical/x-pdb',
|
||||
'pdf' : 'application/pdf',
|
||||
'pem' : 'application/x-x509-ca-cert',
|
||||
'pfx' : 'application/x-pkcs12',
|
||||
'pgm' : 'image/x-portable-graymap',
|
||||
'pgn' : 'application/x-chess-pgn',
|
||||
'pgp' : 'application/pgp-encrypted',
|
||||
'pic' : 'image/pict',
|
||||
'pict' : 'image/pict',
|
||||
'pkg' : 'application/octet-stream',
|
||||
'pko' : 'application/ynd.ms-pkipko',
|
||||
'pl' : 'text/x-script.perl',
|
||||
'pm' : 'text/x-script.perl-module',
|
||||
'pma' : 'application/x-perfmon',
|
||||
'pmc' : 'application/x-perfmon',
|
||||
'pml' : 'application/x-perfmon',
|
||||
'pmr' : 'application/x-perfmon',
|
||||
'pmw' : 'application/x-perfmon',
|
||||
'png' : 'image/png',
|
||||
'pnm' : 'image/x-portable-anymap',
|
||||
'pnt' : 'image/x-macpaint',
|
||||
'pntg' : 'image/x-macpaint',
|
||||
'pot' : 'application/vnd.ms-powerpoint',
|
||||
'ppm' : 'image/x-portable-pixmap',
|
||||
'pps' : 'application/vnd.ms-powerpoint',
|
||||
'ppt' : 'application/vnd.ms-powerpoint',
|
||||
'prf' : 'application/pics-rules',
|
||||
'ps' : 'application/postscript',
|
||||
'psd' : 'image/vnd.adobe.photoshop',
|
||||
'pub' : 'application/x-mspublisher',
|
||||
'py' : 'text/x-script.python',
|
||||
'qt' : 'video/quicktime',
|
||||
'qti' : 'image/x-quicktime',
|
||||
'qtif' : 'image/x-quicktime',
|
||||
'ra' : 'audio/x-pn-realaudio',
|
||||
'rake' : 'text/x-script.ruby',
|
||||
'ram' : 'audio/x-pn-realaudio',
|
||||
'rar' : 'application/x-rar-compressed',
|
||||
'ras' : 'image/x-cmu-raster',
|
||||
'rb' : 'text/x-script.ruby',
|
||||
'rdf' : 'application/rdf+xml',
|
||||
'rgb' : 'image/x-rgb',
|
||||
'rm' : 'application/vnd.rn-realmedia',
|
||||
'rmi' : 'audio/mid',
|
||||
'roff' : 'text/troff',
|
||||
'rpm' : 'application/x-redhat-package-manager',
|
||||
'rss' : 'application/rss+xml',
|
||||
'rtf' : 'application/rtf',
|
||||
'rtx' : 'text/richtext',
|
||||
'ru' : 'text/x-script.ruby',
|
||||
's' : 'text/x-asm',
|
||||
'scd' : 'application/x-msschedule',
|
||||
'sct' : 'text/scriptlet',
|
||||
'setpay' : 'application/set-payment-initiation',
|
||||
'setreg' : 'application/set-registration-initiation',
|
||||
'sgm' : 'text/sgml',
|
||||
'sgml' : 'text/sgml',
|
||||
'sh' : 'application/x-sh',
|
||||
'shar' : 'application/x-shar',
|
||||
'sig' : 'application/pgp-signature',
|
||||
'silo' : 'model/mesh',
|
||||
'sit' : 'application/x-stuffit',
|
||||
'skd' : 'application/x-koan',
|
||||
'skm' : 'application/x-koan',
|
||||
'skp' : 'application/x-koan',
|
||||
'skt' : 'application/x-koan',
|
||||
'smi' : 'application/smil',
|
||||
'smil' : 'application/smil',
|
||||
'snd' : 'audio/basic',
|
||||
'so' : 'application/octet-stream',
|
||||
'spc' : 'application/x-pkcs7-certificates',
|
||||
'spl' : 'application/x-futuresplash',
|
||||
'src' : 'application/x-wais-source',
|
||||
'sst' : 'application/vnd.ms-pkicertstore',
|
||||
'stl' : 'application/vnd.ms-pkistl',
|
||||
'stm' : 'text/html',
|
||||
'sv4cpio' : 'application/x-sv4cpio',
|
||||
'sv4crc' : 'application/x-sv4crc',
|
||||
'svg' : 'image/svg+xml',
|
||||
'svgz' : 'image/svg+xml',
|
||||
'swf' : 'application/x-shockwave-flash',
|
||||
@@ -142,30 +284,60 @@ var types = {
|
||||
'texi' : 'application/x-texinfo',
|
||||
'texinfo' : 'application/x-texinfo',
|
||||
'text' : 'text/plain',
|
||||
'tgz' : 'application/x-compressed',
|
||||
'tif' : 'image/tiff',
|
||||
'tiff' : 'image/tiff',
|
||||
'torrent' : 'application/x-bittorrent',
|
||||
'tr' : 'text/troff',
|
||||
'trm' : 'application/x-msterminal',
|
||||
'tsv' : 'text/tab-seperated-values',
|
||||
'txt' : 'text/plain',
|
||||
'uls' : 'text/iuls',
|
||||
'ustar' : 'application/x-ustar',
|
||||
'vcd' : 'application/x-cdlink',
|
||||
'vcf' : 'text/x-vcard',
|
||||
'vcs' : 'text/x-vcalendar',
|
||||
'vrml' : 'model/vrml',
|
||||
'vxml' : 'application/voicexml+xml',
|
||||
'war' : 'application/java-archive',
|
||||
'wav' : 'audio/x-wav',
|
||||
'wbmp' : 'image/vnd.wap.wbmp',
|
||||
'wbxml' : 'application/vnd.wap.wbxml',
|
||||
'wcm' : 'application/vnd.ms-works',
|
||||
'wdb' : 'application/vnd.ms-works',
|
||||
'wks' : 'application/vnd.ms-works',
|
||||
'wma' : 'audio/x-ms-wma',
|
||||
'wmf' : 'application/x-msmetafile',
|
||||
'wml' : 'text/vnd.wap.wml',
|
||||
'wmls' : 'text/vnd.wap.wmlscript',
|
||||
'wmlsc' : 'application/vnd.wap.wmlscriptc',
|
||||
'wmv' : 'video/x-ms-wmv',
|
||||
'wmx' : 'video/x-ms-wmx',
|
||||
'wps' : 'application/vnd.ms-works',
|
||||
'wri' : 'application/x-mswrite',
|
||||
'wrl' : 'model/vrml',
|
||||
'wrz' : 'x-world/x-vrml',
|
||||
'wsdl' : 'application/wsdl+xml',
|
||||
'xaf' : 'x-world/x-vrml',
|
||||
'xbm' : 'image/x-xbitmap',
|
||||
'xht' : 'application/xhtml+xml',
|
||||
'xhtml' : 'application/xhtml+xml',
|
||||
'xla' : 'application/vnd.ms-excel',
|
||||
'xlc' : 'application/vnd.ms-excel',
|
||||
'xlm' : 'application/vnd.ms-excel',
|
||||
'xls' : 'application/vnd.ms-excel',
|
||||
'xlt' : 'application/vnd.ms-excel',
|
||||
'xml' : 'application/xml',
|
||||
'xof' : 'x-world/x-vrml',
|
||||
'xpm' : 'image/x-xpixmap',
|
||||
'xsl' : 'application/xml',
|
||||
'xslt' : 'application/xslt+xml',
|
||||
'xul' : 'application/vnd.mozilla.xul+xml',
|
||||
'xwd' : 'image/x-xwindowdump',
|
||||
'xyz' : 'chemical/x-xyz',
|
||||
'yaml' : 'text/yaml',
|
||||
'yml' : 'text/yaml',
|
||||
'z' : 'application/x-compress',
|
||||
'zip' : 'application/zip'
|
||||
}
|
||||
|
||||
@@ -178,10 +350,11 @@ var types = {
|
||||
* however this can be altered using the 'default mime type'
|
||||
* setting.
|
||||
*
|
||||
* mime('png') // => 'image/png'
|
||||
* mime('.png') // => 'image/png'
|
||||
* mime('image.png') // => 'image/png'
|
||||
* mime('path/to/image.png') // => 'image/png'
|
||||
* var mime = require('express/mime')
|
||||
* mime.type('png') // => 'image/png'
|
||||
* mime.type('.png') // => 'image/png'
|
||||
* mime.type('image.png') // => 'image/png'
|
||||
* mime.type('path/to/image.png') // => 'image/png'
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
@@ -189,9 +362,9 @@ var types = {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.mime = function(path) {
|
||||
return types[path] ||
|
||||
types[extname(path)] ||
|
||||
exports.type = function(path) {
|
||||
return exports.types[path] ||
|
||||
exports.types[utils.extname(path)] ||
|
||||
set('default mime type') ||
|
||||
'application/octet-stream'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
/**
|
||||
* Push _plugin_ with _options_ to the plugin stack.
|
||||
* If _plugin_ has already been pushed, then it's options
|
||||
* will override any previously set.
|
||||
*
|
||||
* @param {Plugin} plugin
|
||||
* @param {hash} options
|
||||
@@ -10,7 +12,12 @@
|
||||
*/
|
||||
|
||||
exports.use = function(plugin, options) {
|
||||
if ('init' in plugin) plugin.init()
|
||||
if (Express.environment === 'test' && 'init' in plugin)
|
||||
plugin.init(options)
|
||||
$(Express.plugins).each(function(other, i){
|
||||
if (other.klass === plugin)
|
||||
delete Express.plugins[i]
|
||||
})
|
||||
Express.plugins.push({
|
||||
klass: plugin,
|
||||
options: options
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
// Express - Plugins - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
process.mixin(require('express/plugins/hooks'))
|
||||
process.mixin(require('express/plugins/cache'))
|
||||
process.mixin(require('express/plugins/cookie'))
|
||||
process.mixin(require('express/plugins/session'))
|
||||
process.mixin(require('express/plugins/profiler'))
|
||||
process.mixin(require('express/plugins/common-logger'))
|
||||
process.mixin(require('express/plugins/content-length'))
|
||||
|
||||
218
lib/express/plugins/cache.js
Normal file
218
lib/express/plugins/cache.js
Normal file
@@ -0,0 +1,218 @@
|
||||
|
||||
// Express - Cache - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
// --- Cache
|
||||
|
||||
var Cache = Class({
|
||||
|
||||
/**
|
||||
* Initialize cache with _key_ and _val_.
|
||||
*/
|
||||
|
||||
init: function(key, val) {
|
||||
this.key = key
|
||||
this.val = val
|
||||
this.created = Number(new Date)
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store
|
||||
|
||||
exports.Store = Class({
|
||||
|
||||
/**
|
||||
* Ensure that the given _key_ is a string.
|
||||
* Override in subclass to provide data-store specific functionality.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} val
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set: function(key, val) {
|
||||
if (typeof key !== 'string') throw new Error(this.name + ' store #set() key must be a string')
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure that the given _key_ is a string.
|
||||
* Override in subclass to provide data-store specific functionality.
|
||||
*
|
||||
* @param {string} key
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get: function(key) {
|
||||
if (typeof key !== 'string') throw new Error(this.name + 'store #get() key must be a string')
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert to '[NAME Store]'.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[' + this.name + ' Store]'
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store.Memory
|
||||
|
||||
exports.Store.Memory = exports.Store.extend({
|
||||
|
||||
/**
|
||||
* Datastore name.
|
||||
*/
|
||||
|
||||
name: 'Memory',
|
||||
|
||||
/**
|
||||
* Initialize data.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
this.data = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the given _key_ to _val_, returning _val_.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {string} val
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set: function(key, val) {
|
||||
this.__super__(key, val)
|
||||
return this.data[key] = new Cache(key, val), val
|
||||
},
|
||||
|
||||
/**
|
||||
* Get data found matching the given _key_.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cache.get('page:front')
|
||||
* // => '<html>...</html>'
|
||||
*
|
||||
* cache.get('page:*')
|
||||
* // => { 'page:front': '<html>...</html>',
|
||||
* 'page:users': '<html>...</html>',
|
||||
* ... }
|
||||
*
|
||||
* @param {string} key
|
||||
* @return {mixed}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get: function(key) {
|
||||
this.__super__(key)
|
||||
if (key.indexOf('*') === -1)
|
||||
return this.data[key] instanceof Cache ?
|
||||
this.data[key].val :
|
||||
null
|
||||
var regexp = this.normalize(key)
|
||||
return $(this.data).reduce({}, function(vals, cache){
|
||||
if (regexp.test(cache.key))
|
||||
vals[cache.key] = cache.val
|
||||
return vals
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear data matching the given _key_.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* cache.clear('page:front')
|
||||
* cache.clear('page:*')
|
||||
*
|
||||
* @param {string} key
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clear: function(key) {
|
||||
if (key.indexOf('*') === -1)
|
||||
return delete this.data[key]
|
||||
var regexp = this.normalize(key)
|
||||
for (var key in this.data)
|
||||
if (this.data.hasOwnProperty(key))
|
||||
if (regexp.test(key))
|
||||
delete this.data[key]
|
||||
},
|
||||
|
||||
/**
|
||||
* Reap caches older than _ms_.
|
||||
*
|
||||
* @param {int} ms
|
||||
* @api private
|
||||
*/
|
||||
|
||||
reap: function(ms) {
|
||||
var self = this,
|
||||
threshold = Number(new Date(Number(new Date) - ms))
|
||||
$(this.data).each(function(cache){
|
||||
if (cache.created < threshold)
|
||||
self.clear(cache.key)
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Convert the given key matching _pattern_
|
||||
* into a RegExp.
|
||||
*
|
||||
* - * is converted to (.*?)
|
||||
*
|
||||
* @param {string} pattern
|
||||
* @return {regexp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
normalize: function(pattern) {
|
||||
return new RegExp('^' + pattern.replace(/[*]/g, '(.*?)') + '$')
|
||||
}
|
||||
})
|
||||
|
||||
// --- Cache
|
||||
|
||||
exports.Cache = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize memory store and start reaper.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - dataStore constructor name of cache data store, defaults to Store.Memory
|
||||
* - lifetime lifetime of cache in milliseconds, defaults to one day
|
||||
* - reapInterval, reapEvery interval in milliseconds in which to reap old caches, defaults to one hour
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
process.mixin(this, options)
|
||||
this.store = new (this.dataStore || exports.Store.Memory)(options)
|
||||
Request.include({ cache: this.store })
|
||||
this.startReaper()
|
||||
},
|
||||
|
||||
/**
|
||||
* Start reaper.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
startReaper: function() {
|
||||
var self = this,
|
||||
oneDay = 86400000,
|
||||
oneHour = 3600000
|
||||
setInterval(function(){
|
||||
self.store.reap(self.lifetime || oneDay)
|
||||
}, self.reapInterval || self.reapEvery || oneHour)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -56,12 +56,12 @@ exports.Cookie = Plugin.extend({
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - path: Cookie path, defaults to '/'
|
||||
* - domain: Tail matched domain name such as 'vision-media.ca' or 'blog.vision-media.ca' etc
|
||||
* - expires: Date object converted to 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'
|
||||
* - path Cookie path, defaults to '/'
|
||||
* - domain Tail matched domain name such as 'vision-media.ca' or 'blog.vision-media.ca' etc
|
||||
* - expires Date object converted to 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'
|
||||
* when undefined the cookie will last the duration of a the
|
||||
* client's session.
|
||||
* - secure: When true the cookie will be sent by the client only when transfering data via HTTPS
|
||||
* - secure When true the cookie will be sent by the client only when transfering data via HTTPS
|
||||
* - httpOnly When true the cookie will be sent to the server only and will not be accessable via
|
||||
* client-side scripting.
|
||||
*
|
||||
@@ -73,6 +73,8 @@ exports.Cookie = Plugin.extend({
|
||||
*/
|
||||
|
||||
cookie: function(name, val, options) {
|
||||
options = options || {}
|
||||
options.path = options.path || '/'
|
||||
return val ?
|
||||
this.response.cookies.push(exports.compileCookie(name, val, options)) :
|
||||
this.cookies[name]
|
||||
@@ -86,13 +88,14 @@ exports.Cookie = Plugin.extend({
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Parser request cookie data.
|
||||
* Parse request cookie data.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
event.request.response.cookies = []
|
||||
if (event.request.headers.cookie)
|
||||
event.request.cookies = exports.parseCookie(event.request.headers.cookie)
|
||||
event.request.cookies = event.request.headers.cookie ?
|
||||
exports.parseCookie(event.request.headers.cookie) :
|
||||
{}
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -50,7 +50,7 @@ exports.Hooks = Plugin.extend({
|
||||
|
||||
request: function(event) {
|
||||
$(before).each(function(fn){
|
||||
fn.call(event.request)
|
||||
fn.call(event.request, event.request)
|
||||
})
|
||||
},
|
||||
|
||||
@@ -60,7 +60,7 @@ exports.Hooks = Plugin.extend({
|
||||
|
||||
response: function(event) {
|
||||
$(after).each(function(fn){
|
||||
fn.call(event.request)
|
||||
fn.call(event.request, event.request)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
205
lib/express/plugins/session.js
Normal file
205
lib/express/plugins/session.js
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
// Express - Session - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('express/utils')
|
||||
|
||||
// --- Session
|
||||
|
||||
var Session = Class({
|
||||
|
||||
/**
|
||||
* Initialize session _sid_.
|
||||
*/
|
||||
|
||||
init: function(sid) {
|
||||
this.id = sid
|
||||
this.touch()
|
||||
},
|
||||
|
||||
/**
|
||||
* Update last access time.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
touch: function() {
|
||||
this.lastAccess = Number(new Date)
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store
|
||||
|
||||
exports.Store = Class({
|
||||
|
||||
/**
|
||||
* Convert to '[NAME Store]'.
|
||||
*
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
toString: function() {
|
||||
return '[' + this.name + ' Store]'
|
||||
}
|
||||
})
|
||||
|
||||
// --- Store.Memory
|
||||
|
||||
exports.Store.Memory = exports.Store.extend({
|
||||
|
||||
/**
|
||||
* Datastore name.
|
||||
*/
|
||||
|
||||
name: 'Memory',
|
||||
|
||||
/**
|
||||
* Initialize in-memory session store.
|
||||
*/
|
||||
|
||||
init: function() {
|
||||
this.store = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetch session with the given _sid_ or
|
||||
* a new Session is returned.
|
||||
*
|
||||
* @param {int} sid
|
||||
* @return {Session}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
fetch: function(sid) {
|
||||
return this.store[sid] || new Session(sid)
|
||||
},
|
||||
|
||||
/**
|
||||
* Commit _session_ data.
|
||||
*
|
||||
* @param {Session} session
|
||||
* @api private
|
||||
*/
|
||||
|
||||
commit: function(session) {
|
||||
return this.store[session.id] = session
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear all sessions.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
clear: function() {
|
||||
this.store = {}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy session using the given _sid_.
|
||||
*
|
||||
* @param {int} sid
|
||||
* @api public
|
||||
*/
|
||||
|
||||
destroy: function(sid) {
|
||||
delete this.store[sid]
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the number of sessions currently stored.
|
||||
*
|
||||
* @return {int}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
length: function() {
|
||||
return $(this.store).length()
|
||||
},
|
||||
|
||||
/**
|
||||
* Reap sessions older than _ms_.
|
||||
*
|
||||
* @param {int} ms
|
||||
* @api private
|
||||
*/
|
||||
|
||||
reap: function(ms) {
|
||||
var self = this,
|
||||
threshold = Number(new Date(Number(new Date) - ms))
|
||||
$(this.store).each(function(session, sid){
|
||||
if (session.lastAccess < threshold)
|
||||
self.destroy(sid)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// --- Cache
|
||||
|
||||
exports.Session = Plugin.extend({
|
||||
extend: {
|
||||
|
||||
/**
|
||||
* Initialize memory store and start reaper.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - dataStore constructor name of session data store, defaults to Store.Memory
|
||||
* - lifetime lifetime of session in milliseconds, defaults to one day
|
||||
* - reapInterval, reapEvery interval in milliseconds in which to reap old sessions, defaults to one hour
|
||||
*
|
||||
* @param {hash} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
init: function(options) {
|
||||
process.mixin(this, options)
|
||||
this.store = new (this.dataStore || exports.Store.Memory)(options)
|
||||
this.startReaper()
|
||||
},
|
||||
|
||||
/**
|
||||
* Start reaper.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
startReaper: function() {
|
||||
var self = this,
|
||||
oneDay = 86400000,
|
||||
oneHour = 3600000
|
||||
setInterval(function(){
|
||||
self.store.reap(self.lifetime || oneDay)
|
||||
}, self.reapInterval || self.reapEvery || oneHour)
|
||||
}
|
||||
},
|
||||
|
||||
// --- Events
|
||||
|
||||
on: {
|
||||
|
||||
/**
|
||||
* Create session id when not found; delegate to store.
|
||||
*/
|
||||
|
||||
request: function(event) {
|
||||
var sid
|
||||
if (!(sid = event.request.cookie('sid')))
|
||||
event.request.cookie('sid', sid = utils.uid(), set('session cookie'))
|
||||
event.request.session = exports.Session.store.fetch(sid)
|
||||
event.request.session.touch()
|
||||
},
|
||||
|
||||
/**
|
||||
* Delegate to store, allowing it to save sessions changes.
|
||||
*/
|
||||
|
||||
response: function(event) {
|
||||
exports.Session.store.commit(event.request.session)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,21 +1,21 @@
|
||||
|
||||
// Express - View - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
var posix = require('posix')
|
||||
|
||||
/**
|
||||
* Template content cache.
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var cache = {}
|
||||
var posix = require('posix'),
|
||||
utils = require('express/utils')
|
||||
|
||||
/**
|
||||
* Supported template engines.
|
||||
*/
|
||||
|
||||
var engine = {
|
||||
ejs: require('support/ejs/ejs'),
|
||||
haml: require('support/haml/lib/haml')
|
||||
ejs: require('ejs'),
|
||||
haml: require('haml'),
|
||||
sass: require('sass')
|
||||
}
|
||||
|
||||
// --- View
|
||||
@@ -57,7 +57,7 @@ exports.View = Plugin.extend({
|
||||
*
|
||||
* @param {string} view
|
||||
* @param {hash} options
|
||||
* @settings 'views', 'cache views'
|
||||
* @settings 'views', 'cache view contents'
|
||||
* @api public
|
||||
*/
|
||||
|
||||
@@ -66,13 +66,13 @@ exports.View = Plugin.extend({
|
||||
options = options || {},
|
||||
path = set('views') + '/' + view,
|
||||
type = path.split('.').slice(-2)[0],
|
||||
ext = extname(path),
|
||||
ext = utils.extname(path),
|
||||
layout = options.layout === undefined ? true : options.layout
|
||||
self.contentType(ext)
|
||||
function render(content) {
|
||||
content = engine[type].render(content, options)
|
||||
if (layout)
|
||||
self.render('layout.' + type + '.' + ext, process.mixin(options, {
|
||||
self.render('layout.' + type + '.' + ext, process.mixin(true, options, {
|
||||
layout: false,
|
||||
locals: {
|
||||
body: content
|
||||
@@ -81,11 +81,13 @@ exports.View = Plugin.extend({
|
||||
else
|
||||
self.halt(200, content)
|
||||
}
|
||||
if (set('cache views') && cache[view])
|
||||
render(cache[view])
|
||||
if (set('cache view contents') && self.cache.get(path))
|
||||
render(self.cache.get(path))
|
||||
else
|
||||
posix.cat(path).addCallback(function(content){
|
||||
render(cache[view] = content)
|
||||
set('cache view contents') ?
|
||||
render(self.cache.set(path, content)) :
|
||||
render(content)
|
||||
}).addErrback(function(e){
|
||||
throw e
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
var StaticFile = require('express/static').File,
|
||||
statusBodies = require('http').STATUS_CODES,
|
||||
queryString = require('querystring'),
|
||||
mime = require('express/mime'),
|
||||
url = require('url')
|
||||
|
||||
// --- InvalidStatusCode
|
||||
@@ -67,11 +68,10 @@ exports.Request = Class({
|
||||
this.response = response
|
||||
this.url = url.parse(this.url)
|
||||
this.url.pathname = exports.normalizePath(this.url.pathname)
|
||||
this.params = {
|
||||
get: this.url.query ? queryString.parseQuery(this.url.query) : {},
|
||||
post: {},
|
||||
path: {}
|
||||
}
|
||||
this.params = this.params || {}
|
||||
this.params.path = {}
|
||||
this.params.get = this.url.query ? queryString.parseQuery(this.url.query) : {}
|
||||
this.params.post = this.params.post || {}
|
||||
this.plugins = $(Express.plugins).map(function(plugin){
|
||||
return new plugin.klass(plugin.options)
|
||||
})
|
||||
@@ -113,20 +113,19 @@ exports.Request = Class({
|
||||
|
||||
/**
|
||||
* Check if Accept header includes the mime type
|
||||
* for the given _path_, which calls mime().
|
||||
* for the given _path_, which calls mime.type().
|
||||
*
|
||||
* When no Accept header is present true will be
|
||||
* returned as stated in the HTTP specification.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {bool}
|
||||
* @see mime()
|
||||
* @api public
|
||||
*/
|
||||
|
||||
accepts: function(path) {
|
||||
return this.header('accept') ?
|
||||
this.header('accept').indexOf(mime(path)) !== -1 :
|
||||
this.header('accept').indexOf(mime.type(path)) !== -1 :
|
||||
true
|
||||
},
|
||||
|
||||
@@ -187,16 +186,15 @@ exports.Request = Class({
|
||||
|
||||
/**
|
||||
* Set Content-Type header to the mime type
|
||||
* for the given _path_, which calls mime().
|
||||
* for the given _path_, which calls mime.type().
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {Request}
|
||||
* @see mime()
|
||||
* @api public
|
||||
*/
|
||||
|
||||
contentType: function(path) {
|
||||
this.header('content-type', mime(path))
|
||||
this.header('content-type', mime.type(path))
|
||||
return this
|
||||
},
|
||||
|
||||
|
||||
@@ -35,19 +35,26 @@ exports.File = Class({
|
||||
* - Ensures the file exists
|
||||
* - Ensures the file is a regular file (not FIFO, Socket, etc)
|
||||
* - Automatically assigns content type
|
||||
* - Halts with 404 when failing
|
||||
*
|
||||
* @param {Request} request
|
||||
* @settings 'cache static files'
|
||||
* @api public
|
||||
*/
|
||||
|
||||
send: function(request) {
|
||||
var file = this.path
|
||||
var cache, file = this.path
|
||||
if (set('cache static files') && (cache = request.cache.get(file)))
|
||||
return request.contentType(cache.type),
|
||||
request.halt(200, cache.content, 'binary')
|
||||
path.exists(file, function(exists){
|
||||
if (!exists) request.halt()
|
||||
if (!exists) return request.halt()
|
||||
posix.stat(file).addCallback(function(stats){
|
||||
if (!stats.isFile()) request.halt()
|
||||
if (!stats.isFile()) return request.halt()
|
||||
posix.cat(file, 'binary').addCallback(function(content){
|
||||
request.contentType(file)
|
||||
if (set('cache static files'))
|
||||
request.cache.set(file, { type: file, content: content })
|
||||
request.halt(200, content, 'binary')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
|
||||
// Express - Helpers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var queryString = require('querystring')
|
||||
|
||||
/**
|
||||
* JSON aliases.
|
||||
*/
|
||||
@@ -9,15 +15,17 @@ JSON.encode = JSON.stringify
|
||||
JSON.decode = JSON.parse
|
||||
|
||||
/**
|
||||
* Return the directory name of the given _path_.
|
||||
* Return a unique identifier.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.dirname = function(path) {
|
||||
return path.split('/').slice(0, -1).join('/')
|
||||
exports.uid = function() {
|
||||
var uid = ''
|
||||
for (var n = 4; n; --n)
|
||||
uid += (Math.abs((Math.random() * 0xFFFFFFF) | 0)).toString(16)
|
||||
return uid
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,4 +100,35 @@ exports.toArray = function(arr, offset) {
|
||||
exports.escapeRegexp = function(string, chars) {
|
||||
var specials = (chars || '/ . * + ? | ( ) [ ] { } \\').split(' ').join('|\\')
|
||||
return string.replace(new RegExp('(\\' + specials + ')', 'g'), '\\$1')
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge param _key_ and _val_ into _params_. Key
|
||||
* should be a query string key such as 'user[name]',
|
||||
* and _val_ is it's associated object. The root _params_
|
||||
* object is returned.
|
||||
*
|
||||
* @param {string} key
|
||||
* @param {mixed} val
|
||||
* @return {hash}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.mergeParam = function(key, val, params) {
|
||||
var orig = params,
|
||||
keys = key.trim().match(/\w+/g),
|
||||
array = /\[\]$/.test(key)
|
||||
$(keys).reduce(queryString.parseQuery(key), function(parts, key, i){
|
||||
if (i === keys.length - 1)
|
||||
if (key in params)
|
||||
params[key] instanceof Array ?
|
||||
params[key].push(val) :
|
||||
params[key] = [params[key], val]
|
||||
else
|
||||
params[key] = array ? [val] : val
|
||||
if (!(key in params)) params[key] = {}
|
||||
params = params[key]
|
||||
return parts[key]
|
||||
})
|
||||
return orig
|
||||
}
|
||||
Submodule lib/support/haml updated: 53fad8e082...b1fa42226b
1
lib/support/sass
Submodule
1
lib/support/sass
Submodule
Submodule lib/support/sass added at dfc1cd027f
@@ -4,7 +4,7 @@
|
||||
;(function(){
|
||||
|
||||
JSpec = {
|
||||
version : '3.1.0',
|
||||
version : '3.2.1',
|
||||
assert : true,
|
||||
cache : {},
|
||||
suites : [],
|
||||
@@ -1046,7 +1046,7 @@
|
||||
*/
|
||||
|
||||
expect : function(actual) {
|
||||
assert = function(matcher, args, negate) {
|
||||
function assert(matcher, args, negate) {
|
||||
var expected = toArray(args, 1)
|
||||
matcher.negate = negate
|
||||
assertion = new JSpec.Assertion(matcher, actual, expected, negate)
|
||||
@@ -1056,11 +1056,11 @@
|
||||
return assertion.result
|
||||
}
|
||||
|
||||
to = function(matcher) {
|
||||
function to(matcher) {
|
||||
return assert(matcher, arguments, false)
|
||||
}
|
||||
|
||||
not_to = function(matcher) {
|
||||
function not_to(matcher) {
|
||||
return assert(matcher, arguments, true)
|
||||
}
|
||||
|
||||
@@ -1692,7 +1692,7 @@
|
||||
case Number:
|
||||
case RegExp:
|
||||
case Function:
|
||||
state = actual.toString().match(arg.toString())
|
||||
state = actual.toString().indexOf(arg) !== -1
|
||||
break
|
||||
|
||||
case Object:
|
||||
|
||||
@@ -27,12 +27,13 @@ specs = {
|
||||
independant: [
|
||||
'core',
|
||||
'routing',
|
||||
'helpers',
|
||||
'utils',
|
||||
'request',
|
||||
'mime',
|
||||
'static',
|
||||
'collection',
|
||||
'plugins',
|
||||
'plugins.cache',
|
||||
'plugins.view',
|
||||
'plugins.common-logger',
|
||||
'plugins.content-length',
|
||||
@@ -41,6 +42,7 @@ specs = {
|
||||
'plugins.redirect',
|
||||
'plugins.hooks',
|
||||
'plugins.cookie',
|
||||
'plugins.session',
|
||||
],
|
||||
dependant: [
|
||||
'element-collection'
|
||||
@@ -62,4 +64,5 @@ switch (process.ARGV[2]) {
|
||||
run([process.ARGV[2]])
|
||||
}
|
||||
|
||||
Express.environment = 'test'
|
||||
JSpec.run({ reporter: JSpec.reporters.Terminal, failuresOnly: true }).report()
|
||||
@@ -1,48 +0,0 @@
|
||||
|
||||
describe 'Express'
|
||||
describe 'toArray()'
|
||||
describe 'when given an array'
|
||||
it 'should return the array'
|
||||
toArray([1,2,3]).should.eql [1,2,3]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when given an object with indexed values and length'
|
||||
it 'should return an array'
|
||||
var args = -{ return arguments }('foo', 'bar')
|
||||
toArray(args).should.eql ['foo', 'bar']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'escape()'
|
||||
it 'should escape html'
|
||||
escape('<p>this & that').should.eql '<p>this & that'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'extname()'
|
||||
it 'should return the a files extension'
|
||||
extname('image.png').should.eql 'png'
|
||||
extname('image.large.png').should.eql 'png'
|
||||
extname('/path/to/image.large.png').should.eql 'png'
|
||||
end
|
||||
|
||||
it 'should return null when not found'
|
||||
extname('path').should.be_null
|
||||
extname('/just/a/path').should.be_null
|
||||
end
|
||||
end
|
||||
|
||||
describe 'dirname()'
|
||||
it 'should return the directory path'
|
||||
dirname('/path/to/images/foo.bar.png').should.eql '/path/to/images'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'basename()'
|
||||
it 'should return a files basename'
|
||||
basename('foo/bar/baz.image.png').should.eql 'baz.image.png'
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,36 +1,42 @@
|
||||
|
||||
describe 'Express'
|
||||
before
|
||||
mime = require('express/mime')
|
||||
end
|
||||
|
||||
before_each
|
||||
reset()
|
||||
end
|
||||
|
||||
describe 'mime()'
|
||||
describe 'when given an extension with leading dot'
|
||||
it 'should return the associated mime type'
|
||||
mime('.png').should.eql 'image/png'
|
||||
describe 'mime'
|
||||
describe 'type()'
|
||||
describe 'when given an extension with leading dot'
|
||||
it 'should return the associated mime type'
|
||||
mime.type('.png').should.eql 'image/png'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when given an extension without leading dot'
|
||||
it 'should return the associated mime type'
|
||||
mime('png').should.eql 'image/png'
|
||||
|
||||
describe 'when given an extension without leading dot'
|
||||
it 'should return the associated mime type'
|
||||
mime.type('png').should.eql 'image/png'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when given a file path'
|
||||
it 'should return the associated mime type'
|
||||
mime('/path/to/an/image.png').should.eql 'image/png'
|
||||
|
||||
describe 'when given a file path'
|
||||
it 'should return the associated mime type'
|
||||
mime.type('/path/to/an/image.png').should.eql 'image/png'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when given an unknown extension'
|
||||
it 'should default to the "default mime type" setting'
|
||||
set('default mime type', 'text/plain')
|
||||
mime('meow').should.eql 'text/plain'
|
||||
end
|
||||
|
||||
it 'should default to "application/octet-stream" otherwise'
|
||||
mime('meow').should.eql 'application/octet-stream'
|
||||
|
||||
describe 'when given an unknown extension'
|
||||
it 'should default to the "default mime type" setting'
|
||||
set('default mime type', 'text/plain')
|
||||
mime.type('meow').should.eql 'text/plain'
|
||||
end
|
||||
|
||||
it 'should default to "application/octet-stream" otherwise'
|
||||
mime.type('meow').should.eql 'application/octet-stream'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
119
spec/spec.plugins.cache.js
Normal file
119
spec/spec.plugins.cache.js
Normal file
@@ -0,0 +1,119 @@
|
||||
|
||||
describe 'Express'
|
||||
before_each
|
||||
reset()
|
||||
use(require('express/plugins/cache').Cache)
|
||||
cache = require('express/plugins/cache')
|
||||
end
|
||||
|
||||
describe 'Cache'
|
||||
describe 'Request'
|
||||
describe '#cache'
|
||||
it 'should use memory store by default'
|
||||
get('/item', function(){
|
||||
return this.cache.toString()
|
||||
})
|
||||
get('/item').body.should.eql '[Memory Store]'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'cache Store.Memory'
|
||||
before_each
|
||||
store = new cache.Store.Memory
|
||||
end
|
||||
|
||||
describe '#toString()'
|
||||
it 'should return [Memory Store]'
|
||||
store.toString().should.eql '[Memory Store]'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#set()'
|
||||
describe 'given a key and value'
|
||||
it 'should set the cache data'
|
||||
store.set('foo', 'bar')
|
||||
store.get('foo').should.eql 'bar'
|
||||
end
|
||||
|
||||
it 'should override existing data'
|
||||
store.set('foo', 'bar')
|
||||
store.set('foo', 'baz')
|
||||
store.get('foo').should.eql 'baz'
|
||||
end
|
||||
|
||||
it 'should return data'
|
||||
store.set('foo', 'bar').should.eql 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'given an abitrary key'
|
||||
it 'should throw an error'
|
||||
-{ store.set({}, 'foo') }.should.throw_error
|
||||
end
|
||||
end
|
||||
|
||||
describe 'given an abitrary value'
|
||||
it 'should serialize as JSON'
|
||||
store.set('user', { name: 'tj' }).should.eql { name: 'tj' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get()'
|
||||
describe 'given a key'
|
||||
it 'should return cached value'
|
||||
store.set('foo', 'bar')
|
||||
store.get('foo').should.eql 'bar'
|
||||
end
|
||||
|
||||
it 'should unserialize JSON data'
|
||||
store.set('user', { name: 'tj' })
|
||||
store.get('user').should.eql { name: 'tj' }
|
||||
end
|
||||
end
|
||||
|
||||
describe 'given wildcards'
|
||||
it 'should return a set of caches'
|
||||
store.set('user:1', 'a')
|
||||
store.set('user:2', 'b')
|
||||
store.set('foo', 'bar')
|
||||
store.get('user:*').should.eql { 'user:1': 'a', 'user:2': 'b' }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear()'
|
||||
describe 'given a key'
|
||||
it 'should delete previous data'
|
||||
store.set('foo', 'bar')
|
||||
store.clear('foo')
|
||||
store.get('foo').should.be_null
|
||||
end
|
||||
end
|
||||
|
||||
describe 'given wildcards'
|
||||
it 'should clear a set of caches'
|
||||
store.set('user:one', '1')
|
||||
store.set('user:two', '2')
|
||||
store.clear('user:*')
|
||||
store.get('user:one').should.be_null
|
||||
store.get('user:two').should.be_null
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reap()'
|
||||
it 'should destroy caches older than the given age in milliseconds'
|
||||
store.set('user:one', '1')
|
||||
store.data['user:one'].created = Number(new Date) - 300
|
||||
store.set('user:two', '2')
|
||||
store.data['user:two'].created = Number(new Date) - 100
|
||||
store.reap(200)
|
||||
store.get('user:one').should.be_null
|
||||
store.get('user:two').should.not.be_null
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -82,7 +82,7 @@ describe 'Express'
|
||||
this.cookie('foo', 'bar')
|
||||
return ''
|
||||
})
|
||||
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure, foo=bar'
|
||||
get('/user').headers['set-cookie'].should.eql 'SID=732423sdfs73243; path=/; secure, foo=bar; path=/'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
|
||||
var mime = require('express/mime')
|
||||
|
||||
CSSColors = Plugin.extend({
|
||||
extend: {
|
||||
init: function() {
|
||||
@@ -7,7 +9,7 @@ CSSColors = Plugin.extend({
|
||||
},
|
||||
on: {
|
||||
response: function(event) {
|
||||
if (event.response.headers['content-type'] == mime('css'))
|
||||
if (event.response.headers['content-type'] == mime.type('css'))
|
||||
event.response.body = event.response.body.replace('black', '#000')
|
||||
}
|
||||
}
|
||||
|
||||
108
spec/spec.plugins.session.js
Normal file
108
spec/spec.plugins.session.js
Normal file
@@ -0,0 +1,108 @@
|
||||
|
||||
describe 'Express'
|
||||
before_each
|
||||
reset()
|
||||
use(require('express/plugins/cookie').Cookie)
|
||||
use(Session = require('express/plugins/session').Session)
|
||||
Session.store.clear()
|
||||
end
|
||||
|
||||
describe 'Session'
|
||||
describe 'when sid cookie is not present'
|
||||
it 'should set sid cookie'
|
||||
get('/login', function(){ return '' })
|
||||
get('/login').headers['set-cookie'].should.match(/^sid=(\w+);/)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when sid cookie is present'
|
||||
it 'should not set sid'
|
||||
get('/login', function(){ return '' })
|
||||
get('/login', { headers: { cookie: 'sid=123' }}).headers.should.not.have_property 'set-cookie'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'session Store.Memory'
|
||||
before_each
|
||||
memory = new (require('express/plugins/session').Store.Memory)
|
||||
end
|
||||
|
||||
it 'should persist'
|
||||
post('/login', function(){
|
||||
return this.session.name = 'tj'
|
||||
})
|
||||
get('/login', function(){
|
||||
return this.session.name
|
||||
})
|
||||
var headers = { headers: { cookie: 'sid=123' }}
|
||||
post('/login', headers)
|
||||
get('/login', headers).status.should.eql 200
|
||||
get('/login', headers).body.should.eql 'tj'
|
||||
end
|
||||
|
||||
describe '#toString()'
|
||||
it 'should return [Memory Store]'
|
||||
memory.toString().should.eql '[Memory Store]'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#fetch()'
|
||||
describe 'when the session does not exist'
|
||||
it 'should return a new Session'
|
||||
memory.fetch('1').should.have_property 'lastAccess'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when the session does exist'
|
||||
it 'should return the previous session'
|
||||
memory.commit({ id: '1', same: true })
|
||||
memory.fetch('1').should.have_property 'same', true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear()'
|
||||
it 'should remove all sessions'
|
||||
memory.commit({ id: '1' })
|
||||
memory.commit({ id: '2' })
|
||||
memory.clear()
|
||||
memory.should.not.have_property '1'
|
||||
memory.should.not.have_property '2'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#length()'
|
||||
it 'should return the number of session'
|
||||
memory.commit({ id: '1' })
|
||||
memory.commit({ id: '2' })
|
||||
memory.length().should.eql 2
|
||||
end
|
||||
end
|
||||
|
||||
describe '#destroy()'
|
||||
it 'should destroy a single session'
|
||||
memory.commit({ id: '1' })
|
||||
memory.commit({ id: '2' })
|
||||
memory.destroy('1')
|
||||
memory.store.should.not.have_property '1'
|
||||
memory.store.should.have_property '2'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reap()'
|
||||
it 'should destroy sessions older than the given age in milliseconds'
|
||||
memory.commit({ id: '1', lastAccess: Number(new Date) - 300 })
|
||||
memory.commit({ id: '2', lastAccess: Number(new Date) - 250 })
|
||||
memory.commit({ id: '3', lastAccess: Number(new Date) - 100 })
|
||||
memory.commit({ id: '4', lastAccess: Number(new Date) })
|
||||
memory.reap(200)
|
||||
memory.store.should.not.have_property '1'
|
||||
memory.store.should.not.have_property '2'
|
||||
memory.store.should.have_property '3'
|
||||
memory.store.should.have_property '4'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -174,7 +174,7 @@ describe 'Express'
|
||||
|
||||
it 'should work with a query string'
|
||||
get('/user', function(){
|
||||
return this.param('page') || 'First page'
|
||||
return String(this.param('page') || 'First page')
|
||||
})
|
||||
get('/user').body.should.eql 'First page'
|
||||
get('/user?page=2').body.should.eql '2'
|
||||
|
||||
138
spec/spec.utils.js
Normal file
138
spec/spec.utils.js
Normal file
@@ -0,0 +1,138 @@
|
||||
|
||||
describe 'Express'
|
||||
before
|
||||
utils = require('express/utils')
|
||||
end
|
||||
|
||||
describe 'toArray()'
|
||||
describe 'when given an array'
|
||||
it 'should return the array'
|
||||
utils.toArray([1,2,3]).should.eql [1,2,3]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when given an object with indexed values and length'
|
||||
it 'should return an array'
|
||||
var args = -{ return arguments }('foo', 'bar')
|
||||
utils.toArray(args).should.eql ['foo', 'bar']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'escape()'
|
||||
it 'should escape html'
|
||||
utils.escape('<p>this & that').should.eql '<p>this & that'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'uid()'
|
||||
it 'should return a string of random characters'
|
||||
utils.uid().should.not.eql utils.uid()
|
||||
utils.uid().length.should.be_greater_than 20
|
||||
end
|
||||
end
|
||||
|
||||
describe 'extname()'
|
||||
it 'should return the a files extension'
|
||||
utils.extname('image.png').should.eql 'png'
|
||||
utils.extname('image.large.png').should.eql 'png'
|
||||
utils.extname('/path/to/image.large.png').should.eql 'png'
|
||||
end
|
||||
|
||||
it 'should return null when not found'
|
||||
utils.extname('path').should.be_null
|
||||
utils.extname('/just/a/path').should.be_null
|
||||
end
|
||||
end
|
||||
|
||||
describe 'basename()'
|
||||
it 'should return a files basename'
|
||||
utils.basename('foo/bar/baz.image.png').should.eql 'baz.image.png'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'mergeParam()'
|
||||
describe 'with empty params'
|
||||
it 'should merge the given key and value'
|
||||
params = {}
|
||||
utils.mergeParam('user[names][first]', 'tj', params)
|
||||
params.user.names.first.should.eql 'tj'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with populated params'
|
||||
it 'should merge not overwrite'
|
||||
params = { user: { name: 'tj' }}
|
||||
utils.mergeParam('user[email]', 'tj@vision-media.ca', params)
|
||||
params.user.name.should.eql 'tj'
|
||||
params.user.email.should.eql 'tj@vision-media.ca'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with an object as value'
|
||||
it 'should preserve it'
|
||||
params = {}
|
||||
utils.mergeParam('images[]', { name: 1 }, params)
|
||||
utils.mergeParam('images[]', { name: 2 }, params)
|
||||
params.images.should.eql [{ name: 1}, { name: 2 }]
|
||||
end
|
||||
end
|
||||
|
||||
describe 'key[number]'
|
||||
it 'should merge correctly'
|
||||
params = { images: { one: 'foo.png' }}
|
||||
utils.mergeParam('images[0]', 'bar.png', params)
|
||||
params.images.one.should.eql 'foo.png'
|
||||
params.images[0].should.eql 'bar.png'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'key[]'
|
||||
describe 'with a single value'
|
||||
it 'should still be an array'
|
||||
params = {}
|
||||
utils.mergeParam('images[]', '1', params)
|
||||
params.images.should.eql ['1']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with empty params'
|
||||
it 'should merge correctly'
|
||||
params = {}
|
||||
utils.mergeParam('images[]', '1', params)
|
||||
utils.mergeParam('images[]', '2', params)
|
||||
params.images.should.eql ['1', '2']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with populated params'
|
||||
it 'should convert to an array'
|
||||
params = { images: 'foo.png'}
|
||||
utils.mergeParam('images[]', '1', params)
|
||||
params.images.should.eql ['foo.png', '1']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'with several merges'
|
||||
it 'should push values'
|
||||
params = {}
|
||||
utils.mergeParam('images[]', '1', params)
|
||||
utils.mergeParam('images[]', '2', params)
|
||||
utils.mergeParam('images[]', '3', params)
|
||||
params.images.should.eql ['1', '2', '3']
|
||||
end
|
||||
end
|
||||
|
||||
describe 'when nested'
|
||||
it 'should marge correctly'
|
||||
params = {}
|
||||
utils.mergeParam('user[tj][images][]', '1', params)
|
||||
utils.mergeParam('user[tj][images][]', '2', params)
|
||||
utils.mergeParam('user[tj][images][]', '3', params)
|
||||
params.user.should.eql { tj: { images: ['1', '2', '3'] }}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
Submodule spec/support/libxmljs updated: 584f3aa303...0be1b6f48f
Reference in New Issue
Block a user