Use camel case for variable names in Article.php
[lhc/web/wiklou.git] / skins / Vector / csshover.htc
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>