1// OPP 2window.OPP = window.OPP || {}; 3 4// Modernizr Tests 5(function (w, d) { 6 if (!w.Modernizr) { return; } 7 8 // Detect support for media queries and layouts for use by js and css 9 Modernizr.addTest('mediaquery', Modernizr.mq( OPP.modernMQ )); 10 11 // Detect stickyhover using a crude regex to detect known ios devices 12 Modernizr.addTest('stickyhover', Modernizr.touch && !!navigator.userAgent.match(/\biP(ad|hone|od)\b/i)); 13 14 // Detect flexbox layout bug in Safari (See #520) 15 Modernizr.testStyles('#modernizr{display:flex;flex-wrap:wrap;width:2px;}#modernizr1,#modernizr2{flex:none;min-width:2px;width:1px;height:1px}', function (elem) { 16 var fixed = elem.firstChild.offsetTop !== elem.lastChild.offsetTop; 17 Modernizr.addTest('webkit-bug-136041', !fixed); 18 }, 2); 19 20})(window, window.document); 21 22// OPP.ajax (also used in contact.js) 23(function (w) { 24 var OPP = w.OPP; 25 26 function noop() {} 27 28 function xhrSettings(options) { 29 // default settings 30 var settings = { 31 async: true, 32 data: null, // no data 33 error: noop, 34 method: 'GET', 35 timeout: 0, // no timeout 36 success: noop 37 }; 38 39 // shallow copy 40 for (prop in options) { 41 if( options.hasOwnProperty(prop) ) { 42 settings[prop] = options[prop]; 43 } 44 } 45 46 // normalize method name 47 settings.method = settings.method.toUpperCase(); 48 49 return settings; 50 } 51 52 OPP.ajax = function (options) { 53 var xhr = new XMLHttpRequest(); 54 var settings = xhrSettings(options); 55 var context = settings.context || settings; 56 xhr.onreadystatechange = function (e) { 57 if (xhr.readyState === 4) { 58 if (xhr.status === 200) { 59 settings.success.call(context, e); 60 } 61 else { 62 settings.error.call(context, e); 63 } 64 } 65 } 66 xhr.open(settings.method, settings.uri, settings.async); 67 xhr.timeout = settings.timeout; 68 xhr.send(settings.data); 69 }; 70}(window)); 71 72// OPP.{has,add,remove,toggle}Class 73(function (w) { 74 var OPP = w.OPP; 75 76 // copied from lazySizes v1.1.3, thanks :) 77 var hasClass = OPP.hasClass = function(ele, cls) { 78 var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)'); 79 return ele.className.match(reg) && reg; 80 }; 81 82 // copied from lazySizes v1.1.3, thanks :) 83 OPP.addClass = function(ele, cls) { 84 if (!hasClass(ele, cls)){ 85 ele.className += ' '+cls; 86 } 87 }; 88 89 // copied from lazySizes v1.1.3, thanks :) 90 OPP.removeClass = function(ele, cls) { 91 var reg; 92 if ((reg = hasClass(ele,cls))) { 93 ele.className = ele.className.replace(reg, ' '); 94 } 95 }; 96 97 OPP.toggleClass = function(ele, cls) { 98 var reg; 99 if ((reg = hasClass(ele,cls))) { 100 ele.className = ele.className.replace(reg, ' '); 101 } else { 102 ele.className += ' '+cls; 103 } 104 }; 105})(window); 106 107// OPP Modal 108(function (w, d) { 109 var OPP = w.OPP; 110 111 var hasClass = OPP.hasClass; 112 var addClass = OPP.addClass; 113 var removeClass = OPP.removeClass; 114 if (!hasClass || !addClass || !removeClass) { return; } 115 if (!'getElementsByClassName' in d) { return; } 116 (function () { 117 var Modal = OPP.Modal = OPP.Modal || {}; 118 119 var Class = { 120 BODY: 'modal-open', 121 MODAL: 'modal', 122 SHOW: 'show', 123 }; 124 125 function isOpen() { 126 return hasClass(d.body, Class.BODY); 127 } 128 129 Modal.elems = d.getElementsByClassName(Class.MODAL); 130 131 function hideAll() { 132 var i = 0, elems = Modal.elems, len = elems.length; 133 for (; i < len; i++) { removeClass(elems[i], Class.SHOW); } 134 } 135 136 function show(elem) { 137 if ( !elem || !hasClass(elem, Class.MODAL) ) { return; } 138 hideAll(); 139 addClass(elem, Class.SHOW); 140 addClass(d.body, Class.BODY); 141 }; 142 143 function hide() { 144 removeClass(d.body, Class.BODY); 145 hideAll(); 146 }; 147 148 OPP.isModalOpen = Modal.isOpen = isOpen; 149 OPP.showModal = Modal.show = show; 150 OPP.hideModal = Modal.hide = hide; 151 152 addClass(d.documentElement, 'can-use-modal'); 153 })(); 154})(window, document); 155 156// NOTE: This is intended to run on every page load for large AND small 157// layouts. Do not be confused by the name of the file. It is loaded regardles 158// of the device classification. 159 160// handle redirect cookie used to trigger welcome page 161(function (w, d) { 162 var OPP = w.OPP; 163 164 function hasCookie(name) { 165 return d.cookie.match(new RegExp('\\b'+name+'=')); 166 } 167 168 function killCookie(name) { 169 d.cookie = name + "=; expires=Thu, 01 Jan 1970 00:00:01 GMT; path=/" 170 } 171 172 var addClass = OPP.addClass; 173 174 if (!addClass) { return; } 175 176 (function () { 177 var rootRedirectCookie = '_opp-root-redirect'; 178
179 if (w.location.pathname == "/home.html" && hasCookie(rootRedirectCookie)) { 180 killCookie(rootRedirectCookie); 181 addClass(d.documentElement, 'is-root-redirect'); 182 } 183 }()); 184 185}(window, window.document)); 186 187// NOTE: This is intended to run on every page load for large AND small 188// layouts. Do not be confused by the name of the file. It is loaded regardles 189// of the device classification. 190 191// check .down and .grace_period 192(function (w, d) { 193 var OPP = w.OPP; 194 195 var ajax = OPP.ajax; 196 var addClass = OPP.addClass; 197 var downURI = OPP.downURI; 198 var gracePeriodURI = OPP.gracePeriodURI; 199 200 if (!ajax || !addClass || !downURI || !gracePeriodURI) { return; } 201 202 (function () { 203 function takeDown() { 204 window.scrollTo(0, 0); // required by offline css. 205 addClass(d.documentElement, 'is-down'); 206 } 207 208 function checkGracePeriod() { 209 ajax({ 210 uri: gracePeriodURI, 211 timeout: 1000, 212 error: takeDown 213 }); 214 } 215 216 function checkDown() { 217 ajax({ 218 uri: downURI, 219 timeout: 1000, 220 success: checkGracePeriod 221 }); 222 } 223 224 // autorun 225 checkDown(); 226 }()); 227}(window, window.document)); 228 229// OPP.{on,}refreshLayout 230(function (w) { 231 var OPP = w.OPP; 232 233 var queue = []; 234 var length = queue.length; 235 236 OPP.onRefreshLayout = function (f) { 237 length = queue.push(f); 238 }; 239 240 OPP.refreshLayout = function () { 241 for(var i = 0; i < length; i++) { 242 queue[i](); 243 } 244 }; 245})(window); 246 247// OPP.masonryInit 248// TODO: split this module for clarity 249(function (w, d) { 250 var OPP = w.OPP; 251 var docElem = d.documentElement; 252 var transEndEventNames = { 253 'WebkitTransition' : 'webkitTransitionEnd',// Saf 6, Android Browser 254 'MozTransition' : 'transitionend', // only for FF < 15 255 'transition' : 'transitionend' // IE10, Opera, Chrome, FF 15+, Saf 7+ 256 }; 257 var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; 258 259 var hasClass = OPP.hasClass; 260 var addClass = OPP.addClass; 261 var removeClass = OPP.removeClass; 262 var refreshLayout = OPP.refreshLayout; 263 var onRefreshLayout = OPP.onRefreshLayout; 264 265 // do nothing if Masonry or required DOM features are missing 266 if (!w.Masonry || !w.addEventListener || !w.getComputedStyle || !d.querySelector || !d.getElementsByTagName) { 267 return; 268 } 269 270 function debounce(f, delay) { 271 var timer, run = function (context, args) { 272 f.apply(context, args); 273 }; 274 275 return function () { 276 w.clearTimeout(timer); 277 timer = w.setTimeout(run, delay, this, arguments); 278 }; 279 } 280 281 function masonryAddZIndex(items) { 282 var colXs = {}, i = this.cols - 1; 283 while (i >= 0) { 284 colXs[Math.floor(i * this.columnWidth)] = i; 285 i--; 286 } 287 288 // add z-index to each item to prevent staggered overlap. 289 items.forEach(function(item) { 290 var x = item.position.x; 291 item.element.style.zIndex = colXs[x]; 292 }); 293 } 294 295 var getScrollbarWidth = (function () { 296 var width; 297 return function () { 298 if (width === undefined) { 299 var outerDiv = d.createElement('div'); 300 var innerDiv = d.createElement('div'); 301 outerDiv.style.cssText = 'overflow:auto;' + 302 'position:absolute;top:-9999px;width:100px;height:100px'; 303 innerDiv.style.cssText = 'width:100%;height:200px'; 304 outerDiv.appendChild(innerDiv); 305 d.body.appendChild(outerDiv); 306 width = outerDiv.offsetWidth - outerDiv.clientWidth; 307 d.body.removeChild(outerDiv); 308 } 309 return width; 310 }; 311 })(); 312 313 // Returns a function that will toggle the vertical scrollbar on and off 314 // for `el` based on the difference between it's `offsetHeight` and 315 // `scrollHeight` properties. By design, the returned function does 316 // nothing unless the result of this comparison has changed. 317 function toggleScrollYFn(el) { 318 var scrollbarWidth = getScrollbarWidth(); 319 var computedStyle = w.getComputedStyle(el); 320 // parse computed value of paddingRight 321 var paddingRightBase = Math.max( 322 parseInt(computedStyle.paddingRight, 10), 323 scrollbarWidth 324 ); 325 // pre-compute the value that will be used for `paddingRight` 326 var paddingRightNoScrollY = paddingRightBase + 'px'; 327 var paddingRightScrollY = (paddingRightBase - scrollbarWidth) + 'px'; 328 // Use `overflow-y` if supported, otherwise fallback to `overflow` 329 var overflowProp = ('overflowY' in computedStyle) ? 'overflowY' : 'overflow'; 330 // track scrollbar state internally 331 var hasScrollY; 332 333 return function () { 334 var init = (hasScrollY === undefined); 335 var shouldScrollY = (el.scrollHeight - el.offsetHeight) > 0; 336 if ( (init || !hasScrollY) && shouldScrollY ) { 337 el.style[overflowProp] = 'scroll'; 338 el.style.paddingRight = paddingRightScrollY; 339 hasScrollY = true; 340 } else if ( (init || hasScrollY) && !shouldScrollY ) { 341 el.style[overflowProp] = 'hidden'; 342 el.style.paddingRight = paddingRightNoScrollY; 343 hasScrollY = false;
344 } 345 return hasScrollY; 346 }; 347 } 348 349 function masonryScrollbarFix(msnry) { 350 var scrollParent = d.getElementsByTagName('main')[0]; 351 var toggleScrollY = debounce( toggleScrollYFn(scrollParent), 250 ); 352 353 // listen for resize events which may or may not trigger a masonry layout 354 w.addEventListener('resize', toggleScrollY); 355 356 // listen for `transitionend` events affecting on the masonry element's height 357 msnry.element.addEventListener(transEndEventName, function (evt) { 358 if (evt.target === msnry.element && evt.propertyName === "height") { 359 toggleScrollY(evt); 360 } 361 }); 362 363 // init 364 toggleScrollY(); 365 } 366 367 function masonryInit() { 368 var msnry; 369 370 addClass(docElem, 'scrollbarfix'); 371 d.addEventListener("DOMContentLoaded", function() { 372 var small = d.querySelector('.previews'); 373 var large = small.cloneNode(true); 374 // hide when large layout is active 375 small.className += ' hidden--large'; 376 // hide when small layout is active 377 large.className += ' hidden--small'; 378 small.parentNode.insertBefore(large, small); 379 380 msnry = new Masonry(large, { // options 381 itemSelector: '.preview', 382 transitionDuration: 0 // disable transitions 383 }); 384 385 msnry.on('layoutComplete', masonryAddZIndex); 386 387 // do nothing if scrollbar width is zero when present 388 if (getScrollbarWidth() > 0) { 389 masonryScrollbarFix(msnry); 390 } else { 391 removeClass(docElem, 'scrollbarfix'); 392 } 393 394 // trigger masonry layout on refresh 395 onRefreshLayout(function () { 396 msnry.layout(); 397 }); 398 399 // refresh layout in case of changes 400 refreshLayout(); 401 }); 402 } 403 404 OPP.masonryInit = masonryInit; 405})(window, window.document); 406 407// main 408(function (w, d) { 409 var OPP = w.OPP; 410 411 // lookup common functions 412 var hasClass = OPP.hasClass; 413 var masonryInit = OPP.masonryInit; 414 var refreshLayout = OPP.refreshLayout; 415 var onRefreshLayout = OPP.onRefreshLayout; 416 417 // for repeated calls to `hasClass` 418 var docElem = d.documentElement; 419 420 // process ratios and update sizes attribute on refresh 421 onRefreshLayout(function () { 422 !!w.imageRatio && imageRatio.processImages(); 423 !!w.lazySizes && lazySizes.autoSizer.checkElems(); 424 }); 425 426 // make sure the aspect ratio is set before being unveiled (good for FF) 427 !!w.imageRatio && d.addEventListener('lazybeforeunveil', function (e) { 428 imageRatio.processImages([e.target]); 429 }); 430 431 // run layout and page specific js only if media queries are supported, 432 // since we will be using `minimal.css` otherwise. 433 if ( Modernizr.mediaquery ) { 434 if ( hasClass(docElem, 'l-detailed') && hasClass(docElem, 'p-gallery') ) { 435 !!masonryInit && masonryInit(); 436 } 437 } 438 439 // ensure sizes attribute is accurate 440 w.addEventListener("load", refreshLayout); 441})(window, 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.