[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/skins/Vector/ -> csshover.htc (source)

   1  <public:attach event="ondocumentready" onevent="CSSHover()" />
   2  <script>
   3  /**
   4   *    Whatever:hover - V3.11
   5   *    ------------------------------------------------------------
   6   *    Author  - Peter Nederlof, http://www.xs4all.nl/~peterned
   7   *    License - http://creativecommons.org/licenses/LGPL/2.1
   8   *
   9   *    Special thanks to Sergiu Dumitriu, http://purl.org/net/sergiu,
  10   *    for fixing the expression loop.
  11   *
  12   *    Whatever:hover is free software; you can redistribute it and/or
  13   *    modify it under the terms of the GNU Lesser General Public
  14   *    License as published by the Free Software Foundation; either
  15   *    version 2.1 of the License, or (at your option) any later version.
  16   *
  17   *    Whatever:hover is distributed in the hope that it will be useful,
  18   *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  19   *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  20   *    Lesser General Public License for more details.
  21   *
  22   *    howto: body { behavior:url("csshover3.htc"); }
  23   *    ------------------------------------------------------------
  24   */
  25  
  26  window.CSSHover = (function(){
  27  
  28      // regular expressions, used and explained later on.
  29      var REG_INTERACTIVE = /(^|\s)((([^a]([^ ]+)?)|(a([^#.][^ ]+)+)):(hover|active|focus))/i;
  30      var REG_AFFECTED = /(.*?)\:(hover|active|focus)/i;
  31      var REG_PSEUDO = /[^:]+:([a-z\-]+).*/i;
  32      var REG_SELECT = /(\.([a-z0-9_\-]+):[a-z]+)|(:[a-z]+)/gi;
  33      var REG_CLASS = /\.([a-z0-9_\-]*on(hover|active|focus))/i;
  34      var REG_MSIE = /msie (5|6|7)/i;
  35      var REG_COMPAT = /backcompat/i;
  36  
  37      // property mapping, real css properties must be used in order to clear expressions later on...
  38      // Uses obscure css properties that no-one is likely to use. The properties are borrowed to
  39      // set an expression, and are then restored to the most likely correct value.
  40      var Properties = {
  41          index: 0,
  42          list: ['text-kashida', 'text-kashida-space', 'text-justify'],
  43          get: function() {
  44              return this.list[(this.index++)%this.list.length];
  45          }
  46      };
  47  
  48      // camelize is used to convert css properties from (eg) text-kashida to textKashida
  49      var camelize = function(str) {
  50          return str.replace(/-(.)/mg, function(result, match){
  51              return match.toUpperCase();
  52          });
  53      };
  54      
  55      /**
  56       *    Local CSSHover object
  57       *    --------------------------
  58       */
  59      
  60      var CSSHover = {
  61          
  62          // array of CSSHoverElements, used to unload created events
  63          elements: [], 
  64          
  65          // buffer used for checking on duplicate expressions
  66          callbacks: {}, 
  67          
  68          // init, called once ondomcontentready via the exposed window.CSSHover function
  69          init:function() {
  70              // don't run in IE8 standards; expressions don't work in standards mode anyway, 
  71              // and the stuff we're trying to fix should already work properly
  72              if(!REG_MSIE.test(navigator.userAgent) && !REG_COMPAT.test(window.document.compatMode)) {
  73                  return;
  74              }
  75  
  76              // start parsing the existing stylesheets
  77              var sheets = window.document.styleSheets, l = sheets.length;
  78              for(var i=0; i<l; i++) {
  79                  this.parseStylesheet(sheets[i]);
  80              }
  81          },
  82  
  83          // called from init, parses individual stylesheets
  84          parseStylesheet:function(sheet) {
  85              // check sheet imports and parse those recursively
  86              if(sheet.imports) {
  87                  try {
  88                      var imports = sheet.imports;
  89                      var l = imports.length;
  90                      for(var i=0; i<l; i++) {
  91                          this.parseStylesheet(sheet.imports[i]);
  92                      }
  93                  } catch(securityException){
  94                      // trycatch for various possible errors
  95                  }
  96              }
  97              
  98              // interate the sheet's rules and send them to the parser
  99              try {
 100                  var rules = sheet.rules;
 101                  var r = rules.length;
 102                  for(var j=0; j<r; j++) {
 103                      this.parseCSSRule(rules[j], sheet);
 104                  }
 105              } catch(someException){
 106                  // trycatch for various errors, most likely accessing the sheet's rules.
 107              }
 108          },
 109  
 110          // magic starts here ...
 111          parseCSSRule:function(rule, sheet) {
 112              
 113              // The sheet is used to insert new rules into, this must be the same sheet the rule 
 114              // came from, to ensure that relative paths keep pointing to the right location.
 115  
 116              // only parse a rule if it contains an interactive pseudo.
 117              var select = rule.selectorText;
 118              if(REG_INTERACTIVE.test(select)) {
 119                  var style = rule.style.cssText;
 120                      
 121                  // affected elements are found by truncating the selector after the interactive pseudo,
 122                  // eg: "div li:hover" >>  "div li"
 123                  var affected = REG_AFFECTED.exec(select)[1];
 124                      
 125                  // that pseudo is needed for a classname, and defines the type of interaction (focus, hover, active)
 126                  // eg: "li:hover" >> "onhover"
 127                  var pseudo = select.replace(REG_PSEUDO, 'on$1');
 128                      
 129                  // the new selector is going to use that classname in a new css rule,
 130                  // since IE6 doesn't support multiple classnames, this is merged into one classname
 131                  // eg: "li:hover" >> "li.onhover",  "li.folder:hover" >> "li.folderonhover"
 132                  var newSelect = select.replace(REG_SELECT, '.$2' + pseudo);
 133                      
 134                  // the classname is needed for the events that are going to be set on affected nodes
 135                  // eg: "li.folder:hover" >> "folderonhover"
 136                  var className = REG_CLASS.exec(newSelect)[1];
 137  
 138                  // no need to set the same callback more than once when the same selector uses the same classname
 139                  var hash = affected + className;
 140                  if(!this.callbacks[hash]) {
 141                      
 142                      // affected elements are given an expression under a borrowed css property, because fake properties
 143                      // can't have their expressions cleared. Different properties are used per pseudo, to avoid
 144                      // expressions from overwriting eachother. The expression does a callback to CSSHover.patch, 
 145                      // rerouted via the exposed window.CSSHover function.
 146                      var property = Properties.get();
 147                      var atRuntime = camelize(property);
 148  
 149                      // because the expression is added to the stylesheet, and styles are always applied to html that is
 150                      // dynamically added to the dom, the expression will also trigger for those new elements (provided
 151                      // they are selected by the affected selector). 
 152                      sheet.addRule(affected, property + ':expression(CSSHover(this, "'+pseudo+'", "'+className+'", "'+atRuntime+'"))');
 153                      
 154                      // hash it, so an identical selector/class combo does not duplicate the expression
 155                      this.callbacks[hash] = true;
 156                  }
 157                  
 158                  // duplicate expressions need not be set, but the style could differ
 159                  sheet.addRule(newSelect, style);
 160              }
 161          },
 162  
 163          // called via the expression, patches individual nodes
 164          patch:function(node, type, className, property) {
 165  
 166              // restores the borrowed css property to the value of its immediate parent, clearing
 167              // the expression so that it's not repeatedly called. 
 168              try {
 169                  var value = node.parentNode.currentStyle[property];
 170                  node.style[property] = value;    
 171              } catch(e) {
 172                  // the above reset should never fail, but just in case, clear the runtimeStyle if it does.
 173                  // this will also stop the expression.
 174                  node.runtimeStyle[property] = '';
 175              }            
 176          
 177              // just to make sure, also keep track of patched classnames locally on the node
 178              if(!node.csshover) {
 179                  node.csshover = [];
 180              }
 181  
 182              // and check for it to prevent duplicate events with the same classname from being set
 183              if(!node.csshover[className]) {
 184                  node.csshover[className] = true;
 185  
 186                  // create an instance for the given type and class
 187                  var element = new CSSHoverElement(node, type, className);
 188                  
 189                  // and store that instance for unloading later on
 190                  this.elements.push(element);
 191              }
 192  
 193              // returns a dummy value to the expression
 194              return type;
 195          },
 196  
 197          // unload stuff onbeforeunload
 198          unload:function() {
 199              try {
 200                  
 201                  // remove events
 202                  var l = this.elements.length;
 203                  for(var i=0; i<l; i++) {
 204                      this.elements[i].unload();
 205                  }
 206  
 207                  // and set properties to null 
 208                  this.elements = [];
 209                  this.callbacks = {};
 210  
 211              } catch (e) {
 212              }
 213          }
 214      };
 215  
 216      /**
 217       *    CSSHoverElement
 218       *    --------------------------
 219       */
 220  
 221      // the event types associated with the interactive pseudos
 222      var CSSEvents = {
 223          onhover:  { activator: 'onmouseenter', deactivator: 'onmouseleave' },
 224          onactive: { activator: 'onmousedown',  deactivator: 'onmouseup' },
 225          onfocus:  { activator: 'onfocus',      deactivator: 'onblur' }
 226      };
 227      
 228      // CSSHoverElement constructor, called via CSSHover.patch
 229  	function CSSHoverElement(node, type, className) {
 230  
 231          // the CSSHoverElement patches individual nodes by manually applying the events that should 
 232          // have fired by the css pseudoclasses, eg mouseenter and mouseleave for :hover. 
 233  
 234          this.node = node;
 235          this.type = type;
 236          var replacer = new RegExp('(^|\\s)'+className+'(\\s|$)', 'g');
 237  
 238          // store event handlers for removal onunload
 239          this.activator =   function(){ node.className += ' ' + className; };
 240          this.deactivator = function(){ node.className = node.className.replace(replacer, ' '); };
 241          
 242          // add the events
 243          node.attachEvent(CSSEvents[type].activator, this.activator);
 244          node.attachEvent(CSSEvents[type].deactivator, this.deactivator);
 245      }
 246      
 247      CSSHoverElement.prototype = {
 248          // onbeforeunload, called via CSSHover.unload
 249          unload:function() {
 250  
 251              // remove events 
 252              this.node.detachEvent(CSSEvents[this.type].activator, this.activator);
 253              this.node.detachEvent(CSSEvents[this.type].deactivator, this.deactivator);
 254  
 255              // and set properties to null 
 256              this.activator = null;
 257              this.deactivator = null;
 258              this.node = null;
 259              this.type = null;
 260          }
 261      };
 262  
 263      // add the unload to the onbeforeunload event
 264      window.attachEvent('onbeforeunload', function(){
 265          CSSHover.unload();
 266      });
 267  
 268      /**
 269       *    Public hook
 270       *    --------------------------
 271       */
 272      
 273      return function(node, type, className, property) {
 274          if(node) {
 275              // called via the css expression; patches individual nodes
 276              return CSSHover.patch(node, type, className, property);
 277          } else {
 278              // called ondomcontentready via the public:attach node
 279              CSSHover.init();
 280          }
 281      };
 282  
 283  })();
 284  </script>


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1