css-variables-polyfill.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. let cssVarPoly = {
  2. init: function() {
  3. // first lets see if the browser supports CSS variables
  4. // No version of IE supports window.CSS.supports, so if that isn't supported in the first place we know CSS variables is not supported
  5. // Edge supports supports, so check for actual variable support
  6. if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) {
  7. // this browser does support variables, abort
  8. console.log('your browser supports CSS variables, aborting and letting the native support handle things.');
  9. return;
  10. } else {
  11. // edge barfs on console statements if the console is not open... lame!
  12. console.log('no support for you! polyfill all (some of) the things!!');
  13. document.querySelector('body').classList.add('cssvars-polyfilled');
  14. }
  15. cssVarPoly.ratifiedVars = {};
  16. cssVarPoly.varsByBlock = {};
  17. cssVarPoly.oldCSS = {};
  18. cssVarPoly.media = {};
  19. cssVarPoly.hrefs = {};
  20. // start things off
  21. cssVarPoly.findCSS();
  22. cssVarPoly.updateCSS();
  23. },
  24. // see https://stackoverflow.com/a/6969486
  25. escapeRegExp: function(str) {
  26. return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
  27. },
  28. // find all the css blocks, save off the content, and look for variables
  29. findCSS: function() {
  30. let styleBlocks = document.querySelectorAll('style:not(.inserted),link[type="text/css"]');
  31. // we need to track the order of the style/link elements when we save off the CSS, set a counter
  32. let counter = 1;
  33. // loop through all CSS blocks looking for CSS variables being set
  34. [].forEach.call(styleBlocks, function(block) {
  35. // console.log(block.nodeName);
  36. let theCSS = '';
  37. if (block.nodeName === 'STYLE') {
  38. // console.log("style");
  39. theCSS = block.innerHTML;
  40. cssVarPoly.findSetters(theCSS, counter);
  41. } else if (block.nodeName === 'LINK' && block.getAttribute('href').match( /bb-plugin|wp-content\/themes/ ) ) {
  42. cssVarPoly.media[counter] = block.getAttribute( 'media' );
  43. cssVarPoly.hrefs[counter] = block.getAttribute( 'href' );
  44. cssVarPoly.getLink(block.getAttribute( 'href' ), counter, function(counter, request) {
  45. // no setters in links for us
  46. // cssVarPoly.findSetters(request.responseText, counter);
  47. cssVarPoly.oldCSS[counter] = request.responseText;
  48. cssVarPoly.updateCSS();
  49. });
  50. theCSS = '';
  51. }
  52. // save off the CSS to parse through again later. the value may be empty for links that are waiting for their ajax return, but this will maintain the order
  53. // console.log( counter, block );
  54. cssVarPoly.oldCSS[counter] = theCSS;
  55. counter++;
  56. });
  57. },
  58. // find all the "--variable: value" matches in a provided block of CSS and add them to the master list
  59. findSetters: function(theCSS, counter) {
  60. // console.log(theCSS);
  61. cssVarPoly.varsByBlock[counter] = theCSS.match(/(--[-\w]+:.*;)/g) || [];
  62. },
  63. // run through all the CSS blocks to update the variables and then inject on the page
  64. updateCSS: function() {
  65. // first lets loop through all the variables to make sure later vars trump earlier vars
  66. cssVarPoly.ratifySetters(cssVarPoly.varsByBlock);
  67. // loop through the css blocks (styles and links)
  68. for (let curCSSID in cssVarPoly.oldCSS) {
  69. // console.log("curCSS:",cssVarPoly.oldCSS[curCSSID]);
  70. let cachedCSS = cssVarPoly.hrefs[ curCSSID ] ? localStorage.getItem( 'vamtam-theme-css-' + cssVarPoly.hrefs[ curCSSID ] ) : null;
  71. let newCSS;
  72. if ( ! cachedCSS ) {
  73. newCSS = cssVarPoly.replaceGetters(cssVarPoly.oldCSS[curCSSID], cssVarPoly.ratifiedVars);
  74. if ( cssVarPoly.hrefs[ curCSSID ] ) {
  75. localStorage.setItem( 'vamtam-theme-css-' + cssVarPoly.hrefs[ curCSSID ], newCSS );
  76. }
  77. } else {
  78. newCSS = cachedCSS;
  79. }
  80. // put it back into the page
  81. // first check to see if this block exists already
  82. if (document.querySelector('#inserted' + curCSSID)) {
  83. // console.log("updating")
  84. document.querySelector('#inserted' + curCSSID).innerHTML = newCSS;
  85. } else {
  86. var style = document.createElement('style');
  87. style.type = 'text/css';
  88. style.innerHTML = newCSS;
  89. style.classList.add('inserted');
  90. style.id = 'inserted' + curCSSID;
  91. if ( cssVarPoly.media[ curCSSID ] ) {
  92. style.media = cssVarPoly.media[ curCSSID ];
  93. }
  94. document.getElementsByTagName('head')[0].appendChild(style);
  95. // console.log("adding", curCSSID, style);
  96. }
  97. };
  98. },
  99. // parse a provided block of CSS looking for a provided list of variables and replace the --var-name with the correct value
  100. replaceGetters: function(curCSS, varList) {
  101. if ( curCSS ) {
  102. // console.log(varList);
  103. for (let theVar in varList) {
  104. // console.log(theVar);
  105. // match the variable with the actual variable name
  106. let getterRegex = new RegExp('var\\(\\s*' + cssVarPoly.escapeRegExp( theVar ) + '\\s*\\)', 'g');
  107. curCSS = curCSS.replace(getterRegex, varList[theVar]);
  108. }
  109. /*
  110. // now check for any getters that are left that have fallbacks
  111. let getterRegex2 = new RegExp('var\\(\\s*.+\\s*,\\s*(.+)\\)', 'g');
  112. // console.log(curCSS);
  113. let matches = curCSS.match(getterRegex2);
  114. if (matches) {
  115. // console.log("matches",matches);
  116. matches.forEach(function(match) {
  117. // console.log(match.match(/var\(.+,\s*(.+)\)/))
  118. // find the fallback within the getter
  119. curCSS = curCSS.replace(match, match.match(/var\(.+,\s*(.+)\)/)[1]);
  120. });
  121. }
  122. */
  123. }
  124. // console.log(curCSS);
  125. return curCSS;
  126. },
  127. // determine the css variable name value pair and track the latest
  128. ratifySetters: function(varList) {
  129. // console.log("varList:",varList);
  130. // loop through each block in order, to maintain order specificity
  131. for (let curBlock in varList) {
  132. let curVars = varList[curBlock];
  133. // console.log("curVars:",curVars);
  134. // loop through each var in the block
  135. curVars.forEach(function(theVar) {
  136. // console.log(theVar);
  137. // split on the name value pair separator
  138. let matches = theVar.split(':');
  139. // console.log(matches);
  140. // put it in an object based on the varName. Each time we do this it will override a previous use and so will always have the last set be the winner
  141. // 0 = the name, 1 = the value, strip off the ; if it is there
  142. let name = matches[0].trim();
  143. let value = matches.slice(1).join( ':' ).trim();
  144. cssVarPoly.ratifiedVars[ name ] = value.replace(/;/, '');
  145. });
  146. };
  147. // console.log(ratifiedVars);
  148. },
  149. // get the CSS file (same domain for now)
  150. getLink: function(url, counter, success) {
  151. // console.log( 'will get: ', url );
  152. var request = new XMLHttpRequest();
  153. request.open('GET', url, true);
  154. request.overrideMimeType('text/css;');
  155. request.onload = function() {
  156. if (request.status >= 200 && request.status < 400) {
  157. // Success!
  158. // console.log(request.responseText);
  159. if (typeof success === 'function') {
  160. success(counter, request);
  161. }
  162. } else {
  163. // We reached our target server, but it returned an error
  164. console.warn('an error was returned from:', url);
  165. }
  166. };
  167. request.onerror = function() {
  168. // There was a connection error of some sort
  169. console.warn('we could not get anything from:', url);
  170. };
  171. request.send();
  172. }
  173. };
  174. // hash = function(s){
  175. // return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);
  176. // }
  177. cssVarPoly.init();