1'use strict'; 2 3/* 4 Buoy v1.2 5 A simple vanilla JS micro-library by Chris Ferdinandi. 6 http://gomakethings.com 7 8 Class handlers by Todd Motto. 9 https://github.com/toddmotto/apollo 10 11 Module pattern by Keith Rousseau. 12 https://twitter.com/keithtri 13 14 Free to use under the MIT License. 15 http://gomakethings.com/mit/ 16*/ 17 18window.$$ = (function(){ 19 20 // Check for classList support 21 var classList = document.documentElement.classList; 22 23 // Check if an element has a class 24 var hasClass = function (elem, className) { 25 if (classList && elem) { 26 return elem.classList.contains(className); 27 } else { 28 return new RegExp('(^|\\s)' + className + '(\\s|$)').test(elem.className); 29 } 30 }; 31 32 // Add a class to an element 33 var addClass = function (elem, className) { 34 if (classList && elem) { 35 elem.classList.add(className); 36 } else if ( elem && !hasClass(elem, className) ) { 37 elem.className += (elem.className ? ' ' : '') + className; 38 } 39 }; 40 41 // Remove a class from an element 42 var removeClass = function (elem, className) { 43 if (classList && elem) { 44 elem.classList.remove(className); 45 } else if (elem && hasClass(elem, className)) { 46 elem.className = elem.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), ''); 47 } 48 }; 49 50 // Toggle a class on an element 51 var toggleClass = function (elem, className) { 52 if (classList && elem) { 53 elem.classList.toggle(className); 54 } else if (elem) { 55 if ( hasClass(elem, className ) ) { 56 removeClass(elem, className); 57 } 58 else { 59 addClass(elem, className); 60 } 61 } 62 }; 63 64 // Get siblings of an element 65 var getSiblings = function (elem) { 66 var siblings = []; 67 var sibling = elem.parentNode.firstChild; 68 var skipMe = elem; 69 for ( ; sibling; sibling = sibling.nextSibling ) { 70 if ( sibling.nodeType == 1 && sibling != elem ) { 71 siblings.push( sibling ); 72 } 73 } 74 return siblings; 75 }; 76 77 var xhr = function() { 78 if (typeof XMLHttpRequest !== 'undefined') { 79 return new XMLHttpRequest(); 80 } 81 82 var versions = [ 83 "MSXML2.XmlHttp.5.0", 84 "MSXML2.XmlHttp.4.0", 85 "MSXML2.XmlHttp.3.0", 86 "MSXML2.XmlHttp.2.0", 87 "Microsoft.XmlHttp" 88 ]; 89 90 var x; 91 for (var i = 0; i < versions.length; i++) { 92 try { 93 x = new ActiveXObject(versions[i]); 94 break; 95 } catch (e) { 96 } 97 } 98 return x; 99 }; 100 101 var ajax = function(url, callback, method, data, sync) { 102 var x = xhr(); 103 x.open(method, url, sync); 104 x.onreadystatechange = function() { 105 if (x.readyState == 4) { 106 callback(x.responseText); 107 } 108 }; 109 if (method == 'POST') { 110 x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); 111 } 112 x.send(data); 113 }; 114 115 // submit ajax GET request 116 var get = function(url, data, callback, sync) { 117 var query = []; 118 for (var key in data) { 119 query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); 120 } 121 ajax(url + '?' + query.join('&'), callback, 'GET', null, sync); 122 }; 123 124 // submit ajax POST request 125 var post = function(url, data, callback, sync) { 126 var query = []; 127 for (var key in data) { 128 query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key])); 129 } 130 ajax(url, callback, 'POST', query.join('&'), sync); 131 }; 132 133 // Return functions 134 return { 135 toggleClass: toggleClass, 136 removeClass: removeClass, 137 addClass: addClass, 138 hasClass: hasClass, 139 getSiblings: getSiblings, 140 get: get, 141 post: post 142 }; 143})(); 144 145 146// 'for' loop shortcut 147function forEach(items, action) { 148 for (var itemsIndex = 0; itemsIndex < items.length; itemsIndex++) { 149 action(items[itemsIndex], itemsIndex); 150 } 151} 152 153 154// helper functions: element parents 155Element.prototype.getParents = function() { 156 var parents = [], 157 obj = this; 158 while ( obj !== document.documentElement ) { 159 parents.push(obj); 160 obj = obj.parentNode; 161 } 162 return parents; 163}; 164Element.prototype.getParentsByTagName = function(tag) { 165 tag = tag.toLowerCase(); 166 return this.getParents().filter(function(el) { 167 return el.tagName.toLowerCase() === tag; 168 }); 169}; 170Element.prototype.getParentsByClassName = function(cl) { 171 return this.getParents().filter(function(el) { 172 return el.classList.contains(cl); 173 }); 174}; 175 176 177// get index of element within its parent 178function elementIndex(el) { 179 var result = 0; 180 while ( (el = el.previousSibling) != null) {
181 if (el.nodeType === 1) { 182 result++; 183 } 184 } 185 return result; 186} 187 188 189// attach a click event with ie8 support 190Element.prototype.clickEvent = function(action) { 191 var thisEl = this; 192 if(window.addEventListener) { 193 thisEl.addEventListener('click', function(ev) { 194 ev.preventDefault(); 195 action(thisEl); 196 }); 197 } else { // ie8 198 thisEl.attachEvent('onclick', function() { 199 action(thisEl); 200 }); 201 } 202}; 203// attach a hover event with ie8 support 204Element.prototype.hoverEvent = function(onAction, offAction) { 205 var thisEl = this; 206 if(window.addEventListener) { 207 thisEl.addEventListener('mouseenter', function() { 208 onAction(thisEl); 209 }); 210 thisEl.addEventListener('mouseleave', function() { 211 offAction(thisEl); 212 }); 213 } else { // ie8 214 thisEl.attachEvent('onmouseenter', function() { 215 onAction(thisEl); 216 }); 217 thisEl.attachEvent('onmouseleave', function() { 218 offAction(thisEl); 219 }); 220 } 221}; 222 223 224// debounced window resize event 225// https://github.com/porada/resizeend 226(function(window) { 227 var currentOrientation, debounce, dispatchResizeEndEvent, document, events, getCurrentOrientation, initialOrientation, resizeDebounceTimeout; 228 document = window.document; 229 if (!(window.addEventListener && document.createEvent)) { 230 return; 231 } 232 events = ['resize:end', 'resizeend'].map(function(name) { 233 var event; 234 event = document.createEvent('Event'); 235 event.initEvent(name, false, false); 236 return event; 237 }); 238 dispatchResizeEndEvent = function() { 239 return events.forEach(window.dispatchEvent.bind(window)); 240 }; 241 getCurrentOrientation = function() { 242 return Math.abs(+window.orientation || 0) % 180; 243 }; 244 initialOrientation = getCurrentOrientation(); 245 currentOrientation = null; 246 resizeDebounceTimeout = null; 247 debounce = function() { 248 currentOrientation = getCurrentOrientation(); 249 if (currentOrientation !== initialOrientation) { 250 dispatchResizeEndEvent(); 251 initialOrientation = currentOrientation; 252 } else { 253 clearTimeout(resizeDebounceTimeout); 254 resizeDebounceTimeout = setTimeout(dispatchResizeEndEvent, 100); 255 } 256 }; 257 return window.addEventListener('resize', debounce, false); 258})(window); 259 260 261// array.prototype.indexOf polyfill (ie8) 262if (!Array.prototype.indexOf) { 263 Array.prototype.indexOf = function (searchElement, fromIndex) { 264 if ( this === undefined || this === null ) { 265 throw new TypeError( '"this" is null or not defined' ); 266 } 267 268 var length = this.length >>> 0; // Hack to convert object.length to a UInt32 269 270 fromIndex = +fromIndex || 0; 271 272 if (Math.abs(fromIndex) === Infinity) { 273 fromIndex = 0; 274 } 275 276 if (fromIndex < 0) { 277 fromIndex += length; 278 if (fromIndex < 0) { 279 fromIndex = 0; 280 } 281 } 282 283 for (;fromIndex < length; fromIndex++) { 284 if (this[fromIndex] === searchElement) { 285 return fromIndex; 286 } 287 } 288 289 return -1; 290 }; 291} 292 293 294// array.prototype.filter polyfill (ie8) 295if (!Array.prototype.filter) { 296 Array.prototype.filter = function(fun /*, thisArg */) { 297 if (this === void 0 || this === null) { 298 throw new TypeError(); 299 } 300 301 var t = Object(this); 302 var len = t.length >>> 0; 303 if (typeof fun != "function") { 304 throw new TypeError(); 305 } 306 307 var res = []; 308 var thisArg = arguments.length >= 2 ? arguments[1] : void 0; 309 for (var i = 0; i < len; i++) { 310 if (i in t) { 311 var val = t[i]; 312 if (fun.call(thisArg, val, i, t)) { 313 res.push(val); 314 } 315 } 316 } 317 318 return res; 319 }; 320} 321 322 323// helper function: nodelist to array 324function toArray(obj) { 325 var array = []; 326 for (var i = obj.length >>> 0; i--;) { 327 array[i] = obj[i]; 328 } 329 return array; 330} 331 332 333// generic toggles 334// <el class="toggle" data-toggle-target="[next|parentnext|css-selector]" /> 335function toggles(el) { 336 var toggleTarget = el.getAttribute('data-toggle-target'), 337 targetObject = null, 338 onText = el.getAttribute('data-on-text'), 339 offText = el.getAttribute('data-off-text'), 340 behavior = null; 341 342 function doToggle(target, action) { 343 var action = action || null; 344 if (!action) { 345 $$.toggleClass(target, 'active'); 346 } else if (action === 'on') { 347 $$.addClass(target, 'active'); 348 } else if (action === 'off') { 349 $$.removeClass(target, 'active'); 350 } 351 } 352 353 if ( toggleTarget == 'next' ) { 354 targetObject = el.nextElementSibling; 355 } else if ( toggleTarget == 'parent' ) { 356 targetObject = el.parentNode; 357 } else if ( toggleTarget == 'parentnext' ) { 358 targetObject = el.parentNode.nextElementSibling; 359 } else if ( toggleTarget == 'parentparent' ) { 360 targetObject = el.parentNode.parentNode; 361 } else if ( toggleTarget == 'parentparentnext' ) { 362 targetObject = el.parentNode.parentNode.nextElementSibling; 363 } else if (toggleTarget === 'self') { 364 targetObject = el; 365 } 366 367 // this is dirty. Clean up when able to edit markup 368 if (el.innerHTML === 'Expand All') { 369 behavior = 'on'; 370 } else if (el.innerHTML === 'Collapse All') { 371 behavior = 'off'; 372 } 373 374 375 if (targetObject) { 376 doToggle(targetObject, behavior); 377 } else if ( toggleTarget !== null ) { 378 var targetObjects = document.querySelectorAll(toggleTarget); 379 for ( var j = 0; j < targetObjects.length; j++ ) { 380 doToggle(targetObjects[j], behavior); 381 } 382 } 383 384 if (toggleTarget !== 'self') { 385 $$.toggleClass(el, 'active'); 386 387 if ($$.hasClass(el, 'active') && onText) { 388 el.innerHTML = onText; 389 } else if (!$$.hasClass(el, 'active') && offText) { 390 el.innerHTML = offText; 391 } 392 } 393} 394forEach(document.querySelectorAll('.toggle'), function(el) { 395 el.clickEvent(function(el) { 396 toggles(el); 397 }); 398}); 399var escapeCloseItems = document.querySelectorAll('.escape-close'); 400if (escapeCloseItems) { 401 document.addEventListener('keyup', function (ev) { 402 if (ev.keyCode === 27) {
403 forEach(escapeCloseItems, function (el) { 404 $$.removeClass(el, 'active'); 405 }); 406 } 407 }); 408} 409 410 411// tabs + automation 412function tabs(tabGroup) { 413 var tabEls = tabGroup.querySelectorAll('.tab'), 414 paneEls = tabGroup.querySelectorAll('.pane'), 415 nextEls = tabGroup.querySelectorAll('.tabs-next'), 416 prevEls = tabGroup.querySelectorAll('.tabs-prev'), 417 panesCount = Math.max(tabEls.length, paneEls.length), 418 slideSpeed = 6000, 419 currentSlide = 0, 420 slideInterval; 421 422 function changeTo(index) { 423 // changes tabs/panes in current context to specified index 424 425 // reset state 426 forEach(tabEls, function(tabEl) { 427 $$.removeClass(tabEl, 'active'); 428 $$.removeClass(tabEl, 'next'); 429 $$.removeClass(tabEl, 'prev'); 430 }); 431 forEach(paneEls, function(paneEl) { 432 $$.removeClass(paneEl, 'active'); 433 $$.removeClass(paneEl, 'next'); 434 $$.removeClass(paneEl, 'prev'); 435 }); 436 // current active set 437 if ( tabEls[index] ) { 438 $$.addClass(tabEls[index], 'active'); 439 } 440 if ( paneEls[index] ) { 441 $$.addClass(paneEls[index], 'active'); 442 } 443 // set at current index + 1 444 if ( index < (panesCount - 1) ) { 445 $$.addClass(tabEls[index + 1], 'next'); 446 $$.addClass(paneEls[index + 1], 'next'); 447 } else { 448 $$.addClass(tabEls[0], 'next'); 449 $$.addClass(paneEls[0], 'next'); 450 } 451 // set at current index - 1 452 if ( index == 0 ) { 453 $$.addClass(tabEls[panesCount - 1], 'prev'); 454 $$.addClass(paneEls[panesCount - 1], 'prev'); 455 } else { 456 $$.addClass(tabEls[index - 1], 'prev'); 457 $$.addClass(paneEls[index - 1], 'prev'); 458 } 459 460 // trigger window resize 461 window.dispatchEvent(new Event('resize')); 462 463 // carry on 464 currentSlide = index; 465 } 466 467 function bindTabClick(el, action) { 468 var index = parseInt(el.getAttribute('data-tabs-index')) || 0; 469 470 function handleClick() { 471 if ( $$.hasClass(tabGroup, 'playing') ) { 472 pauseSlideshow(); 473 } 474 if (action == 'next') { 475 if (currentSlide < panesCount - 1) { 476 changeTo(currentSlide + 1); 477 } else { 478 changeTo(0); 479 } 480 } else if (action == 'prev') { 481 if (currentSlide <= 0) { 482 changeTo(panesCount - 1); 483 } else { 484 changeTo(currentSlide - 1); 485 } 486 } else { 487 changeTo(index); 488 } 489 if ( $$.hasClass(tabGroup, 'slideshow') ) { 490 startSlideshow(); 491 } 492 } 493 494 el.clickEvent(function() { 495 handleClick(); 496 }); 497 } 498 499 function startSlideshow() { 500 slideInterval = setInterval(switchForward, slideSpeed); 501 $$.addClass(tabGroup, 'playing'); 502 } 503 function pauseSlideshow() { 504 window.clearInterval(slideInterval); 505 $$.removeClass(tabGroup, 'playing'); 506 } 507 function switchForward() { 508 if ( currentSlide < (panesCount - 1) ) { 509 changeTo((currentSlide + 1)); 510 } else { 511 changeTo(0); 512 } 513 } 514 515 // initialize 516 forEach(tabEls, function(el, i) { 517 el.setAttribute('data-tabs-index', i); 518 bindTabClick(el); 519 }); 520 forEach(paneEls, function(el, i) { 521 el.setAttribute('data-tabs-index', i); 522 }); 523 changeTo(0); 524 525 // 'next' clicks 526 forEach(nextEls, function(el) { 527 bindTabClick(el, 'next'); 528 }); 529 // 'prev' clicks 530 forEach(prevEls, function(el) { 531 bindTabClick(el, 'prev'); 532 }); 533 534 // automation 535 if ( $$.hasClass(tabGroup, 'slideshow') ) { 536 startSlideshow(); 537 } 538} 539forEach(document.querySelectorAll('.tabs'), function(tabGroup) { 540 tabs(tabGroup); 541}); 542 543 544// column class recognition 545function isAColumn(el) { 546 var classes = ['pagePresentationLayoutColumn', 547 'col-sm-12', 'col-sm-6', 'col-sm-4', 'col-sm-3', 'col-sm-2', 548 'col-md-12', 'col-md-6', 'col-md-4', 'col-md-3', 'col-md-2', 549 'col-lg-12', 'col-lg-6', 'col-lg-4', 'col-lg-3', 'col-lg-2'], 550 candidate; 551 while (classes.length > 0) { 552 candidate = classes.pop(); 553 if ($$.hasClass(el, candidate)) { 554 return true; 555 } 556 } 557 return false;
558} 559 560// automatically set up shared-height groups for similar items 561function autoHeightGroups(groupSelector) { 562 var boxes = document.querySelectorAll(groupSelector); 563 forEach(boxes, function (box, boxId) { 564 var parentColumn = box.parentNode, 565 groupId = 'auto-group-' + boxId.toString(), 566 boxIndex, 567 adjacentColumns = []; 568 569 // TODO: discard this box if it already is in a height group 570 571 // if is a direct child of a column 572 if (isAColumn(parentColumn)) { 573 // if column is not first in row, discard 574 if (elementIndex(parentColumn) === 0) { 575 $$.addClass(box, 'shared-height'); 576 box.setAttribute('data-height-group', groupId); 577 578 // store this element's index within column 579 boxIndex = elementIndex(box); 580 581 // find adjacent columns 582 while ( (parentColumn = parentColumn.nextSibling) != null) { 583 if (parentColumn.nodeType === 1 && isAColumn(parentColumn)) { 584 adjacentColumns.push(parentColumn); 585 } 586 } 587 588 forEach(adjacentColumns, function (column) { 589 // apply same height group to similar index 590 var columnBoxes = column.querySelectorAll(groupSelector), 591 relevantBox = columnBoxes[boxIndex]; 592 if (relevantBox) { 593 $$.addClass(relevantBox, 'shared-height'); 594 relevantBox.setAttribute('data-height-group', groupId); 595 } 596 }); 597 } 598 } 599 }); 600}
601// auto-heights for all content boxes 602autoHeightGroups('.content-box'); 603 604 605 606// shared height elements 607// <el class="shared-height" data-height-group="[string]"> 608(function() { 609 var heightEls = document.querySelectorAll('.shared-height'), 610 heightGroups = [], uniqueHeightGroups = []; 611 612 // get height groups together 613 for ( var i = 0; i < heightEls.length; i++ ) { 614 heightGroups.push(heightEls[i].getAttribute('data-height-group')); 615 } 616 uniqueHeightGroups = heightGroups.filter(function(val, pos) { 617 return heightGroups.indexOf(val) == pos; 618 }) 619 620 function evenHeights() { 621 // blank slate 622 for ( var i = 0; i < heightEls.length; i++ ) { 623 heightEls[i].style.minHeight = 'auto'; 624 } 625 // iterate over groups 626 for ( var i = 0; i < uniqueHeightGroups.length; i++ ) { 627 var thisHeight, maxHeight = 1, 628 groupEls = document.querySelectorAll('.shared-height[data-height-group="' + uniqueHeightGroups[i] + '"]'); 629 for ( var j = 0; j < groupEls.length; j++ ) { 630 var el = groupEls[j]; 631 thisHeight = parseInt(el.clientHeight); 632 if (thisHeight > maxHeight) { 633 maxHeight = thisHeight; 634 } 635 } 636 for ( var j = 0; j < groupEls.length; j++ ) { 637 var el = groupEls[j]; 638 el.style.minHeight = maxHeight + 'px'; 639 } 640 } 641 } 642 643 if(window.addEventListener) { 644 window.addEventListener('load', function () { 645 // shove this to the bottom of the stack so autoHeightGroups can run first 646 window.setTimeout(evenHeights, 1); 647 }); 648 window.addEventListener('resize', function () { 649 window.setTimeout(evenHeights, 1); 650 }); 651 } else { // ie8 652 window.attachEvent('onload', function () { 653 window.setTimeout(evenHeights, 1); 654 }); 655 window.attachEvent('onresize', function () { 656 window.setTimeout(evenHeights, 1); 657 }); 658 } 659})(); 660 661 662// mobile drilldown menu 663Element.prototype.getPreviousAnchor = function() { 664 var prevSibling = this.previousSibling; 665 while (prevSibling) { 666 if (prevSibling.nodeType == 1 && (prevSibling.tagName == 'a' || prevSibling.tagName == 'A')) { 667 return prevSibling; 668 } 669 prevSibling = prevSibling.previousSibling; 670 } 671 return null; 672}; 673forEach(document.querySelectorAll('.page-nav'), function(navContainer) { 674 var originalList = navContainer.querySelector('ul'), 675 list = navContainer.insertBefore(originalList.cloneNode(true), originalList); 676 $$.addClass(originalList, 'list-large'); 677 $$.addClass(list, 'list-small'); 678 list.setAttribute('aria-hidden', 'true'); 679 680 forEach(list.getElementsByTagName('ul'), function(ul) { 681 var a = ul.getPreviousAnchor(); 682 683 // add drilldown clicks 684 $$.addClass(a, 'drilldown'); 685 a.clickEvent(function(el) { 686 $$.addClass(el, 'active'); 687 $$.addClass(ul, 'active'); 688 }); 689 690 // include overview link if the top-level page actually exists. 691 if(a.className.indexOf("emptyLink") < 0) { 692 // add 'overview' links 693 var overviewLi = document.createElement('li'); 694 $$.addClass(overviewLi, 'overview'); 695 var overviewA = overviewLi.appendChild(a.cloneNode(true)); 696 $$.removeClass(overviewA, 'drilldown'); 697 overviewA.innerHTML += ' Overview'; 698 ul.insertBefore(overviewLi, ul.querySelector('li')); 699 } 700 701 // add 'back' links 702 var backLi = document.createElement('li'), 703 backA = document.createElement('a'), 704 prevA = a.getParentsByTagName('ul')[0].getPreviousAnchor(), 705 prevName = prevA ? prevA.innerHTML : 'Main Navigation'; 706 backLi.appendChild(backA); 707 $$.addClass(backLi, 'back'); 708 backA.setAttribute('href', '#'); 709 backA.innerHTML = 'Back to ' + prevName; 710 ul.insertBefore(backLi, ul.querySelector('li')); 711 backA.clickEvent(function(el) { 712 $$.removeClass(a, 'active'); 713 $$.removeClass(ul, 'active'); 714 }); 715 }); 716 717 // add disclosure arrows to desktop nav 718 var disclosureNode = document.createElement('span'); 719 $$.addClass(disclosureNode, 'disclose');
720 forEach(navContainer.querySelectorAll('.list-large > li > ul > li > a'), function(link) { 721 link.appendChild(disclosureNode.cloneNode(true)); 722 }); 723}); 724 725 726// add odd hover behavior on certain boxes 727forEach(document.querySelectorAll('.no-touch .hover-box'), function(box) { 728 forEach(box.querySelectorAll('.item-title a, .disclose, .market-data-indicator-image'), function(a) { 729 a.hoverEvent(function() { 730 $$.addClass(box, 'hovered'); 731 }, function() { 732 $$.removeClass(box, 'hovered'); 733 }); 734 }); 735}); 736 737 738// netflix-style item sliders 739function itemSlider(container, rows) { 740 var items = toArray(container.querySelectorAll('.item')), 741 rows = rows || 1, 742 itemWidth = 0, 743 containerWidth, moveAmount, maxPos, curPos, navItems, itemGroups; 744 745 // quick helper function to make elements 746 function createElementWithClass(el, cl) { 747 var element = document.createElement(el); 748 if (el === 'a') { 749 element.setAttribute('href', '#'); 750 } 751 element.setAttribute('class', cl); 752 return element; 753 } 754 755 // wrap items in sliding container 756 var wrapper = createElementWithClass('div', 'item-slider-wrapper group'); 757 forEach(items, function(item) { 758 wrapper.appendChild(item); 759 }); 760 container.appendChild(wrapper); 761 762 // create nav container and arrows 763 var navWrapper = createElementWithClass('div', 'item-slider-nav'), 764 navDotsWrapper = createElementWithClass('div', 'item-slider-dots'), 765 prevButton = createElementWithClass('a', 'item-slider-prev'), 766 nextButton = createElementWithClass('a', 'item-slider-next'); 767 navWrapper.appendChild(prevButton); 768 navWrapper.appendChild(navDotsWrapper); 769 navWrapper.appendChild(nextButton); 770 771 // moves the wrapper. Starting position at 0 772 function setPosition(n) { 773 curPos = n; 774 if (window.getComputedStyle(wrapper).getPropertyValue('direction') === 'rtl') { 775 wrapper.style.right = (n * moveAmount * -1) + 'px'; 776 } else { 777 wrapper.style.left = (n * moveAmount * -1) + 'px'; 778 } 779 forEach(navItems, function(navItem) { 780 $$.removeClass(navItem, 'current'); 781 }); 782 $$.addClass(navItems[n], 'current'); 783 } 784 785 // calculates frame movement amount, number of frames, etc 786 function setup() { 787 788 containerWidth = container.clientWidth; 789 // taking average item width 790 itemWidth = 0; 791 forEach(items, function (item) { 792 itemWidth += item.offsetWidth; 793 $$.removeClass(item, 'right'); 794 }); 795 itemWidth /= items.length; 796 maxPos = Math.floor(itemWidth * items.length / containerWidth); 797 moveAmount = Math.floor(containerWidth / itemWidth) * itemWidth; 798 799 wrapper.style.width = (itemWidth * items.length) + 'px'; 800 801 // add class for switching the hover info side 802 forEach(items, function (item, i) { 803 if ((i + 1) * itemWidth % moveAmount === 0) { 804 $$.addClass(item, 'right'); 805 } 806 }); 807 808 // row splitting, if relevant 809 if (rows > 1) { 810 // in case this has already been generated, reset groups 811 forEach(items, function(item) { 812 wrapper.appendChild(item); 813 item.style.clear = ''; 814 }); 815 if (itemGroups) { 816 forEach(itemGroups, function(itemGroup) { 817 wrapper.removeChild(itemGroup); 818 }); 819 } 820 821 var itemsPerGroup = Math.floor(containerWidth / itemWidth) * rows, 822 numGroups = Math.ceil(items.length / itemsPerGroup), 823 itemGroups = []; 824 moveAmount = itemsPerGroup / rows * itemWidth; 825 826 for (var i = 0; i < numGroups; i++) { 827 itemGroups.unshift(createElementWithClass('div', 'item-group')); 828 for (var j = 0; j < itemsPerGroup; j++) { 829 var item = items[i * itemsPerGroup + j]; 830 if (j % (itemsPerGroup / rows) === 0) { 831 item.style.clear = 'left'; 832 } 833 if (item) itemGroups[0].appendChild(item); 834 } 835 wrapper.appendChild(itemGroups[0]); 836 itemGroups[0].style.width = moveAmount + 'px'; 837 } 838 maxPos = numGroups; 839 maxPos = numGroups - 1; 840 } 841 842 // navigation 843 container.appendChild(navWrapper); 844 navDotsWrapper.innerHTML = ''; 845 navItems = []; 846 for (var i = 0; i <= maxPos; i++) {
847 navItems.push(createElementWithClass('a', 'item-slider-dot')); 848 navDotsWrapper.appendChild(navItems[i]); 849 } 850 forEach(navItems, function(navItem, i) { 851 navItem.clickEvent(function() { 852 setPosition(i); 853 }); 854 }); 855 navWrapper.style.display = maxPos > 0 ? '' : 'none'; 856 857 // reset position to 0 858 setPosition(0); 859 } 860 setup(); 861 862 // next and prev buttons 863 prevButton.clickEvent(function(button) { 864 setPosition(curPos == 0 ? maxPos : curPos - 1); 865 }); 866 nextButton.clickEvent(function(button) { 867 setPosition(curPos == maxPos ? 0 : curPos + 1); 868 }); 869 870 // resize 871 window.addEventListener('resize:end', setup); 872 873 $$.addClass(container, 'enabled'); 874} 875forEach(document.querySelectorAll('.item-slider'), function(el) { 876 if ($$.hasClass(el, 'stack-2')) { 877 itemSlider(el, 2); 878 } else { 879 itemSlider(el); 880 } 881}); 882 883 884// resize tableau iframe to ancestor 885function resizeTableau(ancestor) { 886 var target = ancestor.querySelector('.tableauPlaceholder'), 887 supportedTransform = detectTransform(), 888 ancestorWidth, targetWidth, ratio; 889 890 if (!supportedTransform || !target) 891 return; 892 893 // detect transform support 894 function detectTransform() { 895 var tmpDiv = document.createElement('div'), 896 props = ['transform', 'webkitTransform', 'MozTransform', 'msTransform', 'OTransform']; 897 for (var i = 0; i < props.length; i++) { 898 if (typeof tmpDiv.style[props[i]] != 'undefined') { 899 return props[i]; 900 } 901 } 902 return false; 903 } 904 905 // function to resize the iframe 906 function doResize() { 907 target.style[supportedTransform] = ''; 908 ancestorWidth = ancestor.clientWidth; 909 targetWidth = target.offsetWidth; 910 ratio = ancestorWidth / targetWidth; 911 target.style[supportedTransform] = 'scale(' + ratio + ')'; 912 ancestor.style.height = target.offsetHeight * ratio + 'px'; 913 } 914 915 // init and watch 916 doResize(); 917 window.addEventListener('load', doResize); 918 window.addEventListener('resize:end', doResize); 919} 920window.addEventListener('load', function() { 921 forEach(document.querySelectorAll('.resize-tableau'), function(el) { 922 resizeTableau(el); 923 }); 924}); 925 926 927// hover groups 928[].forEach.call(document.querySelectorAll('[data-hover-group]'), function (el) { 929 el.setAttribute('data-hover-group', el.getAttribute('data-hover-group').replace(/&|[\s&;]+/g, '')); 930 el.hoverEvent(function (el) { 931 [].forEach.call(document.querySelectorAll('[data-hover-group=' + el.getAttribute('data-hover-group') + ']'), function (el) { 932 $$.addClass(el, 'hovered'); 933 }); 934 }, function (el) { 935 [].forEach.call(document.querySelectorAll('[data-hover-group=' + el.getAttribute('data-hover-group') + ']'), function (el) { 936 $$.removeClass(el, 'hovered'); 937 }); 938 }); 939}); 940 941 942// fix: remove .page-contents-nav if useless 943(function () { 944 var navs = document.querySelectorAll('.page-contents-nav'), 945 i, anchors; 946 for (i = navs.length - 1; i >= 0; i--) { 947 anchors = navs[i].querySelectorAll('a'); 948 if (anchors.length <= 1) { 949 navs[i].style.display = 'none'; 950 } 951 } 952}()); 953 954// align bottom of homepage boxes to bottom of twitter widget 955/* function homeHeights() { 956 var boxes = Array.prototype.slice.call(document.querySelectorAll('.home-match-height'), 0), 957 ref = document.querySelector('.home-match-height-ref'); 958 if (!ref) 959 return; 960 forEach(boxes, function (box) { 961 box.style.minHeight = 0; 962 var offset = box.getBoundingClientRect().top + (window.pageYOffset || document.scrollTop || 0), 963 height = box.offsetHeight, 964 refOffset = ref.getBoundingClientRect().top + (window.pageYOffset || document.scrollTop || 0), 965 refHeight = ref.offsetHeight, 966 difference = (refOffset + refHeight) - (offset + height); 967 968 if (difference > 0) { 969 box.style.minHeight = (height + difference) + 'px'; 970 } 971 }); 972} 973window.addEventListener('resize:end', homeHeights); 974 975*/ 976 977// consume rss feed async 978forEach(document.querySelectorAll('.rss-content'), function(el) { 979 var url = el.getAttribute('data-url'); 980 var feedData = { 981 url: el.getAttribute('data-feedurl'), 982 itemsToSkip: el.getAttribute('data-skip'), 983 itemsToDisplay: el.getAttribute('data-take'), 984 itemTemplate: el.getAttribute('data-template'), 985 blogUrl: el.getAttribute('data-blog'), 986 blogName: el.getAttribute('data-blogName'), 987 itemCss: el.getAttribute('data-css') 988 }; 989 $$.post(url, feedData, function(result) { 990 el.innerHTML = result; 991 //el.style.overflow = 'hidden';
992 forEach(el.querySelectorAll('span[style]'), function(span) { 993 span.removeAttribute('style'); 994 }); 995 // homeHeights(); 996 stButtons.locateElements(); 997 }, true); 998}); 999 1000// shareable links 1001(function () { 1002 var links = document.getElementsByClassName('shareable-link'), 1003 shareList = [ 1004 ['Facebook', 'https://www.facebook.com/sharer/sharer.php?u={0}&t={1}'], 1005 ['Twitter', 'https://twitter.com/intent/tweet?url={0}&text={1}'], 1006 ['LinkedIn', 'https://www.linkedin.com/shareArticle?mini=true&url={0}&title={1}'], 1007 ['Email', 'mailto:?body={0}&subject={1}'] 1008 ], 1009 trigger, container, item, url; 1010 1011 // closer function 1012 function closeShares() { 1013 forEach(document.getElementsByClassName('share-inline'), function (trigger) { 1014 $$.removeClass(trigger, 'is-active'); 1015 }); 1016 } 1017 1018 if (links.length > 0) { 1019 // create our base button 1020 trigger = document.createElement('div'); 1021 trigger.className = 'share-inline'; 1022 trigger.textContent = 'Share'; 1023 1024 // create our base popup and prototype item 1025 container = document.createElement('div'); 1026 container.className = 'share-inline__container'; 1027 item = document.createElement('a'); 1028 item.setAttribute('target', '_blank'); 1029 1030 // add buttons and popups to each relevant link 1031 forEach(links, function (link) { 1032 var thisTrigger = trigger.cloneNode(true), 1033 thisContainer = container.cloneNode(true); 1034 forEach(shareList, function (shareItem) { 1035 var thisItem = item.cloneNode(true), 1036 thisURL = encodeURIComponent(link.href), 1037 thisTitle = encodeURIComponent(link.getAttribute('data-share-title') || ' '); 1038 thisItem.className = 'share-inline__item share-inline__item--' + shareItem[0].toLowerCase(); 1039 thisItem.href = shareItem[1].replace(/\{0\}/, thisURL).replace(/\{1\}/, thisTitle); 1040 thisItem.textContent = shareItem[0]; 1041 thisContainer.appendChild(thisItem); 1042 }); 1043 thisTrigger.appendChild(thisContainer); 1044 link.parentNode.insertBefore(thisTrigger, link.nextSibling); 1045 1046 thisTrigger.addEventListener('click', function (ev) { 1047 if (ev.target === thisTrigger) { 1048 ev.preventDefault(); 1049 } 1050 // throw to event loop to make way for closer 1051 setTimeout(function () { 1052 $$.addClass(thisTrigger, 'is-active'); 1053 }, 1); 1054 }); 1055 }); 1056 1057 // close by clicking anywhere 1058 document.body.addEventListener('click', closeShares); 1059 // close via esc 1060 document.addEventListener('keyup', function (ev) { 1061 if (ev.keyCode === 27) { 1062 closeShares(); 1063 } 1064 }); 1065 } 1066})(); 1067 1068$$.removeClass(document.documentElement, 'no-js'); 1069$$.addClass(document.documentElement, 'js');
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.