Don't load nonexistent fix files for non-Monobook
[mediawiki.git] / skins / vector / csshover.htc
bloba88fa08dc4e380f5ce235c5c511431d974999d26
1 <public:attach event="ondocumentready" onevent="CSSHover()" />
2 <script>
3 // <![CDATA[
4 /**
5  *      Whatever:hover - V3.00.081222
6  *      ------------------------------------------------------------
7  *      Author  - Peter Nederlof, http://www.xs4all.nl/~peterned
8  *      License - http://creativecommons.org/licenses/LGPL/2.1
9  *
10  *      Whatever:hover is free software; you can redistribute it and/or
11  *      modify it under the terms of the GNU Lesser General Public
12  *      License as published by the Free Software Foundation; either
13  *      version 2.1 of the License, or (at your option) any later version.
14  *
15  *      Whatever:hover is distributed in the hope that it will be useful,
16  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  *      Lesser General Public License for more details.
19  *
20  *      howto: body { behavior:url("csshover3.htc"); }
21  *      ------------------------------------------------------------
22  */
24 window.CSSHover = (function(){
26         // regular expressions, used and explained later on.
27         var REG_INTERACTIVE = /(^|\s)((([^a]([^ ]+)?)|(a([^#.][^ ]+)+)):(hover|active|focus))/i,
28                 REG_AFFECTED = /(.*?)\:(hover|active|focus)/i,
29                 REG_PSEUDO = /[^:]+:([a-z-]+).*/i,
30                 REG_SELECT = /(\.([a-z0-9_-]+):[a-z]+)|(:[a-z]+)/gi,
31                 REG_CLASS = /\.([a-z0-9_-]*on(hover|active|focus))/i,
32                 REG_MSIE = /msie (5|6|7)/i,
33                 REG_COMPAT = /backcompat/i;
35         // css prefix, a leading dash would be nice (spec), but IE6 doesn't like that.
36         var CSSHOVER_PREFIX = 'csh-';
37         
38         /**
39          *      Local CSSHover object
40          *      --------------------------
41          */
42         
43         var CSSHover = {
44                 
45                 // array of CSSHoverElements, used to unload created events
46                 elements: [], 
47                 
48                 // buffer used for checking on duplicate expressions
49                 callbacks: {}, 
50                 
51                 // init, called once ondomcontentready via the exposed window.CSSHover function
52                 init:function() {
53                         // don't run in IE8 standards; expressions don't work in standards mode anyway, 
54                         // and the stuff we're trying to fix should already work properly
55                         if(!REG_MSIE.test(navigator.userAgent) && !REG_COMPAT.test(window.document.compatMode)) return;
57                         // start parsing the existing stylesheets
58                         var sheets = window.document.styleSheets, l = sheets.length;
59                         for(var i=0; i<l; i++) {
60                                 this.parseStylesheet(sheets[i]);
61                         }
62                 },
64                 // called from init, parses individual stylesheets
65                 parseStylesheet:function(sheet) {
66                         // check sheet imports and parse those recursively
67                         if(sheet.imports) {
68                                 try {
69                                         var imports = sheet.imports, l = imports.length;
70                                         for(var i=0; i<l; i++) {
71                                                 this.parseStylesheet(sheet.imports[i]);
72                                         }
73                                 } catch(securityException){
74                                         // trycatch for various possible errors,
75                                         // todo; might need to be placed inside the for loop, since an error
76                                         // on an import stops following imports from being processed.
77                                 }
78                         }
79                         
80                         // interate the sheet's rules and send them to the parser
81                         try {
82                                 var rules = sheet.rules, l = rules.length;
83                                 for(var j=0; j<l; j++) {
84                                         this.parseCSSRule(rules[j], sheet);
85                                 }
86                         } catch(securityException){
87                                 // trycatch for various errors, most likely accessing the sheet's rules,
88                                 // don't see how individual rules would throw errors, but you never know.
89                         }
90                 },
92                 // magic starts here ...
93                 parseCSSRule:function(rule, sheet) {
94                         
95                         // The sheet is used to insert new rules into, this must be the same sheet the rule 
96                         // came from, to ensure that relative paths keep pointing to the right location.
98                         // only parse a rule if it contains an interactive pseudo.
99                         var select = rule.selectorText;
100                         if(REG_INTERACTIVE.test(select)) {
101                                 var style = rule.style.cssText,
102                                         
103                                         // affected elements are found by truncating the selector after the interactive pseudo,
104                                         // eg: "div li:hover" >>  "div li"
105                                         affected = REG_AFFECTED.exec(select)[1],
106                                         
107                                         // that pseudo is needed for a classname, and defines the type of interaction (focus, hover, active)
108                                         // eg: "li:hover" >> "onhover"
109                                         pseudo = select.replace(REG_PSEUDO, 'on$1'),
110                                         
111                                         // the new selector is going to use that classname in a new css rule,
112                                         // since IE6 doesn't support multiple classnames, this is merged into one classname
113                                         // eg: "li:hover" >> "li.onhover",  "li.folder:hover" >> "li.folderonhover"
114                                         newSelect = select.replace(REG_SELECT, '.$2' + pseudo),
115                                         
116                                         // the classname is needed for the events that are going to be set on affected nodes
117                                         // eg: "li.folder:hover" >> "folderonhover"
118                                         className = REG_CLASS.exec(newSelect)[1];
120                                 // no need to set the same callback more than once when the same selector uses the same classname
121                                 var hash = affected + className;
122                                 if(!this.callbacks[hash]) {
124                                         // affected elements are given an expression under a fake css property, the classname is used
125                                         // because a unique name (eg "behavior:") would be overruled (in IE6, not 7) by a following rule 
126                                         // selecting the same element. The expression does a callback to CSSHover.patch, rerouted via the
127                                         // exposed window.CSSHover function. 
129                                         // because the expression is added to the stylesheet, and styles are always applied to html that is
130                                         // dynamically added to the dom, the expression will also trigger for those new elements (provided
131                                         // they are selected by the affected selector). 
133                                         sheet.addRule(affected, CSSHOVER_PREFIX + className + ':expression(CSSHover(this, "'+pseudo+'", "'+className+'"))');
134                                         
135                                         // hash it, so an identical selector/class combo does not duplicate the expression
136                                         this.callbacks[hash] = true;
137                                 }
138                                 
139                                 // duplicate expressions need not be set, but the style could differ
140                                 sheet.addRule(newSelect, style);
141                         }
142                 },
144                 // called via the expression, patches individual nodes
145                 patch:function(node, type, className) {
146                         
147                         // the patch's type is returned to the expression. That way the expression property
148                         // can be found and removed, to stop it from calling patch over and over. 
149                         // The if will fail the first time, since the expression has not yet received a value.
150                         var property = CSSHOVER_PREFIX + className;
151                         if(node.style[property]) {
152                                 node.style[property] = null;
153                         }
155                         // just to make sure, also keep track of patched classnames locally on the node
156                         if(!node.csshover) node.csshover = [];
158                         // and check for it to prevent duplicate events with the same classname from being set
159                         if(!node.csshover[className]) {
160                                 node.csshover[className] = true;
162                                 // create an instance for the given type and class
163                                 var element = new CSSHoverElement(node, type, className);
164                                 
165                                 // and store that instance for unloading later on
166                                 this.elements.push(element);
167                         }
169                         // returns a dummy value to the expression
170                         return type;
171                 },
173                 // unload stuff onbeforeunload
174                 unload:function() {
175                         try {
176                                 
177                                 // remove events
178                                 var l = this.elements.length;
179                                 for(var i=0; i<l; i++) {
180                                         this.elements[i].unload();
181                                 }
183                                 // and set properties to null 
184                                 this.elements = [];
185                                 this.callbacks = {};
187                         } catch (e) {
188                         }
189                 }
190         };
192         // add the unload to the onbeforeunload event
193         window.attachEvent('onbeforeunload', function(){
194                 CSSHover.unload();
195         });
197         /**
198          *      CSSHoverElement
199          *      --------------------------
200          */
202         // the event types associated with the interactive pseudos
203         var CSSEvents = {
204                 onhover:  { activator: 'onmouseenter', deactivator: 'onmouseleave' },
205                 onactive: { activator: 'onmousedown',  deactivator: 'onmouseup' },
206                 onfocus:  { activator: 'onfocus',      deactivator: 'onblur' }
207         };
208         
209         // CSSHoverElement constructor, called via CSSHover.patch
210         function CSSHoverElement(node, type, className) {
212                 // the CSSHoverElement patches individual nodes by manually applying the events that should 
213                 // have fired by the css pseudoclasses, eg mouseenter and mouseleave for :hover. 
215                 this.node = node;
216                 this.type = type;
217                 var replacer = new RegExp('(^|\\s)'+className+'(\\s|$)', 'g');
219                 // store event handlers for removal onunload
220                 this.activator =   function(){ node.className += ' ' + className; };
221                 this.deactivator = function(){ node.className = node.className.replace(replacer, ' '); };
222                 
223                 // add the events
224                 node.attachEvent(CSSEvents[type].activator, this.activator);
225                 node.attachEvent(CSSEvents[type].deactivator, this.deactivator);
226         }
227         
228         CSSHoverElement.prototype = {
229                 // onbeforeunload, called via CSSHover.unload
230                 unload:function() {
232                         // remove events 
233                         this.node.detachEvent(CSSEvents[this.type].activator, this.activator);
234                         this.node.detachEvent(CSSEvents[this.type].deactivator, this.deactivator);
236                         // and set properties to null 
237                         this.activator = null;
238                         this.deactivator = null;
239                         this.node = null;
240                         this.type = null;
241                 }
242         };
244         /**
245          *      Public hook
246          *      --------------------------
247          */
248         
249         return function(node, type, className) {
250                 if(node) {
251                         // called via the css expression; patches individual nodes
252                         return CSSHover.patch(node, type, className);
253                 } else {
254                         // called ondomcontentready via the public:attach node
255                         CSSHover.init();
256                 }
257         };
259 })();
261 // ]]>
262 </script>