nitro-auth 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/LICENSE +31 -0
- data/README +115 -0
- data/TODO +7 -0
- data/examples/basic/public/error.xhtml +81 -0
- data/examples/basic/public/js/behaviour.js +254 -0
- data/examples/basic/public/js/controls.js +446 -0
- data/examples/basic/public/js/dragdrop.js +537 -0
- data/examples/basic/public/js/effects.js +612 -0
- data/examples/basic/public/js/prototype.js +1038 -0
- data/examples/basic/public/register.xhtml +65 -0
- data/examples/basic/run.rb +32 -0
- data/examples/basic/src/basic.rb +4 -0
- data/examples/basic/src/basic/auth_controller.rb +9 -0
- data/examples/basic/src/basic/controller.rb +29 -0
- data/examples/basic/src/basic/site.rb +13 -0
- data/examples/basic/src/basic/user.rb +22 -0
- data/examples/basic/src/basic/view/index.xhtml +35 -0
- data/lib/nitro/auth.rb +41 -0
- data/lib/nitro/auth/auth_controller.rb +199 -0
- data/lib/nitro/auth/controller.rb +166 -0
- data/lib/nitro/auth/model/user.rb +147 -0
- data/lib/nitro/auth/util/crypt.rb +55 -0
- data/lib/nitro/auth/view/access_denied.xhtml +13 -0
- data/lib/nitro/auth/view/login.xhtml +38 -0
- data/lib/nitro/auth/view/logout.xhtml +16 -0
- data/lib/nitro/auth/view/register.xhtml +51 -0
- metadata +90 -0
data/CHANGELOG
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
<b>0.2.0</b> (2005-07-26)
|
2
|
+
|
3
|
+
* Updated for Nitro 0.21.0.
|
4
|
+
* Moved paths into 'nitro/auth' instead of just 'auth'.
|
5
|
+
* Fixes for overriding Auth::User with an application version.
|
6
|
+
* Got the basic example working.
|
7
|
+
* Fixed a bug in access_denied.xhtml.
|
8
|
+
* Actually got the license details set up.
|
9
|
+
|
10
|
+
<b>0.1.0</b> (2005-07-21)
|
11
|
+
|
12
|
+
* Initial implementation of nitro-auth. Lots still to do.
|
data/LICENSE
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
The BSD License
|
2
|
+
|
3
|
+
Copyright (c) 2005, Deborah 'Ysabel' Hooker
|
4
|
+
All rights reserved.
|
5
|
+
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
7
|
+
modification, are permitted provided that the following conditions are
|
8
|
+
met:
|
9
|
+
|
10
|
+
* Redistributions of source code must retain the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer.
|
12
|
+
|
13
|
+
* Redistributions in binary form must reproduce the above copyright
|
14
|
+
notice, this list of conditions and the following disclaimer in the
|
15
|
+
documentation and/or other materials provided with the distribution.
|
16
|
+
|
17
|
+
* Ms. Hooker's name may not be used to endorse or promote products
|
18
|
+
derived from this software without specific prior written permission.
|
19
|
+
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
21
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
22
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
23
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
24
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
25
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
26
|
+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
27
|
+
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
28
|
+
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
29
|
+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
30
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
31
|
+
|
data/README
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
= nitro-auth: Authentication and Authorization for Nitro
|
2
|
+
|
3
|
+
nitro-auth provides basic forms-based authentication and role-based
|
4
|
+
authorization for applications built on the
|
5
|
+
Nitro[http://www.nitrohq.com/] web engine. It is designed
|
6
|
+
to allow easy integration with an application, including declarative
|
7
|
+
authorization rules and easy application-specific rendering of auth-related
|
8
|
+
pages.
|
9
|
+
|
10
|
+
nitro-auth is currently version 0.2.0 and is, as you might expect, far
|
11
|
+
from feature-complete. But even so, it hopefully has enough to get you
|
12
|
+
started.
|
13
|
+
|
14
|
+
== Features
|
15
|
+
|
16
|
+
* Persistent Auth::User and Auth::Role objects. nitro-auth only declares
|
17
|
+
the minimum fields necessary for doing authentication (fields like +login+,
|
18
|
+
+password+ and +session_key+) and lets the application extend them further.
|
19
|
+
|
20
|
+
* Auth::Controller mixin which provides authentication information and
|
21
|
+
declarative role-based security to Nitro controllers::
|
22
|
+
|
23
|
+
require 'nitro'
|
24
|
+
require 'nitro/auth'
|
25
|
+
|
26
|
+
class MyController < Nitro::Controller
|
27
|
+
include Auth::Controller
|
28
|
+
|
29
|
+
def list_details
|
30
|
+
[...]
|
31
|
+
end
|
32
|
+
protect :list_details
|
33
|
+
|
34
|
+
def edit_details
|
35
|
+
[...]
|
36
|
+
end
|
37
|
+
required_role :edit_details, :manager
|
38
|
+
|
39
|
+
def edit_user
|
40
|
+
[...]
|
41
|
+
end
|
42
|
+
administrative :edit_user
|
43
|
+
end
|
44
|
+
|
45
|
+
* Passwords are stored in a salted, hashed form using SHA1 hashes.
|
46
|
+
|
47
|
+
* Cookie-based login session persistence.
|
48
|
+
|
49
|
+
* Authentication controller uses Nitro templates for easy application
|
50
|
+
integration.
|
51
|
+
|
52
|
+
== Coming soon
|
53
|
+
|
54
|
+
* Challenge-response authentication, including JavaScript client-side
|
55
|
+
challenge-response validation.
|
56
|
+
|
57
|
+
== Download
|
58
|
+
|
59
|
+
See the Nitro Rubyforge Page (http://rubyforge.org/projects/nitro) for
|
60
|
+
the latest nitro-auth package.
|
61
|
+
|
62
|
+
== Documentation
|
63
|
+
|
64
|
+
(TODO: Get the rdoc up, probably at http://nitrohq.com/rdoc/nitro-auth)
|
65
|
+
In the meantime, you can see it at http://www.ysabel.org/ruby/doc/nitro-auth
|
66
|
+
if you'd like to browse.
|
67
|
+
|
68
|
+
== Requirements
|
69
|
+
|
70
|
+
nitro-auth requires Nitro, of course. See NitroHQ[http://www.nitrohq.com/]
|
71
|
+
(http://www.nitrohq.com) for current releases
|
72
|
+
of Nitro.
|
73
|
+
|
74
|
+
[Ruby 1.8.1 and greater]
|
75
|
+
http://www.ruby-lang.org
|
76
|
+
(Version 1.8.2 is recomended)
|
77
|
+
|
78
|
+
== Installation
|
79
|
+
|
80
|
+
= rubygem install (simplest, recommended):
|
81
|
+
|
82
|
+
<tt>> gem install nitro-auth</tt>
|
83
|
+
|
84
|
+
= zip/tgz install
|
85
|
+
|
86
|
+
Unzip/untar and make sure the lib directory is in your path.
|
87
|
+
(Use the gem, trust me, it's easier.)
|
88
|
+
|
89
|
+
== Contents
|
90
|
+
|
91
|
+
[examples/]
|
92
|
+
Examples of using nitro-auth. (Well, really, example right now.)
|
93
|
+
|
94
|
+
[lib/]
|
95
|
+
nitro-auth library source files.
|
96
|
+
|
97
|
+
[test/]
|
98
|
+
nitro-auth tests.
|
99
|
+
|
100
|
+
== Support
|
101
|
+
|
102
|
+
The Nitro mailing list is nitro-general@rubyforge.org, and is a good place
|
103
|
+
to start. You can subscribe and/or browse archives at
|
104
|
+
http://rubyforge.org/mailman/listinfo/nitro-general
|
105
|
+
|
106
|
+
You may also drop the author an email at mailto:deb@ysabel.org.
|
107
|
+
|
108
|
+
TBD: Bug/feature request tracking
|
109
|
+
|
110
|
+
== License
|
111
|
+
|
112
|
+
Copyright (c) 2005, Deborah 'Ysabel' Hooker
|
113
|
+
|
114
|
+
nitro-auth is copyrighted free software released under the BSD license.
|
115
|
+
For details consult the LICENSE file.
|
data/TODO
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<title>Error</title>
|
4
|
+
<style>
|
5
|
+
.path {
|
6
|
+
padding: 5px;
|
7
|
+
font-size: 140%;
|
8
|
+
background: #ddd;
|
9
|
+
}
|
10
|
+
.error {
|
11
|
+
padding: 5px;
|
12
|
+
padding-top: 15px;
|
13
|
+
font-size: 140%;
|
14
|
+
color: #f00;
|
15
|
+
}
|
16
|
+
.load {
|
17
|
+
padding: 5px;
|
18
|
+
color: #555;
|
19
|
+
}
|
20
|
+
.source {
|
21
|
+
border: 1px solid #ccc;
|
22
|
+
padding: 10px;
|
23
|
+
margin-top: 10px; margin-bottom: 10px;
|
24
|
+
}
|
25
|
+
h2 {
|
26
|
+
padding-left: 5px;
|
27
|
+
background: #eee;
|
28
|
+
}
|
29
|
+
</style>
|
30
|
+
</head>
|
31
|
+
<body>
|
32
|
+
<h1>Error</h1>
|
33
|
+
|
34
|
+
<?r for error, path in @context.rendering_errors ?>
|
35
|
+
<div class="path"><strong>Path:</strong> #{path}</div>
|
36
|
+
<div class="error"><strong>#{error.to_s}</strong></div>
|
37
|
+
<div class="load">Click here to <strong><a href="#{request.uri}">reload</a></strong>.</div>
|
38
|
+
<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>
|
39
|
+
<?r if error.respond_to?(:source_extract) ?>
|
40
|
+
<div class="source">
|
41
|
+
<?r
|
42
|
+
extract = error.source_extract.split("\n")
|
43
|
+
extract.each_with_index do |line, idx|
|
44
|
+
line = Nitro::Markup.expand(line)
|
45
|
+
if 4 == idx
|
46
|
+
?>
|
47
|
+
<div style="background: #eee">#{line}</div>
|
48
|
+
<?r else ?>
|
49
|
+
<div>#{line}</div>
|
50
|
+
<?r
|
51
|
+
end
|
52
|
+
end
|
53
|
+
?>
|
54
|
+
</div>
|
55
|
+
<?r end ?>
|
56
|
+
<h2><a href="#" onclick="document.getElementById('trace').style.display = 'block'; return false">Stack Trace</a></h2>
|
57
|
+
<p id="trace" style="display: none">#{error.backtrace.join('<br />')}</p>
|
58
|
+
<?r end ?>
|
59
|
+
|
60
|
+
<h2><a href="#" onclick="document.getElementById('request').style.display = 'block'; return false">Request</a></h2>
|
61
|
+
<div id="request" style="display: none">
|
62
|
+
<p><strong>Parameters:</strong> #{request.params.reject{ |k,v| k == :__RELOADED__ }.inspect}</p>
|
63
|
+
<p><strong>Cookies:</strong> #{request.cookies.inspect}</p>
|
64
|
+
<p><strong>Headers:</strong><br />#{request.headers.collect { |k, v| "#{k} => #{v}" }.join('<br />')}</p>
|
65
|
+
</div>
|
66
|
+
|
67
|
+
<h2><a href="#" onclick="document.getElementById('response').style.display = 'block'; return false">Response</a></h2>
|
68
|
+
<div id="response" style="display: none">
|
69
|
+
<p><strong>Headers:</strong> #{request.response_headers.inspect}</p>
|
70
|
+
<p><strong>Cookies:</strong> #{request.response_cookies.inspect}</p>
|
71
|
+
</div>
|
72
|
+
|
73
|
+
<h2><a href="#" onclick="document.getElementById('session').style.display = 'block'; return false">Session</a></h2>
|
74
|
+
<div id="session" style="display: none">
|
75
|
+
<p><strong>Values:</strong> #{session.inspect}</p>
|
76
|
+
</div>
|
77
|
+
|
78
|
+
<br /><br />
|
79
|
+
Powered by <a href="http://www.nitrohq.com">Nitro</a> version #{Nitro::Version}
|
80
|
+
</body>
|
81
|
+
</html>
|
@@ -0,0 +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
|
+
*/
|