2 Behaviour v1.1 by Ben Nolan, June 2005. Based largely on the work
3 of Simon Willison (see comments by Simon below).
4 Small fixes by J.Dobrowolski for Front Accounting May 2008
7 Uses css selectors to apply javascript behaviours to enable
8 unobtrusive javascript in html documents.
13 'b.someclass' : function(element){
14 element.onclick = function(){
15 alert(this.innerHTML);
18 '#someid u' : function(element){
19 element.onmouseover = function(){
20 this.innerHTML = "BLAH!";
25 Behaviour.register(myrules);
27 // Call Behaviour.apply() to re-apply the rules (if you
28 // update the dom, etc).
32 This file is entirely BSD licensed.
36 http://ripcord.co.nz/behaviour/
43 register : function(sheet){
44 Behaviour.list.push(sheet);
48 Behaviour.addLoadEvent(function(){
54 for (h=0;sheet=Behaviour.list[h];h++){
55 for (selector in sheet){
56 var sels = selector.split(',');
57 for (var n = 0; n < sels.length; n++) {
58 list = document.getElementsBySelector(sels[n]);
64 for (i=0;element=list[i];i++){
65 sheet[selector](element);
72 addLoadEvent : function(func){
73 var oldonload = window.onload;
75 if (typeof window.onload != 'function') {
78 window.onload = function() {
89 The following code is Copyright (C) Simon Willison 2004.
91 document.getElementsBySelector(selector)
92 - returns an array of element objects from the current document
93 matching the CSS selector. Selectors can contain element names,
94 class names and ids and can be nested. For example:
96 elements = document.getElementsBySelect('div#main p a.external')
98 Will return an array of all 'a' elements with 'external' in their
99 class attribute that are contained inside 'p' elements that are
100 contained inside the 'div' element which has id="main"
102 New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
103 See http://www.w3.org/TR/css3-selectors/#attribute-selectors
105 Version 0.4 - Simon Willison, March 25th 2003
106 -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
110 function getAllChildren(e) {
111 // Returns all children of element. Workaround required for IE5/Windows. Ugh.
112 return e.all ? e.all : e.getElementsByTagName('*');
115 document.getElementsBySelector = function(selector) {
116 // Attempt to fail gracefully in lesser browsers
117 if (!document.getElementsByTagName) {
120 // Split selector in to tokens
121 var tokens = selector.split(' ');
122 var currentContext = new Array(document);
123 for (var i = 0; i < tokens.length; i++) {
124 token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
125 if (token.indexOf('#') > -1) {
126 // Token is an ID selector
127 var bits = token.split('#');
128 var tagName = bits[0];
130 var element = document.getElementById(id);
131 if (tagName && element.nodeName.toLowerCase() != tagName) {
132 // tag with that ID not found, return false
135 // Set currentContext to contain just this element
136 currentContext = new Array(element);
137 continue; // Skip to next token
139 if (token.indexOf('.') > -1) {
140 // Token contains a class selector
141 var bits = token.split('.');
142 var tagName = bits[0];
143 var className = bits[1];
147 // Get elements matching tag, filter them for class selector
148 var found = new Array;
150 for (var h = 0; h < currentContext.length; h++) {
152 if (tagName == '*') {
153 elements = getAllChildren(currentContext[h]);
155 elements = currentContext[h].getElementsByTagName(tagName);
157 for (var j = 0; j < elements.length; j++) {
158 found[foundCount++] = elements[j];
161 currentContext = new Array;
162 var currentContextIndex = 0;
163 for (var k = 0; k < found.length; k++) {
164 if (found[k].className && found[k].className.match(new RegExp('\\b'+className+'\\b'))) {
165 currentContext[currentContextIndex++] = found[k];
168 continue; // Skip to next token
170 // Code to deal with attribute selectors
171 /* Original reg expression /^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
172 was replaced by new RegExp() cuz compressor fault */
173 if (token.match(new RegExp('^(\\w*)\\[(\\w+)([=~\\|\\^\\$\\*]?)=?"?([^\\]"]*)"?\\]$'))) {
174 var tagName = RegExp.$1;
175 var attrName = RegExp.$2;
176 var attrOperator = RegExp.$3;
177 var attrValue = RegExp.$4;
181 // Grab all of the tagName elements within current context
182 var found = new Array;
184 for (var h = 0; h < currentContext.length; h++) {
186 if (tagName == '*') {
187 elements = getAllChildren(currentContext[h]);
189 elements = currentContext[h].getElementsByTagName(tagName);
191 for (var j = 0; j < elements.length; j++) {
192 found[foundCount++] = elements[j];
195 currentContext = new Array;
196 var currentContextIndex = 0;
197 var checkFunction; // This function will be used to filter the elements
198 switch (attrOperator) {
199 case '=': // Equality
200 checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
202 case '~': // Match one of space seperated words
203 checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.match(new RegExp('\\b'+attrValue+'\\b'))); };
205 case '|': // Match start with value followed by optional hyphen
206 checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.match(new RegExp('^'+attrValue+'-?'))); };
208 case '^': // Match starts with value
209 checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.indexOf(attrValue) == 0); };
211 case '$': // Match ends with value - fails with "Warning" in Opera 7
212 checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
214 case '*': // Match contains value
215 checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.indexOf(attrValue) > -1); };
218 // Just test for existence of attribute
219 checkFunction = function(e) { return e.getAttribute(attrName); };
221 currentContext = new Array;
222 var currentContextIndex = 0;
223 for (var k = 0; k < found.length; k++) {
224 if (checkFunction(found[k])) {
225 currentContext[currentContextIndex++] = found[k];
228 // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
229 continue; // Skip to next token
232 if (!currentContext[0]){
236 // If we get here, token is JUST an element (not a class or ID selector)
238 var found = new Array;
240 for (var h = 0; h < currentContext.length; h++) {
241 var elements = currentContext[h].getElementsByTagName(tagName);
242 for (var j = 0; j < elements.length; j++) {
243 found[foundCount++] = elements[j];
246 currentContext = found;
248 return currentContext;
251 /* That revolting regular expression explained
252 /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
253 \---/ \---/\-------------/ \-------/