/hudson-war/src/main/webapp/scripts/behavior.js

http://github.com/hudson/hudson · JavaScript · 257 lines · 174 code · 15 blank · 68 comment · 22 complexity · e3b782dc6868bfbe8432818777afac67 MD5 · raw file

  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. Description:
  5. Uses css selectors to apply javascript behaviours to enable
  6. unobtrusive javascript in html documents.
  7. Usage:
  8. var myrules = {
  9. 'b.someclass' : function(element){
  10. element.onclick = function(){
  11. alert(this.innerHTML);
  12. }
  13. },
  14. '#someid u' : function(element){
  15. element.onmouseover = function(){
  16. this.innerHTML = "BLAH!";
  17. }
  18. }
  19. };
  20. Behaviour.register(myrules);
  21. // Call Behaviour.apply() to re-apply the rules (if you
  22. // update the dom, etc).
  23. License:
  24. This file is entirely BSD licensed.
  25. More information:
  26. http://ripcord.co.nz/behaviour/
  27. */
  28. var Behaviour = {
  29. list : new Array,
  30. register : function(sheet){
  31. Behaviour.list.push(sheet);
  32. },
  33. start : function(){
  34. Behaviour.addLoadEvent(function(){
  35. Behaviour.apply();
  36. });
  37. },
  38. apply : function(){
  39. this.applySubtree(document);
  40. },
  41. applySubtree : function(startNode) {
  42. Behaviour.list._each(function(sheet) {
  43. for (var selector in sheet){
  44. var list = findElementsBySelector(startNode,selector);
  45. list._each(sheet[selector]);
  46. }
  47. });
  48. },
  49. addLoadEvent : function(func){
  50. var oldonload = window.onload;
  51. if (typeof window.onload != 'function') {
  52. window.onload = func;
  53. } else {
  54. window.onload = function() {
  55. oldonload();
  56. func();
  57. }
  58. }
  59. }
  60. }
  61. Behaviour.start();
  62. /*
  63. The following code is Copyright (C) Simon Willison 2004.
  64. document.getElementsBySelector(selector)
  65. - returns an array of element objects from the current document
  66. matching the CSS selector. Selectors can contain element names,
  67. class names and ids and can be nested. For example:
  68. elements = document.getElementsBySelect('div#main p a.external')
  69. Will return an array of all 'a' elements with 'external' in their
  70. class attribute that are contained inside 'p' elements that are
  71. contained inside the 'div' element which has id="main"
  72. New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
  73. See http://www.w3.org/TR/css3-selectors/#attribute-selectors
  74. Version 0.4 - Simon Willison, March 25th 2003
  75. -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
  76. -- Opera 7 fails
  77. */
  78. function getAllChildren(e) {
  79. // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  80. return e.all ? e.all : e.getElementsByTagName('*');
  81. }
  82. function isAncestor(p,c) {
  83. while(true) {
  84. if(p==c) return true;
  85. if(c==null) return false;
  86. c = c.parentNode;
  87. }
  88. }
  89. function findElementsBySelector(startNode,selector) {
  90. // Split selector in to tokens
  91. var tokens = selector.replace(/^\s+/,'').replace(/\s+$/,'').split(' ');
  92. var currentContext = new Array(startNode);
  93. for (var i = 0; i < tokens.length; i++) {
  94. var token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
  95. if (token.indexOf('#') > -1) {
  96. // Token is an ID selector
  97. var bits = token.split('#');
  98. var tagName = bits[0];
  99. var id = bits[1];
  100. var element = document.getElementById(id);
  101. if (tagName && element.nodeName.toLowerCase() != tagName) {
  102. // tag with that ID not found, return false
  103. return [];
  104. }
  105. // make sure this node is a descendant of the current context
  106. if(currentContext.find(function(n) {return isAncestor(n,element)})==null)
  107. return []; // not a descendant
  108. // Set currentContext to contain just this element
  109. currentContext = [element];
  110. continue; // Skip to next token
  111. }
  112. if (token.indexOf('.') > -1) {
  113. // Token contains a class selector
  114. var bits = token.split('.');
  115. var tagName = bits[0];
  116. var className = new RegExp('\\b'+bits[1]+'\\b');
  117. if (!tagName) {
  118. tagName = '*';
  119. }
  120. // Get elements matching tag, filter them for class selector
  121. var found = [];
  122. for (var h = 0; h < currentContext.length; h++) {
  123. var elements;
  124. if (tagName == '*') {
  125. elements = getAllChildren(currentContext[h]);
  126. } else {
  127. elements = currentContext[h].getElementsByTagName(tagName);
  128. }
  129. for (var j = 0; j < elements.length; j++) {
  130. found.push(elements[j]);
  131. }
  132. }
  133. currentContext = [];
  134. for (var k = 0; k < found.length; k++) {
  135. if (found[k].className && found[k].className.match(className)) {
  136. currentContext.push(found[k]);
  137. }
  138. }
  139. continue; // Skip to next token
  140. }
  141. // Code to deal with attribute selectors
  142. bits = /^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/.exec(token);
  143. if (bits!=null) {
  144. var tagName = bits[1];
  145. var attrName = bits[2];
  146. var attrOperator = bits[3];
  147. var attrValue = bits[4];
  148. if (!tagName) {
  149. tagName = '*';
  150. }
  151. // Grab all of the tagName elements within current context
  152. var found = new Array;
  153. var foundCount = 0;
  154. for (var h = 0; h < currentContext.length; h++) {
  155. var elements;
  156. if (tagName == '*') {
  157. elements = getAllChildren(currentContext[h]);
  158. } else {
  159. elements = currentContext[h].getElementsByTagName(tagName);
  160. }
  161. for (var j = 0; j < elements.length; j++) {
  162. found.push(elements[j]);
  163. }
  164. }
  165. var checkFunction; // This function will be used to filter the elements
  166. switch (attrOperator) {
  167. case '=': // Equality
  168. checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
  169. break;
  170. case '~': // Match one of space seperated words
  171. checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
  172. break;
  173. case '|': // Match start with value followed by optional hyphen
  174. checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
  175. break;
  176. case '^': // Match starts with value
  177. checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
  178. break;
  179. case '$': // Match ends with value - fails with "Warning" in Opera 7
  180. checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
  181. break;
  182. case '*': // Match ends with value
  183. checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
  184. break;
  185. default :
  186. // Just test for existence of attribute
  187. checkFunction = function(e) { return e.getAttribute(attrName); };
  188. }
  189. currentContext = found.findAll(checkFunction);
  190. // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
  191. continue; // Skip to next token
  192. }
  193. if (!currentContext[0]){
  194. return [];
  195. }
  196. // If we get here, token is JUST an element (not a class or ID selector)
  197. tagName = token;
  198. var found = new Array;
  199. for (var h = 0; h < currentContext.length; h++) {
  200. var elements = currentContext[h].getElementsByTagName(tagName);
  201. for (var j = 0; j < elements.length; j++) {
  202. found.push(elements[j]);
  203. }
  204. }
  205. currentContext = found;
  206. }
  207. return currentContext;
  208. }
  209. document.getElementsBySelector = function(selector) {
  210. return findElementsBySelector(document,selector);
  211. }
  212. /* That revolting regular expression explained
  213. /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
  214. \---/ \---/\-------------/ \-------/
  215. | | | |
  216. | | | The value
  217. | | ~,|,^,$,* or =
  218. | Attribute
  219. Tag
  220. */