vendor: 7,927 bytes, lines 1-258
1/*! 2 * skrollr stylesheets. 3 * Parses stylesheets and searches for skrollr keyframe declarations. 4 * Converts them to data-attributes. 5 * Doesn't expose any globals. 6 */ 7(function(window, document, undefined) { 8 'use strict'; 9 10 var content; 11 var contents = []; 12 13 //Finds the declaration of an animation block. 14 var rxAnimation = /@-skrollr-keyframes\s+([\w-]+)/g; 15 16 //Finds the block of keyframes inside an animation block. 17 //http://regexpal.com/ saves your ass with stuff like this. 18 var rxKeyframes = /\s*\{\s*((?:[^{]+\{[^}]*\}\s*)+?)\s*\}/g; 19 20 //Gets a single keyframe and the properties inside. 21 var rxSingleKeyframe = /([\w\-]+)\s*\{([^}]+)\}/g; 22 23 //Optional keyframe name prefix to work around SASS (>3.4) issues 24 var keyframeNameOptionalPrefix = 'skrollr-'; 25 26 //Finds usages of the animation. 27 var rxAnimationUsage = /-skrollr-animation-name\s*:\s*([\w-]+)/g; 28 29 //Finds usages of attribute setters. 30 var rxAttributeSetter = /-skrollr-(anchor-target|smooth-scrolling|emit-events|menu-offset)\s*:\s*['"]([^'"]+)['"]/g; 31 32 var fetchRemote = function(url) { 33 var xhr = new XMLHttpRequest(); 34 35 /* 36 * Yes, these are SYNCHRONOUS requests. 37 * Simply because skrollr stylesheets should run while the page is loaded. 38 * Get over it. 39 */ 40 try { 41 xhr.open('GET', url, false); 42 xhr.send(null); 43 } catch (e) { 44 //Fallback to XDomainRequest if available 45 if (window.XDomainRequest) { 46 xhr = new XDomainRequest(); 47 xhr.open('GET', url, false); 48 xhr.send(null); 49 } 50 } 51 52 return xhr.responseText; 53 }; 54 55 //"main" 56 var kickstart = function(stylesheets) { 57 //Iterate over all stylesheets, embedded and remote. 58 for(var stylesheetIndex = 0; stylesheetIndex < stylesheets.length; stylesheetIndex++) { 59 var sheet = stylesheets[stylesheetIndex]; 60 61 if(sheet.tagName === 'LINK') { 62 if(sheet.getAttribute('data-skrollr-stylesheet') === null) { 63 continue; 64 } 65 66 //Test media attribute if matchMedia available. 67 if(window.matchMedia) { 68 var media = sheet.getAttribute('media'); 69 70 if(media && !matchMedia(media).matches) { 71 continue; 72 } 73 } 74 75 //Remote stylesheet, fetch it (synchrnonous). 76 content = fetchRemote(sheet.href); 77 } else { 78 //Embedded stylesheet, grab the node content. 79 content = sheet.textContent || sheet.innerText || sheet.innerHTML; 80 } 81 82 if(content) { 83 contents.push(content); 84 } 85 } 86 87 //We take the stylesheets in reverse order. 88 //This is needed to ensure correct order of stylesheets and inline styles. 89 contents.reverse(); 90 91 var animations = {}; 92 var selectors = []; 93 var attributes = []; 94 95 //Now parse all stylesheets. 96 for(var contentIndex = 0; contentIndex < contents.length; contentIndex++) { 97 content = contents[contentIndex]; 98 99 parseAnimationDeclarations(content, animations); 100 parseAnimationUsage(content, selectors); 101 parseAttributeSetters(content, attributes); 102 } 103 104 applyKeyframeAttributes(animations, selectors); 105 applyAttributeSetters(attributes); 106 }; 107 108 //Finds animation declarations and puts them into the output map. 109 var parseAnimationDeclarations = function(input, output) { 110 rxAnimation.lastIndex = 0; 111 112 var animation; 113 var rawKeyframes; 114 var keyframe; 115 var curAnimation; 116 117 while((animation = rxAnimation.exec(input)) !== null) { 118 //Grab the keyframes inside this animation. 119 rxKeyframes.lastIndex = rxAnimation.lastIndex; 120 rawKeyframes = rxKeyframes.exec(input); 121 122 //Grab the single keyframes with their CSS properties. 123 rxSingleKeyframe.lastIndex = 0; 124 125 //Save the animation in an object using it's name as key. 126 curAnimation = output[animation[1]] = {}; 127 128 while((keyframe = rxSingleKeyframe.exec(rawKeyframes[1])) !== null) { 129 //Put all keyframes inside the animation using the keyframe (like botttom-top, or 100) as key 130 //and the properties as value (just the raw string, newline stripped). 131 curAnimation[keyframe[1]] = keyframe[2].replace(/[\n\r\t]/g, ''); 132 } 133 } 134 }; 135 136 //Extracts the selector of the given block by walking backwards to the start of the block. 137 var extractSelector = function(input, startIndex) { 138 var begin; 139 var end = startIndex; 140 141 //First find the curly bracket that opens this block. 142 while(end-- && input.charAt(end) !== '{') {} 143 144 //The end is now fixed to the right of the selector. 145 //Now start there to find the begin of the selector. 146 begin = end; 147 148 //Now walk farther backwards until we grabbed the whole selector. 149 //This either ends at beginning of string or at end of next block. 150 while(begin-- && input.charAt(begin - 1) !== '}') {} 151 152 //Return the cleaned selector. 153 return input.substring(begin, end).replace(/[\n\r\t]/g, ''); 154 }; 155 156 //Finds usage of animations and puts the selectors into the output array. 157 var parseAnimationUsage = function(input, output) { 158 var match; 159 var selector; 160 161 rxAnimationUsage.lastIndex = 0; 162 163 while((match = rxAnimationUsage.exec(input)) !== null) { 164 //Extract the selector of the block we found the animation in. 165 selector = extractSelector(input, rxAnimationUsage.lastIndex); 166 167 //Associate this selector with the animation name. 168 output.push([selector, match[1]]); 169 } 170 }; 171 172 //Finds usage of attribute setters and puts the selector and attribute data into the output array. 173 var parseAttributeSetters = function(input, output) { 174 var match; 175 var selector; 176 177 rxAttributeSetter.lastIndex = 0; 178 179 while((match = rxAttributeSetter.exec(input)) !== null) { 180 //Extract the selector of the block we found the animation in. 181 selector = extractSelector(input, rxAttributeSetter.lastIndex); 182 183 //Associate this selector with the attribute name and value. 184 output.push([selector, match[1], match[2]]); 185 } 186 }; 187 188 //Applies the keyframes (as data-attributes) to the elements. 189 var applyKeyframeAttributes = function(animations, selectors) { 190 var elements; 191 var keyframes; 192 var keyframeName; 193 var cleanKeyframeName; 194 var elementIndex; 195 var attributeName; 196 var attributeValue; 197 var curElement; 198 199 for(var selectorIndex = 0; selectorIndex < selectors.length; selectorIndex++) { 200 elements = document.querySelectorAll(selectors[selectorIndex][0]); 201 202 if(!elements) { 203 continue; 204 } 205 206 keyframes = animations[selectors[selectorIndex][1]]; 207 208 for(keyframeName in keyframes) { 209 if(keyframeName.indexOf(keyframeNameOptionalPrefix) === 0) { 210 cleanKeyframeName = keyframeName.substring(keyframeNameOptionalPrefix.length); 211 } else { 212 cleanKeyframeName = keyframeName; 213 } 214 215 for(elementIndex = 0; elementIndex < elements.length; elementIndex++) { 216 curElement = elements[elementIndex]; 217 attributeName = 'data-' + cleanKeyframeName; 218 attributeValue = keyframes[keyframeName]; 219 220 //If the element already has this keyframe inline, give the inline one precedence by putting it on the right side. 221 //The inline one may actually be the result of the keyframes from another stylesheet. 222 //Since we reversed the order of the stylesheets, everything comes together correctly here. 223 if(curElement.hasAttribute(attributeName)) { 224 attributeValue += curElement.getAttribute(attributeName); 225 } 226 227 curElement.setAttribute(attributeName, attributeValue); 228 } 229 } 230 } 231 }; 232 233 //Applies the keyframes (as data-attributes) to the elements. 234 var applyAttributeSetters = function(selectors) { 235 var curSelector; 236 var elements; 237 var attributeName; 238 var attributeValue; 239 var elementIndex; 240 241 for(var selectorIndex = 0; selectorIndex < selectors.length; selectorIndex++) { 242 curSelector = selectors[selectorIndex]; 243 elements = document.querySelectorAll(curSelector[0]); 244 attributeName = 'data-' + curSelector[1]; 245 attributeValue = curSelector[2]; 246 247 if(!elements) { 248 continue; 249 } 250 251 for(elementIndex = 0; elementIndex < elements.length; elementIndex++) { 252 elements[elementIndex].setAttribute(attributeName, attributeValue); 253 } 254 } 255 }; 256 257 kickstart(document.querySelectorAll('link, style')); 258}(window, document));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.