[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
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>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |