nitro 0.23.0 → 0.24.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/CHANGELOG +350 -0
- data/INSTALL +2 -2
- data/ProjectInfo +61 -0
- data/README +5 -4
- data/Rakefile +5 -4
- data/bin/nitrogen +3 -1
- data/doc/AUTHORS +27 -3
- data/doc/RELEASES +193 -0
- data/doc/lhttpd.txt +4 -0
- data/lib/nitro.rb +1 -1
- data/lib/nitro/adapter/cgi.rb +6 -321
- data/lib/nitro/adapter/fastcgi.rb +2 -14
- data/lib/nitro/adapter/scgi.rb +237 -71
- data/lib/nitro/adapter/webrick.rb +25 -7
- data/lib/nitro/caching.rb +1 -0
- data/lib/nitro/cgi.rb +296 -0
- data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
- data/lib/nitro/cgi/http.rb +62 -0
- data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
- data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
- data/lib/nitro/cgi/stream.rb +43 -0
- data/lib/nitro/cgi/utils.rb +38 -0
- data/lib/nitro/compiler.rb +23 -11
- data/lib/nitro/compiler/css.rb +8 -0
- data/lib/nitro/compiler/morphing.rb +66 -0
- data/lib/nitro/context.rb +21 -30
- data/lib/nitro/controller.rb +23 -100
- data/lib/nitro/dispatcher.rb +18 -8
- data/lib/nitro/element.rb +6 -2
- data/lib/nitro/flash.rb +2 -2
- data/lib/nitro/mixin/buffer.rb +2 -2
- data/lib/nitro/mixin/form.rb +204 -93
- data/lib/nitro/mixin/javascript.rb +170 -11
- data/lib/nitro/mixin/markup.rb +1 -0
- data/lib/nitro/mixin/pager.rb +7 -4
- data/lib/nitro/mixin/rss.rb +2 -0
- data/lib/nitro/mixin/table.rb +23 -6
- data/lib/nitro/mixin/xhtml.rb +2 -2
- data/lib/nitro/render.rb +19 -5
- data/lib/nitro/scaffold.rb +12 -6
- data/lib/nitro/server.rb +4 -6
- data/lib/nitro/server/runner.rb +2 -2
- data/lib/nitro/session.rb +8 -1
- data/lib/nitro/session/file.rb +40 -0
- data/lib/part/admin.rb +2 -0
- data/lib/part/admin/controller.rb +7 -3
- data/lib/part/admin/skin.rb +8 -1
- data/lib/part/admin/template/index.xhtml +39 -1
- data/proto/public/error.xhtml +5 -3
- data/proto/public/js/behaviour.js +254 -254
- data/proto/public/js/controls.js +427 -165
- data/proto/public/js/dragdrop.js +255 -276
- data/proto/public/js/effects.js +476 -277
- data/proto/public/js/prototype.js +561 -127
- data/proto/public/js/scaffold.js +74 -0
- data/proto/public/js/scriptaculous.js +44 -0
- data/proto/public/js/util.js +548 -0
- data/proto/public/scaffold/list.xhtml +4 -1
- data/proto/scgi.rb +333 -0
- data/script/scgi_ctl +221 -0
- data/script/scgi_service +120 -0
- data/test/nitro/adapter/raw_post1.bin +0 -0
- data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
- data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
- data/test/nitro/mixin/tc_xhtml.rb +1 -1
- data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
- data/test/nitro/tc_controller.rb +9 -5
- metadata +159 -169
- data/benchmark/bench.rb +0 -5
- data/benchmark/simple-webrick-n-200.txt +0 -44
- data/benchmark/static-webrick-n-200.txt +0 -43
- data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
- data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
- data/benchmark/tiny-webrick-n-200.txt +0 -44
- data/benchmark/tiny2-webrick-n-200.txt +0 -44
- data/examples/README +0 -7
data/lib/nitro/scaffold.rb
CHANGED
@@ -6,7 +6,6 @@ require 'nitro/mixin/form'
|
|
6
6
|
module Nitro
|
7
7
|
|
8
8
|
# The scaffolder adds default actions to a Controller.
|
9
|
-
#
|
10
9
|
#--
|
11
10
|
# FIXME: handle controller base in generated routes.
|
12
11
|
# FIXME: better handle templates (check if action exists).
|
@@ -108,7 +107,13 @@ module Scaffolding
|
|
108
107
|
template.gsub!(/%list_name%/, "#{list_name}")
|
109
108
|
end
|
110
109
|
=end
|
111
|
-
unless compiler.template_for_action(list_name, self.template_root)
|
110
|
+
unless compiler.template_for_action(list_name, self.template_root) or
|
111
|
+
#--
|
112
|
+
# FIXME: this is a hack fix. to better fix, implemente deferred scaffolding.
|
113
|
+
#++
|
114
|
+
compiler.template_for_action(list_name, 'public') or
|
115
|
+
compiler.template_for_action(list_name, 'src/template') or
|
116
|
+
compiler.template_for_action(list_name, 'src/view')
|
112
117
|
source = File.read(File.join(Nitro.proto_path, 'public', 'scaffold', 'list.xhtml'))
|
113
118
|
template = Compiler.new.transform_template(source)
|
114
119
|
|
@@ -129,9 +134,10 @@ module Scaffolding
|
|
129
134
|
|
130
135
|
def save#{suffix}
|
131
136
|
if oid = request['#{oid}']
|
132
|
-
|
137
|
+
oid = oid.to_s # in case oid is a StringIO (multipart).
|
138
|
+
obj = request.fill(#{klass}[oid], :assign_relations => true, :force_boolean => true)
|
133
139
|
else
|
134
|
-
obj = request.fill(#{klass}.new)
|
140
|
+
obj = request.fill(#{klass}.new, :assign_relations => true)
|
135
141
|
end
|
136
142
|
unless obj.valid?
|
137
143
|
session[:ERRORS] = obj.errors
|
@@ -153,8 +159,6 @@ module Scaffolding
|
|
153
159
|
}
|
154
160
|
end
|
155
161
|
|
156
|
-
# puts '--', klass, '..', code
|
157
|
-
|
158
162
|
class_eval(code)
|
159
163
|
end
|
160
164
|
|
@@ -163,3 +167,5 @@ module Scaffolding
|
|
163
167
|
end
|
164
168
|
|
165
169
|
end
|
170
|
+
|
171
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/server.rb
CHANGED
@@ -6,11 +6,8 @@ module Nitro
|
|
6
6
|
class Server
|
7
7
|
|
8
8
|
# The server listening address.
|
9
|
-
#--
|
10
|
-
# 0.0.0.0 may be a better default?
|
11
|
-
#++
|
12
9
|
|
13
|
-
setting :address, :default => '
|
10
|
+
setting :address, :default => '0.0.0.0', :doc => 'The server listening address'
|
14
11
|
|
15
12
|
# The server listening port.
|
16
13
|
|
@@ -18,7 +15,7 @@ class Server
|
|
18
15
|
|
19
16
|
# The map.
|
20
17
|
|
21
|
-
setting :map, :default => { '/' =>
|
18
|
+
setting :map, :default => { '/' => Controller }, :doc => 'The server map'
|
22
19
|
|
23
20
|
# The public files root directory.
|
24
21
|
|
@@ -89,7 +86,8 @@ class Server
|
|
89
86
|
|
90
87
|
def start(options = {})
|
91
88
|
@map['/'] = options[:controller] if options[:controller]
|
92
|
-
@dispatcher = options[:
|
89
|
+
@dispatcher = options[:dispatcher] || Dispatcher.new(@map)
|
90
|
+
return self
|
93
91
|
end
|
94
92
|
|
95
93
|
def root=(controller)
|
data/lib/nitro/server/runner.rb
CHANGED
@@ -239,7 +239,7 @@ class Runner
|
|
239
239
|
|
240
240
|
elsif 'cgi_proc' == ENV['NITRO_INVOKE']
|
241
241
|
require 'nitro/adapter/cgi'
|
242
|
-
|
242
|
+
CgiAdapter.start(server)
|
243
243
|
|
244
244
|
elsif 'irb' == ENV['NITRO_INVOKE']
|
245
245
|
$server = server
|
@@ -283,7 +283,7 @@ class Runner
|
|
283
283
|
|
284
284
|
when :lhttpd
|
285
285
|
require 'nitro/adapter/fastcgi'
|
286
|
-
`lighttpd -f conf/
|
286
|
+
`lighttpd -f conf/lhttpd_fcgi.conf`
|
287
287
|
|
288
288
|
when :lhttpd_scgi
|
289
289
|
require 'nitro/adapter/scgi'
|
data/lib/nitro/session.rb
CHANGED
@@ -7,7 +7,7 @@ require 'mega/time_in_english'
|
|
7
7
|
require 'glue/attribute'
|
8
8
|
require 'glue/configuration'
|
9
9
|
|
10
|
-
require 'nitro/cookie'
|
10
|
+
require 'nitro/cgi/cookie'
|
11
11
|
|
12
12
|
module Nitro
|
13
13
|
|
@@ -92,6 +92,13 @@ class Session < Hash
|
|
92
92
|
Session.store.delete_if { |key, s| s.invalid? }
|
93
93
|
end
|
94
94
|
alias_method :gc!, :garbage_collection!
|
95
|
+
|
96
|
+
# Helper method that returns all sessions.
|
97
|
+
|
98
|
+
def all
|
99
|
+
Session.store.values
|
100
|
+
end
|
101
|
+
|
95
102
|
end
|
96
103
|
|
97
104
|
# The unique id of this session.
|
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'nitro/session'
|
4
|
+
|
5
|
+
module Nitro
|
6
|
+
class FileSessionStore
|
7
|
+
# The session keepalive time. The session is eligable for
|
8
|
+
# garbage collection after this time passes.
|
9
|
+
|
10
|
+
setting :path, :default => "/tmp/nitro_session", :doc => 'The directory to store file session'
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@path = FileSessionStore.path
|
14
|
+
Dir.mkdir(@path) unless File.exists?(@path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(k,v)
|
18
|
+
File.open(File.join(@path, k), "w") { |f| f.write(Marshal.dump(v)) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](k)
|
22
|
+
fn = File.join(@path, k)
|
23
|
+
return nil unless File.exists?(fn)
|
24
|
+
Marshal.load( File.read(fn) )
|
25
|
+
end
|
26
|
+
|
27
|
+
# def values
|
28
|
+
# []
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def each
|
32
|
+
# #yield
|
33
|
+
# end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
Session.store = FileSessionStore.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/lib/part/admin.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'nitro/controller'
|
2
|
+
require 'nitro/mixin/form'
|
2
3
|
|
3
4
|
class AdminController < Nitro::Controller
|
4
|
-
|
5
|
-
@template_root = File.join(File.dirname(__FILE__), 'template')
|
5
|
+
include Nitro::FormMixin
|
6
6
|
|
7
|
+
def self.template_root
|
8
|
+
File.join(File.dirname(__FILE__), 'template')
|
9
|
+
end
|
10
|
+
|
7
11
|
def index
|
8
12
|
@classes = self.class.managed_classes
|
9
13
|
end
|
@@ -15,7 +19,7 @@ class AdminController < Nitro::Controller
|
|
15
19
|
|
16
20
|
# Called when this controller is mounted.
|
17
21
|
|
18
|
-
def mounted
|
22
|
+
def mounted(path)
|
19
23
|
@managed_classes = Og.manager.manageable_classes
|
20
24
|
@managed_classes.each { |c| scaffold(c) }
|
21
25
|
end
|
data/lib/part/admin/skin.rb
CHANGED
@@ -8,13 +8,20 @@ class SystemPage < Nitro::Element
|
|
8
8
|
<title>#@name</title>
|
9
9
|
<base href="\#{context.host_url}/" />
|
10
10
|
<link rel="stylesheet" href="/system.css" type="text/css" media="screen" />
|
11
|
+
<script src="/js/behaviour.js" type="text/javascript"> </script>
|
12
|
+
<script src="/js/prototype.js" type="text/javascript"> </script>
|
13
|
+
<script src="/js/effects.js" type="text/javascript"> </script>
|
14
|
+
<script src="/js/dragdrop.js" type="text/javascript"> </script>
|
15
|
+
<script src="/js/controls.js" type="text/javascript"> </script>
|
16
|
+
<script src="/js/scaffold.js" type="text/javascript"> </script>
|
11
17
|
</head>
|
12
18
|
<body>
|
13
|
-
<h1
|
19
|
+
<h1><a href="/">Home</a> / #@name</h1>
|
14
20
|
#{content}
|
15
21
|
<br />
|
16
22
|
Powered by <a href="http://www.nitrohq.com">Nitro</a> version #{Nitro::Version}
|
17
23
|
</body>
|
24
|
+
<script type="text/javascript" src="js/scaffold.js" />
|
18
25
|
</html>
|
19
26
|
~
|
20
27
|
end
|
@@ -1,9 +1,47 @@
|
|
1
1
|
<SystemPage name="System">
|
2
|
+
|
3
|
+
<style>
|
4
|
+
th {
|
5
|
+
border: 1px solid #ccc;
|
6
|
+
background: #eee;
|
7
|
+
text-align: left;
|
8
|
+
}
|
9
|
+
</style>
|
10
|
+
|
11
|
+
<h2>Og managed classes</h2>
|
12
|
+
|
2
13
|
<table>
|
14
|
+
<tr>
|
15
|
+
<th>Class</th>
|
16
|
+
<th>Count</th>
|
17
|
+
<th>Properties</th>
|
18
|
+
</tr>
|
3
19
|
<?r for c in @classes ?>
|
4
20
|
<tr>
|
5
|
-
<td><a href="#@base/#{c.name.plural.underscore}">#{c.name}</a
|
21
|
+
<td><a href="#@base/#{c.name.plural.underscore}">#{c.name}</a></td>
|
22
|
+
<td>#{c.count}</td>
|
23
|
+
<td width="100%">#{c.properties.values.join(', ')}</td>
|
24
|
+
</tr>
|
25
|
+
<?r end ?>
|
26
|
+
</table>
|
27
|
+
|
28
|
+
<h2>System configuration</h2>
|
29
|
+
|
30
|
+
<table width="100%">
|
31
|
+
<tr>
|
32
|
+
<th>Name</th>
|
33
|
+
<th>Value</th>
|
34
|
+
<th>Type</th>
|
35
|
+
<th>Description</th>
|
36
|
+
</tr>
|
37
|
+
<?r for s in Configuration.settings ?>
|
38
|
+
<tr>
|
39
|
+
<td>#{s.owner}.<strong>#{s.name}</strong></td>
|
40
|
+
<td>#{s.value.inspect}</td>
|
41
|
+
<td>#{s.type}</td>
|
42
|
+
<td>#{s.options[:doc]}</td>
|
6
43
|
</tr>
|
7
44
|
<?r end ?>
|
8
45
|
</table>
|
46
|
+
|
9
47
|
</SystemPage>
|
data/proto/public/error.xhtml
CHANGED
@@ -31,11 +31,13 @@
|
|
31
31
|
<body>
|
32
32
|
<h1>Error</h1>
|
33
33
|
|
34
|
-
<?r
|
35
|
-
|
34
|
+
<?r
|
35
|
+
if Run.mode == :debug
|
36
|
+
require 'cgi'
|
37
|
+
?>
|
36
38
|
<?r for error, path in @context.rendering_errors ?>
|
37
39
|
<div class="path"><strong>Path:</strong> #{path}</div>
|
38
|
-
<div class="error"><strong>#{error.to_s}</strong></div>
|
40
|
+
<div class="error"><strong>#{CGI.escapeHTML(error.to_s)}</strong></div>
|
39
41
|
<div class="load">Click here to <strong><a href="#{request.uri}">reload</a></strong>.</div>
|
40
42
|
<div class="load">Click here to go to the <strong><a href="#{request.referer}">referer</a></strong> or the <strong><a href="/">home page</a></strong>.</div>
|
41
43
|
<?r if error.respond_to?(:source_extract) ?>
|
@@ -1,254 +1,254 @@
|
|
1
|
-
/*
|
2
|
-
Behaviour v1.0 by Ben Nolan, June 2005. Based largely on the work
|
3
|
-
of Simon Willison (see comments by Simon below).
|
4
|
-
|
5
|
-
Description:
|
6
|
-
|
7
|
-
Uses css selectors to apply javascript behaviours to enable
|
8
|
-
unobtrusive javascript in html documents.
|
9
|
-
|
10
|
-
Usage:
|
11
|
-
|
12
|
-
var myrules = {
|
13
|
-
'b.someclass' : function(element){
|
14
|
-
element.onclick = function(){
|
15
|
-
alert(this.innerHTML);
|
16
|
-
}
|
17
|
-
},
|
18
|
-
'#someid u' : function(element){
|
19
|
-
element.onmouseover = function(){
|
20
|
-
this.innerHTML = "BLAH!";
|
21
|
-
}
|
22
|
-
}
|
23
|
-
);
|
24
|
-
|
25
|
-
Behaviour.register(myrules);
|
26
|
-
|
27
|
-
// Call Behaviour.apply() to re-apply the rules (if you
|
28
|
-
// update the dom, etc).
|
29
|
-
|
30
|
-
License:
|
31
|
-
|
32
|
-
My stuff is BSD licensed. Not sure about Simon's.
|
33
|
-
|
34
|
-
More information:
|
35
|
-
|
36
|
-
http://ripcord.co.nz/behaviour/
|
37
|
-
|
38
|
-
*/
|
39
|
-
|
40
|
-
var Behaviour = {
|
41
|
-
list : new Array,
|
42
|
-
|
43
|
-
register : function(sheet){
|
44
|
-
Behaviour.list.push(sheet);
|
45
|
-
},
|
46
|
-
|
47
|
-
start : function(){
|
48
|
-
Behaviour.addLoadEvent(function(){
|
49
|
-
Behaviour.apply();
|
50
|
-
});
|
51
|
-
},
|
52
|
-
|
53
|
-
apply : function(){
|
54
|
-
for (h=0;sheet=Behaviour.list[h];h++){
|
55
|
-
for (selector in sheet){
|
56
|
-
list = document.getElementsBySelector(selector);
|
57
|
-
|
58
|
-
if (!list){
|
59
|
-
continue;
|
60
|
-
}
|
61
|
-
|
62
|
-
for (i=0;element=list[i];i++){
|
63
|
-
sheet[selector](element);
|
64
|
-
}
|
65
|
-
}
|
66
|
-
}
|
67
|
-
},
|
68
|
-
|
69
|
-
addLoadEvent : function(func){
|
70
|
-
var oldonload = window.onload;
|
71
|
-
|
72
|
-
if (typeof window.onload != 'function') {
|
73
|
-
window.onload = func;
|
74
|
-
} else {
|
75
|
-
window.onload = function() {
|
76
|
-
oldonload();
|
77
|
-
func();
|
78
|
-
}
|
79
|
-
}
|
80
|
-
}
|
81
|
-
}
|
82
|
-
|
83
|
-
Behaviour.start();
|
84
|
-
|
85
|
-
/*
|
86
|
-
The following code is Copyright (C) Simon Willison 2004.
|
87
|
-
|
88
|
-
document.getElementsBySelector(selector)
|
89
|
-
- returns an array of element objects from the current document
|
90
|
-
matching the CSS selector. Selectors can contain element names,
|
91
|
-
class names and ids and can be nested. For example:
|
92
|
-
|
93
|
-
elements = document.getElementsBySelect('div#main p a.external')
|
94
|
-
|
95
|
-
Will return an array of all 'a' elements with 'external' in their
|
96
|
-
class attribute that are contained inside 'p' elements that are
|
97
|
-
contained inside the 'div' element which has id="main"
|
98
|
-
|
99
|
-
New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
|
100
|
-
See http://www.w3.org/TR/css3-selectors/#attribute-selectors
|
101
|
-
|
102
|
-
Version 0.4 - Simon Willison, March 25th 2003
|
103
|
-
-- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
|
104
|
-
-- Opera 7 fails
|
105
|
-
*/
|
106
|
-
|
107
|
-
function getAllChildren(e) {
|
108
|
-
// Returns all children of element. Workaround required for IE5/Windows. Ugh.
|
109
|
-
return e.all ? e.all : e.getElementsByTagName('*');
|
110
|
-
}
|
111
|
-
|
112
|
-
document.getElementsBySelector = function(selector) {
|
113
|
-
// Attempt to fail gracefully in lesser browsers
|
114
|
-
if (!document.getElementsByTagName) {
|
115
|
-
return new Array();
|
116
|
-
}
|
117
|
-
// Split selector in to tokens
|
118
|
-
var tokens = selector.split(' ');
|
119
|
-
var currentContext = new Array(document);
|
120
|
-
for (var i = 0; i < tokens.length; i++) {
|
121
|
-
token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
|
122
|
-
if (token.indexOf('#') > -1) {
|
123
|
-
// Token is an ID selector
|
124
|
-
var bits = token.split('#');
|
125
|
-
var tagName = bits[0];
|
126
|
-
var id = bits[1];
|
127
|
-
var element = document.getElementById(id);
|
128
|
-
if (tagName && element.nodeName.toLowerCase() != tagName) {
|
129
|
-
// tag with that ID not found, return false
|
130
|
-
return new Array();
|
131
|
-
}
|
132
|
-
// Set currentContext to contain just this element
|
133
|
-
currentContext = new Array(element);
|
134
|
-
continue; // Skip to next token
|
135
|
-
}
|
136
|
-
if (token.indexOf('.') > -1) {
|
137
|
-
// Token contains a class selector
|
138
|
-
var bits = token.split('.');
|
139
|
-
var tagName = bits[0];
|
140
|
-
var className = bits[1];
|
141
|
-
if (!tagName) {
|
142
|
-
tagName = '*';
|
143
|
-
}
|
144
|
-
// Get elements matching tag, filter them for class selector
|
145
|
-
var found = new Array;
|
146
|
-
var foundCount = 0;
|
147
|
-
for (var h = 0; h < currentContext.length; h++) {
|
148
|
-
var elements;
|
149
|
-
if (tagName == '*') {
|
150
|
-
elements = getAllChildren(currentContext[h]);
|
151
|
-
} else {
|
152
|
-
elements = currentContext[h].getElementsByTagName(tagName);
|
153
|
-
}
|
154
|
-
for (var j = 0; j < elements.length; j++) {
|
155
|
-
found[foundCount++] = elements[j];
|
156
|
-
}
|
157
|
-
}
|
158
|
-
currentContext = new Array;
|
159
|
-
var currentContextIndex = 0;
|
160
|
-
for (var k = 0; k < found.length; k++) {
|
161
|
-
if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
|
162
|
-
currentContext[currentContextIndex++] = found[k];
|
163
|
-
}
|
164
|
-
}
|
165
|
-
continue; // Skip to next token
|
166
|
-
}
|
167
|
-
// Code to deal with attribute selectors
|
168
|
-
if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
|
169
|
-
var tagName = RegExp.$1;
|
170
|
-
var attrName = RegExp.$2;
|
171
|
-
var attrOperator = RegExp.$3;
|
172
|
-
var attrValue = RegExp.$4;
|
173
|
-
if (!tagName) {
|
174
|
-
tagName = '*';
|
175
|
-
}
|
176
|
-
// Grab all of the tagName elements within current context
|
177
|
-
var found = new Array;
|
178
|
-
var foundCount = 0;
|
179
|
-
for (var h = 0; h < currentContext.length; h++) {
|
180
|
-
var elements;
|
181
|
-
if (tagName == '*') {
|
182
|
-
elements = getAllChildren(currentContext[h]);
|
183
|
-
} else {
|
184
|
-
elements = currentContext[h].getElementsByTagName(tagName);
|
185
|
-
}
|
186
|
-
for (var j = 0; j < elements.length; j++) {
|
187
|
-
found[foundCount++] = elements[j];
|
188
|
-
}
|
189
|
-
}
|
190
|
-
currentContext = new Array;
|
191
|
-
var currentContextIndex = 0;
|
192
|
-
var checkFunction; // This function will be used to filter the elements
|
193
|
-
switch (attrOperator) {
|
194
|
-
case '=': // Equality
|
195
|
-
checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
|
196
|
-
break;
|
197
|
-
case '~': // Match one of space seperated words
|
198
|
-
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
|
199
|
-
break;
|
200
|
-
case '|': // Match start with value followed by optional hyphen
|
201
|
-
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
|
202
|
-
break;
|
203
|
-
case '^': // Match starts with value
|
204
|
-
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
|
205
|
-
break;
|
206
|
-
case '$': // Match ends with value - fails with "Warning" in Opera 7
|
207
|
-
checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
|
208
|
-
break;
|
209
|
-
case '*': // Match ends with value
|
210
|
-
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
|
211
|
-
break;
|
212
|
-
default :
|
213
|
-
// Just test for existence of attribute
|
214
|
-
checkFunction = function(e) { return e.getAttribute(attrName); };
|
215
|
-
}
|
216
|
-
currentContext = new Array;
|
217
|
-
var currentContextIndex = 0;
|
218
|
-
for (var k = 0; k < found.length; k++) {
|
219
|
-
if (checkFunction(found[k])) {
|
220
|
-
currentContext[currentContextIndex++] = found[k];
|
221
|
-
}
|
222
|
-
}
|
223
|
-
// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
|
224
|
-
continue; // Skip to next token
|
225
|
-
}
|
226
|
-
|
227
|
-
if (!currentContext[0]){
|
228
|
-
return;
|
229
|
-
}
|
230
|
-
|
231
|
-
// If we get here, token is JUST an element (not a class or ID selector)
|
232
|
-
tagName = token;
|
233
|
-
var found = new Array;
|
234
|
-
var foundCount = 0;
|
235
|
-
for (var h = 0; h < currentContext.length; h++) {
|
236
|
-
var elements = currentContext[h].getElementsByTagName(tagName);
|
237
|
-
for (var j = 0; j < elements.length; j++) {
|
238
|
-
found[foundCount++] = elements[j];
|
239
|
-
}
|
240
|
-
}
|
241
|
-
currentContext = found;
|
242
|
-
}
|
243
|
-
return currentContext;
|
244
|
-
}
|
245
|
-
|
246
|
-
/* That revolting regular expression explained
|
247
|
-
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
|
248
|
-
\---/ \---/\-------------/ \-------/
|
249
|
-
| | | |
|
250
|
-
| | | The value
|
251
|
-
| | ~,|,^,$,* or =
|
252
|
-
| Attribute
|
253
|
-
Tag
|
254
|
-
*/
|
1
|
+
/*
|
2
|
+
Behaviour v1.0 by Ben Nolan, June 2005. Based largely on the work
|
3
|
+
of Simon Willison (see comments by Simon below).
|
4
|
+
|
5
|
+
Description:
|
6
|
+
|
7
|
+
Uses css selectors to apply javascript behaviours to enable
|
8
|
+
unobtrusive javascript in html documents.
|
9
|
+
|
10
|
+
Usage:
|
11
|
+
|
12
|
+
var myrules = {
|
13
|
+
'b.someclass' : function(element){
|
14
|
+
element.onclick = function(){
|
15
|
+
alert(this.innerHTML);
|
16
|
+
}
|
17
|
+
},
|
18
|
+
'#someid u' : function(element){
|
19
|
+
element.onmouseover = function(){
|
20
|
+
this.innerHTML = "BLAH!";
|
21
|
+
}
|
22
|
+
}
|
23
|
+
);
|
24
|
+
|
25
|
+
Behaviour.register(myrules);
|
26
|
+
|
27
|
+
// Call Behaviour.apply() to re-apply the rules (if you
|
28
|
+
// update the dom, etc).
|
29
|
+
|
30
|
+
License:
|
31
|
+
|
32
|
+
My stuff is BSD licensed. Not sure about Simon's.
|
33
|
+
|
34
|
+
More information:
|
35
|
+
|
36
|
+
http://ripcord.co.nz/behaviour/
|
37
|
+
|
38
|
+
*/
|
39
|
+
|
40
|
+
var Behaviour = {
|
41
|
+
list : new Array,
|
42
|
+
|
43
|
+
register : function(sheet){
|
44
|
+
Behaviour.list.push(sheet);
|
45
|
+
},
|
46
|
+
|
47
|
+
start : function(){
|
48
|
+
Behaviour.addLoadEvent(function(){
|
49
|
+
Behaviour.apply();
|
50
|
+
});
|
51
|
+
},
|
52
|
+
|
53
|
+
apply : function(){
|
54
|
+
for (h=0;sheet=Behaviour.list[h];h++){
|
55
|
+
for (selector in sheet){
|
56
|
+
list = document.getElementsBySelector(selector);
|
57
|
+
|
58
|
+
if (!list){
|
59
|
+
continue;
|
60
|
+
}
|
61
|
+
|
62
|
+
for (i=0;element=list[i];i++){
|
63
|
+
sheet[selector](element);
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
},
|
68
|
+
|
69
|
+
addLoadEvent : function(func){
|
70
|
+
var oldonload = window.onload;
|
71
|
+
|
72
|
+
if (typeof window.onload != 'function') {
|
73
|
+
window.onload = func;
|
74
|
+
} else {
|
75
|
+
window.onload = function() {
|
76
|
+
oldonload();
|
77
|
+
func();
|
78
|
+
}
|
79
|
+
}
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
Behaviour.start();
|
84
|
+
|
85
|
+
/*
|
86
|
+
The following code is Copyright (C) Simon Willison 2004.
|
87
|
+
|
88
|
+
document.getElementsBySelector(selector)
|
89
|
+
- returns an array of element objects from the current document
|
90
|
+
matching the CSS selector. Selectors can contain element names,
|
91
|
+
class names and ids and can be nested. For example:
|
92
|
+
|
93
|
+
elements = document.getElementsBySelect('div#main p a.external')
|
94
|
+
|
95
|
+
Will return an array of all 'a' elements with 'external' in their
|
96
|
+
class attribute that are contained inside 'p' elements that are
|
97
|
+
contained inside the 'div' element which has id="main"
|
98
|
+
|
99
|
+
New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
|
100
|
+
See http://www.w3.org/TR/css3-selectors/#attribute-selectors
|
101
|
+
|
102
|
+
Version 0.4 - Simon Willison, March 25th 2003
|
103
|
+
-- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
|
104
|
+
-- Opera 7 fails
|
105
|
+
*/
|
106
|
+
|
107
|
+
function getAllChildren(e) {
|
108
|
+
// Returns all children of element. Workaround required for IE5/Windows. Ugh.
|
109
|
+
return e.all ? e.all : e.getElementsByTagName('*');
|
110
|
+
}
|
111
|
+
|
112
|
+
document.getElementsBySelector = function(selector) {
|
113
|
+
// Attempt to fail gracefully in lesser browsers
|
114
|
+
if (!document.getElementsByTagName) {
|
115
|
+
return new Array();
|
116
|
+
}
|
117
|
+
// Split selector in to tokens
|
118
|
+
var tokens = selector.split(' ');
|
119
|
+
var currentContext = new Array(document);
|
120
|
+
for (var i = 0; i < tokens.length; i++) {
|
121
|
+
token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');;
|
122
|
+
if (token.indexOf('#') > -1) {
|
123
|
+
// Token is an ID selector
|
124
|
+
var bits = token.split('#');
|
125
|
+
var tagName = bits[0];
|
126
|
+
var id = bits[1];
|
127
|
+
var element = document.getElementById(id);
|
128
|
+
if (tagName && element.nodeName.toLowerCase() != tagName) {
|
129
|
+
// tag with that ID not found, return false
|
130
|
+
return new Array();
|
131
|
+
}
|
132
|
+
// Set currentContext to contain just this element
|
133
|
+
currentContext = new Array(element);
|
134
|
+
continue; // Skip to next token
|
135
|
+
}
|
136
|
+
if (token.indexOf('.') > -1) {
|
137
|
+
// Token contains a class selector
|
138
|
+
var bits = token.split('.');
|
139
|
+
var tagName = bits[0];
|
140
|
+
var className = bits[1];
|
141
|
+
if (!tagName) {
|
142
|
+
tagName = '*';
|
143
|
+
}
|
144
|
+
// Get elements matching tag, filter them for class selector
|
145
|
+
var found = new Array;
|
146
|
+
var foundCount = 0;
|
147
|
+
for (var h = 0; h < currentContext.length; h++) {
|
148
|
+
var elements;
|
149
|
+
if (tagName == '*') {
|
150
|
+
elements = getAllChildren(currentContext[h]);
|
151
|
+
} else {
|
152
|
+
elements = currentContext[h].getElementsByTagName(tagName);
|
153
|
+
}
|
154
|
+
for (var j = 0; j < elements.length; j++) {
|
155
|
+
found[foundCount++] = elements[j];
|
156
|
+
}
|
157
|
+
}
|
158
|
+
currentContext = new Array;
|
159
|
+
var currentContextIndex = 0;
|
160
|
+
for (var k = 0; k < found.length; k++) {
|
161
|
+
if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
|
162
|
+
currentContext[currentContextIndex++] = found[k];
|
163
|
+
}
|
164
|
+
}
|
165
|
+
continue; // Skip to next token
|
166
|
+
}
|
167
|
+
// Code to deal with attribute selectors
|
168
|
+
if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
|
169
|
+
var tagName = RegExp.$1;
|
170
|
+
var attrName = RegExp.$2;
|
171
|
+
var attrOperator = RegExp.$3;
|
172
|
+
var attrValue = RegExp.$4;
|
173
|
+
if (!tagName) {
|
174
|
+
tagName = '*';
|
175
|
+
}
|
176
|
+
// Grab all of the tagName elements within current context
|
177
|
+
var found = new Array;
|
178
|
+
var foundCount = 0;
|
179
|
+
for (var h = 0; h < currentContext.length; h++) {
|
180
|
+
var elements;
|
181
|
+
if (tagName == '*') {
|
182
|
+
elements = getAllChildren(currentContext[h]);
|
183
|
+
} else {
|
184
|
+
elements = currentContext[h].getElementsByTagName(tagName);
|
185
|
+
}
|
186
|
+
for (var j = 0; j < elements.length; j++) {
|
187
|
+
found[foundCount++] = elements[j];
|
188
|
+
}
|
189
|
+
}
|
190
|
+
currentContext = new Array;
|
191
|
+
var currentContextIndex = 0;
|
192
|
+
var checkFunction; // This function will be used to filter the elements
|
193
|
+
switch (attrOperator) {
|
194
|
+
case '=': // Equality
|
195
|
+
checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
|
196
|
+
break;
|
197
|
+
case '~': // Match one of space seperated words
|
198
|
+
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
|
199
|
+
break;
|
200
|
+
case '|': // Match start with value followed by optional hyphen
|
201
|
+
checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
|
202
|
+
break;
|
203
|
+
case '^': // Match starts with value
|
204
|
+
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
|
205
|
+
break;
|
206
|
+
case '$': // Match ends with value - fails with "Warning" in Opera 7
|
207
|
+
checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
|
208
|
+
break;
|
209
|
+
case '*': // Match ends with value
|
210
|
+
checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
|
211
|
+
break;
|
212
|
+
default :
|
213
|
+
// Just test for existence of attribute
|
214
|
+
checkFunction = function(e) { return e.getAttribute(attrName); };
|
215
|
+
}
|
216
|
+
currentContext = new Array;
|
217
|
+
var currentContextIndex = 0;
|
218
|
+
for (var k = 0; k < found.length; k++) {
|
219
|
+
if (checkFunction(found[k])) {
|
220
|
+
currentContext[currentContextIndex++] = found[k];
|
221
|
+
}
|
222
|
+
}
|
223
|
+
// alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
|
224
|
+
continue; // Skip to next token
|
225
|
+
}
|
226
|
+
|
227
|
+
if (!currentContext[0]){
|
228
|
+
return;
|
229
|
+
}
|
230
|
+
|
231
|
+
// If we get here, token is JUST an element (not a class or ID selector)
|
232
|
+
tagName = token;
|
233
|
+
var found = new Array;
|
234
|
+
var foundCount = 0;
|
235
|
+
for (var h = 0; h < currentContext.length; h++) {
|
236
|
+
var elements = currentContext[h].getElementsByTagName(tagName);
|
237
|
+
for (var j = 0; j < elements.length; j++) {
|
238
|
+
found[foundCount++] = elements[j];
|
239
|
+
}
|
240
|
+
}
|
241
|
+
currentContext = found;
|
242
|
+
}
|
243
|
+
return currentContext;
|
244
|
+
}
|
245
|
+
|
246
|
+
/* That revolting regular expression explained
|
247
|
+
/^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
|
248
|
+
\---/ \---/\-------------/ \-------/
|
249
|
+
| | | |
|
250
|
+
| | | The value
|
251
|
+
| | ~,|,^,$,* or =
|
252
|
+
| Attribute
|
253
|
+
Tag
|
254
|
+
*/
|