]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blobdiff - skins/vector/csshover.htc
MediaWiki 1.16.0
[autoinstallsdev/mediawiki.git] / skins / vector / csshover.htc
diff --git a/skins/vector/csshover.htc b/skins/vector/csshover.htc
new file mode 100644 (file)
index 0000000..a88fa08
--- /dev/null
@@ -0,0 +1,262 @@
+<public:attach event="ondocumentready" onevent="CSSHover()" />
+<script>
+// <![CDATA[
+/**
+ *     Whatever:hover - V3.00.081222
+ *     ------------------------------------------------------------
+ *     Author  - Peter Nederlof, http://www.xs4all.nl/~peterned
+ *     License - http://creativecommons.org/licenses/LGPL/2.1
+ *
+ *     Whatever:hover is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU Lesser General Public
+ *     License as published by the Free Software Foundation; either
+ *     version 2.1 of the License, or (at your option) any later version.
+ *
+ *     Whatever:hover is distributed in the hope that it will be useful,
+ *     but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *     Lesser General Public License for more details.
+ *
+ *     howto: body { behavior:url("csshover3.htc"); }
+ *     ------------------------------------------------------------
+ */
+
+window.CSSHover = (function(){
+
+       // regular expressions, used and explained later on.
+       var REG_INTERACTIVE = /(^|\s)((([^a]([^ ]+)?)|(a([^#.][^ ]+)+)):(hover|active|focus))/i,
+               REG_AFFECTED = /(.*?)\:(hover|active|focus)/i,
+               REG_PSEUDO = /[^:]+:([a-z-]+).*/i,
+               REG_SELECT = /(\.([a-z0-9_-]+):[a-z]+)|(:[a-z]+)/gi,
+               REG_CLASS = /\.([a-z0-9_-]*on(hover|active|focus))/i,
+               REG_MSIE = /msie (5|6|7)/i,
+               REG_COMPAT = /backcompat/i;
+
+       // css prefix, a leading dash would be nice (spec), but IE6 doesn't like that.
+       var CSSHOVER_PREFIX = 'csh-';
+       
+       /**
+        *      Local CSSHover object
+        *      --------------------------
+        */
+       
+       var CSSHover = {
+               
+               // array of CSSHoverElements, used to unload created events
+               elements: [], 
+               
+               // buffer used for checking on duplicate expressions
+               callbacks: {}, 
+               
+               // init, called once ondomcontentready via the exposed window.CSSHover function
+               init:function() {
+                       // don't run in IE8 standards; expressions don't work in standards mode anyway, 
+                       // and the stuff we're trying to fix should already work properly
+                       if(!REG_MSIE.test(navigator.userAgent) && !REG_COMPAT.test(window.document.compatMode)) return;
+
+                       // start parsing the existing stylesheets
+                       var sheets = window.document.styleSheets, l = sheets.length;
+                       for(var i=0; i<l; i++) {
+                               this.parseStylesheet(sheets[i]);
+                       }
+               },
+
+               // called from init, parses individual stylesheets
+               parseStylesheet:function(sheet) {
+                       // check sheet imports and parse those recursively
+                       if(sheet.imports) {
+                               try {
+                                       var imports = sheet.imports, l = imports.length;
+                                       for(var i=0; i<l; i++) {
+                                               this.parseStylesheet(sheet.imports[i]);
+                                       }
+                               } catch(securityException){
+                                       // trycatch for various possible errors,
+                                       // todo; might need to be placed inside the for loop, since an error
+                                       // on an import stops following imports from being processed.
+                               }
+                       }
+                       
+                       // interate the sheet's rules and send them to the parser
+                       try {
+                               var rules = sheet.rules, l = rules.length;
+                               for(var j=0; j<l; j++) {
+                                       this.parseCSSRule(rules[j], sheet);
+                               }
+                       } catch(securityException){
+                               // trycatch for various errors, most likely accessing the sheet's rules,
+                               // don't see how individual rules would throw errors, but you never know.
+                       }
+               },
+
+               // magic starts here ...
+               parseCSSRule:function(rule, sheet) {
+                       
+                       // The sheet is used to insert new rules into, this must be the same sheet the rule 
+                       // came from, to ensure that relative paths keep pointing to the right location.
+
+                       // only parse a rule if it contains an interactive pseudo.
+                       var select = rule.selectorText;
+                       if(REG_INTERACTIVE.test(select)) {
+                               var style = rule.style.cssText,
+                                       
+                                       // affected elements are found by truncating the selector after the interactive pseudo,
+                                       // eg: "div li:hover" >>  "div li"
+                                       affected = REG_AFFECTED.exec(select)[1],
+                                       
+                                       // that pseudo is needed for a classname, and defines the type of interaction (focus, hover, active)
+                                       // eg: "li:hover" >> "onhover"
+                                       pseudo = select.replace(REG_PSEUDO, 'on$1'),
+                                       
+                                       // the new selector is going to use that classname in a new css rule,
+                                       // since IE6 doesn't support multiple classnames, this is merged into one classname
+                                       // eg: "li:hover" >> "li.onhover",  "li.folder:hover" >> "li.folderonhover"
+                                       newSelect = select.replace(REG_SELECT, '.$2' + pseudo),
+                                       
+                                       // the classname is needed for the events that are going to be set on affected nodes
+                                       // eg: "li.folder:hover" >> "folderonhover"
+                                       className = REG_CLASS.exec(newSelect)[1];
+
+                               // no need to set the same callback more than once when the same selector uses the same classname
+                               var hash = affected + className;
+                               if(!this.callbacks[hash]) {
+
+                                       // affected elements are given an expression under a fake css property, the classname is used
+                                       // because a unique name (eg "behavior:") would be overruled (in IE6, not 7) by a following rule 
+                                       // selecting the same element. The expression does a callback to CSSHover.patch, rerouted via the
+                                       // exposed window.CSSHover function. 
+
+                                       // because the expression is added to the stylesheet, and styles are always applied to html that is
+                                       // dynamically added to the dom, the expression will also trigger for those new elements (provided
+                                       // they are selected by the affected selector). 
+
+                                       sheet.addRule(affected, CSSHOVER_PREFIX + className + ':expression(CSSHover(this, "'+pseudo+'", "'+className+'"))');
+                                       
+                                       // hash it, so an identical selector/class combo does not duplicate the expression
+                                       this.callbacks[hash] = true;
+                               }
+                               
+                               // duplicate expressions need not be set, but the style could differ
+                               sheet.addRule(newSelect, style);
+                       }
+               },
+
+               // called via the expression, patches individual nodes
+               patch:function(node, type, className) {
+                       
+                       // the patch's type is returned to the expression. That way the expression property
+                       // can be found and removed, to stop it from calling patch over and over. 
+                       // The if will fail the first time, since the expression has not yet received a value.
+                       var property = CSSHOVER_PREFIX + className;
+                       if(node.style[property]) {
+                               node.style[property] = null;
+                       }
+
+                       // just to make sure, also keep track of patched classnames locally on the node
+                       if(!node.csshover) node.csshover = [];
+
+                       // and check for it to prevent duplicate events with the same classname from being set
+                       if(!node.csshover[className]) {
+                               node.csshover[className] = true;
+
+                               // create an instance for the given type and class
+                               var element = new CSSHoverElement(node, type, className);
+                               
+                               // and store that instance for unloading later on
+                               this.elements.push(element);
+                       }
+
+                       // returns a dummy value to the expression
+                       return type;
+               },
+
+               // unload stuff onbeforeunload
+               unload:function() {
+                       try {
+                               
+                               // remove events
+                               var l = this.elements.length;
+                               for(var i=0; i<l; i++) {
+                                       this.elements[i].unload();
+                               }
+
+                               // and set properties to null 
+                               this.elements = [];
+                               this.callbacks = {};
+
+                       } catch (e) {
+                       }
+               }
+       };
+
+       // add the unload to the onbeforeunload event
+       window.attachEvent('onbeforeunload', function(){
+               CSSHover.unload();
+       });
+
+       /**
+        *      CSSHoverElement
+        *      --------------------------
+        */
+
+       // the event types associated with the interactive pseudos
+       var CSSEvents = {
+               onhover:  { activator: 'onmouseenter', deactivator: 'onmouseleave' },
+               onactive: { activator: 'onmousedown',  deactivator: 'onmouseup' },
+               onfocus:  { activator: 'onfocus',      deactivator: 'onblur' }
+       };
+       
+       // CSSHoverElement constructor, called via CSSHover.patch
+       function CSSHoverElement(node, type, className) {
+
+               // the CSSHoverElement patches individual nodes by manually applying the events that should 
+               // have fired by the css pseudoclasses, eg mouseenter and mouseleave for :hover. 
+
+               this.node = node;
+               this.type = type;
+               var replacer = new RegExp('(^|\\s)'+className+'(\\s|$)', 'g');
+
+               // store event handlers for removal onunload
+               this.activator =   function(){ node.className += ' ' + className; };
+               this.deactivator = function(){ node.className = node.className.replace(replacer, ' '); };
+               
+               // add the events
+               node.attachEvent(CSSEvents[type].activator, this.activator);
+               node.attachEvent(CSSEvents[type].deactivator, this.deactivator);
+       }
+       
+       CSSHoverElement.prototype = {
+               // onbeforeunload, called via CSSHover.unload
+               unload:function() {
+
+                       // remove events 
+                       this.node.detachEvent(CSSEvents[this.type].activator, this.activator);
+                       this.node.detachEvent(CSSEvents[this.type].deactivator, this.deactivator);
+
+                       // and set properties to null 
+                       this.activator = null;
+                       this.deactivator = null;
+                       this.node = null;
+                       this.type = null;
+               }
+       };
+
+       /**
+        *      Public hook
+        *      --------------------------
+        */
+       
+       return function(node, type, className) {
+               if(node) {
+                       // called via the css expression; patches individual nodes
+                       return CSSHover.patch(node, type, className);
+               } else {
+                       // called ondomcontentready via the public:attach node
+                       CSSHover.init();
+               }
+       };
+
+})();
+
+// ]]>
+</script>
\ No newline at end of file