Project

General

Profile

Download (7.71 KB) Statistics
| Branch: | Tag: | Revision:
1
/*
2
   Behaviour v1.1 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
   	This file is entirely BSD licensed.
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
*/
(2-2/5)