Fixed javascript error appearing on themes using SVGElements.
[fa-stable.git] / js / behaviour.js
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    Small fixes by J.Dobrowolski for Front Accounting May 2008
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                                 var sels =  selector.split(',');
57                                 for (var n = 0; n < sels.length; n++) {
58                                   list = document.getElementsBySelector(sels[n]);
59
60                                   if (!list){
61                                         continue;
62                                   }
63
64                                   for (i=0;element=list[i];i++){
65                                         sheet[selector](element);
66                                   }
67                                 }
68                         }
69                 }
70         },
71
72         addLoadEvent : function(func){
73                 var oldonload = window.onload;
74
75                 if (typeof window.onload != 'function') {
76                         window.onload = func;
77                 } else {
78                         window.onload = function() {
79                                 oldonload();
80                                 func();
81                         }
82                 }
83         }
84 }
85
86 Behaviour.start();
87
88 /*
89    The following code is Copyright (C) Simon Willison 2004.
90
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:
95
96        elements = document.getElementsBySelect('div#main p a.external')
97
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"
101
102    New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
103    See http://www.w3.org/TR/css3-selectors/#attribute-selectors
104
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
107    -- Opera 7 fails
108 */
109
110 function getAllChildren(e) {
111   // Returns all children of element. Workaround required for IE5/Windows. Ugh.
112   return e.all ? e.all : e.getElementsByTagName('*');
113 }
114
115 document.getElementsBySelector = function(selector) {
116   // Attempt to fail gracefully in lesser browsers
117   if (!document.getElementsByTagName) {
118     return new Array();
119   }
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];
129       var id = bits[1];
130       var element = document.getElementById(id);
131       if (tagName && element.nodeName.toLowerCase() != tagName) {
132         // tag with that ID not found, return false
133         return new Array();
134       }
135       // Set currentContext to contain just this element
136       currentContext = new Array(element);
137       continue; // Skip to next token
138     }
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];
144       if (!tagName) {
145         tagName = '*';
146       }
147       // Get elements matching tag, filter them for class selector
148       var found = new Array;
149       var foundCount = 0;
150       for (var h = 0; h < currentContext.length; h++) {
151         var elements;
152         if (tagName == '*') {
153             elements = getAllChildren(currentContext[h]);
154         } else {
155             elements = currentContext[h].getElementsByTagName(tagName);
156         }
157         for (var j = 0; j < elements.length; j++) {
158           found[foundCount++] = elements[j];
159         }
160       }
161       currentContext = new Array;
162       var currentContextIndex = 0;
163       for (var k = 0; k < found.length; k++) {
164         if (found[k].getAttribute('class') != null && found[k].getAttribute('class').match(new RegExp('\\b'+className+'\\b'))) {
165           currentContext[currentContextIndex++] = found[k];
166         }
167       }
168       continue; // Skip to next token
169     }
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;
178       if (!tagName) {
179         tagName = '*';
180       }
181       // Grab all of the tagName elements within current context
182       var found = new Array;
183       var foundCount = 0;
184       for (var h = 0; h < currentContext.length; h++) {
185         var elements;
186         if (tagName == '*') {
187             elements = getAllChildren(currentContext[h]);
188         } else {
189             elements = currentContext[h].getElementsByTagName(tagName);
190         }
191         for (var j = 0; j < elements.length; j++) {
192           found[foundCount++] = elements[j];
193         }
194       }
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); };
201           break;
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'))); };
204           break;
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+'-?'))); };
207           break;
208         case '^': // Match starts with value
209           checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.indexOf(attrValue) == 0); };
210           break;
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); };
213           break;
214         case '*': // Match contains value
215           checkFunction = function(e) { var a=e.getAttribute(attrName); return (a && a.indexOf(attrValue) > -1); };
216           break;
217         default :
218           // Just test for existence of attribute
219           checkFunction = function(e) { return e.getAttribute(attrName); };
220       }
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];
226         }
227       }
228       // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
229       continue; // Skip to next token
230     }
231
232     if (!currentContext[0]){
233         return;
234     }
235
236     // If we get here, token is JUST an element (not a class or ID selector)
237     tagName = token;
238     var found = new Array;
239     var foundCount = 0;
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];
244       }
245     }
246     currentContext = found;
247   }
248   return currentContext;
249 }
250
251 /* That revolting regular expression explained
252 /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
253   \---/  \---/\-------------/    \-------/
254     |      |         |               |
255     |      |         |           The value
256     |      |    ~,|,^,$,* or =
257     |   Attribute
258    Tag
259 */