eCSStender.js 70 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504
  1. /*------------------------------------------------------------------------------
  2. Function: eCSStender()
  3. Author: Aaron Gustafson (aaron at easy-designs dot net)
  4. Creation Date: 2006-12-03
  5. Version: 1.2.6.4
  6. Homepage: http://eCSStender.org
  7. License: MIT License (see homepage)
  8. ------------------------------------------------------------------------------*/
  9. (function(){
  10. var
  11. // common references
  12. UNDEFINED,
  13. TRUE = true,
  14. FALSE = false,
  15. NULL = null,
  16. STRING = 'string',
  17. NUMBER = 'number',
  18. OBJECT = 'object',
  19. ARRAY = Array,
  20. FUNCTION = Function,
  21. REGEXP = RegExp,
  22. DOCUMENT = document,
  23. WINDOW = window,
  24. LOCATION = WINDOW.location.href,
  25. EMPTY_FN = function(){},
  26. // Common Strings
  27. ECSSTENDER = 'eCSStender',
  28. EXTENSION = 'extension',
  29. SELECTOR = 'selector',
  30. PROPERTY = 'property',
  31. SPECIFICITY = 'specificity',
  32. SCREEN = 'screen',
  33. ALL = 'all',
  34. MEDIA = 'media',
  35. FIND_BY = 'find_by',
  36. TEST = 'test',
  37. LOOKUP = 'lookup',
  38. FRAGMENT = 'fragment',
  39. PREFIX = 'prefix',
  40. PROPERTIES = 'properties',
  41. CALLBACK = 'callback',
  42. FILTER = 'filter',
  43. PROCESSED = 'processed',
  44. FINGERPRINT = 'fingerprint',
  45. PIPES = '||||',
  46. EMPTY = '',
  47. SPACE = ' ',
  48. STAR = '*',
  49. SLASH = '/',
  50. COLON = ':',
  51. SEMICOLON = ';',
  52. HYPHEN = '-',
  53. OPEN_CURLY = '{',
  54. CLOSE_CURLY = '}',
  55. DIV = 'div',
  56. TYPE = 'type',
  57. COMPLETE = 'complete',
  58. // Regex Bits
  59. ANYTHING = '.*?',
  60. HYPHEN_ANYTHING = '-.*',
  61. CAPTURE = '$1',
  62. // placeholders
  63. PH_ATMEDIA = '!' + ECSSTENDER + '-media-placeholder!',
  64. // internals
  65. __eCSStensions = {}, // eCSStensions for parsing
  66. __e_count = 0, // count of registered extensions
  67. __t_count = 0, // count of triggered extensions
  68. __stylesheets = [], // stylesheets to parse
  69. __s = 0, // index of current stylesheet
  70. __style_objects = {}, // style rules to track
  71. __media_groups = {},
  72. __xhr = NULL,
  73. __initialized = FALSE,
  74. __ignored_css = [],
  75. __ignored_props = [ SELECTOR, SPECIFICITY ],
  76. __location = LOCATION.replace( /^\w+:\/\/\/?(.*?)\/.*/, CAPTURE ),
  77. __local = ( LOCATION.indexOf( 'http' ) !== 0 ),
  78. __delayed = {}, // delayed stylesheets to write
  79. __on_complete = [],
  80. // expando handling (for IE)
  81. __expando = DOCUMENT.expando,
  82. toggleExpando = EMPTY_FN,
  83. // for embedding stylesheets
  84. __head = DOCUMENT.getElementsByTagName( 'head' )[0],
  85. __body = NULL,
  86. __style = newElement( 'style' ),
  87. __embedded_css = [],
  88. addRules = EMPTY_FN,
  89. // caching
  90. COUNT = '-count',
  91. __modified = {},
  92. __no_cache = FALSE,
  93. __cached_out = FALSE,
  94. __local_cache = {
  95. xhr: {},
  96. extension: {},
  97. selector: {},
  98. property: {}
  99. },
  100. __headers = {},
  101. __cache_object,
  102. readFromBrowserCache = EMPTY_FN,
  103. writeToBrowserCache = EMPTY_FN,
  104. clearBrowserCache = EMPTY_FN,
  105. // other stuff
  106. __script = newElement( 'script' ),
  107. // useful RegExps
  108. // for breaking on commas
  109. REGEXP_COMMA = /\s*,\s*/,
  110. // for getting file names
  111. REGEXP_FILE = /.*\/(.*?\..*?)(?:\?.*)?$/,
  112. // generalized @ rules
  113. REGEXP_ATRULE = /@([\w-]+)(.*?)\{([^}]*)\}/ig,
  114. // for splitting properties from values
  115. REGEXP_P_V = /:(?!\/\/)/,
  116. // eCSStender Object
  117. eCSStender = {
  118. name: ECSSTENDER,
  119. version: '1.2.6.4',
  120. fonts: [],
  121. pages: {},
  122. at: {},
  123. methods: {},
  124. cache: FALSE,
  125. exec_time: 0
  126. };
  127. // window object
  128. WINDOW.eCSStender = eCSStender;
  129. /*------------------------------------*
  130. * Private Methods *
  131. *------------------------------------*/
  132. function initialize()
  133. {
  134. // keep us from going off more than 1x
  135. if ( __initialized ){ return; }
  136. __initialized = TRUE;
  137. // performance logging
  138. eCSStender.exec_time = now();
  139. // DOM Access
  140. __body = DOCUMENT.getElementsByTagName( 'body' )[0];
  141. // innards
  142. readBrowserCache();
  143. getActiveStylesheets();
  144. parseStyles();
  145. }
  146. function wrapUp()
  147. {
  148. validateCache();
  149. runTests();
  150. eCSStend();
  151. triggerCallbacks();
  152. writeBrowserCache();
  153. // performance logging
  154. eCSStender.exec_time = ( now() - eCSStender.exec_time ) * .001;
  155. triggerOnCompletes();
  156. }
  157. function getActiveStylesheets()
  158. {
  159. var
  160. stylesheets = DOCUMENT.styleSheets,
  161. s = 0, sLen = stylesheets.length;
  162. for ( ; s<sLen; s++ )
  163. {
  164. // add the stylesheet
  165. addStyleSheet( stylesheets[s] );
  166. }
  167. // cssText is truly unreliable, we need to rely on XHR
  168. if ( sLen > 0 )
  169. {
  170. __xhr = TRUE;
  171. }
  172. }
  173. function parseStyles()
  174. {
  175. var s=0, sLen=__stylesheets.length, media, m, mLen;
  176. for ( ; s<sLen; s++ )
  177. {
  178. // determine the media type
  179. media = determineMedia( __stylesheets[s] );
  180. createMediaContainers( media );
  181. determinePath( __stylesheets[s] );
  182. }
  183. getCSSFiles();
  184. }
  185. function validateCache()
  186. {
  187. if ( __no_cache || __local ){ return; }
  188. // check the xhr headers against what we read from the cache
  189. var key, cached = __local_cache.xhr, i=0, j=0;
  190. eCSStender.cache = TRUE;
  191. for ( key in __modified )
  192. {
  193. i++;
  194. if ( ! isInheritedProperty( __modified, key ) &&
  195. __modified[key] != NULL )
  196. {
  197. j++;
  198. if ( ! defined( cached[key] ) ||
  199. cached[key] != __modified[key] )
  200. {
  201. eCSStender.cache = FALSE;
  202. }
  203. // update the cache
  204. __local_cache.xhr[key] = __modified[key];
  205. }
  206. }
  207. if ( i>j || ( i === 0 && j === 0 ) ){ eCSStender.cache = FALSE; }
  208. }
  209. function runTests()
  210. {
  211. if ( eCSStender.cache ){ return; }
  212. var temp = {}, e, count=0, extension_test;
  213. for ( e in __eCSStensions )
  214. {
  215. if ( ! isInheritedProperty( __eCSStensions, e ) )
  216. {
  217. extension_test = __eCSStensions[e][TEST];
  218. // verify test (if any)
  219. if ( ! defined( extension_test ) ||
  220. ( is( extension_test, FUNCTION ) &&
  221. extension_test() ) )
  222. {
  223. // if no test or test is passed, push to the temp array
  224. temp[e] = __eCSStensions[e];
  225. count++;
  226. }
  227. }
  228. }
  229. // reset the __eCSStensions array
  230. __eCSStensions = temp;
  231. __e_count = count;
  232. }
  233. function eCSStend()
  234. {
  235. // see if there's anything to do
  236. if ( __e_count < 1 ){ return; }
  237. var
  238. medium, styles, sorted_styles, s, sLen, selector,
  239. eLoop, e, e_media, e_lookup, e_find_by,
  240. lookups, l, lLen, temp, t, lookup,
  241. property;
  242. // no cache
  243. if ( ! eCSStender.cache )
  244. {
  245. // parse by medium
  246. for ( medium in __style_objects )
  247. {
  248. // safety for users who are using Prototype or any code that extends Object
  249. if ( ! isInheritedProperty( __style_objects, medium ) )
  250. {
  251. // start the processing in earnest
  252. styles = __style_objects[medium];
  253. sorted_styles = getSortedArray( styles );
  254. for ( s=0, sLen=sorted_styles.length; s<sLen; s++ )
  255. {
  256. selector = sorted_styles[s][SELECTOR];
  257. // loop the extensions
  258. eLoop:
  259. for ( e in __eCSStensions )
  260. {
  261. // safety for users who are using Prototype or any code that extends Object
  262. if ( ! isInheritedProperty( __eCSStensions, e ) )
  263. {
  264. e_media = __eCSStensions[e][MEDIA];
  265. // verify any media restrictions
  266. if ( defined( e_media ) &&
  267. e_media != ALL )
  268. {
  269. e_media = e_media.split(REGEXP_COMMA);
  270. if ( medium != ALL &&
  271. ! in_object( medium, e_media ) ){
  272. continue;
  273. }
  274. }
  275. e_find_by = __eCSStensions[e][FIND_BY];
  276. e_lookup = __eCSStensions[e][LOOKUP];
  277. lLen = e_lookup.length;
  278. // eCSStension is triggered by a selector
  279. if ( e_find_by == SELECTOR )
  280. {
  281. for ( l=0; l<lLen; l++ )
  282. {
  283. if ( selectorMatches( selector, e_lookup[l] ) )
  284. {
  285. trackCallback( e, medium, selector );
  286. continue eLoop;
  287. }
  288. }
  289. }
  290. // eCSStension uses a property
  291. else if ( e_find_by == PROPERTY )
  292. {
  293. for ( l=0; l<lLen; l++ )
  294. {
  295. if ( defined( styles[selector][e_lookup[l]] ) )
  296. {
  297. trackCallback( e, medium, selector );
  298. continue eLoop;
  299. }
  300. }
  301. }
  302. // eCSStension uses a fragment or prefix
  303. else if ( e_find_by == FRAGMENT ||
  304. e_find_by == PREFIX )
  305. {
  306. lookup = ( e_find_by == FRAGMENT ) ? ANYTHING + e_lookup + ANYTHING
  307. : HYPHEN + e_lookup + HYPHEN_ANYTHING;
  308. lookup = newRegExp( lookup );
  309. for ( property in styles[selector] )
  310. {
  311. if ( ! isInheritedProperty( styles, selector ) && // fix for tainted Object
  312. ! in_object( property, __ignored_props ) &&
  313. property.match( lookup ) )
  314. {
  315. trackCallback( e, medium, selector );
  316. continue eLoop;
  317. }
  318. }
  319. } // end if eCSStension uses a fragment or prefix
  320. } // end extended object test
  321. } // end eCSStensions loop
  322. } // end styles loop
  323. } // end extended object test
  324. } // end medium loop
  325. } // end if no cache
  326. }
  327. // log callbacks
  328. function trackCallback( fingerprint, medium, selector )
  329. {
  330. var selector_id = medium + PIPES + selector;
  331. writeToLocalCache( EXTENSION, EXTENSION + ( __t_count++ ), fingerprint + PIPES + selector_id, FALSE );
  332. }
  333. // callback functionality
  334. function triggerCallbacks()
  335. {
  336. var s = 0, e, extension, style_rule, selector_id, properties, specificity, oLen, result;
  337. for ( ; s<__t_count; s++ )
  338. {
  339. e = __local_cache[EXTENSION][EXTENSION+s].split(PIPES);
  340. extension = __eCSStensions[e[0]];
  341. if ( defined( __style_objects[e[1]] ) )
  342. {
  343. style_rule = __style_objects[e[1]][e[2]];
  344. selector_id = e[1] + PIPES + e[2];
  345. if ( ! defined( extension ) ||
  346. ! defined( style_rule ) ||
  347. in_object( selector_id, extension[PROCESSED] ) ||
  348. // apply any filters
  349. ( defined( extension[FILTER] ) &&
  350. ! filtersMatched( style_rule, extension[FILTER] ) ) ) { continue; }
  351. specificity = ( ! eCSStender.cache ) ? style_rule[SPECIFICITY]
  352. : getSpecificity( e[2] );
  353. properties = extractProperties( e[1], e[2], extension[PROPERTIES] );
  354. result = extension[CALLBACK]( e[2], properties, e[1], specificity );
  355. // allow on-the-fly re-definition of callback
  356. if ( is( result, FUNCTION ) )
  357. {
  358. __eCSStensions[e[0]][CALLBACK] = result;
  359. }
  360. __eCSStensions[e[0]][PROCESSED].push( selector_id );
  361. }
  362. }
  363. }
  364. function triggerOnCompletes()
  365. {
  366. var o = __on_complete.length;
  367. while ( o-- )
  368. {
  369. __on_complete[o]();
  370. }
  371. }
  372. /*------------------------------------*
  373. * Private Utils *
  374. *------------------------------------*/
  375. function findImportedStylesheets( stylesheet )
  376. {
  377. // W3C
  378. if ( ! defined( stylesheet.imports ) )
  379. {
  380. findImportedStylesheets = function( stylesheet ){
  381. var
  382. blocks = stylesheet.cssRules || stylesheet.rules,
  383. i = 0, iLen;
  384. // for a strange Chrome error
  385. if ( blocks === NULL ){ return; }
  386. for ( iLen = blocks.length; i<iLen; i++ )
  387. {
  388. // imports must come first, so when we don't find one, return
  389. if ( blocks[i].type != 3 ){ return; }
  390. // add the stylesheet
  391. addStyleSheet( blocks[i].styleSheet );
  392. }
  393. // no need to XHR stylesheets that only import other stylesheets
  394. if ( i === iLen &&
  395. stylesheet.href )
  396. {
  397. __ignored_css.push( stylesheet.href.replace( REGEXP_FILE, CAPTURE ) );
  398. }
  399. };
  400. }
  401. // IE (old)
  402. else
  403. {
  404. findImportedStylesheets = function( stylesheet ){
  405. var imports = stylesheet.imports,
  406. i = 0, iLen = imports.length;
  407. for ( ; i<iLen; i++ )
  408. {
  409. // add the stylesheet
  410. addStyleSheet( imports[i] );
  411. }
  412. };
  413. }
  414. findImportedStylesheets( stylesheet );
  415. }
  416. function addStyleSheet( stylesheet )
  417. {
  418. var href = stylesheet.href;
  419. // skip if disabled
  420. if ( stylesheet.disabled ||
  421. ( href &&
  422. // or foreign
  423. ( determinePath( stylesheet ).indexOf( __location ) == -1 ||
  424. // or ignored
  425. in_object( href.replace( REGEXP_FILE, CAPTURE ), __ignored_css ) ) ) ){ return; }
  426. // does it have imports?
  427. findImportedStylesheets( stylesheet );
  428. // push the current stylesheet to the collection
  429. __stylesheets.push( stylesheet );
  430. }
  431. function getSortedArray( styles )
  432. {
  433. var arr = [], temp, selector;
  434. for ( selector in styles )
  435. {
  436. // fix for tainated Object
  437. if ( ! isInheritedProperty( styles, selector ) )
  438. {
  439. // continue
  440. temp = styles[selector];
  441. temp[SELECTOR] = selector;
  442. temp[SPECIFICITY] = getSpecificity( selector );
  443. arr.push( temp );
  444. }
  445. }
  446. arr.sort( sortBySpecificity );
  447. return arr;
  448. }
  449. function sortBySpecificity( a, b )
  450. {
  451. var x = a[SPECIFICITY], y = b[SPECIFICITY];
  452. return ( ( x < y ) ? -1 : ( ( x > y ) ? 1 : 0 ) );
  453. }
  454. function getSpecificity( selector )
  455. {
  456. var s = 0, matches;
  457. // replace all child and adjascent sibling selectors
  458. selector = selector.replace( /\s*\+\s*|\s*\>\s*/, SPACE );
  459. // adjust :not() to simplify calculations (since it counts toward specificity, as do its contents)
  460. selector = selector.replace( /(:not)\((.*)\)/, '$1 $2' );
  461. // match id selectors (weight: 100)
  462. matches = selector.match( /#/ );
  463. if ( matches != NULL ) s += ( matches.length * 100 );
  464. selector = selector.replace( /#[\w-_]+/, EMPTY ); // remove (to keep the regexs simple)
  465. // match class, pseudo-class, and attribute selectors (weight: 10)
  466. matches = selector.match( /::|:|\.|\[.*?\]/ );
  467. if ( matches != NULL ) s += ( matches.length * 10 );
  468. selector = selector.replace( /(?:::|:|\.)[\w-_()]+|\[.*?\]/, EMPTY ); // remove
  469. // match element selectors (weight: 1) - they should be all that's left
  470. matches = trim( selector ) != EMPTY ? selector.split( SPACE ) : [];
  471. s += matches.length;
  472. return s;
  473. }
  474. function determinePath( stylesheet )
  475. {
  476. var
  477. // for determining if it's a fully-executed path
  478. REGEXP_URL = /\w+?\:\/\//,
  479. actual_path = stylesheet.actual_path,
  480. css_path = actual_path || stylesheet.href,
  481. parent = stylesheet.parentStyleSheet,
  482. parent_path = EMPTY,
  483. prefix = EMPTY,
  484. full_url = FALSE,
  485. curr_path, path_last_slash, file_name;
  486. if ( ! css_path )
  487. {
  488. css_path = NULL;
  489. }
  490. else
  491. {
  492. full_url = css_path.match( REGEXP_URL );
  493. if ( is( full_url, ARRAY ) )
  494. {
  495. full_url = ( full_url.length > 0 );
  496. }
  497. }
  498. // we only want sheets
  499. if ( ! actual_path && // that don't already have a path
  500. ! full_url ) // that don't have a full URL
  501. {
  502. if ( css_path.indexOf( SLASH ) === 0 ) { css_path = css_path.substring( 1 ); }
  503. curr_path = LOCATION.substring( 0, LOCATION.lastIndexOf( SLASH ) );
  504. path_last_slash = css_path.lastIndexOf( SLASH );
  505. file_name = css_path.substring( path_last_slash + 1 );
  506. // check for an owner
  507. if ( parent == NULL )
  508. {
  509. if ( defined( stylesheet.ownerNode ) &&
  510. defined( CSSImportRule ) &&
  511. is( stylesheet.ownerRule, CSSImportRule ) )
  512. {
  513. parent = stylesheet.ownerRule.parentStyleSheet;
  514. }
  515. }
  516. // still no parent, use the css path itself
  517. if ( parent == NULL )
  518. {
  519. prefix = curr_path + SLASH + css_path.substring( 0, path_last_slash );
  520. }
  521. // get the owner's path
  522. else
  523. {
  524. parent_path = determinePath( parent );
  525. prefix = parent_path.substring( 0, parent_path.lastIndexOf( SLASH ) );
  526. }
  527. css_path = prefix + SLASH + file_name;
  528. }
  529. stylesheet.actual_path = css_path;
  530. return css_path;
  531. }
  532. function determineMedia( stylesheet )
  533. {
  534. var media = stylesheet.media;
  535. // W3C compliant
  536. if ( ! is( media, STRING ) )
  537. {
  538. determineMedia = function( stylesheet ){
  539. var
  540. media = stylesheet.media,
  541. owner = stylesheet.ownerRule,
  542. mediaText = FALSE;
  543. if ( ! is( media, STRING ) )
  544. {
  545. // imported
  546. if ( owner != NULL )
  547. {
  548. // media assignment in the import
  549. mediaText = owner.media.mediaText;
  550. if ( ! mediaText )
  551. {
  552. // no media assignment... inherit
  553. mediaText = determineMedia( owner.parentStyleSheet );
  554. }
  555. }
  556. // media is defined
  557. else
  558. {
  559. mediaText = media.mediaText;
  560. }
  561. }
  562. // default = screen
  563. stylesheet.actual_media = mediaText ? mediaText : SCREEN;
  564. if ( is( stylesheet.actual_media, STRING ) )
  565. {
  566. stylesheet.actual_media = stylesheet.actual_media.split( REGEXP_COMMA );
  567. }
  568. return stylesheet.actual_media;
  569. };
  570. }
  571. // old school
  572. else
  573. {
  574. determineMedia = function( stylesheet ){
  575. var mediaText = stylesheet.media;
  576. // default = screen
  577. stylesheet.actual_media = mediaText ? mediaText : SCREEN;
  578. if ( is( stylesheet.actual_media, STRING ) )
  579. {
  580. stylesheet.actual_media = stylesheet.actual_media.split( REGEXP_COMMA );
  581. }
  582. return stylesheet.actual_media;
  583. };
  584. }
  585. return determineMedia( stylesheet );
  586. }
  587. function extractAtBlocks( css )
  588. {
  589. // extract @font-face
  590. css = extractFonts( css );
  591. // extract @page
  592. css = extractPages( css );
  593. // handle @media
  594. css = handleMediaGroups( css );
  595. // extract other @ rules
  596. css = extractOtherAtRules( css );
  597. // return the string
  598. return css;
  599. }
  600. function extractFonts( css )
  601. {
  602. var
  603. REGEXP_FONTS = /@font-face\s*?\{(.*?)\s*?\}/ig,
  604. match;
  605. while ( ( match = REGEXP_FONTS.exec( css ) ) != NULL )
  606. {
  607. eCSStender.fonts.push( gatherProperties( match[1] ) );
  608. }
  609. return css.replace( REGEXP_FONTS, EMPTY );
  610. }
  611. function extractPages( css )
  612. {
  613. // TODO: make pages media-aware
  614. var
  615. PAGES = 'pages', AT = '@',
  616. match, page, box, content, group, props, prop,
  617. /* Regex must match all of the following
  618. @page { ... }
  619. @page :left { ... }
  620. @page :right { ... }
  621. @page LandscapeTable { ... }
  622. @page CompanyLetterHead:first { ... }
  623. @page:first { ... }
  624. Plus margin boxes inside:
  625. @top-left-corner, @top-left, @top-center, @top-right, @top-right-corner,
  626. @bottom-left-corner, @bottom-left, @bottom-center, @bottom-right, @bottom-right-corner,
  627. @left-top, @left-middle, @left-bottom, @right-top, @right-middle, @right-bottom
  628. For example
  629. @page{margin:0;@top-left{content:title}padding:2px;} */
  630. REGEXP_PAGES = /@page\s*?([\w:]*){0,1}\{\s*?((?:@[\w-]+\{[^\}]*\}|[\w-]+:[^;]+;)*)\s*?\}/ig;
  631. while ( ( match = REGEXP_PAGES.exec( css ) ) != NULL )
  632. {
  633. page = match[1];
  634. if ( ! defined( page ) || page == EMPTY )
  635. {
  636. page = ALL;
  637. }
  638. else if ( page.indexOf(COLON) == 0 )
  639. {
  640. page = page.replace( COLON, EMPTY );
  641. }
  642. content = match[2];
  643. if ( ! defined( eCSStender[PAGES][page] ) )
  644. {
  645. eCSStender[PAGES][page] = {};
  646. }
  647. // Margin boxes
  648. while ( ( box = REGEXP_ATRULE.exec( content ) ) != NULL )
  649. {
  650. group = box[1];
  651. props = gatherProperties( box[3] );
  652. if ( ! defined( eCSStender[PAGES][page][AT] ) )
  653. {
  654. eCSStender[PAGES][page][AT] = {};
  655. }
  656. if ( ! defined( eCSStender[PAGES][page][AT][group] ) )
  657. {
  658. eCSStender[PAGES][page][AT][group] = props;
  659. }
  660. else
  661. {
  662. for ( prop in props )
  663. {
  664. if ( ! isInheritedProperty( props, prop ) )
  665. {
  666. eCSStender[PAGES][page][AT][group][prop] = props[prop];
  667. }
  668. }
  669. }
  670. content = content.replace( box[0], EMPTY );
  671. }
  672. // properties
  673. props = gatherProperties( content );
  674. for ( prop in props )
  675. {
  676. if ( ! isInheritedProperty( props, prop ) )
  677. {
  678. eCSStender[PAGES][page][prop] = props[prop];
  679. }
  680. }
  681. }
  682. return css.replace( REGEXP_PAGES, EMPTY );
  683. }
  684. function handleMediaGroups( css )
  685. {
  686. // TODO: @media can contain @page, not just declaration blocks.
  687. var
  688. REGEXP_MEDIA = /@media\s*(.*?)\s*\{(.*?})\}/ig,
  689. match, media, m, mLen, styles, id = 0;
  690. while ( ( match = REGEXP_MEDIA.exec( css ) ) != NULL )
  691. {
  692. css = collapseAtMedia( css, match, id );
  693. id++;
  694. }
  695. return css;
  696. }
  697. function extractOtherAtRules( css )
  698. {
  699. var
  700. match, group, keys, k, props, prop;
  701. while ( ( match = REGEXP_ATRULE.exec( css ) ) != NULL )
  702. {
  703. group = match[1];
  704. keys = trim( match[2] );
  705. keys = ( keys == EMPTY ) ? FALSE : keys.split( REGEXP_COMMA );
  706. props = gatherProperties( match[3] );
  707. if ( ! defined( eCSStender.at[group] ) )
  708. {
  709. eCSStender.at[group] = ! keys ? addPush( [] ) : {};
  710. }
  711. if ( ! keys )
  712. {
  713. eCSStender.at[group].push( props );
  714. }
  715. else
  716. {
  717. k = keys.length;
  718. while ( k-- )
  719. {
  720. if ( ! defined( eCSStender.at[group][keys[k]] ) )
  721. {
  722. eCSStender.at[group][keys[k]] = props;
  723. }
  724. else
  725. {
  726. for ( prop in props )
  727. {
  728. if ( ! isInheritedProperty( props, prop ) )
  729. {
  730. eCSStender.at[group][keys[k]][prop] = props[prop];
  731. }
  732. }
  733. }
  734. }
  735. }
  736. }
  737. return css.replace( REGEXP_ATRULE, EMPTY );
  738. }
  739. function collapseAtMedia( css, match, id )
  740. {
  741. var
  742. media = match[1].split(REGEXP_COMMA),
  743. styles = match[2];
  744. createMediaContainers( media );
  745. __media_groups[id] = {
  746. media: media,
  747. styles: styles
  748. };
  749. return css.replace( match[0], PH_ATMEDIA + '{id:' + id + CLOSE_CURLY );
  750. }
  751. function expandAtMedia( id )
  752. {
  753. var media_group = __media_groups[id];
  754. extractStyleBlocks( media_group.media, media_group.styles );
  755. __media_groups[id] = NULL;
  756. }
  757. function extractStyleBlocks( media, css, delayed_id )
  758. {
  759. media = arrayify(media);
  760. // parse it into blocks & remove the last item (which is empty)
  761. var blocks = css.split(CLOSE_CURLY),
  762. b=0, m=0, a=0, bLen, mLen = media.length,
  763. props, prop, selector, medium, arr, aLen;
  764. blocks.pop();
  765. // loop
  766. for ( bLen=blocks.length; b<bLen; b++ )
  767. {
  768. // separate the selector and the properties
  769. blocks[b] = blocks[b].split(OPEN_CURLY);
  770. // gather the properties
  771. props = gatherProperties( blocks[b][1] );
  772. // build the selectors (which are part of the master object)
  773. selector = blocks[b][0];
  774. // single selector
  775. if ( selector.indexOf( PH_ATMEDIA ) != -1 )
  776. {
  777. expandAtMedia( props.id );
  778. }
  779. else
  780. {
  781. arr = selector.split(REGEXP_COMMA);
  782. for ( a=0, aLen=arr.length; a<aLen; a++ )
  783. {
  784. selector = trim( arr[a] );
  785. for ( m=0; m<mLen; m++ )
  786. {
  787. medium = media[m];
  788. // normal run
  789. if ( ! defined( delayed_id ) )
  790. {
  791. if ( ! defined( __style_objects[medium][selector] ) )
  792. {
  793. __style_objects[medium][selector] = {};
  794. }
  795. for ( prop in props )
  796. {
  797. if ( ! isInheritedProperty( props, prop ) )
  798. {
  799. __style_objects[medium][selector][prop] = props[prop];
  800. }
  801. }
  802. }
  803. // delayed run
  804. else
  805. {
  806. if ( ! defined( __delayed[delayed_id][selector] ) )
  807. {
  808. __delayed[delayed_id][selector] = {};
  809. }
  810. for ( prop in props )
  811. {
  812. if ( ! isInheritedProperty( props, prop ) )
  813. {
  814. __delayed[delayed_id][selector][prop] = props[prop];
  815. }
  816. }
  817. }
  818. }
  819. }
  820. }
  821. }
  822. }
  823. function gatherProperties( properties )
  824. {
  825. if ( ! is( properties, STRING ) ){ return {}; }
  826. properties = properties.split(SEMICOLON);
  827. var props = {},
  828. p = 0, pLen = properties.length,
  829. property, arr, prop, val;
  830. for ( ; p<pLen; p++ )
  831. {
  832. property = trim( properties[p] );
  833. // skip empties
  834. if ( property == EMPTY ){ continue; }
  835. arr = property.split(REGEXP_P_V);
  836. prop = arr.shift();
  837. val = arr.join(COLON);
  838. props[trim(prop)] = trim( val );
  839. }
  840. return props;
  841. }
  842. function determineProperties( lookup, requested_properties )
  843. {
  844. var properties = [], property, fragment, prefix, i, iLen;
  845. // properties is set
  846. if ( ! is_false( requested_properties ) )
  847. {
  848. // user doesn't want everything
  849. if ( requested_properties != STAR )
  850. {
  851. // gather requested properties
  852. if ( is( requested_properties, STRING ) )
  853. {
  854. properties.push( requested_properties );
  855. }
  856. else if ( is( requested_properties, ARRAY ) )
  857. {
  858. for ( i=0, iLen=requested_properties.length; i<iLen; i++ )
  859. {
  860. properties.push( requested_properties[i] );
  861. }
  862. }
  863. }
  864. else
  865. {
  866. properties = requested_properties;
  867. }
  868. }
  869. // now for the remainder
  870. if ( requested_properties != STAR )
  871. {
  872. // retrieve properties that were explicitly looked up
  873. property = lookup[PROPERTY];
  874. fragment = lookup[FRAGMENT];
  875. prefix = lookup[PREFIX];
  876. if ( defined( property ) )
  877. {
  878. if ( is( property, STRING ) )
  879. {
  880. properties.push( property );
  881. }
  882. else if ( is( property, ARRAY ) )
  883. {
  884. for ( i=0, iLen=property.length; i<iLen; i++ )
  885. {
  886. properties.push( property[i] );
  887. }
  888. }
  889. }
  890. // retrieve fragment matches
  891. else if ( defined( fragment ) )
  892. {
  893. properties.push( newRegExp( ANYTHING + fragment + ANYTHING ) );
  894. }
  895. // retrieve prefix matches
  896. else if ( defined( prefix ) )
  897. {
  898. properties.push( newRegExp( HYPHEN + prefix + HYPHEN_ANYTHING ) );
  899. }
  900. }
  901. return properties;
  902. }
  903. function extractProperties( medium, selector, requested_properties )
  904. {
  905. var requested_property, property, properties = {}, p = 0, pLen,
  906. style_rule = __style_objects[medium][selector];
  907. // grab the requested properties
  908. if ( is( requested_properties, ARRAY ) )
  909. {
  910. for ( pLen=requested_properties.length; p<pLen; p++ )
  911. {
  912. requested_property = requested_properties[p];
  913. if ( is( requested_property, REGEXP ) )
  914. {
  915. for ( property in style_rule )
  916. {
  917. if ( ! isInheritedProperty( style_rule, property ) && // fix for tainted Object
  918. ! in_object( property, __ignored_props ) &&
  919. property.match( requested_property ) != NULL )
  920. {
  921. properties[property] = style_rule[property];
  922. }
  923. }
  924. }
  925. else if ( is( requested_property, STRING ) &&
  926. defined( style_rule[requested_property] ) )
  927. {
  928. properties[requested_property] = style_rule[requested_property];
  929. }
  930. }
  931. }
  932. // all properties (*)
  933. else
  934. {
  935. for ( property in style_rule )
  936. {
  937. if ( ! isInheritedProperty( style_rule, property ) &&
  938. ! in_object( property, __ignored_props ) )
  939. {
  940. properties[property] = style_rule[property];
  941. }
  942. }
  943. }
  944. return properties;
  945. }
  946. function arrayify( something )
  947. {
  948. var arr=addPush([]), i=0, iLen, temp, t, tLen;
  949. if ( ! is( something, ARRAY ) )
  950. {
  951. if ( is( something, OBJECT ) &&
  952. ! is( something, REGEXP ) )
  953. {
  954. for ( i in something )
  955. {
  956. if ( ! isInheritedProperty( something, i ) )
  957. {
  958. arr.push( something[i] );
  959. }
  960. }
  961. }
  962. else if ( is( something, STRING ) &&
  963. something.indexOf(',') != -1 )
  964. {
  965. temp = something.split( REGEXP_COMMA );
  966. for ( iLen=temp.length; i<iLen; i++ )
  967. {
  968. arr.push( temp[i] );
  969. }
  970. }
  971. else
  972. {
  973. arr = [ something ];
  974. }
  975. }
  976. else
  977. {
  978. for ( iLen=something.length; i<iLen; i++ )
  979. {
  980. if ( is( something[i], STRING ) &&
  981. something[i].indexOf(',') != -1 )
  982. {
  983. temp = something[i].split( REGEXP_COMMA );
  984. for ( t=0, tLen=temp.length; t<tLen; t++ )
  985. {
  986. arr.push( temp[t] );
  987. }
  988. }
  989. else
  990. {
  991. arr.push( something[i] );
  992. }
  993. }
  994. }
  995. return arr;
  996. }
  997. function createMediaContainers( media )
  998. {
  999. if ( ! is( media, ARRAY ) )
  1000. {
  1001. media = ( media + EMPTY ).split( REGEXP_COMMA );
  1002. }
  1003. for ( var m=0, mLen=media.length; m<mLen; m++ )
  1004. {
  1005. if ( ! defined( __style_objects[media[m]] ) )
  1006. {
  1007. __style_objects[media[m]] = {};
  1008. }
  1009. }
  1010. }
  1011. function selectorMatches( selector, test )
  1012. {
  1013. var useless = /\*(?!\s|>|\+)/g;
  1014. return ( ( is( test, REGEXP ) &&
  1015. selector.match( test ) != NULL ) ||
  1016. ( is( test, FUNCTION ) &&
  1017. test.call( selector ) === TRUE ) ||
  1018. ( is( test, STRING ) &&
  1019. selector.indexOf( trim( test.replace( useless, EMPTY ) ) ) != -1 ) );
  1020. }
  1021. function filtersMatched( properties, filters )
  1022. {
  1023. var count, required_count, prop, filter;
  1024. for ( prop in properties )
  1025. {
  1026. if ( ! isInheritedProperty( properties, prop ) &&
  1027. ! in_object( prop, __ignored_props ) )
  1028. {
  1029. count = required_count = 0;
  1030. for ( filter in filters )
  1031. {
  1032. if ( ! isInheritedProperty( filters, filter ) )
  1033. {
  1034. required_count++;
  1035. if ( filter == PROPERTY )
  1036. {
  1037. if ( prop.match( filters[filter] ) ){ count++; }
  1038. }
  1039. else if ( filter == 'value' )
  1040. {
  1041. if ( properties[prop].match( filters[filter] ) ){ count++; }
  1042. }
  1043. }
  1044. }
  1045. }
  1046. if ( count == required_count ){ return TRUE; }
  1047. }
  1048. return FALSE;
  1049. }
  1050. function clean( css )
  1051. {
  1052. var
  1053. // IE returns all uppercase tags and property names (except on XHR)
  1054. // low_function = low,
  1055. // lowercase = /(?:\s?([^.#:]+).*?[,{]|\s?([^:]+):)/ig,
  1056. html_comments = /\s*(?:\<\!--|--\>)\s*/g, // strip HTML comments
  1057. css_comments = /\/\*(?:.|\s)*?\*\//g, // strip CSS comments
  1058. // remove returns and indenting whitespace (not not spaces before pseudo-element & pseudo-class selectors)
  1059. whitespace = /\s*([,{};]|:(?!nth|first|last|only|empty|checked|(dis|en)abled))\s*/g,
  1060. at_imports = /@import.*?;/g; // axe imports
  1061. return css.replace( html_comments, EMPTY )
  1062. .replace( css_comments, EMPTY )
  1063. .replace( whitespace, CAPTURE )
  1064. .replace( at_imports, EMPTY );
  1065. // removed with removal of IE's cssText
  1066. // .replace( lowercase, low_function );
  1067. }
  1068. function in_object( needle, haystack )
  1069. {
  1070. for ( var key in haystack )
  1071. {
  1072. if ( haystack[key] == needle ){ return TRUE; }
  1073. }
  1074. return FALSE;
  1075. }
  1076. function now()
  1077. {
  1078. return new Date().getTime();
  1079. }
  1080. function is( obj, test )
  1081. {
  1082. var r = FALSE;
  1083. try
  1084. {
  1085. r = obj instanceof test;
  1086. }
  1087. catch ( e )
  1088. {
  1089. r = ( typeof( test ) == STRING &&
  1090. typeof( obj ) == test );
  1091. }
  1092. return r;
  1093. }
  1094. function is_false( a )
  1095. {
  1096. return a === FALSE;
  1097. }
  1098. function defined( a )
  1099. {
  1100. return a != UNDEFINED;
  1101. }
  1102. function charAt( str )
  1103. {
  1104. return String.fromCharCode( str );
  1105. }
  1106. function getCSSFiles()
  1107. {
  1108. if ( __xhr )
  1109. {
  1110. getCSSFiles = function()
  1111. {
  1112. var stylesheet, file, css, status;
  1113. if ( stylesheet = __stylesheets[__s] )
  1114. {
  1115. if ( file = stylesheet.actual_path )
  1116. {
  1117. if ( file === NULL ||
  1118. in_object( file.replace( REGEXP_FILE, CAPTURE ), __ignored_css ) )
  1119. {
  1120. __s++;
  1121. getCSSFiles();
  1122. }
  1123. else
  1124. {
  1125. __xhr = new XHR();
  1126. __xhr.onreadystatechange = xhrHandler;
  1127. __xhr.open( 'GET', file, FALSE );
  1128. __xhr.send( NULL );
  1129. try {
  1130. if ( __xhr.onreadystatechange != xhrHandler )
  1131. {
  1132. __xhr.onreadystatechange = xhrHandler();
  1133. }
  1134. } catch ( e ) { }
  1135. }
  1136. }
  1137. else
  1138. {
  1139. readCSS( extract( stylesheet ), stylesheet.actual_media );
  1140. __s++;
  1141. getCSSFiles();
  1142. }
  1143. }
  1144. else
  1145. {
  1146. wrapUp();
  1147. }
  1148. };
  1149. getCSSFiles();
  1150. }
  1151. else
  1152. {
  1153. for ( var i=0, iLen=__stylesheets; i<iLen; i++ )
  1154. {
  1155. if ( ! in_object( __stylesheets[i].actual_path.replace( REGEXP_FILE, CAPTURE ), __ignored_css ) )
  1156. {
  1157. readCSS( __stylesheets[i].cssText, __stylesheets[i].actual_media );
  1158. }
  1159. }
  1160. }
  1161. }
  1162. function xhrHandler( e )
  1163. {
  1164. if ( __xhr.readyState == 4 )
  1165. {
  1166. var status = __xhr.status;
  1167. if ( status == 0 || // local
  1168. ( status >= 200 && status < 300 ) || // good
  1169. status == 304 ) // cached
  1170. {
  1171. readCSS( __xhr.responseText, __stylesheets[__s].actual_media );
  1172. __modified[fingerprint( __stylesheets[__s].actual_path )] = __xhr.getResponseHeader('Last-Modified');
  1173. }
  1174. __s++;
  1175. getCSSFiles();
  1176. }
  1177. }
  1178. function readCSS( css, media )
  1179. {
  1180. css = clean( css );
  1181. css = extractAtBlocks( css );
  1182. // handle remaining rules
  1183. extractStyleBlocks( media, css );
  1184. }
  1185. function extract( stylesheet )
  1186. {
  1187. var r;
  1188. try {
  1189. r = stylesheet.ownerNode.innerHTML;
  1190. extract = function( stylesheet )
  1191. {
  1192. return stylesheet.ownerNode.innerHTML;
  1193. };
  1194. }
  1195. catch ( e )
  1196. {
  1197. r = stylesheet.owningElement.innerHTML;
  1198. extract = function( stylesheet )
  1199. {
  1200. return stylesheet.owningElement.innerHTML;
  1201. };
  1202. }
  1203. return r;
  1204. }
  1205. function low( w )
  1206. {
  1207. return is( w, STRING ) ? w.toLowerCase() : w;
  1208. }
  1209. function camelize( str )
  1210. {
  1211. var
  1212. bits = str.split(HYPHEN), len = bits.length, new_str, i = 1;
  1213. if ( len == 1 ) { return bits[0]; }
  1214. if ( str.charAt(0) == HYPHEN ) {
  1215. new_str = bits[0].charAt(0).toUpperCase() + bits[0].substring(1);
  1216. } else {
  1217. new_str = bits[0];
  1218. }
  1219. while ( i < len ) {
  1220. new_str += bits[i].charAt(0).toUpperCase() + bits[i].substring(1);
  1221. i++;
  1222. }
  1223. return new_str;
  1224. }
  1225. function zero_out( str )
  1226. {
  1227. if ( is( str, STRING ) )
  1228. {
  1229. str = str.replace( /(\s0)px/g, CAPTURE );
  1230. }
  1231. return str;
  1232. }
  1233. function addInlineStyle( el, property, value )
  1234. {
  1235. try {
  1236. el.style[property] = value;
  1237. el.style[camelize( property )] = value;
  1238. } catch( e ){
  1239. return FALSE;
  1240. }
  1241. return TRUE;
  1242. }
  1243. function XHR()
  1244. {
  1245. var connection;
  1246. try { connection = new XMLHttpRequest(); }
  1247. catch( e ){
  1248. try { connection = new ActiveXObject('Msxml2.XMLHTTP'); }
  1249. catch( e ){
  1250. try { connection = new ActiveXObject('Microsoft.XMLHTTP'); }
  1251. catch( e ){
  1252. connection = FALSE;
  1253. }
  1254. }
  1255. }
  1256. return ( ! connection ) ? NULL : connection;
  1257. }
  1258. function newElement( el )
  1259. {
  1260. return DOCUMENT.createElement( el );
  1261. }
  1262. function newRegExp( rxp )
  1263. {
  1264. return new RegExp( rxp );
  1265. }
  1266. function makeClassRegExp( the_class )
  1267. {
  1268. return newRegExp( '(\\s|^)' + the_class + '(\\s|$)' );
  1269. }
  1270. // push handling
  1271. function addPush( arr )
  1272. {
  1273. return arr;
  1274. }
  1275. if ( Array.prototype.push == NULL )
  1276. {
  1277. var push = function( obj )
  1278. {
  1279. this[this.length] = obj;
  1280. return this.length;
  1281. };
  1282. eCSStender.fonts.push = __eCSStensions.push = __stylesheets.push = __embedded_css.push = __on_complete.push = push;
  1283. addPush = function( arr )
  1284. {
  1285. if ( typeof( arr ) == ARRAY )
  1286. {
  1287. arr.push = push;
  1288. }
  1289. return arr;
  1290. };
  1291. }
  1292. // expando handling
  1293. if ( defined( __expando ) )
  1294. {
  1295. toggleExpando = function()
  1296. {
  1297. if ( ! defined( DOCUMENT.old_expando ) ||
  1298. DOCUMENT.old_expando == NULL )
  1299. {
  1300. DOCUMENT.old_expando = DOCUMENT.expando;
  1301. DOCUMENT.expando = FALSE;
  1302. }
  1303. else
  1304. {
  1305. DOCUMENT.expando = DOCUMENT.old_expando;
  1306. DOCUMENT.old_expando = NULL;
  1307. }
  1308. };
  1309. }
  1310. /*-------------------------------------*
  1311. * Delayed Writing
  1312. *-------------------------------------*/
  1313. function writeStyleSheets()
  1314. {
  1315. var id, style, styles, selector;
  1316. for ( id in __delayed )
  1317. {
  1318. if ( ! isInheritedProperty( __delayed, id ) )
  1319. {
  1320. style = DOCUMENT.getElementById( id );
  1321. styles = '';
  1322. for ( selector in __delayed[id] )
  1323. {
  1324. if ( ! isInheritedProperty( __delayed, id ) )
  1325. {
  1326. styles += selector + OPEN_CURLY + styleObjToString( __delayed[id][selector], selector ) + CLOSE_CURLY;
  1327. }
  1328. }
  1329. addRules( style, styles );
  1330. }
  1331. }
  1332. }
  1333. __on_complete.push( writeStyleSheets );
  1334. /*-------------------------------------*
  1335. * Caching
  1336. *-------------------------------------*/
  1337. function enableCache()
  1338. {
  1339. // HTML5 and/or Mozilla
  1340. if ( defined( WINDOW.localStorage ) )
  1341. {
  1342. __cache_object = WINDOW.localStorage;
  1343. clearBrowserCache = function()
  1344. {
  1345. var i = __cache_object.length, key;
  1346. // cherry-pick only our own items in the cache
  1347. while ( i-- )
  1348. {
  1349. key = __cache_object.key(i);
  1350. if ( key &&
  1351. key.indexOf( ECSSTENDER ) === 0 )
  1352. {
  1353. delete( __cache_object[key] );
  1354. }
  1355. }
  1356. };
  1357. readFromBrowserCache = function( cache, key )
  1358. {
  1359. // make sure our cached objects are prefixed
  1360. if ( cache != ECSSTENDER )
  1361. {
  1362. cache = ECSSTENDER + HYPHEN + cache;
  1363. }
  1364. return __cache_object.getItem( cache + HYPHEN + key );
  1365. };
  1366. writeToBrowserCache = function( cache, key, value )
  1367. {
  1368. // make sure our cached objects are prefixed
  1369. if ( cache != ECSSTENDER )
  1370. {
  1371. cache = ECSSTENDER + HYPHEN + cache;
  1372. }
  1373. __cache_object.setItem( cache + HYPHEN + key, value );
  1374. };
  1375. }
  1376. // IE (old school)
  1377. else
  1378. {
  1379. var
  1380. div = newElement(DIV),
  1381. tomorrow = new Date();
  1382. div.style.behavior = 'url(#default#userData)';
  1383. __body.appendChild(div);
  1384. if ( defined( div.XMLDocument ) )
  1385. {
  1386. __cache_object = div;
  1387. __cache_object.load( ECSSTENDER );
  1388. // set the expiration for 1 week
  1389. tomorrow.setMinutes( tomorrow.getMinutes() + 10080 );
  1390. tomorrow = tomorrow.toUTCString();
  1391. __cache_object.expires = tomorrow;
  1392. clearBrowserCache = function()
  1393. {
  1394. var
  1395. attr = __cache_object.XMLDocument.firstChild.attributes,
  1396. i = attr.length;
  1397. while ( i-- )
  1398. {
  1399. __cache_object.removeAttribute( attr[i].nodeName );
  1400. }
  1401. __cache_object.save( ECSSTENDER );
  1402. };
  1403. readFromBrowserCache = function( cache, key )
  1404. {
  1405. return __cache_object.getAttribute( cache + HYPHEN + key );
  1406. };
  1407. writeToBrowserCache = function( cache, key, value )
  1408. {
  1409. __cache_object.setAttribute( cache + HYPHEN + key, value );
  1410. __cache_object.save( ECSSTENDER );
  1411. };
  1412. }
  1413. }
  1414. }
  1415. function readBrowserCache()
  1416. {
  1417. if ( __no_cache || __local ){ return; }
  1418. enableCache();
  1419. var cache_group, item, count,
  1420. version = 'version',
  1421. cache_version = readFromBrowserCache( ECSSTENDER, version );
  1422. // only use the cache if it was created from the same version of eCSStender
  1423. // this allows us to tweak the cache going forward
  1424. if ( cache_version == eCSStender[version] )
  1425. {
  1426. for ( cache_group in __local_cache )
  1427. {
  1428. if ( ! isInheritedProperty( __local_cache, cache_group ) &&
  1429. defined( cache_group ) )
  1430. {
  1431. count = readFromBrowserCache( ECSSTENDER, cache_group + COUNT );
  1432. if ( defined( count ) )
  1433. {
  1434. if ( cache_group == EXTENSION )
  1435. {
  1436. __t_count = count;
  1437. if ( count < 1 ){ eCSStender.cache = FALSE; }
  1438. }
  1439. while ( count >= 0 )
  1440. {
  1441. item = readFromBrowserCache( cache_group, count );
  1442. if ( item != NULL )
  1443. {
  1444. if ( cache_group == EXTENSION )
  1445. {
  1446. __local_cache[cache_group][EXTENSION+count] = item;
  1447. }
  1448. else
  1449. {
  1450. item = item.split(PIPES);
  1451. if ( item[1] == 'true' ){ item[1] = TRUE; }
  1452. if ( item[1] == 'false' ){ item[1] = FALSE; }
  1453. __local_cache[cache_group][item[0]] = item[1];
  1454. }
  1455. }
  1456. count--;
  1457. }
  1458. }
  1459. }
  1460. }
  1461. }
  1462. clearBrowserCache();
  1463. }
  1464. function writeBrowserCache()
  1465. {
  1466. if ( __no_cache || __local ){ return; }
  1467. var cache_group, key, count, extension;
  1468. for ( cache_group in __local_cache )
  1469. {
  1470. if ( ! isInheritedProperty( __local_cache, cache_group ) &&
  1471. defined( cache_group ) )
  1472. {
  1473. count = 0;
  1474. for ( key in __local_cache[cache_group] )
  1475. {
  1476. if ( ! isInheritedProperty( __local_cache[cache_group], key ) &&
  1477. defined( cache_group ) )
  1478. {
  1479. if ( cache_group == EXTENSION )
  1480. {
  1481. extension = __local_cache[cache_group][key];
  1482. extension[PROCESSED] = [];
  1483. writeToBrowserCache( cache_group, count, extension );
  1484. }
  1485. else
  1486. {
  1487. writeToBrowserCache( cache_group, count, key + PIPES + __local_cache[cache_group][key] );
  1488. }
  1489. count++;
  1490. }
  1491. }
  1492. writeToBrowserCache( ECSSTENDER, cache_group + COUNT, count );
  1493. }
  1494. }
  1495. writeToBrowserCache( ECSSTENDER, 'version', eCSStender.version );
  1496. __cached_out = TRUE;
  1497. }
  1498. function styleObjToString( obj )
  1499. {
  1500. var str = EMPTY, key;
  1501. for ( key in obj )
  1502. {
  1503. if ( ! isInheritedProperty( obj, key ) )
  1504. {
  1505. str += key + COLON + obj[key] + SEMICOLON;
  1506. }
  1507. }
  1508. return str;
  1509. }
  1510. function stringToStyleObj( str )
  1511. {
  1512. var matches, obj = FALSE;
  1513. if ( ( matches = str.exec( /^\{(.*?)\}$/ ) ) != NULL )
  1514. {
  1515. obj = gatherProperties( matches[1] );
  1516. }
  1517. return obj;
  1518. }
  1519. function fingerprint( str )
  1520. {
  1521. // UTF8 encode
  1522. str = str.replace(/\r\n/g,'\n');
  1523. var
  1524. keystr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
  1525. i, iLen = str.length, newstr = EMPTY, c1, c2, c3, e1, e2, e3, e4;
  1526. for ( i=0; i<iLen; i++ )
  1527. {
  1528. c1 = str.charCodeAt(i);
  1529. if ( c1 < 128 )
  1530. {
  1531. newstr += charAt( c1 );
  1532. }
  1533. else if ( (c1 > 127) && (c1 < 2048) )
  1534. {
  1535. newstr += charAt( (c1 >> 6) | 192 );
  1536. newstr += charAt( (c1 & 63) | 128 );
  1537. }
  1538. else
  1539. {
  1540. newstr += charAt( (c1 >> 12) | 224 );
  1541. newstr += charAt( ((c1 >> 6) & 63) | 128 );
  1542. newstr += charAt( (c1 & 63) | 128 );
  1543. }
  1544. }
  1545. str = newstr;
  1546. newstr = EMPTY;
  1547. // base64
  1548. i = 0;
  1549. iLen = str.length;
  1550. while ( i < iLen )
  1551. {
  1552. c1 = str.charCodeAt(i++);
  1553. c2 = str.charCodeAt(i++);
  1554. c3 = str.charCodeAt(i++);
  1555. e1 = c1 >> 2;
  1556. e2 = ((c1 & 3) << 4) | (c2 >> 4);
  1557. e3 = ((c2 & 15) << 2) | (c3 >> 6);
  1558. e4 = c3 & 63;
  1559. if ( isNaN(c2) )
  1560. {
  1561. e3 = e4 = 64;
  1562. }
  1563. else if ( isNaN(c3) )
  1564. {
  1565. e4 = 64;
  1566. }
  1567. newstr += keystr.charAt(e1) + keystr.charAt(e2) +
  1568. keystr.charAt(e3) + keystr.charAt(e4);
  1569. }
  1570. return newstr;
  1571. }
  1572. function writeToLocalCache( group, key, value, encode )
  1573. {
  1574. encode = ! defined( encode ) ? TRUE : encode;
  1575. if ( ! is_false( encode ) ){
  1576. key = fingerprint(key);
  1577. }
  1578. __local_cache[group][key] = value;
  1579. if ( __cached_out )
  1580. {
  1581. writeToBrowserCache( group, key, value );
  1582. }
  1583. }
  1584. function readFromLocalCache( group, key, encode )
  1585. {
  1586. encode = ! defined( encode ) ? TRUE : encode;
  1587. if ( ! is_false( encode ) ){
  1588. key = fingerprint(key);
  1589. }
  1590. var value = __local_cache[group][key];
  1591. return ! defined( value ) ? UNDEFINED : value;
  1592. }
  1593. /*-------------------------------------*
  1594. * Public Methods *
  1595. *-------------------------------------*/
  1596. /**
  1597. * eCSStender::register()
  1598. * registers an extension
  1599. *
  1600. * @param obj keys - define the lookup; supports the following properties:
  1601. * * Mutually exclusive lookup methods:
  1602. * * 'fragment' (str) - a portion of the property to look up
  1603. * * 'prefix' (str) - to lookup by vendor-specific prefix
  1604. * * 'property' (mixed)
  1605. * * to lookup by property, use a string
  1606. * * to lookup by multiple, use an array of strings
  1607. * * 'selector' (mixed) - to find by selector; accepts
  1608. * * compound selectors in a string, separated by commas
  1609. * * a RegExp to match
  1610. * * an array of selector strings or RegExp objects to match
  1611. * * 'filter' (obj) - allows you to filter the lookup results by property name or value
  1612. * * 'property' (mixed) - string or regex to match for the extension to apply
  1613. * * 'value' (mixed) - string or regex to match for the extension to apply
  1614. * * 'media' (str) - restricts the media to which you want to
  1615. * restrict the extension
  1616. * * 'test' (fn) - a test function to run that will determine
  1617. * whether or not the extension should run; should return
  1618. * TRUE if the extension SHOULD run, FALSE if it SHOULD NOT.
  1619. * * 'fingerprint' (str) - a unique ID for the extension (not required)
  1620. * @param mixed properties - the properties you want back; supports for:
  1621. * * FALSE - returns only the looked-up property or properties with
  1622. * the given fragment or prefix
  1623. * * '*' (str) - returns all properties of the selected elements
  1624. * * single property (str) - returns the supplied property
  1625. * * multiple properties (arr) - returns the requested properties
  1626. * @param fn callback - the function to be called when the extension
  1627. * finds a subject
  1628. */
  1629. eCSStender.register = function( keys, properties, callback )
  1630. {
  1631. var eCSStension = {}, lookups, l, temp, t, props=[], key, id=EMPTY;
  1632. // set the lookup type
  1633. if ( defined( keys[SELECTOR] ) )
  1634. {
  1635. eCSStension[FIND_BY] = SELECTOR;
  1636. eCSStension[LOOKUP] = keys[SELECTOR];
  1637. }
  1638. else if ( defined( keys[PROPERTY] ) )
  1639. {
  1640. eCSStension[FIND_BY] = PROPERTY;
  1641. eCSStension[LOOKUP] = keys[PROPERTY];
  1642. }
  1643. else if ( defined( keys[FRAGMENT] ) )
  1644. {
  1645. eCSStension[FIND_BY] = FRAGMENT;
  1646. eCSStension[LOOKUP] = keys[FRAGMENT];
  1647. }
  1648. else if ( defined( keys[PREFIX] ) )
  1649. {
  1650. eCSStension[FIND_BY] = PREFIX;
  1651. eCSStension[LOOKUP] = keys[PREFIX];
  1652. }
  1653. // convert some lookups to arrays to simplify things later on
  1654. if ( eCSStension[FIND_BY] == SELECTOR ||
  1655. eCSStension[FIND_BY] == PROPERTY )
  1656. {
  1657. eCSStension[LOOKUP] = arrayify( eCSStension[LOOKUP] );
  1658. }
  1659. // filter?
  1660. if ( defined( keys[FILTER] ) )
  1661. {
  1662. eCSStension[FILTER] = keys[FILTER];
  1663. }
  1664. // media restriction?
  1665. if ( defined( keys[MEDIA] ) )
  1666. {
  1667. eCSStension[MEDIA] = keys[MEDIA];
  1668. }
  1669. // test first?
  1670. if ( defined( keys[TEST] ) )
  1671. {
  1672. eCSStension[TEST] = keys[TEST];
  1673. }
  1674. // set the properties to capture
  1675. eCSStension[PROPERTIES] = determineProperties( keys, properties );
  1676. // create the fingerprint
  1677. if ( ! defined( keys[FINGERPRINT] ) )
  1678. {
  1679. for ( key in eCSStension )
  1680. {
  1681. if ( ! isInheritedProperty( eCSStension, key ) )
  1682. {
  1683. id += key + COLON + eCSStension[key].toString() + SEMICOLON;
  1684. }
  1685. }
  1686. id = fingerprint(id+'::'+__e_count);
  1687. }
  1688. else
  1689. {
  1690. id = keys[FINGERPRINT];
  1691. }
  1692. // set the callback
  1693. eCSStension[CALLBACK] = callback;
  1694. // create the processed array
  1695. eCSStension[PROCESSED] = [];
  1696. // save the extension
  1697. __eCSStensions[id] = eCSStension;
  1698. __e_count++;
  1699. };
  1700. /**
  1701. * eCSStender::lookup()
  1702. * looks up and returns styles based on the supplied info
  1703. *
  1704. * @param obj lookup - define the lookup; supports the following properties:
  1705. * * Mutually exclusive lookup methods:
  1706. * * FRAGMENT (str) - a portion of the property to look up
  1707. * * PREFIX (str) - to lookup by vendor-specific prefix
  1708. * * PROPERTY (mixed)
  1709. * * to lookup by property, use a string
  1710. * * to lookup by multiple, use an array of strings
  1711. * * SELECTOR (mixed) - to find by selector; accepts
  1712. * * compound selectors in a string, separated by commas
  1713. * * a RegExp to match
  1714. * * an array of selector strings or RegExp objects to match
  1715. * * a function that returns TRUE for a positive match
  1716. * * MEDIA (str) - restricts the media within which you want to search
  1717. * * SPECIFICITY (mixed) - the desired specificity parameters
  1718. * * if an integer, implies maximum specificity
  1719. * * if an object, use keys to set the thresholds:
  1720. * * 'max' (int) - maximum specificity to match
  1721. * * 'min' (int) - minimum specificity to match
  1722. * @param mixed properties - the properties you want back; support for:
  1723. * * FALSE - returns only the looked-up property or properties with
  1724. * the given fragment or prefix
  1725. * * '*' (str) - returns all properties of the selected elements
  1726. * * single property (str) - returns the supplied property
  1727. * * multiple properties (arr) - returns the requested properties
  1728. *
  1729. * @return arr of style objects, each with the following keys:
  1730. * * 'medium' - the medium of the match
  1731. * * PROPERTIES - the properties requested
  1732. * * SELECTOR - the selector matched
  1733. * * SPECIFICITY - the specificity of the selector
  1734. */
  1735. eCSStender.lookup = function( lookup, properties )
  1736. {
  1737. var
  1738. l_specificity = lookup[SPECIFICITY],
  1739. l_selector = lookup[SELECTOR],
  1740. l_property = lookup[PROPERTY],
  1741. l_fragment = lookup[FRAGMENT],
  1742. l_prefix = lookup[PREFIX],
  1743. l_media = lookup[MEDIA],
  1744. props, property,
  1745. min, max,
  1746. medium, e_media,
  1747. sLoop, styles, sorted_styles,
  1748. s, sLen, selector, block, found,
  1749. i, iLen, test, matches = [];
  1750. // figure out specificity params
  1751. if ( defined( l_specificity ) )
  1752. {
  1753. if ( is( l_specificity, NUMBER ) )
  1754. {
  1755. max = l_specificity;
  1756. min = 0;
  1757. }
  1758. else if ( is( l_specificity, OBJECT ) )
  1759. {
  1760. max = l_specificity['max'];
  1761. min = l_specificity['min'];
  1762. }
  1763. }
  1764. // make the selector setup consistent
  1765. if ( defined( l_selector ) )
  1766. {
  1767. l_selector = arrayify( l_selector );
  1768. }
  1769. else if ( defined( l_property ) )
  1770. {
  1771. l_property = arrayify( l_property );
  1772. }
  1773. // figure out properties to return
  1774. props = determineProperties( lookup, properties );
  1775. // loop
  1776. for ( medium in __style_objects )
  1777. {
  1778. // safety for users who are using Prototype or any code that extends Object
  1779. if ( ! isInheritedProperty( __style_objects, medium ) )
  1780. {
  1781. // verify any media restrictions
  1782. if ( defined( l_media ) &&
  1783. l_media != ALL )
  1784. {
  1785. e_media = l_media.split(REGEXP_COMMA);
  1786. if ( medium != ALL &&
  1787. ! in_object( medium, e_media ) )
  1788. {
  1789. continue;
  1790. }
  1791. }
  1792. // start the processing in earnest
  1793. styles = __style_objects[medium];
  1794. sorted_styles = getSortedArray( styles );
  1795. sLoop:
  1796. for ( s=0, sLen=sorted_styles.length; s<sLen; s++ )
  1797. {
  1798. // check the selector
  1799. selector = sorted_styles[s][SELECTOR];
  1800. block = styles[selector];
  1801. if ( defined( l_selector ) )
  1802. {
  1803. found = FALSE;
  1804. for ( i=0, iLen=l_selector.length; i<iLen; i++ )
  1805. {
  1806. if ( selectorMatches( selector, l_selector[i] ) )
  1807. {
  1808. found = TRUE;
  1809. break;
  1810. }
  1811. }
  1812. if ( is_false( found ) )
  1813. {
  1814. continue;
  1815. }
  1816. }
  1817. // check properties
  1818. else if ( defined( l_property ) )
  1819. {
  1820. found = FALSE;
  1821. for ( i=0, iLen=l_property.length; i<iLen; i++ )
  1822. {
  1823. if ( defined( block[l_property[i]] ) )
  1824. {
  1825. found = TRUE;
  1826. break;
  1827. }
  1828. }
  1829. if ( is_false( found ) )
  1830. {
  1831. continue;
  1832. }
  1833. }
  1834. // check fragments, and/or prefixes
  1835. else if ( defined( l_fragment ) ||
  1836. defined( l_prefix ) )
  1837. {
  1838. found = FALSE;
  1839. test = ( defined( l_fragment ) ) ? ANYTHING + l_fragment + ANYTHING
  1840. : HYPHEN + l_prefix + HYPHEN_ANYTHING;
  1841. test = newRegExp( test );
  1842. for ( property in block )
  1843. {
  1844. if ( ! isInheritedProperty( styles, selector ) && // fix for tainted Object
  1845. ! in_object( property, __ignored_props ) &&
  1846. property.match( test ) )
  1847. {
  1848. found = TRUE;
  1849. break;
  1850. }
  1851. }
  1852. if ( is_false( found ) )
  1853. {
  1854. continue;
  1855. }
  1856. }
  1857. // check the specificity
  1858. if ( defined( l_specificity ) )
  1859. {
  1860. if ( block[SPECIFICITY] < min ||
  1861. block[SPECIFICITY] > max )
  1862. {
  1863. continue;
  1864. }
  1865. }
  1866. // if you made it this far, you passed the tests
  1867. matches.push({
  1868. medium: medium,
  1869. properties: extractProperties( medium, selector, props ),
  1870. selector: selector,
  1871. specificity: block[SPECIFICITY]
  1872. });
  1873. } // end styles loop
  1874. }
  1875. } // end medium loop
  1876. // back what we found
  1877. return matches;
  1878. };
  1879. /**
  1880. * eCSStender::addMethod()
  1881. * sets a custom JavaScript method in eCSStender's methods object
  1882. *
  1883. * @param str name - a name for the method
  1884. * @param func the_function - the function to store
  1885. */
  1886. eCSStender.addMethod = function( name, the_function )
  1887. {
  1888. if ( ! defined( eCSStender.methods[name] ) )
  1889. {
  1890. eCSStender.methods[name] = the_function;
  1891. }
  1892. };
  1893. /**
  1894. * eCSStender::onComplete()
  1895. * adds a function to be called on completion of eCSStender's run
  1896. *
  1897. * @param func the_function - the function to store
  1898. */
  1899. eCSStender.onComplete = function( the_function )
  1900. {
  1901. __on_complete.push( the_function );
  1902. };
  1903. /**
  1904. * eCSStender::embedCSS()
  1905. * embeds styles to the appropriate media
  1906. *
  1907. * @param str styles - the styles to embed
  1908. * @param str media - the media to apply the stylesheet to (optional)
  1909. * @param bool delay - whether or not to delay the writing of the stylesheet (default = true)
  1910. *
  1911. * @return obj - the STYLE element
  1912. */
  1913. eCSStender.embedCSS = function( styles, media, delay )
  1914. {
  1915. // determine the medium
  1916. media = media || ALL;
  1917. // determine whether to delay the write or not
  1918. delay = defined( delay ) ? delay : TRUE;
  1919. // determine the id
  1920. var id = 'eCSStension-' + media, style;
  1921. // find or create the embedded stylesheet
  1922. if ( ! in_object( media, __embedded_css ) )
  1923. {
  1924. // make the new style element
  1925. style = newStyleElement( media, id, delay );
  1926. // store the medium
  1927. __embedded_css.push( media );
  1928. }
  1929. else
  1930. {
  1931. style = DOCUMENT.getElementById( id );
  1932. }
  1933. // add the rules to the sheet
  1934. if ( style != NULL )
  1935. {
  1936. if ( ! delay )
  1937. {
  1938. addRules( style, styles );
  1939. }
  1940. else
  1941. {
  1942. extractStyleBlocks( media, styles, id );
  1943. }
  1944. }
  1945. // return the style element
  1946. return style;
  1947. };
  1948. /**
  1949. * eCSStender::newStyleElement()
  1950. * adds a new stylesheet to the document
  1951. *
  1952. * @param str media - the media to apply the stylesheet to
  1953. * @param str id - the id to give the stylesheet (optional)
  1954. * @param bool delay - whether or not to delay the writing of the stylesheet (default = true)
  1955. *
  1956. * @return obj - the STYLE element
  1957. */
  1958. function newStyleElement( media, id, delay )
  1959. {
  1960. // clone the model style element
  1961. var style = __style.cloneNode( TRUE );
  1962. // set the media type & id
  1963. media = media || ALL;
  1964. style.setAttribute( MEDIA, media );
  1965. id = id || 'temp-' + Math.round( Math.random() * 2 + 1 );
  1966. style.setAttribute( 'id', id );
  1967. // determine whether to delay the write or not
  1968. delay = defined( delay ) ? delay : TRUE;
  1969. if ( delay )
  1970. {
  1971. __delayed[id] = {};
  1972. //style.disabled = TRUE;
  1973. }
  1974. __head.appendChild( style );
  1975. // return the element reference
  1976. return style;
  1977. }
  1978. eCSStender.newStyleElement = newStyleElement;
  1979. /**
  1980. * eCSStender::addRules()
  1981. * adds rules to a specific stylesheet
  1982. *
  1983. * @param obj el - the stylesheet
  1984. * @param str styles - the style rules to add
  1985. */
  1986. __style.setAttribute( TYPE, 'text/css' );
  1987. if ( defined( __style.styleSheet ) )
  1988. {
  1989. addRules = function( el, styles )
  1990. {
  1991. el.styleSheet.cssText += styles;
  1992. };
  1993. }
  1994. else
  1995. {
  1996. addRules = function( el, styles )
  1997. {
  1998. el.appendChild( DOCUMENT.createTextNode( styles ) );
  1999. };
  2000. }
  2001. eCSStender.addRules = addRules;
  2002. /**
  2003. * eCSStender::isSupported()
  2004. * tests support for properties and selectors
  2005. *
  2006. * Option 1: Selector test
  2007. * eCSStender::isSupported( type, selector, html, el )
  2008. * @param str type - 'property'
  2009. * @param str selector - the selector
  2010. * @param obj html - HTML to test against
  2011. * @param obj el - the element the selector should select
  2012. * @return bool - TRUE for success, FALSE for failure
  2013. *
  2014. * Option 2: Property Test (simple)
  2015. * eCSStender::isSupported( type, test )
  2016. * @param str type - 'selector'
  2017. * @param str test - the property: value pair to test
  2018. * @return bool - TRUE for success, FALSE for failure
  2019. *
  2020. * Option 3: Property Test (complex)
  2021. * eCSStender::isSupported( type, property, value )
  2022. * @param str type - 'selector'
  2023. * @param str property - the property to test
  2024. * @param mixed value - the string value or an array of possible values
  2025. * @return bool - TRUE for success, FALSE for failure
  2026. *
  2027. * Option 3: Storage
  2028. * eCSStender::isSupported( type, what, result )
  2029. * @param str type - 'selector'
  2030. * @param str what - the key to store
  2031. * @param bool result - the result of the test you want stored
  2032. * @return bool - the result you passed in
  2033. */
  2034. eCSStender.isSupported = function( type )
  2035. {
  2036. var result,
  2037. arg = arguments,
  2038. aLen = arg.length,
  2039. // global test vars
  2040. what = arg[1],
  2041. value = arg[2] || NULL,
  2042. html = value,
  2043. el = arg[3] || NULL,
  2044. // property test vars
  2045. property, settable = TRUE, i,
  2046. computed = WINDOW.getComputedStyle,
  2047. VISIBILITY = 'visibility',
  2048. HIDDEN = 'hidden',
  2049. // selector test vars
  2050. style;
  2051. // check for cached value
  2052. if ( defined( result = readFromLocalCache( type, what ) ) )
  2053. {
  2054. // we'll return the result at the end
  2055. }
  2056. // actual tests
  2057. else
  2058. {
  2059. result = FALSE;
  2060. // just caching the value for later
  2061. if ( is( value, 'boolean' ) )
  2062. {
  2063. result = value;
  2064. }
  2065. // property test
  2066. else if ( type == PROPERTY )
  2067. {
  2068. // test element
  2069. el = newElement(DIV);
  2070. // are property and value flowing in separately?
  2071. if ( value )
  2072. {
  2073. property = what;
  2074. value = arrayify( value );
  2075. }
  2076. else
  2077. {
  2078. what = what.split(REGEXP_P_V);
  2079. property = what[0];
  2080. value = [ trim( what[1] ) ];
  2081. // reset what for the cache
  2082. what = arg[1];
  2083. }
  2084. __body.appendChild( el );
  2085. toggleExpando();
  2086. if ( ! addInlineStyle( el, property, value[0] ) )
  2087. {
  2088. settable = FALSE;
  2089. }
  2090. toggleExpando();
  2091. if ( settable )
  2092. {
  2093. i = value.length;
  2094. while ( i-- &&
  2095. ! result )
  2096. {
  2097. result = ( zero_out( getCSSValue( el, property ) ) == value[i] );
  2098. }
  2099. }
  2100. // cleanup
  2101. __body.removeChild( el );
  2102. }
  2103. // selector test
  2104. else if ( type == SELECTOR )
  2105. {
  2106. // append the test markup (if it exists) and the test style element
  2107. if ( html )
  2108. {
  2109. __body.appendChild( html );
  2110. }
  2111. style = newStyleElement( SCREEN, FALSE, FALSE );
  2112. // if the browser doesn't support the selector, it should error out
  2113. try {
  2114. addRules( style, what + OPEN_CURLY + VISIBILITY + COLON + HIDDEN + SEMICOLON + CLOSE_CURLY );
  2115. // if it succeeds, we don't want to run the eCSStension
  2116. if ( getCSSValue( el, VISIBILITY ) == HIDDEN )
  2117. {
  2118. result = TRUE;
  2119. }
  2120. } catch( e ){}
  2121. // cleanup
  2122. if ( html )
  2123. {
  2124. __body.removeChild( html );
  2125. }
  2126. style.parentNode.removeChild( style );
  2127. }
  2128. // write it to the cache and clean up
  2129. writeToLocalCache( type, what, result );
  2130. value = html = el = style = NULL;
  2131. }
  2132. return result;
  2133. };
  2134. /**
  2135. * eCSStender::applyWeightedStyle()
  2136. * apply a weighted inline style (based on specificity)
  2137. *
  2138. * @param obj el - the element to apply the property to
  2139. * @param obj properties - a hash of the properties to assign
  2140. * @param int specificity - the specificity of the selector
  2141. */
  2142. eCSStender.applyWeightedStyle = function( el, properties, specificity )
  2143. {
  2144. if ( ! defined( el.inlineStyles ) )
  2145. {
  2146. el.inlineStyles = {};
  2147. }
  2148. var prop, styles = el.inlineStyles;
  2149. for ( prop in properties )
  2150. {
  2151. if ( ! isInheritedProperty( properties, prop ) &&
  2152. ( ! defined( styles[prop] ) ||
  2153. styles[prop] <= specificity ) )
  2154. {
  2155. addInlineStyle( el, prop, properties[prop] );
  2156. el.inlineStyles[prop] = specificity;
  2157. }
  2158. }
  2159. };
  2160. /**
  2161. * eCSStender::ignore()
  2162. * tells eCSStendet to ignore a file or files
  2163. *
  2164. * @param mixed sheets - a string or array of stylesheet paths to ignore
  2165. */
  2166. eCSStender.ignore = function( sheets )
  2167. {
  2168. if ( is( sheets, STRING ) )
  2169. {
  2170. sheets = [ sheets ];
  2171. }
  2172. else if ( ! is( sheets, ARRAY ) )
  2173. {
  2174. return;
  2175. }
  2176. for ( var i=0, iLen=sheets.length; i<iLen; i++ )
  2177. {
  2178. __ignored_css.push( sheets[i] );
  2179. }
  2180. };
  2181. /**
  2182. * eCSStender::disableCache()
  2183. * tells eCSStender not to cache or work from the cache
  2184. */
  2185. eCSStender.disableCache = function()
  2186. {
  2187. __no_cache = TRUE;
  2188. };
  2189. /**
  2190. * eCSStender::trim()
  2191. * trims a string
  2192. *
  2193. * @param str str - the string to trim
  2194. *
  2195. * @return str - the trimmed string
  2196. */
  2197. function trim( str )
  2198. {
  2199. if ( is( str, STRING ) )
  2200. {
  2201. return str.replace( /^\s+|\s+$/g, EMPTY );
  2202. }
  2203. return str;
  2204. }
  2205. eCSStender.trim = trim;
  2206. /**
  2207. * eCSStender::loadScript()
  2208. * dynamically loads a JavaScript file without loading the same one twice
  2209. *
  2210. * @param str src - the path to the JavaScript
  2211. * @param fn callback - optional callback to run when script is loaded
  2212. */
  2213. __script.setAttribute( TYPE, 'text/javascript' );
  2214. eCSStender.loadScript = function( src, callback )
  2215. {
  2216. var
  2217. scripts = DOCUMENT.getElementsByTagName('script'),
  2218. i = scripts.length,
  2219. script = __script.cloneNode( TRUE ),
  2220. loaded = FALSE;
  2221. callback = callback || EMPTY_FN;
  2222. while ( i-- )
  2223. {
  2224. if ( scripts[i].src == src )
  2225. {
  2226. script = FALSE;
  2227. }
  2228. }
  2229. if ( script )
  2230. {
  2231. script.onload = script.onreadystatechange = function(){
  2232. if ( ! loaded &&
  2233. ( ! defined( script.readyState ) ||
  2234. script.readyState == 'loaded' ||
  2235. script.readyState == COMPLETE ) )
  2236. {
  2237. loaded = TRUE;
  2238. script.onload = script.onreadystatechange = NULL;
  2239. callback();
  2240. }
  2241. };
  2242. script.setAttribute( 'src', src );
  2243. __head.appendChild( script );
  2244. }
  2245. else
  2246. {
  2247. setTimeout( callback, 100 );
  2248. }
  2249. };
  2250. /**
  2251. * eCSStender::isInheritedProperty()
  2252. * tests whether the given property is inherited
  2253. *
  2254. * @param obj obj - the object to test
  2255. * @param str prop - the property to look for
  2256. *
  2257. * @return bool - TRUE is property is inherited, FALSE is it isn't
  2258. */
  2259. function isInheritedProperty( obj, prop )
  2260. {
  2261. if ( obj.hasOwnProperty )
  2262. {
  2263. isInheritedProperty = function( obj, prop )
  2264. {
  2265. return ! obj.hasOwnProperty(prop);
  2266. };
  2267. }
  2268. else
  2269. {
  2270. isInheritedProperty = function( obj, prop )
  2271. {
  2272. var c = obj.constructor;
  2273. if ( c &&
  2274. c.prototype )
  2275. {
  2276. return obj[prop] === c.prototype[prop];
  2277. }
  2278. return TRUE;
  2279. };
  2280. }
  2281. eCSStender.isInheritedProperty = isInheritedProperty;
  2282. return isInheritedProperty( obj, prop );
  2283. }
  2284. /**
  2285. * eCSStender::getCSSValue()
  2286. * gets the computed value of a CSS property
  2287. *
  2288. * @param obj el - the element
  2289. * @param str prop - the property name
  2290. *
  2291. * @return str - the value
  2292. */
  2293. function getCSSValue( el, prop )
  2294. {
  2295. var computed = WINDOW.getComputedStyle;
  2296. if ( el.currentStyle )
  2297. {
  2298. getCSSValue = function( el, prop )
  2299. {
  2300. return el.currentStyle[camelize( prop )];
  2301. };
  2302. }
  2303. else if ( computed )
  2304. {
  2305. getCSSValue = function( el, prop )
  2306. {
  2307. return computed( el, NULL ).getPropertyValue( prop );
  2308. };
  2309. }
  2310. else
  2311. {
  2312. getCSSValue = function()
  2313. {
  2314. return FALSE;
  2315. };
  2316. }
  2317. eCSStender.getCSSValue = getCSSValue;
  2318. return getCSSValue( el, prop );
  2319. }
  2320. /**
  2321. * eCSStender::makeUniqueClass()
  2322. * creates a unique class for an element
  2323. *
  2324. * @return str - the unique class
  2325. */
  2326. eCSStender.makeUniqueClass = function()
  2327. {
  2328. var start = new Date();
  2329. start = start.getTime();
  2330. function init()
  2331. {
  2332. return ECSSTENDER + HYPHEN + start++;
  2333. }
  2334. eCSStender.makeUniqueClass = init;
  2335. return init();
  2336. };
  2337. /**
  2338. * eCSStender::addClass()
  2339. * adds a class to an element
  2340. *
  2341. * @param obj el - the element to have its class augmented
  2342. * @param str the_class - the class to add
  2343. * @param RegExp re - a regular expression to match the class (optional)
  2344. */
  2345. function addClass( el, the_class, re )
  2346. {
  2347. re = re || makeClassRegExp( the_class );
  2348. if ( ! hasClass( el, the_class, re ) )
  2349. {
  2350. el.className += SPACE + the_class;
  2351. }
  2352. }
  2353. eCSStender.addClass = addClass;
  2354. /**
  2355. * eCSStender::removeClass()
  2356. * removes a class from an element
  2357. *
  2358. * @param obj el - the element to have its class augmented
  2359. * @param str the_class - the class to add
  2360. * @param RegExp re - a regular expression to match the class (optional)
  2361. */
  2362. function removeClass( el, the_class, re )
  2363. {
  2364. re = re || makeClassRegExp( the_class );
  2365. if ( hasClass( el, the_class, re ) )
  2366. {
  2367. el.className = trim( el.className.replace( re, SPACE ) );
  2368. }
  2369. };
  2370. eCSStender.removeClass = removeClass;
  2371. /**
  2372. * eCSStender::hasClass()
  2373. * checks to see if an element has the given class
  2374. *
  2375. * @param obj el - the element to have its class augmented
  2376. * @param str the_class - the class to add
  2377. * @param RegExp re - a regular expression to match the class (optional)
  2378. */
  2379. function hasClass( el, the_class, re )
  2380. {
  2381. re = re || makeClassRegExp( the_class );
  2382. return el.className.match( re );
  2383. };
  2384. eCSStender.hasClass = hasClass;
  2385. /**
  2386. * eCSStender::toggleClass()
  2387. * adds or removes a class based on whether it's already there
  2388. *
  2389. * @param obj el - the element to have its class augmented
  2390. * @param str the_class - the class to add
  2391. */
  2392. function toggleClass( el, the_class )
  2393. {
  2394. var re = makeClassRegExp( the_class );
  2395. if ( hasClass( el, the_class, re ) )
  2396. {
  2397. removeClass( el, the_class, re );
  2398. }
  2399. else
  2400. {
  2401. addClass( el, the_class, re );
  2402. }
  2403. };
  2404. eCSStender.toggleClass = toggleClass;
  2405. /*-------------------------------------*
  2406. * DOM Loaded Trigger *
  2407. * Based on jQuery's *
  2408. *-------------------------------------*/
  2409. (function(){
  2410. var
  2411. DCL = 'DOMContentLoaded',
  2412. ORC = 'onreadystatechange',
  2413. __old_onload = WINDOW.onload,
  2414. doScroll = DOCUMENT.documentElement.doScroll;
  2415. // for Mozilla/Safari/Opera9
  2416. if ( DOCUMENT.addEventListener )
  2417. {
  2418. DOCUMENT.addEventListener( DCL, function(){
  2419. DOCUMENT.removeEventListener( DCL, arguments.callee, FALSE );
  2420. initialize();
  2421. }, FALSE );
  2422. }
  2423. // If IE event model is used
  2424. else if ( DOCUMENT.attachEvent )
  2425. {
  2426. // ensure firing before onload, maybe late but safe also for iframes
  2427. DOCUMENT.attachEvent( ORC, function(){
  2428. if ( DOCUMENT.readyState === COMPLETE ) {
  2429. DOCUMENT.detachEvent( ORC, arguments.callee );
  2430. initialize();
  2431. }
  2432. });
  2433. // If IE and not an iframe, continually check to see if the document is ready
  2434. if ( doScroll &&
  2435. WINDOW == WINDOW.top )
  2436. {
  2437. (function(){
  2438. try {
  2439. // If IE is used, use the trick by Diego Perini
  2440. // http://javascript.nwbox.com/IEContentLoaded/
  2441. doScroll('left');
  2442. }
  2443. catch( error )
  2444. {
  2445. setTimeout( arguments.callee, 0 );
  2446. return;
  2447. }
  2448. // and execute any waiting functions
  2449. initialize();
  2450. })();
  2451. }
  2452. }
  2453. })();
  2454. })();