root / ui / static / snf / js / lib / selectivizr.js @ fdf79142
History | View | Annotate | Download (18.6 kB)
1 |
/*
|
---|---|
2 |
selectivizr v1.0.2 - (c) Keith Clark, freely distributable under the terms
|
3 |
of the MIT license.
|
4 |
|
5 |
selectivizr.com
|
6 |
*/
|
7 |
/*
|
8 |
|
9 |
Notes about this source
|
10 |
-----------------------
|
11 |
|
12 |
* The #DEBUG_START and #DEBUG_END comments are used to mark blocks of code
|
13 |
that will be removed prior to building a final release version (using a
|
14 |
pre-compression script)
|
15 |
|
16 |
|
17 |
References:
|
18 |
-----------
|
19 |
|
20 |
* CSS Syntax : http://www.w3.org/TR/2003/WD-css3-syntax-20030813/#style
|
21 |
* Selectors : http://www.w3.org/TR/css3-selectors/#selectors
|
22 |
* IE Compatability : http://msdn.microsoft.com/en-us/library/cc351024(VS.85).aspx
|
23 |
* W3C Selector Tests : http://www.w3.org/Style/CSS/Test/CSS3/Selectors/current/html/tests/
|
24 |
|
25 |
*/
|
26 |
|
27 |
(function(win) {
|
28 |
|
29 |
// If browser isn't IE, then stop execution! This handles the script
|
30 |
// being loaded by non IE browsers because the developer didn't use
|
31 |
// conditional comments.
|
32 |
if (/*@cc_on!@*/true) return; |
33 |
|
34 |
// =========================== Init Objects ============================
|
35 |
|
36 |
var doc = document;
|
37 |
var root = doc.documentElement;
|
38 |
var xhr = getXHRObject();
|
39 |
var ieVersion = /MSIE (\d+)/.exec(navigator.userAgent)[1]; |
40 |
|
41 |
// If were not in standards mode, IE is too old / new or we can't create
|
42 |
// an XMLHttpRequest object then we should get out now.
|
43 |
if (doc.compatMode != 'CSS1Compat' || ieVersion<6 || ieVersion>8 || !xhr) { |
44 |
return;
|
45 |
} |
46 |
|
47 |
|
48 |
// ========================= Common Objects ============================
|
49 |
|
50 |
// Compatiable selector engines in order of CSS3 support. Note: '*' is
|
51 |
// a placholder for the object key name. (basically, crude compression)
|
52 |
var selectorEngines = {
|
53 |
"NW" : "*.Dom.select", |
54 |
"MooTools" : "$$", |
55 |
"DOMAssistant" : "*.$", |
56 |
"Prototype" : "$$", |
57 |
"YAHOO" : "*.util.Selector.query", |
58 |
"Sizzle" : "*", |
59 |
"jQuery" : "*", |
60 |
"dojo" : "*.query" |
61 |
}; |
62 |
|
63 |
var selectorMethod;
|
64 |
var enabledWatchers = []; // array of :enabled/:disabled elements to poll |
65 |
var ie6PatchID = 0; // used to solve ie6's multiple class bug |
66 |
var patchIE6MultipleClasses = true; // if true adds class bloat to ie6 |
67 |
var namespace = "slvzr"; |
68 |
|
69 |
// Stylesheet parsing regexp's
|
70 |
var RE_COMMENT = /(\/\*[^*]*\*+([^\/][^*]*\*+)*\/)\s*/g; |
71 |
var RE_IMPORT = /@import\s*(?:(?:(?:url\(\s*(['"]?)(.*)\1)\s*\))|(?:(['"])(.*)\3))[^;]*;/g; |
72 |
var RE_ASSET_URL = /\burl\(\s*(["']?)(?!data:)([^"')]+)\1\s*\)/g; |
73 |
var RE_PSEUDO_STRUCTURAL = /^:(empty|(first|last|only|nth(-last)?)-(child|of-type))$/; |
74 |
var RE_PSEUDO_ELEMENTS = /:(:first-(?:line|letter))/g; |
75 |
var RE_SELECTOR_GROUP = /(^|})\s*([^\{]*?[\[:][^{]+)/g; |
76 |
var RE_SELECTOR_PARSE = /([ +~>])|(:[a-z-]+(?:\(.*?\)+)?)|(\[.*?\])/g; |
77 |
var RE_LIBRARY_INCOMPATIBLE_PSEUDOS = /(:not\()?:(hover|enabled|disabled|focus|checked|target|active|visited|first-line|first-letter)\)?/g; |
78 |
var RE_PATCH_CLASS_NAME_REPLACE = /[^\w-]/g; |
79 |
|
80 |
// HTML UI element regexp's
|
81 |
var RE_INPUT_ELEMENTS = /^(INPUT|SELECT|TEXTAREA|BUTTON)$/; |
82 |
var RE_INPUT_CHECKABLE_TYPES = /^(checkbox|radio)$/; |
83 |
|
84 |
// Broken attribute selector implementations (IE7/8 native [^=""], [$=""] and [*=""])
|
85 |
var BROKEN_ATTR_IMPLEMENTATIONS = ieVersion>6 ? /[\$\^*]=(['"])\1/ : null; |
86 |
|
87 |
// Whitespace normalization regexp's
|
88 |
var RE_TIDY_TRAILING_WHITESPACE = /([(\[+~])\s+/g; |
89 |
var RE_TIDY_LEADING_WHITESPACE = /\s+([)\]+~])/g; |
90 |
var RE_TIDY_CONSECUTIVE_WHITESPACE = /\s+/g; |
91 |
var RE_TIDY_TRIM_WHITESPACE = /^\s*((?:[\S\s]*\S)?)\s*$/; |
92 |
|
93 |
// String constants
|
94 |
var EMPTY_STRING = ""; |
95 |
var SPACE_STRING = " "; |
96 |
var PLACEHOLDER_STRING = "$1"; |
97 |
|
98 |
// =========================== Patching ================================
|
99 |
|
100 |
// --[ patchStyleSheet() ]----------------------------------------------
|
101 |
// Scans the passed cssText for selectors that require emulation and
|
102 |
// creates one or more patches for each matched selector.
|
103 |
function patchStyleSheet( cssText ) { |
104 |
return cssText.replace(RE_PSEUDO_ELEMENTS, PLACEHOLDER_STRING).
|
105 |
replace(RE_SELECTOR_GROUP, function(m, prefix, selectorText) {
|
106 |
var selectorGroups = selectorText.split(","); |
107 |
for (var c = 0, cs = selectorGroups.length; c < cs; c++) { |
108 |
var selector = normalizeSelectorWhitespace(selectorGroups[c]) + SPACE_STRING;
|
109 |
var patches = [];
|
110 |
selectorGroups[c] = selector.replace(RE_SELECTOR_PARSE, |
111 |
function(match, combinator, pseudo, attribute, index) {
|
112 |
if (combinator) {
|
113 |
if (patches.length>0) { |
114 |
applyPatches( selector.substring(0, index), patches );
|
115 |
patches = []; |
116 |
} |
117 |
return combinator;
|
118 |
} |
119 |
else {
|
120 |
var patch = (pseudo) ? patchPseudoClass( pseudo ) : patchAttribute( attribute );
|
121 |
if (patch) {
|
122 |
patches.push(patch); |
123 |
return "." + patch.className; |
124 |
} |
125 |
return match;
|
126 |
} |
127 |
} |
128 |
); |
129 |
} |
130 |
return prefix + selectorGroups.join(","); |
131 |
}); |
132 |
}; |
133 |
|
134 |
// --[ patchAttribute() ]-----------------------------------------------
|
135 |
// returns a patch for an attribute selector.
|
136 |
function patchAttribute( attr ) { |
137 |
return (!BROKEN_ATTR_IMPLEMENTATIONS || BROKEN_ATTR_IMPLEMENTATIONS.test(attr)) ?
|
138 |
{ className: createClassName(attr), applyClass: true } : null; |
139 |
}; |
140 |
|
141 |
// --[ patchPseudoClass() ]---------------------------------------------
|
142 |
// returns a patch for a pseudo-class
|
143 |
function patchPseudoClass( pseudo ) { |
144 |
|
145 |
var applyClass = true; |
146 |
var className = createClassName(pseudo.slice(1)); |
147 |
var isNegated = pseudo.substring(0, 5) == ":not("; |
148 |
var activateEventName;
|
149 |
var deactivateEventName;
|
150 |
|
151 |
// if negated, remove :not()
|
152 |
if (isNegated) {
|
153 |
pseudo = pseudo.slice(5, -1); |
154 |
} |
155 |
|
156 |
// bracket contents are irrelevant - remove them
|
157 |
var bracketIndex = pseudo.indexOf("(") |
158 |
if (bracketIndex > -1) { |
159 |
pseudo = pseudo.substring(0, bracketIndex);
|
160 |
} |
161 |
|
162 |
// check we're still dealing with a pseudo-class
|
163 |
if (pseudo.charAt(0) == ":") { |
164 |
switch (pseudo.slice(1)) { |
165 |
|
166 |
case "root": |
167 |
applyClass = function(e) { |
168 |
return isNegated ? e != root : e == root;
|
169 |
} |
170 |
break;
|
171 |
|
172 |
case "target": |
173 |
// :target is only supported in IE8
|
174 |
if (ieVersion == 8) { |
175 |
applyClass = function(e) { |
176 |
var handler = function() { |
177 |
var hash = location.hash;
|
178 |
var hashID = hash.slice(1); |
179 |
return isNegated ? (hash == EMPTY_STRING || e.id != hashID) : (hash != EMPTY_STRING && e.id == hashID);
|
180 |
}; |
181 |
addEvent( win, "hashchange", function() { |
182 |
toggleElementClass(e, className, handler()); |
183 |
}) |
184 |
return handler();
|
185 |
} |
186 |
break;
|
187 |
} |
188 |
return false; |
189 |
|
190 |
case "checked": |
191 |
applyClass = function(e) { |
192 |
if (RE_INPUT_CHECKABLE_TYPES.test(e.type)) {
|
193 |
addEvent( e, "propertychange", function() { |
194 |
if (event.propertyName == "checked") { |
195 |
toggleElementClass( e, className, e.checked !== isNegated ); |
196 |
} |
197 |
}) |
198 |
} |
199 |
return e.checked !== isNegated;
|
200 |
} |
201 |
break;
|
202 |
|
203 |
case "disabled": |
204 |
isNegated = !isNegated; |
205 |
|
206 |
case "enabled": |
207 |
applyClass = function(e) { |
208 |
if (RE_INPUT_ELEMENTS.test(e.tagName)) {
|
209 |
addEvent( e, "propertychange", function() { |
210 |
if (event.propertyName == "$disabled") { |
211 |
toggleElementClass( e, className, e.$disabled === isNegated );
|
212 |
} |
213 |
}); |
214 |
enabledWatchers.push(e); |
215 |
e.$disabled = e.disabled;
|
216 |
return e.disabled === isNegated;
|
217 |
} |
218 |
return pseudo == ":enabled" ? isNegated : !isNegated; |
219 |
} |
220 |
break;
|
221 |
|
222 |
case "focus": |
223 |
activateEventName = "focus";
|
224 |
deactivateEventName = "blur";
|
225 |
|
226 |
case "hover": |
227 |
if (!activateEventName) {
|
228 |
activateEventName = "mouseenter";
|
229 |
deactivateEventName = "mouseleave";
|
230 |
} |
231 |
applyClass = function(e) { |
232 |
addEvent( e, isNegated ? deactivateEventName : activateEventName, function() {
|
233 |
toggleElementClass( e, className, true );
|
234 |
}) |
235 |
addEvent( e, isNegated ? activateEventName : deactivateEventName, function() {
|
236 |
toggleElementClass( e, className, false );
|
237 |
}) |
238 |
return isNegated;
|
239 |
} |
240 |
break;
|
241 |
|
242 |
// everything else
|
243 |
default:
|
244 |
// If we don't support this pseudo-class don't create
|
245 |
// a patch for it
|
246 |
if (!RE_PSEUDO_STRUCTURAL.test(pseudo)) {
|
247 |
return false; |
248 |
} |
249 |
break;
|
250 |
} |
251 |
} |
252 |
return { className: className, applyClass: applyClass }; |
253 |
}; |
254 |
|
255 |
// --[ applyPatches() ]-------------------------------------------------
|
256 |
// uses the passed selector text to find DOM nodes and patch them
|
257 |
function applyPatches(selectorText, patches) { |
258 |
var elms;
|
259 |
|
260 |
// Although some selector libraries can find :checked :enabled etc.
|
261 |
// we need to find all elements that could have that state because
|
262 |
// it can be changed by the user.
|
263 |
var domSelectorText = selectorText.replace(RE_LIBRARY_INCOMPATIBLE_PSEUDOS, EMPTY_STRING);
|
264 |
|
265 |
// If the dom selector equates to an empty string or ends with
|
266 |
// whitespace then we need to append a universal selector (*) to it.
|
267 |
if (domSelectorText == EMPTY_STRING || domSelectorText.charAt(domSelectorText.length - 1) == SPACE_STRING) { |
268 |
domSelectorText += "*";
|
269 |
} |
270 |
|
271 |
// Ensure we catch errors from the selector library
|
272 |
try {
|
273 |
elms = selectorMethod( domSelectorText ); |
274 |
} catch (ex) {
|
275 |
// #DEBUG_START
|
276 |
log( "Selector '" + selectorText + "' threw exception '" + ex + "'" ); |
277 |
// #DEBUG_END
|
278 |
} |
279 |
|
280 |
|
281 |
if (elms) {
|
282 |
for (var d = 0, dl = elms.length; d < dl; d++) { |
283 |
var elm = elms[d];
|
284 |
var cssClasses = elm.className;
|
285 |
for (var f = 0, fl = patches.length; f < fl; f++) { |
286 |
var patch = patches[f];
|
287 |
|
288 |
if (!hasPatch(elm, patch)) {
|
289 |
if (patch.applyClass && (patch.applyClass === true || patch.applyClass(elm) === true)) { |
290 |
cssClasses = toggleClass(cssClasses, patch.className, true );
|
291 |
} |
292 |
} |
293 |
} |
294 |
elm.className = cssClasses; |
295 |
} |
296 |
} |
297 |
}; |
298 |
|
299 |
// --[ hasPatch() ]-----------------------------------------------------
|
300 |
// checks for the exsistence of a patch on an element
|
301 |
function hasPatch( elm, patch ) { |
302 |
return new RegExp("(^|\\s)" + patch.className + "(\\s|$)").test(elm.className); |
303 |
}; |
304 |
|
305 |
|
306 |
// =========================== Utility =================================
|
307 |
|
308 |
function createClassName( className ) { |
309 |
return namespace + "-" + ((ieVersion == 6 && patchIE6MultipleClasses) ? |
310 |
ie6PatchID++ |
311 |
: |
312 |
className.replace(RE_PATCH_CLASS_NAME_REPLACE, function(a) { return a.charCodeAt(0) })); |
313 |
}; |
314 |
|
315 |
// --[ log() ]----------------------------------------------------------
|
316 |
// #DEBUG_START
|
317 |
function log( message ) { |
318 |
if (win.console) {
|
319 |
win.console.log(message); |
320 |
} |
321 |
}; |
322 |
// #DEBUG_END
|
323 |
|
324 |
// --[ trim() ]---------------------------------------------------------
|
325 |
// removes leading, trailing whitespace from a string
|
326 |
function trim( text ) { |
327 |
return text.replace(RE_TIDY_TRIM_WHITESPACE, PLACEHOLDER_STRING);
|
328 |
}; |
329 |
|
330 |
// --[ normalizeWhitespace() ]------------------------------------------
|
331 |
// removes leading, trailing and consecutive whitespace from a string
|
332 |
function normalizeWhitespace( text ) { |
333 |
return trim(text).replace(RE_TIDY_CONSECUTIVE_WHITESPACE, SPACE_STRING);
|
334 |
}; |
335 |
|
336 |
// --[ normalizeSelectorWhitespace() ]----------------------------------
|
337 |
// tidies whitespace around selector brackets and combinators
|
338 |
function normalizeSelectorWhitespace( selectorText ) { |
339 |
return normalizeWhitespace(selectorText.
|
340 |
replace(RE_TIDY_TRAILING_WHITESPACE, PLACEHOLDER_STRING). |
341 |
replace(RE_TIDY_LEADING_WHITESPACE, PLACEHOLDER_STRING) |
342 |
); |
343 |
}; |
344 |
|
345 |
// --[ toggleElementClass() ]-------------------------------------------
|
346 |
// toggles a single className on an element
|
347 |
function toggleElementClass( elm, className, on ) { |
348 |
var oldClassName = elm.className;
|
349 |
var newClassName = toggleClass(oldClassName, className, on);
|
350 |
if (newClassName != oldClassName) {
|
351 |
elm.className = newClassName; |
352 |
elm.parentNode.className += EMPTY_STRING; |
353 |
} |
354 |
}; |
355 |
|
356 |
// --[ toggleClass() ]--------------------------------------------------
|
357 |
// adds / removes a className from a string of classNames. Used to
|
358 |
// manage multiple class changes without forcing a DOM redraw
|
359 |
function toggleClass( classList, className, on ) { |
360 |
var re = RegExp("(^|\\s)" + className + "(\\s|$)"); |
361 |
var classExists = re.test(classList);
|
362 |
if (on) {
|
363 |
return classExists ? classList : classList + SPACE_STRING + className;
|
364 |
} else {
|
365 |
return classExists ? trim(classList.replace(re, PLACEHOLDER_STRING)) : classList;
|
366 |
} |
367 |
}; |
368 |
|
369 |
// --[ addEvent() ]-----------------------------------------------------
|
370 |
function addEvent(elm, eventName, eventHandler) { |
371 |
elm.attachEvent("on" + eventName, eventHandler);
|
372 |
}; |
373 |
|
374 |
// --[ getXHRObject() ]-------------------------------------------------
|
375 |
function getXHRObject() |
376 |
{ |
377 |
if (win.XMLHttpRequest) {
|
378 |
return new XMLHttpRequest; |
379 |
} |
380 |
try {
|
381 |
return new ActiveXObject('Microsoft.XMLHTTP'); |
382 |
} catch(e) {
|
383 |
return null; |
384 |
} |
385 |
}; |
386 |
|
387 |
// --[ loadStyleSheet() ]-----------------------------------------------
|
388 |
function loadStyleSheet( url ) { |
389 |
xhr.open("GET", url, false); |
390 |
xhr.send(); |
391 |
return (xhr.status==200) ? xhr.responseText : EMPTY_STRING; |
392 |
}; |
393 |
|
394 |
// --[ resolveUrl() ]---------------------------------------------------
|
395 |
// Converts a URL fragment to a fully qualified URL using the specified
|
396 |
// context URL. Returns null if same-origin policy is broken
|
397 |
function resolveUrl( url, contextUrl ) { |
398 |
|
399 |
function getProtocolAndHost( url ) { |
400 |
return url.substring(0, url.indexOf("/", 8)); |
401 |
}; |
402 |
|
403 |
// absolute path
|
404 |
if (/^https?:\/\//i.test(url)) { |
405 |
return getProtocolAndHost(contextUrl) == getProtocolAndHost(url) ? url : null; |
406 |
} |
407 |
|
408 |
// root-relative path
|
409 |
if (url.charAt(0)=="/") { |
410 |
return getProtocolAndHost(contextUrl) + url;
|
411 |
} |
412 |
|
413 |
// relative path
|
414 |
var contextUrlPath = contextUrl.split(/[?#]/)[0]; // ignore query string in the contextUrl |
415 |
if (url.charAt(0) != "?" && contextUrlPath.charAt(contextUrlPath.length - 1) != "/") { |
416 |
contextUrlPath = contextUrlPath.substring(0, contextUrlPath.lastIndexOf("/") + 1); |
417 |
} |
418 |
|
419 |
return contextUrlPath + url;
|
420 |
}; |
421 |
|
422 |
// --[ parseStyleSheet() ]----------------------------------------------
|
423 |
// Downloads the stylesheet specified by the URL, removes it's comments
|
424 |
// and recursivly replaces @import rules with their contents, ultimately
|
425 |
// returning the full cssText.
|
426 |
function parseStyleSheet( url ) { |
427 |
if (url) {
|
428 |
return loadStyleSheet(url).replace(RE_COMMENT, EMPTY_STRING).
|
429 |
replace(RE_IMPORT, function( match, quoteChar, importUrl, quoteChar2, importUrl2 ) {
|
430 |
return parseStyleSheet(resolveUrl(importUrl || importUrl2, url));
|
431 |
}). |
432 |
replace(RE_ASSET_URL, function( match, quoteChar, assetUrl ) {
|
433 |
quoteChar = quoteChar || EMPTY_STRING; |
434 |
return " url(" + quoteChar + resolveUrl(assetUrl, url) + quoteChar + ") "; |
435 |
}); |
436 |
} |
437 |
return EMPTY_STRING;
|
438 |
}; |
439 |
|
440 |
// --[ init() ]---------------------------------------------------------
|
441 |
function init() { |
442 |
// honour the <base> tag
|
443 |
var url, stylesheet;
|
444 |
var baseTags = doc.getElementsByTagName("BASE"); |
445 |
var baseUrl = (baseTags.length > 0) ? baseTags[0].href : doc.location.href; |
446 |
|
447 |
/* Note: This code prevents IE from freezing / crashing when using
|
448 |
@font-face .eot files but it modifies the <head> tag and could
|
449 |
trigger the IE stylesheet limit. It will also cause FOUC issues.
|
450 |
If you choose to use it, make sure you comment out the for loop
|
451 |
directly below this comment.
|
452 |
|
453 |
var head = doc.getElementsByTagName("head")[0];
|
454 |
for (var c=doc.styleSheets.length-1; c>=0; c--) {
|
455 |
stylesheet = doc.styleSheets[c]
|
456 |
head.appendChild(doc.createElement("style"))
|
457 |
var patchedStylesheet = doc.styleSheets[doc.styleSheets.length-1];
|
458 |
|
459 |
if (stylesheet.href != EMPTY_STRING) {
|
460 |
url = resolveUrl(stylesheet.href, baseUrl)
|
461 |
if (url) {
|
462 |
patchedStylesheet.cssText = patchStyleSheet( parseStyleSheet( url ) )
|
463 |
stylesheet.disabled = true
|
464 |
setTimeout( function () {
|
465 |
stylesheet.owningElement.parentNode.removeChild(stylesheet.owningElement)
|
466 |
})
|
467 |
}
|
468 |
}
|
469 |
}
|
470 |
*/
|
471 |
|
472 |
for (var c = 0; c < doc.styleSheets.length; c++) { |
473 |
stylesheet = doc.styleSheets[c] |
474 |
if (stylesheet.href != EMPTY_STRING) {
|
475 |
url = resolveUrl(stylesheet.href, baseUrl); |
476 |
if (url) {
|
477 |
stylesheet.cssText = patchStyleSheet( parseStyleSheet( url ) ); |
478 |
} |
479 |
} |
480 |
} |
481 |
|
482 |
// :enabled & :disabled polling script (since we can't hook
|
483 |
// onpropertychange event when an element is disabled)
|
484 |
if (enabledWatchers.length > 0) { |
485 |
setInterval( function() {
|
486 |
for (var c = 0, cl = enabledWatchers.length; c < cl; c++) { |
487 |
var e = enabledWatchers[c];
|
488 |
if (e.disabled !== e.$disabled) { |
489 |
if (e.disabled) {
|
490 |
e.disabled = false;
|
491 |
e.$disabled = true; |
492 |
e.disabled = true;
|
493 |
} |
494 |
else {
|
495 |
e.$disabled = e.disabled;
|
496 |
} |
497 |
} |
498 |
} |
499 |
},250)
|
500 |
} |
501 |
}; |
502 |
|
503 |
// Bind selectivizr to the ContentLoaded event.
|
504 |
ContentLoaded(win, function() {
|
505 |
// Determine the "best fit" selector engine
|
506 |
for (var engine in selectorEngines) { |
507 |
var members, member, context = win;
|
508 |
if (win[engine]) {
|
509 |
members = selectorEngines[engine].replace("*", engine).split("."); |
510 |
while ((member = members.shift()) && (context = context[member])) {}
|
511 |
if (typeof context == "function") { |
512 |
selectorMethod = context; |
513 |
init(); |
514 |
return;
|
515 |
} |
516 |
} |
517 |
} |
518 |
}); |
519 |
|
520 |
|
521 |
/*!
|
522 |
* ContentLoaded.js by Diego Perini, modified for IE<9 only (to save space)
|
523 |
*
|
524 |
* Author: Diego Perini (diego.perini at gmail.com)
|
525 |
* Summary: cross-browser wrapper for DOMContentLoaded
|
526 |
* Updated: 20101020
|
527 |
* License: MIT
|
528 |
* Version: 1.2
|
529 |
*
|
530 |
* URL:
|
531 |
* http://javascript.nwbox.com/ContentLoaded/
|
532 |
* http://javascript.nwbox.com/ContentLoaded/MIT-LICENSE
|
533 |
*
|
534 |
*/
|
535 |
|
536 |
// @w window reference
|
537 |
// @f function reference
|
538 |
function ContentLoaded(win, fn) { |
539 |
|
540 |
var done = false, top = true, |
541 |
init = function(e) { |
542 |
if (e.type == "readystatechange" && doc.readyState != "complete") return; |
543 |
(e.type == "load" ? win : doc).detachEvent("on" + e.type, init, false); |
544 |
if (!done && (done = true)) fn.call(win, e.type || e); |
545 |
}, |
546 |
poll = function() { |
547 |
try { root.doScroll("left"); } catch(e) { setTimeout(poll, 50); return; } |
548 |
init('poll');
|
549 |
}; |
550 |
|
551 |
if (doc.readyState == "complete") fn.call(win, EMPTY_STRING); |
552 |
else {
|
553 |
if (doc.createEventObject && root.doScroll) {
|
554 |
try { top = !win.frameElement; } catch(e) { } |
555 |
if (top) poll();
|
556 |
} |
557 |
addEvent(doc,"readystatechange", init);
|
558 |
addEvent(win,"load", init);
|
559 |
} |
560 |
}; |
561 |
})(this);
|