nitro 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG +350 -0
  2. data/INSTALL +2 -2
  3. data/ProjectInfo +61 -0
  4. data/README +5 -4
  5. data/Rakefile +5 -4
  6. data/bin/nitrogen +3 -1
  7. data/doc/AUTHORS +27 -3
  8. data/doc/RELEASES +193 -0
  9. data/doc/lhttpd.txt +4 -0
  10. data/lib/nitro.rb +1 -1
  11. data/lib/nitro/adapter/cgi.rb +6 -321
  12. data/lib/nitro/adapter/fastcgi.rb +2 -14
  13. data/lib/nitro/adapter/scgi.rb +237 -71
  14. data/lib/nitro/adapter/webrick.rb +25 -7
  15. data/lib/nitro/caching.rb +1 -0
  16. data/lib/nitro/cgi.rb +296 -0
  17. data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
  18. data/lib/nitro/cgi/http.rb +62 -0
  19. data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
  20. data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
  21. data/lib/nitro/cgi/stream.rb +43 -0
  22. data/lib/nitro/cgi/utils.rb +38 -0
  23. data/lib/nitro/compiler.rb +23 -11
  24. data/lib/nitro/compiler/css.rb +8 -0
  25. data/lib/nitro/compiler/morphing.rb +66 -0
  26. data/lib/nitro/context.rb +21 -30
  27. data/lib/nitro/controller.rb +23 -100
  28. data/lib/nitro/dispatcher.rb +18 -8
  29. data/lib/nitro/element.rb +6 -2
  30. data/lib/nitro/flash.rb +2 -2
  31. data/lib/nitro/mixin/buffer.rb +2 -2
  32. data/lib/nitro/mixin/form.rb +204 -93
  33. data/lib/nitro/mixin/javascript.rb +170 -11
  34. data/lib/nitro/mixin/markup.rb +1 -0
  35. data/lib/nitro/mixin/pager.rb +7 -4
  36. data/lib/nitro/mixin/rss.rb +2 -0
  37. data/lib/nitro/mixin/table.rb +23 -6
  38. data/lib/nitro/mixin/xhtml.rb +2 -2
  39. data/lib/nitro/render.rb +19 -5
  40. data/lib/nitro/scaffold.rb +12 -6
  41. data/lib/nitro/server.rb +4 -6
  42. data/lib/nitro/server/runner.rb +2 -2
  43. data/lib/nitro/session.rb +8 -1
  44. data/lib/nitro/session/file.rb +40 -0
  45. data/lib/part/admin.rb +2 -0
  46. data/lib/part/admin/controller.rb +7 -3
  47. data/lib/part/admin/skin.rb +8 -1
  48. data/lib/part/admin/template/index.xhtml +39 -1
  49. data/proto/public/error.xhtml +5 -3
  50. data/proto/public/js/behaviour.js +254 -254
  51. data/proto/public/js/controls.js +427 -165
  52. data/proto/public/js/dragdrop.js +255 -276
  53. data/proto/public/js/effects.js +476 -277
  54. data/proto/public/js/prototype.js +561 -127
  55. data/proto/public/js/scaffold.js +74 -0
  56. data/proto/public/js/scriptaculous.js +44 -0
  57. data/proto/public/js/util.js +548 -0
  58. data/proto/public/scaffold/list.xhtml +4 -1
  59. data/proto/scgi.rb +333 -0
  60. data/script/scgi_ctl +221 -0
  61. data/script/scgi_service +120 -0
  62. data/test/nitro/adapter/raw_post1.bin +0 -0
  63. data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
  64. data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
  65. data/test/nitro/mixin/tc_xhtml.rb +1 -1
  66. data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
  67. data/test/nitro/tc_controller.rb +9 -5
  68. metadata +159 -169
  69. data/benchmark/bench.rb +0 -5
  70. data/benchmark/simple-webrick-n-200.txt +0 -44
  71. data/benchmark/static-webrick-n-200.txt +0 -43
  72. data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
  73. data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
  74. data/benchmark/tiny-webrick-n-200.txt +0 -44
  75. data/benchmark/tiny2-webrick-n-200.txt +0 -44
  76. data/examples/README +0 -7
@@ -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
- obj = request.fill(#{klass}[oid])
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 => '127.0.0.1', :doc => 'The server listening address'
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 => { '/' => SimpleController }, :doc => 'The server map'
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[:dispatche] || Dispatcher.new(@map)
89
+ @dispatcher = options[:dispatcher] || Dispatcher.new(@map)
90
+ return self
93
91
  end
94
92
 
95
93
  def root=(controller)
@@ -239,7 +239,7 @@ class Runner
239
239
 
240
240
  elsif 'cgi_proc' == ENV['NITRO_INVOKE']
241
241
  require 'nitro/adapter/cgi'
242
- Cgi.start(server)
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/lhttpd.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,3 +1,5 @@
1
+ require 'nano/time/stamp'
2
+
1
3
  require 'nitro/server'
2
4
 
3
5
  require 'part/admin/controller'
@@ -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
@@ -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>#@name</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> (#{c.count})</td>
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>
@@ -31,11 +31,13 @@
31
31
  <body>
32
32
  <h1>Error</h1>
33
33
 
34
- <?r if Run.mode == :debug ?>
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
+ */