1var FE = {}; 2jQuery(function() { 3"use strict"; 4 homebrew.Carousel.prototype.options.transitions.enable = Modernizr.csstransitions; 5 homebrew.Tooltip.prototype.options.appendTo = '#mainContent'; 6 homebrew.Tooltip.prototype.options.transitions.enable = Modernizr.csstransitions; 7 8 9 jQuery.extend(FE, { 10 $els : { 11 backToTop : jQuery('#globalBackToTop'), 12 body : jQuery('body'), 13 mainSite : jQuery('#mainSite'), 14 pageOverlay : jQuery('#pageOverlay'), 15 root : jQuery('html') 16 }, 17 18 events: { 19 transitionEnd : homebrew.events.transitionEnd || 'transitionend oTransitionEnd otransitionend webkitTransitionEnd msTransitionEnd', 20 animationEnd: homebrew.events.animationEnd || 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd' 21 }, 22 23 browser : homebrew.browser, 24 25 baseURL : (typeof fileUrl === 'string') 26 ? fileUrl 27 : (window.location.href.indexOf('') > -1) 28 ? '' 29 : '' 30 }); 31 32 if(!FE.$els.pageOverlay.length) { 33 FE.$els.pageOverlay = jQuery('<div class="page-overlay" id="pageOverlay" />').appendTo(FE.$els.mainSite); 34 } 35 36 if(!FE.$els.backToTop.length) { 37 FE.$els.backToTop = jQuery('<a href="#top" title="Back To Top" class="main-back-to-top" id="mainBackToTop" />').appendTo('#mainContent'); 38 39 FE.$els.backToTop.on('click', function(e) { 40 e.preventDefault(); 41 42 jQuery('html, body').animate({ 43 scrollTop : 0 44 }, 400); 45 }); 46 47 jQuery(window).on('scroll.toggleBackToTop', homebrew.utils.throttle(function() { 48 FE.$els.backToTop.toggleClass('is-shown', jQuery(window).scrollTop() > jQuery(window).height()*0.5); 49 }, 50)); 50 } 51 52 if(typeof FE.browser === 'undefined') { 53 FE.browser = { 54 ie : FE.$els.root.hasClass('ie'), 55 ie9 : FE.$els.root.hasClass('ie9'), 56 lt9 : FE.$els.root.hasClass('lt9'), 57 ie8 : FE.$els.root.hasClass('ie8'), 58 lt8 : FE.$els.root.hasClass('lt8'), 59 ie7 : FE.$els.root.hasClass('ie7'), 60 firefox : (window.mozIndexedDB !== undefined) 61 } 62 } 63 64 65 jQuery.extend(true, FE, { 66 alert : function(args) { 67 this.showAlertPopup(args, 'alert'); 68 }, 69 70 confirm : function(args) { 71 this.showAlertPopup(args, 'confirm'); 72 }, 73 74 showAlertPopup : function(args, method) { 75 args = args || {}; 76 77 var $popup = jQuery('#FE__alertPopup'); 78 79 if(!$popup.length) { 80 $popup = jQuery([ 81 '<div class="ssf-popup text-center" id="FE__alertPopup">', 82 '<h2 class="title space-bottom-2x" />', 83 '<div class="popup-message" />', 84 '<div class="space-top-2x">', 85 '<a href="#/" class="button padded xlarge okay-button">Okay</a>', 86 '<a href="#/" class="button padded xlarge space-left cancel-button" style="display: none;">Cancel</a>', 87 '</div>', 88 '</div>' 89 ].join('')).appendTo('body'); 90 91 $popup.popupify({ 92 addCloseBtns : false, 93 closeOnOverlay : false 94 }); 95 } 96 97 /* Alerts/Confirm dialog boxes should only happen one at a time, 98 * so if it is already shown, then do not proceed. */ 99 if(jQuery('body').hasClass('popup-is-shown') 100 && $popup.hasClass('is-shown')) { 101 return; 102 } 103 104 var $title = $popup.find('.title'), 105 $message = $popup.find('.popup-message'), 106 $prevPopup = jQuery('.popup-is-shown').find('.ssf-popup.is-shown'); 107 108 if(args.title) $title.html(args.title); 109 if(args.message) $message.html(args.message); 110 111 $popup.find('.cancel-button').toggle(method === 'confirm'); 112 113 $popup.find('.okay-button, .cancel-button') 114 .off('.alert') 115 .on('click.alert', function() { 116 if($prevPopup.length 117 && typeof $prevPopup.eq(0).data('popupify') !== 'undefined') { 118 $prevPopup.data('popupify').reveal(); 119 } else { 120 $popup.data('popupify').conceal(); 121 } 122 123 if(typeof args.onConfirm === 'function') { 124 if(method === 'confirm') { 125 args.onConfirm(jQuery(this).is('.okay-button')); 126 } else { 127 args.onConfirm(); 128 } 129 } 130 }); 131 132 $popup.data('popupify').reveal(); 133 }, 134 135 136 FootnoteAnchor : function() { 137 this.init.apply(this, arguments); 138 }, 139 140 Hasher : function(options) { 141 this.options = options; 142 }, 143 144 HeightSlider : function(heightSlider) { 145 if(typeof heightSlider === 'string') heightSlider = jQuery(heightSlider); 146 147 var self = this; 148 149 jQuery.extend(self, { 150 reveal : function() { 151 if(Modernizr.csstransitions) { 152 if(heightSlider.height() !== 0) return; 153 154 var targetHeight = 0; 155 heightSlider.css('height', 'auto'); 156 targetHeight = heightSlider.height(); 157 heightSlider.css('height', ''); 158 159 setTimeout(function() { 160 heightSlider 161 .off(FE.events.transitionEnd) 162 .on(FE.events.transitionEnd, function() { 163 heightSlider 164 .off(FE.events.transitionEnd) 165 .removeClass('is-transitionable') 166 .css('height', '') 167 .addClass('is-toggled'); 168 }) 169 .addClass('is-transitionable') 170 .css('height', targetHeight + 'px'); 171 }, 1); 172 } else { 173 if(heightSlider.css('display') !== 'none') return; 174 175 heightSlider.slideDown(400, function() { 176 jQuery(this).addClass('is-toggled'); 177 }); 178 } 179 }, 180 181 conceal : function() { 182 if(Modernizr.csstransitions) { 183 if(heightSlider.height() === 0) return; 184 185 heightSlider 186 .css('height', heightSlider.height() + 'px') 187 .removeClass('is-toggled'); 188 189 setTimeout(function() { 190 heightSlider 191 .off(FE.events.transitionEnd) 192 .on(FE.events.transitionEnd, function() { 193 heightSlider.off(FE.events.transitionEnd); 194 heightSlider.removeClass('is-transitionable'); 195 heightSlider.css('height', ''); 196 }) 197 .addClass('is-transitionable') 198 .css('height', '0px'); 199 }, 1); 200 } else { 201 if(heightSlider.css('display') === 'none') return; 202 203 heightSlider 204 .removeClass('is-toggled') 205 .slideUp(400); 206 } 207 }, 208 209 toggle : function() { 210 if(Modernizr.csstransitions) { 211 heightSlider.trigger((heightSlider.height() === 0) ? 'reveal' : 'conceal'); 212 } else { 213 heightSlider.trigger((heightSlider.css('display') === 'none') ? 'reveal' : 'conceal'); 214 } 215 } 216 }); 217 218 heightSlider.data('height-slider', self); 219 }, 220 /* End HeightSlider */ 221 222 StickyColumn : function() { 223 this.load.apply(this, arguments); 224 }, 225 226 getHash : function() { 227 var hash = window.location.hash; 228 229 /* 230 * Need to check if second character is a period; sometimes 231 * AddThis adds its own hash to the URL, and its second 232 * character is always a period, which is problematic. 233 */ 234 if(hash === '' || hash.substr(1,1) === '.') { 235 return ''; 236 } else { 237 return hash; 238 } 239 }, 240 /* End getHash */ 241 242 getQueryStr: function(str) { 243 var search = window.location.search; 244 if(!search) return false; 245 246 if(search.substr(0,1) === '?') { 247 search = search.substr(1); 248 } 249 250 var splitSearch = search.split('&'); 251 keyValuePairs = {}; 252 253 splitSearch.map(function(searchStr) { 254 var splitSearchStr = searchStr.split('='); 255 256 splitSearchStr = splitSearchStr.map(function(str) { 257 return jQuery.trim(str); 258 }); 259 260 keyValuePairs[splitSearchStr[0]] = splitSearchStr[1]; 261 }); 262 263 if(typeof str === 'string') { 264 return (typeof keyValuePairs[str] === 'undefined') ? false : keyValuePairs[str]; 265 } else { 266 return keyValuePairs; 267 } 268 }, 269 /* End getQueryStr */ 270 271 initContentTabbers : function() { 272 /* For tabs that toggle contents instead of going to new pages */ 273 274 jQuery('.js-togglerify-tabs').each(function() { 275 var $this = jQuery(this), 276 $nestedTabs = $this.find('.js-togglerify-tabs').find('.tab'), 277 $tabs = $this.find('.tab').not($nestedTabs), 278 $nestedContents = $this.find('.js-togglerify-tabs').find('.tab-content'), 279 $contents = $this.find('.tab-content').not($nestedContents); 280 281 $tabs 282 .togglerify('destroy') 283 .togglerify({ 284 toggledClass: 'current', 285 selfToggleable: false, 286 singleActive: true, 287 content: function(index) { 288 return $contents.eq(index); 289 } 290 }) 291 .on('afterToggleOn', function() { 292 jQuery(window).trigger('resize'); 293 }) 294 .on('toggleOff', function(e, $thisTab, $thisContent) { 295 /* Pause videos if available */ 296 $thisContent.find('.video-holder').find('iframe').each(function() { 297 this.contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*'); 298 }); 299 }); 300 }); 301 }, 302 /* End initContentTabbers */ 303 304 initContentShowers : function() { 305 var types = { 306 TAB: 'tab', 307 TOGGLER: 'toggler', 308 NOTES: 'notes' 309 }, 310 $notesToggler = jQuery('#notesToggler'), 311 $notesContent = jQuery('#notesContent'); 312 313 jQuery('.js-show-content').each(function() { 314 var $this = jQuery(this), 315 thisHref = $this.attr('href'); 316 317 if(typeof thisHref !== 'string') { 318 console.log($this); 319 console.error("No target specified. Set the target's ID as the `href` attribute on the element."); 320 return 321 } 322 323 var $target = jQuery($this.attr('href')); 324 325 if(!$target.length) { 326 console.log($this); 327 console.error("Target `" + $this.attr('href') + "` does not exist."); 328 return; 329 } 330 331 var data = $this.data('show-content-options'); 332 333 if(!data) { 334 console.log($this); 335 console.error('Options undeclared. Declare the options using the `data-show-content-options` attribute on the element.'); 336 return; 337 } 338 339 var options = (typeof data === 'string') 340 ? homebrew.getKeyValuePairsFromString(data) 341 : {}; 342 343 if(!options.type) { 344 console.log($this); 345 console.error('Options declared, but no type specified.'); 346 return; 347 } 348 349 var scrollArgs = [$target]; 350 351 if(options.smoothScroll !== true) { 352 scrollArgs.push('snap'); 353 } 354 355 switch(options.type) { 356 case types.TAB: 357 $this.on('click', function(e) { 358 e.preventDefault(); 359 360 var $tabContent = $target.closest('.tab-content'), 361 $heightSyncers = $tabContent.find('.js-sync-height-wrapper'); 362 363 if($tabContent.hasClass('current')) { 364 FE.scrollTo.apply(null, scrollArgs); 365 } else { 366 if($heightSyncers.length) { 367 $heightSyncers.last().one('afterSync', function() { 368 FE.scrollTo.apply(null, scrollArgs); 369 }); 370 $tabContent.data('togglerify').toggleOn(); 371 } else { 372 $tabContent.data('togglerify').toggleOn(); 373 FE.scrollTo.apply(null, scrollArgs); 374 } 375 } 376 }); 377 break; 378 379 case types.NOTES: 380 if(!$notesToggler.length || !$notesContent.length) { 381 return; 382 } 383 384 $this.on('click', function(e) { 385 e.preventDefault(); 386 387 if(!$notesToggler.hasClass('is-toggled')) { 388 $notesToggler.togglerify('toggleOn', { noSlide : true });
389 } 390 391 FE.scrollTo($target); 392 }); 393 break; 394 395 default: 396 console.log($this); 397 398 var errorMsg = [ 399 'The type `', options.type, '` is not supported. Our currently supported types are: ' 400 ]; 401 402 jQuery.each(types, function(key, value) { 403 errorMsg.push('\n' + value); 404 }); 405 406 errorMsg.push('\n\nCheck the type again.'); 407 408 console.error(errorMsg.join('')); 409 break; 410 } 411 }); 412 }, 413 /* End initContentShowers */ 414 415 initDropdownSwitchers : function($scope) { 416 var $holders = ($scope && $scope.length) 417 ? $scope.find('.dropdown-switcher-holder') 418 : jQuery('.dropdown-switcher-holder'); 419 420 if(!$holders.length) return; 421 422 $holders.each(function() { 423 var $dropdown = jQuery(this).find('.dropdown-switcher'), 424 $contents = jQuery(this).find('.dropdown-content'); 425 426 $dropdown 427 .on('change', function() { 428 $contents 429 .hide() 430 .eq(this.selectedIndex) 431 .show(); 432 }) 433 .trigger('change'); 434 }); 435 }, 436 437 438 initNumberTabbers : function() { 439 jQuery('.number-tabber').each(function() { 440 var $tabs = jQuery(this).find('.number-tabber__tabs').children('li'), 441 $contents = jQuery(this).find('.number-tabber__contents').children('li'); 442 443 $tabs 444 .togglerify('destroy') 445 .togglerify({ 446 toggledClass: 'current', 447 singleActive: true, 448 selfToggleable: false, 449 content: function(index) { 450 return $contents.eq(index); 451 } 452 }); 453 }); 454 }, 455 /* End initNumberTabbers */ 456 457 initPage : function() { 458 jQuery('.js-dropdownify').dropdownify(); 459 jQuery('.js-inputify').inputify(); 460 461 jQuery.each(FE, function(key, value) { 462 if(key.indexOf('init') === 0 && key !== 'initPage') value(); 463 }); 464 465 FE.loadXMLForm(jQuery('.js-xml-form-holder')); 466 FE.loadXMLTable(jQuery('.js-xml-tbl-holder')); 467 FE.loadXMLLocations(jQuery('.js-xml-locations-holder')); 468 469 if(FE.browser.ie) jQuery('[placeholder]').placeholderify(); 470 }, 471 /* End initPage */ 472 473 initSectionSnap : function() { 474 /* Only needed for touchscreens, where the header does not 475 * automatically hide itself. */ 476 if(!Modernizr.touch) return; 477 478 var hash = FE.getHash(); 479 if(!hash || hash.indexOf('#/') > -1) return; 480 481 var $hash = jQuery(hash); 482 if(!$hash.length || !$hash.is('.section')) return; 483 484 setTimeout(function() { 485 FE.scrollTo($hash); 486 }, 25); 487 }, 488 /* End initSectionSnap */ 489 490 initSelfTogglers : function() { 491 /* Self Togglers - for things like FAQ questions */ 492 493 jQuery('.self-toggler__toggler').togglerify({ 494 singleActive: false, 495 slide: true, 496 content: function() { 497 return jQuery(this).next(); 498 } 499 }); 500 }, 501 /* End initSelfTogglers */ 502 503 initSectionContentToggler : function() { 504 /* Section Content togglers - for pages like Popular FAQs */ 505 506 var $sectionContentTogglers = jQuery('.section-content-toggler'); 507 if(!$sectionContentTogglers.length) return; 508 509 var $sectionToggleableContents = jQuery('.section-toggleable-content'), 510 $sectionDropdown = jQuery('<select>'), 511 optionsStrArray = []; 512 513 $sectionContentTogglers 514 .togglerify({ 515 selfToggleable: false, 516 singleActive : true, 517 content : function(index) { 518 return $sectionToggleableContents.eq(index); 519 } 520 }) 521 .each(function(index) { 522 var $dropdownOption = jQuery(['<option>', jQuery.trim(jQuery(this).find('.toggler__title').text()), '</option>'].join(''));
523 $dropdownOption.data('section-content-toggler', jQuery(this)); 524 $dropdownOption.appendTo($sectionDropdown); 525 }); 526 527 jQuery('<div class="column show-for-small-down" />') 528 .prepend($sectionDropdown) 529 .prependTo($sectionContentTogglers.eq(0).closest('.row')); 530 531 $sectionDropdown 532 .dropdownify({ 533 markups : { 534 holder : '<div class="expand space-bottom" />' 535 } 536 }) 537 .on('change', function() { 538 var $thisToggler = jQuery(this).find('option:checked').data('section-content-toggler'); 539 $thisToggler.togglerify('toggleOn'); 540 }); 541 542 FE.watchSize('small', function(isSmallScreen) { 543 var method = (isSmallScreen) ? 'deactivate' : 'activate'; 544 $sectionContentTogglers.togglerify(method); 545 }); 546 }, 547 /* End initSectionContentToggler */ 548 549 initSmallNav : function() { 550 if(Modernizr.touch || jQuery('#mainSmallNav').data('initSmallNav') === true) return; 551 552 jQuery('#mainSmallNav').data('initSmallNav', true); 553 554 var prevScrollTop = jQuery(window).scrollTop(), 555 navHeight = jQuery('#mainSmallNav').height() + jQuery('#mainNav').height(); 556 557 jQuery(window) 558 .on('resize.navHider', function() { 559 navHeight = jQuery('#mainSmallNav').height() + jQuery('#mainNav').height(); 560 }) 561 .trigger('resize.navHider') 562 .on('scroll.navHider', function() { 563 var currentScrollTop = jQuery(window).scrollTop(), 564 partialHiddenBool = currentScrollTop > 47, 565 hiddenBool = currentScrollTop >= prevScrollTop && currentScrollTop > navHeight && !FE.$els.body.hasClass('mobile-nav-is-shown'); 566 567 jQuery('body').toggleClass('main-header-is-partially-hidden', partialHiddenBool); 568 jQuery('body').toggleClass('main-header-is-hidden', hiddenBool); 569 prevScrollTop = currentScrollTop; 570 }) 571 .trigger('scroll.navHider'); 572 }, 573 /* End initSmallNav */ 574 575 initSyncHeightWrappers : function($scope) { 576 var $syncHeightWrappers = jQuery('.js-sync-height-wrapper'); 577 if(!$syncHeightWrappers.length) return; 578 579 $syncHeightWrappers.each(function() { 580 var selectors = jQuery(this).data('sync-height-selector'); 581 if(!selectors) return; 582 583 var selectorsSplitArray = selectors.split(','); 584 585 for(var i = selectorsSplitArray.length-1; i > -1; i--) { 586 selectorsSplitArray[i] = jQuery.trim(selectorsSplitArray[i]); 587 } 588 589 jQuery(this).heightSyncify({ 590 items : selectorsSplitArray 591 }); 592 }); 593 }, 594 /* End initSyncHeightWrappers */ 595 596 initTooltips : function() { 597 var $els = jQuery('.icon--question[title], .js-tooltipify'); 598 599 $els.tooltipify(); 600 }, 601 /* End initTooltips */ 602 603 initQuickLinks : function() { 604 var $quicklinksToggler = jQuery('#mainNav__quicklinksToggler'), 605 $quicklinks = jQuery('#mainNav__quicklinks'); 606 607 if(!$quicklinksToggler.length || !$quicklinks.length) return; 608 if($quicklinksToggler.data('initQuickLinks') === true) return; 609 610 $quicklinksToggler.data('initQuickLinks', true); 611 612 var mouseupEv = 'mouseup.mainNavQuicklinksFauxBlur', 613 resizeEv = 'resize.limitQuicklinksHeight'; 614 615 $quicklinksToggler 616 .togglerify({ 617 slide: true, 618 content: function() { 619 return $quicklinks; 620 } 621 }) 622 .togglerify('deactivate') 623 .on({ 624 click : function(e) { 625 e.preventDefault(); 626 627 var $thisToggler = jQuery(this); 628 629 if($thisToggler.hasClass('is-toggled')) { 630 $thisToggler.togglerify('toggleOff'); 631 } else { 632 var heightLimit = jQuery(window).height() - jQuery('#mainNav').height(), 633 contentHeight; 634 635 $quicklinks.css('height', 'auto'); 636 contentHeight = $quicklinks.height(); 637 $quicklinks.css('height', ''); 638 639 if(contentHeight >= heightLimit) { 640 $thisToggler.togglerify('toggleOn', { contentHeight : heightLimit }); 641 } else { 642 $thisToggler.togglerify('toggleOn'); 643 } 644 } 645 }, 646 647 toggleOn : function(e, $thisToggler, $target) { 648 jQuery(document).on(mouseupEv, function(e) { 649 var container = $thisToggler.add($target); 650 651 if(!container.is(e.target) && container.has(e.target).length === 0) { 652 jQuery(document).off(mouseupEv); 653 $target.data('togglerify').toggleOff(); 654 } 655 }); 656 657 jQuery(window).on(resizeEv, function() { 658 if(!$thisToggler.hasClass('is-toggled')) return; 659 660 var heightLimit = jQuery(window).height() - jQuery('#mainNav').height(); 661 662 $quicklinks.css('height', ''); 663 664 if($quicklinks.height() >= heightLimit) { 665 $quicklinks.height(heightLimit); 666 } 667 }); 668 }, 669 670 toggleOff : function() { 671 jQuery(document).off(mouseupEv); 672 jQuery(window).off(resizeEv); 673 } 674 }); 675 }, 676 /* End initQuickLinks */ 677 678 loadXMLForm : function($holders) { 679 $holders = jQuery($holders); 680 if(!$holders.length) return; 681 682 $holders.each(function() { 683 var $thisHolder = jQuery(this); 684 if(jQuery.data(this, 'loaded-xml-form') === true) return; 685 686 var xmlURL = $thisHolder.data('xml-form-url'); 687 if(typeof xmlURL !== 'string') return; 688 689 jQuery.ajax({ 690 type: 'GET', 691 url: xmlURL, 692 dataType: 'xml', 693 success: function(data) { 694 makeXMLForm(data, $thisHolder); 695 FE.initFancyPlaceholders($thisHolder); 696 FE.initInputFocus($thisHolder); 697 $thisHolder.find('.js-inputify').inputify();
698 $thisHolder.find('.js-dropdownify').dropdownify(); 699 $thisHolder.data('loaded-xml-form', true); 700 }, 701 error: function(d) { 702 console.log(d); 703 } 704 }); 705 }); 706 707 function makeXMLForm(data, $holder) { 708 var $data = jQuery(data), 709 $inputs = $data.find('formInput'), 710 uniqueID = new Date().getTime() + '_' + Math.round(Math.random()*10000), 711 $xmlForm = jQuery([ 712 '<form action="https://docs.google.com/forms/d/', getText($data.find('formID')), '/formResponse"', 713 'target="hiddenIframe_', uniqueID, '"', 714 'method="post" class="xml-form-asset xml-form-contents" />' 715 ].join('')).prependTo($holder), 716 xmlFormStrArray = []; 717 718 xmlFormStrArray.push([ 719 '<div class="row collapse space-bottom medium-space-bottom-2x">', 720 makeTitle($data.find('formTitle')), 721 '</div>' 722 ].join('')); 723 724 $inputs.each(function(index) { 725 var $thisInput = jQuery(this), 726 uniqueInputID = new Date().getTime() + '_' + index, 727 name = getText($thisInput.find('name')), 728 label = getText($thisInput.find('label')), 729 type = getText($thisInput.find('type')).toLowerCase(), 730 required = getText($thisInput.find('required')).toLowerCase(), 731 pattern = getText($thisInput.find('pattern')), 732 tooltip = getText($thisInput.find('tooltip')); 733 734 switch(type) { 735 case 'text': 736 case 'number': 737 case 'email': 738 case 'textarea': 739 var validifyStrArray = ['response: #response_' + uniqueInputID], 740 floaterStrArray = []; 741 742 if(!pattern) { 743 if(type === 'number') { 744 validifyStrArray.unshift('pattern: numbers'); 745 } else if(type === 'email') { 746 validifyStrArray.unshift('pattern: email'); 747 } 748 } 749 750 if(required === 'yes') { 751 validifyStrArray.unshift('required: true'); 752 } 753 754 validifyStrArray.push('floater: #floater_roi_' + uniqueInputID); 755 756 floaterStrArray.push('<div class="validify-floater" id="floater_roi_', uniqueInputID, '">'); 757 if(required === 'yes') { 758 floaterStrArray.push([ 759 '<span class="validify-floater__message validify-floater__message--failed-required">', 760 'This input is required.', 761 '</span>' 762 ].join('')); 763 } 764 765 var errorMessage = getText($thisInput.find('errorMessage')); 766 767 /*if(!errorMessage) {*/ 768 if(type === 'number') { 769 errorMessage = 'This input should consist of numbers only.' 770 } else if(type === 'email') { 771 errorMessage = 'The address should be in this format: [email protected]' 772 } else if(!errorMessage) { 773 errorMessage = 'Invalid input.' 774 } 775 /*}*/ 776 777 floaterStrArray.push([ 778 '<span class="validify-floater__message validify-floater__message--failed-pattern">', 779 errorMessage, 780 '</span>' 781 ].join(''));
782 783 floaterStrArray.push('</div>'); 784 785 var elementStr; 786 787 if(type === 'textarea') { 788 elementStr = [ 789 '<textarea' 790 ]; 791 } else { 792 elementStr = [ 793 '<input', 794 ' type="', (type === 'email') ? 'email' : 'text', '"' 795 ]; 796 } 797 798 elementStr.push( 799 ' data-validify="', validifyStrArray.join(', '), '"', 800 (pattern) 801 ? ' data-validify-pattern="' + pattern + '"' 802 : '', 803 ' name="', name, '"', 804 ' class="field expand"', 805 ' id="roi_', uniqueInputID ,'"', 806 (type === 'textarea') 807 ? '></textarea>' 808 : ' />' 809 ); 810 811 xmlFormStrArray.push([ 812 '<div class="form-obj', (tooltip) ? ' form-obj--with-tooltip' : '', '">', 813 '<div class="field-holder expand">', 814 elementStr.join(''), 815 '<label for="roi_', uniqueInputID, '" class="field-holder__label">', 816 label, (required === 'yes') ? '<span class="text-red">*</span>' : '', 817 '</label>', 818 '</div>', 819 820 '<span class="validify-responses form-obj__icon" id="response_', uniqueInputID, '">', 821 '<span class="validify-response validify-response--success">', 822 '<i class="icon icon--success-tick"></i>', 823 '</span>', 824 '</span>', 825 826 (tooltip) 827 ? '<i title="' + tooltip + '" class="icon icon--question round-circle form-obj__icon"></i>' 828 : '', 829 830 floaterStrArray.join(''), 831 '</div>' 832 ].join('')); 833 break; 834 835 case 'dropdown': 836 var validifyStrArray = ['response: #response_' + uniqueInputID], 837 floaterStr = ''; 838 839 if(required === 'yes') { 840 validifyStrArray.unshift('required: true'); 841 validifyStrArray.push('floater: #floater_roi_' + uniqueInputID); 842 843 floaterStr = [ 844 '<div class="validify-floater" id="floater_roi_', uniqueInputID, '">', 845 '<span class="validify-floater__message validify-floater__message--failed-required">', 846 'This input is required.', 847 '</span>', 848 '</div>' 849 ].join(''); 850 } 851 852 var optionsArray = getText($thisInput.find('options')).split('\n'), 853 options = []; 854 855 while(optionsArray.length) { 856 options.push([ 857 '<option value="', optionsArray[0], '">', 858 optionsArray[0], 859 '</option>' 860 ].join(''));
861 862 optionsArray.shift(); 863 } 864 865 xmlFormStrArray.push([ 866 '<div class="form-obj', (tooltip) ? ' form-obj--with-tooltip' : '', '">', 867 '<span class="expand js-dropdownify">', 868 '<select data-validify="', validifyStrArray.join(', '), '" name="', name, '">', 869 '<option value="">', label, (required === 'yes') ? '*' : '', '</option>', 870 options.join(''), 871 '</select>', 872 '</span>', 873 874 '<span class="validify-responses form-obj__icon" id="response_', uniqueInputID, '">', 875 '<span class="validify-response validify-response--success">', 876 '<i class="icon icon--success-tick"></i>', 877 '</span>', 878 '</span>', 879 880 (tooltip) 881 ? '<i title="' + tooltip + '" class="icon icon--question round-circle form-obj__icon"></i>' 882 : '', 883 884 floaterStr, 885 '</div>' 886 ].join('')); 887 break; 888 889 case 'radio': 890 case 'checkbox': 891 var validifyStrArray = ['groupResponse: #response_' + uniqueInputID], 892 validifyRequiredStr = ''; 893 894 if(required === 'yes') { 895 validifyStrArray.unshift('required: true'); 896 897 validifyRequiredStr = [ 898 '<span class="validify-response validify-response--failed-required">', 899 '<br />This input is required.', 900 '</span>' 901 ].join(''); 902 } 903 904 var optionsArray = getText($thisInput.find('options')).split('\n'), 905 optionsLabelsArray = getText($thisInput.find('optionsLabels')).split('\n'), 906 options = [], 907 currentOption; 908 909 for(var i = 0, ii = optionsArray.length; i < ii; i++) { 910 if(optionsArray[i] === '__other_option__') { 911 912 } else { 913 options.push([ 914 '<label>', 915 '<input type="', type, '" name="', name, '" data-validify value="', optionsArray[i], '" class="js-inputify" /> ', 916 (typeof optionsLabelsArray[i] === 'string' 917 && optionsLabelsArray[i]) 918 ? optionsLabelsArray[i] 919 : optionsArray[i], 920 '</label>' 921 ].join('')); 922 } 923 } 924 925 xmlFormStrArray.push([ 926 '<div class="form-obj" data-validify-group="', validifyStrArray.join(', '), '">', 927 '<p class="form-obj__label">', 928 label, (required === 'yes') ? '<span class="text-red">*</span>' : '', 929 930 (tooltip) 931 ? ' <i title="' + tooltip + '" class="icon icon--question round-circle"></i>' 932 : '', 933 934 ' <span class="validify-responses" id="response_', uniqueInputID, '">', 935 '<span class="validify-response validify-response--success">', 936 '<i class="icon icon--success-tick"></i>', 937 '</span>', 938 939 validifyRequiredStr, 940 '</span>', 941 '</p>', 942 943 options.join(''), 944 '</div>' 945 ].join(''));
946 break; 947 } 948 }); 949 950 xmlFormStrArray.push([ 951 '<div class="text-center space-top medium-space-top-2x">', 952 '<input type="submit" value="Submit" class="button padded xlarge" />', 953 '</div>' 954 ].join('')); 955 956 $xmlForm.append(xmlFormStrArray.join('')); 957 $xmlForm.find('.icon--question[title]').tooltipify(); 958 959 var $hiddenIframe = jQuery('<iframe data-submitted="false" name="hiddenIframe_' + uniqueID + '" style="display: none;" />').appendTo($xmlForm); 960 961 $hiddenIframe.on('load', function() { 962 if(jQuery(this).data('submitted') !== true) return; 963 964 $xmlForm.addClass('is-hidden'); 965 $holder.find('.xml-form-thanks').addClass('is-shown'); 966 FE.scrollTo($holder.find('.xml-form-thanks')); 967 }); 968 969 $xmlForm.validify().on('submit', function(e) { 970 var validifyData = jQuery.data(this, 'validify'); 971 972 validifyData.set('strict', true).validate(); 973 974 if(validifyData.valid !== true) { 975 //alert('Please check the form!'); 976 977 var $errorInput = jQuery(this).find('.is-failed').eq(0); 978 979 FE.scrollTo($errorInput, function() { 980 $errorInput.focus().select(); 981 }); 982 983 e.preventDefault(); 984 } else { 985 $hiddenIframe.data('submitted', true); 986 $xmlForm.find('input[type="submit"]').addClass('is-loading').prop('disabled', true); 987 } 988 }); 989 } 990 991 function getText($node) { 992 return ($node.length) ? jQuery.trim($node.text()) : ''; 993 } 994 995 function makeTitle($title) { 996 var title = getText($title); 997 998 if(title) { 999 return [ 1000 '<div class="large-6 column">', 1001 '<h3>', title, '</h3>', 1002 '</div>', 1003 1004 '<div class="large-6 column medium-down-pad-vertical large-text-right align-to-h3 text-dark-grey">', 1005 '<span class="text-red">*</span> Please fill in the mandatory fields.', 1006 '</div>' 1007 ].join(''); 1008 } else { 1009 return [ 1010 '<div class="column medium-down-pad-vertical large-text-right text-dark-grey">', 1011 '<span class="text-red">*</span> Please fill in the mandatory fields.', 1012 '</div>' 1013 ].join(''); 1014 } 1015 } 1016 }, 1017 /* End loadXMLForm */ 1018 1019 loadXMLTable : function($holders) { 1020 $holders = jQuery($holders); 1021 if(!$holders.length) return; 1022 1023 var worksheets = {}, 1024 xmlSerializer = new XMLSerializer(); // Needed for makeCellInlineFormats 1025 1026 $holders.each(function() { 1027 var $thisHolder = jQuery(this); 1028 if($thisHolder.data('loaded-xml-table') === true) return; 1029 1030 var xmlURL = $thisHolder.data('xml-table-url'); 1031 if(typeof xmlURL !== 'string') return; 1032 1033 jQuery.ajax({ 1034 type: 'GET', 1035 url: xmlURL, 1036 dataType: 'xml', 1037 success: function(data) { 1038 var xmlStyles = getXMLStyles(data); 1039 makeXMLTable(data, xmlStyles, $thisHolder); 1040 attachRowHoverers(); 1041 $thisHolder.data('loaded-xml-table', true); 1042 }, 1043 error: function(d) { 1044 console.log(d); 1045 } 1046 }); 1047 }); 1048 1049 function makeXMLTable(data, xmlStyles, $holder) { 1050 var $data = jQuery(data), 1051 $worksheets = $data.find('Worksheet'), 1052 dropdown = {}, 1053 finalTableStrArr = []; 1054 1055 worksheets = {}; 1056 1057 $worksheets.each(function() { 1058 var $thisWorksheet = jQuery(this), 1059 thisWorksheetName = $thisWorksheet.attr('ss:Name').toLowerCase(); 1060 1061 if(thisWorksheetName.indexOf('dropdown') === 0) { 1062 $thisWorksheet.find('Data').each(function() { 1063 var $Data = jQuery(this), 1064 DataIndex = $Data.index(), 1065 DataText = jQuery(this).text().toLowerCase(); 1066 1067 switch(DataText) { 1068 case 'dropdown label': 1069 dropdown.__label = jQuery(this).closest('Row').next('Row').find('Data').text(); 1070 break; 1071 1072 case 'option label': 1073 jQuery(this).closest('Row').nextUntil().each(function() { 1074 var optionLabelText = jQuery(this).find('Data').eq(DataIndex).text(); 1075 if(optionLabelText === '') return; 1076 dropdown[jQuery(this).find('Data').eq(DataIndex).text()] = jQuery(this).find('Data').eq(DataIndex+1).text().toLowerCase(); 1077 }); 1078 break; 1079 } 1080 }); 1081 } else { 1082 worksheets[thisWorksheetName] = $thisWorksheet; 1083 } 1084 }); 1085 1086 if(jQuery.isEmptyObject(dropdown)) { 1087 var worksheetNames = []; 1088 1089 jQuery.each(worksheets, function(key, value) { 1090 worksheetNames.push(key); 1091 }); 1092 1093 finalTableStrArr.push(iterateWorksheetsArray(worksheetNames, xmlStyles)); 1094 } else { 1095 var dropdownOptionsArray = [], 1096 dropdownContentsArray = []; 1097 1098 jQuery.each(dropdown, function(key, value) { 1099 if(key === '__label') return; 1100 1101 dropdownOptionsArray.push(key); 1102 1103 var dropdownWorksheets = {}, 1104 valueSplitArray = value.split('\n'); 1105 1106 if(valueSplitArray.length === 1) { 1107 valueSplitArray = valueSplitArray[0].split(','); 1108 } 1109 1110 for(var i = valueSplitArray.length-1; i > -1; i--) { 1111 valueSplitArray[i] = jQuery.trim(valueSplitArray[i]); 1112 } 1113 1114 dropdownContentsArray.push(iterateWorksheetsArray(valueSplitArray, xmlStyles)); 1115 }); 1116 1117 var dropdownOptionsStrArray = [], 1118 dropdownContentsStrArray = []; 1119 1120 while(dropdownOptionsArray.length) { 1121 dropdownOptionsStrArray.push([ 1122 '<option>', dropdownOptionsArray.shift(), '</option>' 1123 ].join(''));
1124 } 1125 1126 while(dropdownContentsArray.length) { 1127 dropdownContentsStrArray.push([ 1128 '<div class="dropdown-content">', 1129 dropdownContentsArray.shift(), 1130 '</div>' 1131 ].join('')); 1132 } 1133 1134 finalTableStrArr.push([ 1135 '<div class="dropdown-switcher-holder">', 1136 '<div class="text-center text-large">', 1137 dropdown.__label, 1138 '<span class="js-dropdownify tbl-dropdown">', 1139 '<select class="dropdown-switcher">', 1140 dropdownOptionsStrArray.join(''), 1141 '</select>', 1142 '</span>', 1143 '</div>', 1144 1145 '<div class="dropdown-contents-holder">', 1146 dropdownContentsStrArray.join(''), 1147 '</div>', 1148 '</div>' 1149 ].join('')); 1150 } 1151 1152 $holder.append(finalTableStrArr.join('')); 1153 1154 FE.initFootnotes($holder); 1155 1156 if(!$holder.find('.js-dropdownify').length) return; 1157 1158 $holder.find('.js-dropdownify').dropdownify(); 1159 FE.initDropdownSwitchers($holder); 1160 } 1161 1162 function iterateWorksheetsArray(worksheetNames, xmlStyles) { 1163 var worksheetStrArr = [], 1164 currentWorksheetName, 1165 mediaClass; 1166 1167 while(worksheetNames.length) { 1168 currentWorksheetName = worksheetNames.shift(); 1169 1170 if(typeof worksheets[currentWorksheetName] === 'undefined') { 1171 console.error("FE.loadXMLTable(): Can't find the worksheet '" + currentWorksheetName + "'"); 1172 continue; 1173 } 1174 1175 if(currentWorksheetName.indexOf('desktop') === 0) { 1176 mediaClass = 'show-for-large-up'; 1177 } else if(currentWorksheetName.indexOf('tabletup') === 0) { 1178 mediaClass = 'show-for-medium-up'; 1179 } else if(currentWorksheetName.indexOf('tabletonly') === 0) { 1180 mediaClass = 'show-for-medium-only'; 1181 } else if(currentWorksheetName.indexOf('tabletdown') === 0) { 1182 mediaClass = 'show-for-medium-down'; 1183 } else if(currentWorksheetName.indexOf('mobile') === 0) { 1184 mediaClass = 'show-for-small-down'; 1185 } 1186 1187 worksheetStrArr.push([ 1188 '<div class="tbl-holder', (typeof mediaClass === 'string') ? ' ' + mediaClass : '', '">' 1189 ].join('')); 1190 worksheetStrArr.push(makeTableString(worksheets[currentWorksheetName], xmlStyles)); 1191 worksheetStrArr.push('</div>'); 1192 } 1193 1194 return (worksheetStrArr.length) ? worksheetStrArr.join('') : false; 1195 } 1196 1197 function getColumnCount($worksheet) { 1198 var $worksheetRows = $worksheet.find('Row'), 1199 _columnCount = []; 1200 1201 $worksheetRows.each(function() { 1202 var $cells = jQuery(this).find('Cell'); 1203 1204 /* Filter out empty cells */ 1205 $cells.each(function() { 1206 if(!jQuery(this).text()) { 1207 $cells = $cells.not(jQuery(this)); 1208 } 1209 }); 1210 1211 _columnCount.push($cells.length); 1212 }); 1213 1214 return Math.max.apply(null, _columnCount); 1215 } 1216 1217 function getXMLStyles(data) { 1218 var $styles = jQuery(data).find('Style'), 1219 styleObj = {}; 1220 1221 $styles.each(function() { 1222 var $thisStyle = jQuery(this), 1223 $alignment = $thisStyle.find('Alignment'), 1224 $interior = $thisStyle.find('Interior'), 1225 $numberFormat = $thisStyle.find('NumberFormat'), 1226 $font = $thisStyle.find('Font'), 1227 thisStyleObj = {}; 1228 1229 /* Allow adjustment of alignment in the Excel spreadsheet */ 1230 if($alignment.length && hasAttributes($alignment, ['ss:Horizontal', 'ss:Vertical'])) { 1231 var verticalAlignment, horizontalAlignment; 1232 1233 switch($alignment.attr('ss:Vertical')) { 1234 case 'Top' : verticalAlignment = 'top'; break; 1235 case 'Center' : verticalAlignment = 'middle'; break; 1236 case 'Bottom' : verticalAlignment = 'bottom'; break; 1237 } 1238 1239 switch($alignment.attr('ss:Horizontal')) { 1240 case 'Top' : horizontalAlignment = 'top'; break; 1241 case 'Center' : horizontalAlignment = 'center'; break; 1242 case 'Bottom' : horizontalAlignment = 'bottom'; break; 1243 } 1244 1245 thisStyleObj.alignment = { 1246 'vertical' : verticalAlignment, 1247 'horizontal' : horizontalAlignment 1248 }; 1249 } 1250 1251 /* Interior is where the cell's background color data is stored */ 1252 if($interior.length && hasAttributes($interior, ['ss:Color'])) { 1253 thisStyleObj.bgColor = $interior.attr('ss:Color'); 1254 } 1255 1256 if($font.length && hasAttributes($font, ['ss:Italic', 'ss:Bold', 'ss:Underline', 'ss:Color', 'ss:Size'])) { 1257 thisStyleObj.font = { 1258 'italic' : $font.attr('ss:Italic'), 1259 'bold' : $font.attr('ss:Bold'), 1260 'underline' : $font.attr('ss:Underline'), 1261 'color' : $font.attr('ss:Color'), 1262 'size' : $font.attr('ss:Size') 1263 }; 1264 } 1265 1266 if($numberFormat.length && hasAttributes($numberFormat, ['ss:Format'])) { 1267 thisStyleObj.numberFormat = $numberFormat.attr('ss:Format'); 1268 } 1269 1270 styleObj[$thisStyle.attr('ss:ID')] = thisStyleObj; 1271 }); 1272 1273 return styleObj; 1274 } 1275 1276 function makeTableString($worksheet, xmlStyles) { 1277 var $rows = filterOutEmptyRows($worksheet.find('Row')); 1278 if(!$rows.length) return ''; 1279 1280 var totalColumns = parseInt(getColumnCount($worksheet), 10), 1281 totalRows = $rows.length, 1282 i, ii, j, jj, 1283 1284 tableLayout = []; 1285 1286 for(i = $rows.length-1; i >-1; i--) { 1287 tableLayout.push(new Array(totalColumns)); 1288 } 1289 1290 $rows.each(function(rowIndex) { 1291 1292 var cellCount = 0; 1293 1294 jQuery(this).find('Cell').each(function(cellIndex) { 1295 if(cellCount >= totalColumns) return; 1296 1297 var $thisCell = jQuery(this), 1298 cellMergeDown = parseInt($thisCell.attr('ss:MergeDown'), 10) || 0, 1299 cellMergeAcross = parseInt($thisCell.attr('ss:MergeAcross'), 10) || 0; 1300 1301 1302 while(typeof tableLayout[rowIndex][cellCount] === 'number') { 1303 cellCount++; 1304 } 1305 1306 tableLayout[rowIndex][cellCount] = $thisCell; 1307 1308 /* If there's colspan, then denote the overlapping cells 1309 * as 0 so that it won't be rendered later. */ 1310 if(cellMergeAcross) { 1311 for(i = cellCount, ii = cellCount+cellMergeAcross; i < ii; i++) { 1312 tableLayout[rowIndex][i+1] = 0; 1313 } 1314 1315 cellCount += cellMergeAcross; 1316 } 1317
1318 /* Same thing, if there's rowspan, then denote the 1319 * overlapping cells as 0 so that it won't be 1320 * rendered later. */ 1321 if(cellMergeDown) { 1322 for(i = rowIndex+1, ii = rowIndex+cellMergeDown; i <= ii; i++) { 1323 tableLayout[i][cellCount] = 0; 1324 1325 /* Same thing; this is to account for both rowspan 1326 * and colspan from the same cell. */ 1327 if(cellMergeAcross) { 1328 for(j = cellCount+1, jj = cellCount+cellMergeAcross; j <= jj; j++) { 1329 tableLayout[i][j] = 0; 1330 } 1331 } 1332 } 1333 } 1334 1335 cellCount++; 1336 }); 1337 }); 1338 1339 var tableStrArr = [], 1340 tableStyleID = $worksheet.find('Table').attr('ss:StyleID'), 1341 defaultStyle = (xmlStyles[tableStyleID]) ? xmlStyles[tableStyleID] : {}; 1342 1343 tableStrArr.push('<table class="default-tbl">'); 1344 1345 var cellStrArr, cellArray, cell, k, kk; 1346 while(tableLayout.length) { 1347 cellStrArr = []; 1348 1349 cellArray = tableLayout.shift(); 1350 1351 for(k = 0, kk = cellArray.length; k < kk; k++) { 1352 if(typeof cellArray[k] === 'undefined') { 1353 cellStrArr.push('<td></td>'); 1354 } else if(typeof cellArray[k] === 'object') { 1355 cellStrArr.push(makeCellString(cellArray[k], k)); 1356 } 1357 } 1358 1359 if(cellStrArr.length) { 1360 cellStrArr.unshift('<tr>'); 1361 cellStrArr.push('</tr>'); 1362 tableStrArr.push(cellStrArr.join('')); 1363 } 1364 } 1365 1366 tableStrArr.push('</table>'); 1367 1368 return tableStrArr.join(''); 1369 1370 function makeCellString($cell, cellIndex, log) { 1371 var $thisCell = $cell, 1372 cellContent = $thisCell.text(); 1373 1374 if(cellContent === '') { 1375 if(cellIndex % 2 === 1) { 1376 return '<td class="alt"></td>'; 1377 } else { 1378 return '<td></td>'; 1379 } 1380 } 1381 1382 var cellStyle = jQuery.extend({}, defaultStyle), 1383 cellData = $thisCell.find('Data'), 1384 cellDataType = cellData.attr('ss:Type'); 1385 1386 if(cellDataType === 'Number') { 1387 var decimalSplit = cellContent.split('.'); 1388 1389 if(decimalSplit.length > 1 && decimalSplit[1].length > 2) { 1390 cellContent = parseFloat(cellContent, 10).toFixed(2); 1391 } 1392 } 1393 1394 if(xmlStyles[$thisCell.attr('ss:StyleID')]) { 1395 jQuery.extend(cellStyle, xmlStyles[$thisCell.attr('ss:StyleID')]); 1396 } 1397 1398 var cellNumberFormat = cellStyle.numberFormat, 1399 cellMergeDown = parseInt($thisCell.attr('ss:MergeDown'), 10) || 0, 1400 cellMergeAcross = parseInt($thisCell.attr('ss:MergeAcross'), 10) || 0, 1401 cellSSData = $thisCell.children('ss\\:Data'), 1402 cellInlineTags = { 1403 'italic' : 0, 1404 'bold' : 0, 1405 'underline' : 0 1406 }, 1407 1408 cellTagName = (cellStyle.bgColor && cellStyle.bgColor !== '#FFFFFF') ? 'th' : 'td', 1409 cellClassNames = [], 1410 cellClassName = '', 1411 cellRowspan = '', 1412 cellColspan = '', 1413 cellInlineStyles = []; 1414 1415 if(cellIndex % 2 === 1) { 1416 cellClassNames.push('alt'); 1417 } 1418 1419 if(cellSSData.length) { 1420 cellInlineTags = { 1421 'bold' : cellSSData.find('B').length, 1422 'italic' : cellSSData.find('I').length, 1423 'underline' : cellSSData.find('U').length 1424 }; 1425 1426 cellContent = makeCellInlineFormats(cellSSData); 1427 } 1428 1429 if(typeof cellStyle.bgColor === 'string') { 1430 cellInlineStyles.push('background-color: ' + cellStyle.bgColor); 1431 } 1432 1433 if(typeof cellStyle.alignment === 'object') { 1434 if(typeof cellStyle.alignment.horizontal === 'string') { 1435 cellInlineStyles.push('text-align: ' + cellStyle.alignment.horizontal); 1436 } 1437 if(typeof cellStyle.alignment.vertical === 'string') { 1438 cellInlineStyles.push('vertical-align: ' + cellStyle.alignment.vertical); 1439 } 1440 } 1441 1442 if(typeof cellStyle.font === 'object') { 1443 if(typeof cellStyle.font.italic !== 'undefined') { 1444 cellInlineStyles.push('font-style: italic'); 1445 } 1446 if(typeof cellStyle.font.bold !== 'undefined') { 1447 cellInlineStyles.push('font-weight: bold'); 1448 } 1449 if(typeof cellStyle.font.underline !== 'undefined') { 1450 cellInlineStyles.push('text-decoration: underline'); 1451 } 1452 if(typeof cellStyle.font.color !== 'undefined') { 1453 cellInlineStyles.push('color: ' + cellStyle.font.color); 1454 } 1455 } 1456 1457 1458 /* Compile all the attributes */ 1459 1460 if(cellClassNames.length) { 1461 cellClassName = [' class="', cellClassNames.join(' '), '"'].join(''); 1462 } 1463 1464 if(cellMergeDown) { 1465 cellRowspan = [' rowspan="', cellMergeDown+1, '"'].join(''); 1466 } 1467 1468 if(cellMergeAcross) { 1469 cellColspan = [' colspan="', cellMergeAcross+1, '"'].join(''); 1470 } 1471 1472 if(typeof cellNumberFormat !== 'undefined') { 1473 cellContent = makeCellCurrency(cellNumberFormat, cellContent); 1474 } 1475 1476 if(cellInlineStyles.length) { 1477 cellInlineStyles = [' style="', cellInlineStyles.join('; '), '"'].join(''); 1478 } else { 1479 cellInlineStyles = ''; 1480 } 1481
1482 cellContent = cellContent.replace(/\n/g, '<br />'); 1483 1484 if(typeof cellStyle.font === 'object' 1485 && typeof cellStyle.font.size !== 'undefined' 1486 && cellStyle.font.size !== '11') { 1487 cellContent = [ 1488 '<span style="font-size: ', Math.round(parseFloat(cellStyle.font.size, 10) / 11 * 100, 10), '%;">', 1489 cellContent, 1490 '</span>' 1491 ].join(''); 1492 } 1493 1494 return [ 1495 '<', cellTagName, cellClassName, cellRowspan, cellColspan, cellInlineStyles, '>', 1496 cellContent, 1497 '</', cellTagName, '>' 1498 ].join(''); 1499 } 1500 } 1501 1502 function makeCellInlineFormats($ssData) { 1503 /* 1504 * $ssData.html() was initially used to get the string of the 1505 * contents. However, that produced an error in all versions 1506 * of IE as well as Safari. This was with jQuery 1.11.0. 1507 * 1508 * To work around this, an XMLSerializer is used instead. 1509 * https://developer.mozilla.org/en-US/docs/XMLSerializer 1510 */ 1511 var ssDataChildNodes = jQuery.makeArray($ssData[0].childNodes), 1512 contentsStr = []; 1513 1514 while(ssDataChildNodes.length) { 1515 contentsStr.push(xmlSerializer.serializeToString(ssDataChildNodes.shift())); 1516 } 1517 1518 contentsStr = contentsStr.join(''); 1519 1520 var cellContentStr = contentsStr, 1521 fontMatchArray = cellContentStr.match(/<Font+(.*?)>/g); 1522 1523 var inlineStyles; 1524 for(var i = 0, ii = fontMatchArray.length; i < ii; i++) { 1525 inlineStyles = getInlineStyles(fontMatchArray[i]); 1526 1527 if(inlineStyles === '') { 1528 cellContentStr = cellContentStr 1529 .replace(fontMatchArray[i], '') 1530 .replace('</Font>', ''); 1531 } else { 1532 cellContentStr = cellContentStr 1533 .replace(fontMatchArray[i], '<span style="' + inlineStyles + '">') 1534 .replace('</Font>', '</span>'); 1535 } 1536 } 1537 1538 var inlineTags = ['b', 'i', 'u', 'sup'], 1539 tagName; 1540 1541 while(inlineTags.length) { 1542 tagName = inlineTags.shift(); 1543 1544 cellContentStr = cellContentStr 1545 .replace(new RegExp('<' + tagName + '+(.*?)>', 'gi'), '<' + tagName + '>') 1546 .replace(new RegExp('<\/' + tagName + '+(.*?)>', 'gi'), '</' + tagName + '>'); 1547 } 1548 1549 return cellContentStr; 1550 1551 function getInlineStyles(fontHTMLStr) { 1552 /* 1553 * The subtstring is to remove the opening and closing 1554 * angled brackets (`<` and `>`) at the start and end 1555 * of the string. 1556 */ 1557 var splitArray = fontHTMLStr.substring(1, fontHTMLStr.length-1).split(' '), 1558 attrStr, 1559 splitAttrArray, 1560 inlineStylesArray = []; 1561 1562 while(splitArray.length) { 1563 attrStr = splitArray.shift(); 1564 if(attrStr.indexOf('html:') === -1) continue; 1565 1566 splitAttrArray = attrStr.replace(/(html\:)|"/g, '').toLowerCase().split('='); 1567 1568 1569 switch(splitAttrArray[0]) { 1570 case 'color': 1571 if(splitAttrArray[1] === '#000000') continue; 1572 inlineStylesArray.push(['color: ', splitAttrArray[1]].join(''));
1573 break; 1574 1575 case 'size': 1576 inlineStylesArray.push(['font-size: ', Math.round((parseFloat(splitAttrArray[1], 10) / 11 * 100), 10), '%'].join('')); 1577 break; 1578 } 1579 } 1580 1581 return inlineStylesArray.join('; '); 1582 } 1583 } 1584 1585 function makeCellCurrency(cellNumberFormat, cellContent) { 1586 if(cellNumberFormat === '@' 1587 || cellNumberFormat === '0') return cellContent; 1588 1589 var splitCellContentArray; 1590 1591 if(cellNumberFormat === 'Currency') { 1592 splitCellContentArray = parseFloat(cellContent, 10).toFixed(2).split('.'); 1593 1594 cellContent = 'RM' + contentWithcommas(); 1595 1596 if(splitCellContentArray.length > 1) { 1597 cellContent += '.' + splitCellContentArray[1]; 1598 } 1599 } else if(cellNumberFormat === 'Fixed') { 1600 splitCellContentArray = parseFloat(cellContent, 10).toFixed(2).split('.'); 1601 1602 cellContent = contentWithcommas(); 1603 1604 if(splitCellContentArray.length > 1) { 1605 cellContent += '.' + splitCellContentArray[1]; 1606 } 1607 } else { 1608 1609 var currencyFormatArray = cellNumberFormat.split(';')[0].split(','), 1610 1611 currency = currencyFormatArray[0].replace(/\[|\]| |#|\\|"/g, '').replace(/\$/, '').split('-'), 1612 decimals = currencyFormatArray[1].split('.'); 1613 1614 if(currency.length > 1) currency = currency[0]; 1615 1616 if(decimals.length === 1) { 1617 decimals = 0; 1618 } else { 1619 decimals = decimals[1].replace(/_\)/g, '').length; 1620 } 1621 1622 splitCellContentArray = parseFloat(cellContent, 10).toFixed(decimals).split('.'); 1623 cellContent = currency + contentWithcommas(); 1624 if(splitCellContentArray.length > 1) { 1625 cellContent += '.' + splitCellContentArray[1]; 1626 } 1627 } 1628 1629 return cellContent; 1630 1631 function contentWithcommas() { 1632 return splitCellContentArray[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); 1633 } 1634 } 1635 1636 function attachRowHoverers() { 1637 $holders.find('[rowspan]').each(function() { 1638 var $thisCell = jQuery(this), 1639 rowspan = $thisCell.attr('rowspan'); 1640 1641 if(typeof rowspan !== 'string') return; 1642 1643 rowspan = parseInt(rowspan, 10); 1644 1645 var $thisRow = $thisCell.closest('tr'), 1646 $nextRows = $thisRow.nextAll('tr').slice(0, rowspan-1); 1647 1648 $nextRows.each(function() { 1649 jQuery(this).on({ 1650 mouseenter : function() { 1651 $thisCell.addClass('is-hovered'); 1652 }, 1653 1654 mouseleave : function() { 1655 $thisCell.removeClass('is-hovered'); 1656 } 1657 }); 1658 }); 1659 }); 1660 } 1661 1662 function filterOutEmptyRows($rows) { 1663 /* Filter out rows that only have empty cells */ 1664 $rows.each(function() { 1665 var empty = true; 1666 1667 jQuery(this).find('Cell').each(function() { 1668 if(jQuery(this).text() !== '') empty = false; 1669 return false; 1670 }); 1671 1672 if(empty) $rows = $rows.not(jQuery(this)); 1673 }); 1674 1675 return $rows; 1676 } 1677 1678 function hasAttributes($obj, attrArr) { 1679 while(attrArr.length) { 1680 if(typeof $obj.attr(attrArr.shift()) !== 'undefined') { 1681 return true; 1682 } 1683 } 1684 1685 return false; 1686 } 1687 }, 1688 /* End loadXMLTable */ 1689 1690 loadXMLLocations : function($holders) { 1691 $holders = jQuery($holders); 1692 if(!$holders.length) return; 1693 1694 $holders.each(function() { 1695 var $thisHolder = jQuery(this); 1696 if($thisHolder.data('loaded-xml-locations') === true) return; 1697 1698 var xmlURL = $thisHolder.data('xml-locations-url'); 1699 if(typeof xmlURL !== 'string') return; 1700 1701 jQuery.ajax({ 1702 type: 'GET', 1703 url: xmlURL, 1704 dataType: 'xml', 1705 success: function(data) { 1706 makeLocationsList(data, $thisHolder); 1707 $thisHolder.data('loaded-xml-locations', true); 1708 }, 1709 error: function(d) { 1710 console.log(d); 1711 } 1712 }); 1713 }); 1714 1715 function makeLocationsList(data, $holder) { 1716 var $locations = jQuery(data).find('obj'), 1717 regions = []; 1718 1719 $locations.each(function() { 1720 var $region = jQuery(this).find('region'), 1721 regionStr = jQuery.trim($region.text()); 1722 1723 regionStr = (regionStr) ? regionStr : 'Default'; 1724 1725 if(regions.length) { 1726 for(var i = regions.length-1; i > -1; i--) { 1727 if(regions[i].name === regionStr) { 1728 regions[i].$els.push(jQuery(this)); 1729 return; 1730 } 1731 } 1732 } 1733 1734 regions.push({ 1735 'name' : regionStr, 1736 '$els' : [jQuery(this)] 1737 }); 1738 }); 1739 1740 regions.sort(function(a,b) { 1741 var strA = a.name.toLowerCase(), 1742 strB = b.name.toLowerCase(); 1743 1744 if(strA < strB) return -1; // sort ascending 1745 if(strA > strB) return 1; 1746 return 0; // do nothing 1747 }); 1748 1749 var output = [], 1750 switcherStrArr = ['<span class="location-dropdown"><select>', '', '</select></span>'], 1751 rowMarkup = ['<div class="row location-list', 1, '"><div class="row__flexi-width ', 3, '">'], 1752 i, ii, totalLocationsInRegion; 1753 1754 for(i = 0, ii = regions.length; i < ii; i++) { 1755 rowMarkup[1] = (i === 0) ? ' current' : ''; 1756 1757 totalLocationsInRegion = regions[i].$els.length; 1758 1759 if(totalLocationsInRegion < 2) { 1760 rowMarkup[3] = 'has-1-item'; 1761 } else if(totalLocationsInRegion === 2) { 1762 rowMarkup[3] = 'has-2-items'; 1763 } else if(totalLocationsInRegion === 3) { 1764 rowMarkup[3] = 'has-3-items'; 1765 } else { 1766 rowMarkup[3] = 'more-than-3-items'; 1767 } 1768 1769 output.push(rowMarkup.join(''));
1770 while(regions[i].$els.length) { 1771 output.push([ 1772 '<div class="column">', 1773 '<div class="location">', 1774 '<div class="location__name">', 1775 jQuery.trim(regions[i].$els[0].find('storeName').text()), 1776 '</div>', 1777 1778 '<div class="location__address">', 1779 jQuery.trim(regions[i].$els[0].find('storeAddress').text()), 1780 '</div>', 1781 1782 '<div class="location__footer">', 1783 '<a href="https://maps.google.com/maps?daddr=', 1784 jQuery.trim(regions[i].$els[0].find('storeLat').text()), ',', 1785 jQuery.trim(regions[i].$els[0].find('storeLng').text()), '" target="_blank">', 1786 'Get Directions', 1787 '</a>', 1788 '</div>', 1789 '</div>', 1790 '</div>' 1791 ].join('')); 1792 1793 regions[i].$els.shift(); 1794 } 1795 output.push('</div></div>'); 1796 } 1797 1798 $holder.html(output.join('')); 1799 1800 if(regions.length > 1) { 1801 for(i = 0, ii = regions.length; i < ii; i++) { 1802 switcherStrArr[1] += [ 1803 '<option value="', i, '">', regions[i].name, '</option>' 1804 ].join(''); 1805 } 1806 1807 var $switcher = jQuery(switcherStrArr.join('')); 1808 1809 jQuery('<div class="text-center" />') 1810 .prependTo($holder) 1811 .append($switcher); 1812 1813 $switcher.dropdownify(); 1814 1815 var $locationLists = $holder.find('.location-list'); 1816 1817 $holder.find('select').on('change', function() { 1818 var targetIndex = parseInt(jQuery(this).val(), 10); 1819 1820 $locationLists.each(function(index) { 1821 if(index === targetIndex) { 1822 jQuery(this) 1823 .addClass('current') 1824 .heightSyncify('enable') 1825 .heightSyncify('sync'); 1826 } else { 1827 jQuery(this) 1828 .removeClass('current') 1829 .heightSyncify('disable'); 1830 } 1831 }); 1832 }); 1833 } 1834 1835 var syncHeightSelectors = ['.location__name', '.location__address']; 1836 1837 $holder.find('.location-list').heightSyncify({ 1838 items : syncHeightSelectors 1839 }); 1840 } 1841 }, 1842 /* End loadXMLLocations */ 1843 1844 scrollTo : function($obj, callback) { 1845 if(typeof $obj !== 'object' 1846 || !$obj instanceof jQuery 1847 || !$obj.length) { 1848 return; 1849 } 1850 1851 var targetScrollPos = $obj.offset().top - 50; 1852 1853 if(targetScrollPos < jQuery(window).scrollTop()) { 1854 targetScrollPos -= jQuery('#mainNav').outerHeight(); 1855 1856 if(jQuery('#mainSmallNav').css('display') !== 'none' 1857 && !jQuery('#mainHeader').hasClass('is-partially-hidden')) { 1858 targetScrollPos -= jQuery('#mainSmallNav').outerHeight(); 1859 } 1860 } 1861 1862 if(callback === 'snap') { 1863 jQuery(window).scrollTop(targetScrollPos); 1864 } else { 1865 jQuery('html, body').animate({ 1866 scrollTop : targetScrollPos 1867 }, 400, callback); 1868 } 1869 }, 1870 /* End scrollTo */ 1871 1872 watchSize : function() { 1873 homebrew.watchSize.apply(homebrew, arguments); 1874 } 1875 /* End watchSize */ 1876 });
1877 1878 1879 jQuery.extend(FE.FootnoteAnchor.prototype, { 1880 init : function($el, $footnote) { 1881 if(!$el.length || !$footnote.length) return; 1882 1883 var instance = this; 1884 1885 instance.$el = $el; 1886 instance.$footnote = $footnote; 1887 1888 $el.tooltipify({ 1889 contents : $footnote.html() 1890 }); 1891 1892 instance.$el.data('footnote-anchor', this); 1893 1894 instance.enable(); 1895 }, 1896 1897 enable : function() { 1898 if(Modernizr.touch) return this; 1899 1900 var instance = this; 1901 1902 instance.$el.on({ 1903 'click.footnotesTooltip' : function(e) { 1904 e.preventDefault(); 1905 e.stopPropagation(); 1906 1907 var $thisFootnote = instance.$footnote; 1908 1909 if(!jQuery('#notesToggler').hasClass('is-toggled')) { 1910 jQuery('#notesToggler').togglerify('toggleOn', { noSlide : true }); 1911 } 1912 1913 FE.scrollTo($thisFootnote); 1914 $thisFootnote.addClass('is-active'); 1915 1916 jQuery(document) 1917 .off('mouseup.notesFauxBlur') 1918 .on('mouseup.notesFauxBlur', function(e) { 1919 if(!$thisFootnote.is(e.target) && $thisFootnote.has(e.target).length === 0) { 1920 jQuery(document).off('mouseup.notesFauxBlur'); 1921 $thisFootnote.removeClass('is-active'); 1922 } 1923 }); 1924 } 1925 }); 1926 1927 return this; 1928 }, 1929 1930 disable : function() { 1931 var instance = this; 1932 1933 instance.$el.off('click.footnotesTooltip'); 1934 instance.$el.tooltipify('disable'); 1935 1936 return instance; 1937 } 1938 }); 1939 1940 1941 jQuery.extend(FE.Hasher.prototype, { 1942 get : function(args) { 1943 var instance = this, 1944 hash = window.location.hash; 1945 1946 if(hash === '' 1947 || hash === '#/' 1948 || hash.substr(0,2) !== '#/') { 1949 instance.data = {}; 1950 return instance; 1951 } 1952 1953 var splitHash = hash.substring(2).split('&'), 1954 keyValuePairs, 1955 dataObj = {}; 1956 1957 while(splitHash.length) { 1958 keyValuePairs = splitHash.shift().split('='); 1959 dataObj[jQuery.trim(keyValuePairs[0])] = decodeURIComponent(jQuery.trim(keyValuePairs[1])); 1960 } 1961 1962 instance.data = dataObj; 1963 1964 return instance; 1965 }, 1966 1967 setData : function(data) { 1968 if(!data || !jQuery.isPlainObject(data)) return this; 1969 this.data = jQuery.extend({}, data); 1970 return this; 1971 }, 1972 1973 extendData : function(data) { 1974 if(!data || !jQuery.isPlainObject(data)) return this; 1975 1976 if(this.data) { 1977 jQuery.extend(this.data, data); 1978 } else { 1979 this.data = data; 1980 } 1981 1982 1983 return this; 1984 }, 1985 1986 set : function(args) { 1987 args = args || {}; 1988 1989 var instance = this, 1990 options = jQuery.extend(instance.options, args); 1991 1992 if(options.ignoreDefault && !instance.data) return instance; 1993 1994 instance.data = instance.data || {}; 1995 1996 var hashKeys = [], 1997 hash = [], 1998 sequence = options.sequence; 1999 2000 if(sequence === 'alphabetical' 2001 || typeof sequence === 'function') { 2002 jQuery.each(instance.data, function(key, value) { 2003 if(key === '' || value === '') return; 2004 hashKeys.push(key); 2005 }); 2006 2007 var sorter; 2008 2009 if(sequence === 'alphabetical') { 2010 sorter = function(a, b) { 2011 var _a = a.toLowerCase(), 2012 _b = b.toLowerCase(); 2013 2014 if(_a < _b) return -1; 2015 if(_a > _b) return 1; 2016 return 0; 2017 }; 2018 } else if(typeof sequence === 'function') { 2019 sorter = sequence; 2020 } 2021 2022 hashKeys.sort(sorter); 2023 2024 var currentHashKey; 2025 2026 while(hashKeys.length) { 2027 currentHashKey = hashKeys.shift(); 2028 hash.push([currentHashKey, encodeURIComponent(instance.data[currentHashKey])].join('=')); 2029 } 2030 } else if(typeof sequence === 'string') { 2031 var splitSequence = sequence.split(','), 2032 i, ii, currentSequenceKey; 2033 2034 for(i = 0, ii = splitSequence.length; i < ii; i++) { 2035 currentSequenceKey = splitSequence[i]; 2036 2037 if(instance.data[currentSequenceKey]) { 2038 hash.push([currentSequenceKey, encodeURIComponent(instance.data[currentSequenceKey])].join('=')); 2039 } 2040 } 2041 2042 jQuery.each(instance.data, function(key, value) { 2043 if(key === '' || value === '') return; 2044 2045 for(i = splitSequence.length-1; i > -1; i--) { 2046 if(key === splitSequence[i]) return; 2047 } 2048 2049 hash.push([key, encodeURIComponent(value)].join('=')); 2050 }); 2051 } else { 2052 jQuery.each(instance.data, function(key, value) { 2053 if(key === '' || value === '') return; 2054 hash.push([key, encodeURIComponent(value)].join('=')); 2055 }); 2056 } 2057 2058 window.location.hash = '#/' + hash.join('&'); 2059 }, 2060 2061 isSameWith : function(data) { 2062 if(!data) return false; 2063 2064 var same = true, 2065 count = 0, 2066 instance = this; 2067 2068 jQuery.each(data, function(key, value) { 2069 if(typeof instance.data[key] === 'undefined' 2070 || instance.data[key] !== value) { 2071 same = false; 2072 return false; 2073 } else { 2074 count++; 2075 } 2076 }); 2077 2078 if(same) { 2079 var refCount = 0; 2080 2081 jQuery.each(instance.data, function() { 2082 refCount++; 2083 }); 2084 2085 same = (count === refCount); 2086 } 2087 2088 return same; 2089 } 2090 });
2091 2092 2093 jQuery.extend(FE.StickyColumn.prototype, { 2094 callbacks : jQuery({}), 2095 2096 hasMousewheel : Boolean(jQuery.fn.mousewheel), 2097 2098 classes : { 2099 stickyColumn : 'sticky-column', 2100 sticky : 'is-sticky', 2101 stickBottom : 'is-stick-to-bottom' 2102 }, 2103 2104 load : function(args) { 2105 var instance = this, 2106 proto = FE.StickyColumn.prototype; 2107 2108 /* If Mousewheel isn't available, load it now. */ 2109 if(!proto.hasMousewheel) { 2110 if(!proto.loadingMousewheel) { 2111 proto.loadingMousewheel = true; 2112 2113 jQuery.getScript(FE.baseURL + 'js/plugins/jquery.mousewheel.min.js', function() { 2114 FE.StickyColumn.prototype.hasMousewheel = true; 2115 proto.callbacks.dequeue('mousewheelLoaded'); 2116 }); 2117 } 2118 2119 proto.callbacks.queue('mousewheelLoaded', function(next) { 2120 instance.init(args); 2121 if(typeof next === 'function') next(); 2122 }); 2123 } 2124 }, 2125 2126 init : function(args) { 2127 var $el = args.$el, 2128 $comparee = args.$comparee; 2129 2130 if(!$el || !$comparee 2131 || !$el instanceof jQuery || !$comparee instanceof jQuery 2132 || !$el.length || !$comparee.length) { 2133 return; 2134 } 2135 2136 var instance = this, 2137 proto = FE.StickyColumn.prototype; 2138 2139 instance.$el = $el; 2140 instance.$comparee = $comparee; 2141 instance.uniqueID = homebrew.generateUniqueID(); 2142 2143 $el.addClass(instance.classes.stickyColumn); 2144 2145 FE.watchSize('large', function(isLargeScreen) { 2146 if(isLargeScreen) { 2147 instance.watch(); 2148 } else { 2149 instance.rest(); 2150 instance.unstick(); 2151 instance.updatePosition(); 2152 } 2153 }); 2154 2155 return instance; 2156 }, 2157 2158 determineState : function() { 2159 if(!homebrew.screenSize.large) return 'should-be-unstuck'; 2160 2161 var instance = this, 2162 $stickyCol = instance.$el, 2163 $comparee = instance.$comparee, 2164 windowTop = jQuery(window).scrollTop(), 2165 stickyColMarginTop = parseInt($stickyCol.css('margin-top'), 10); 2166 2167 instance.offsetTop = (jQuery('#mainHeader').css('position') === 'fixed') ? /*instance.$els.mainNav.height()*/ 0 : 0; 2168 instance.offsetBottom = parseInt($comparee.find('.column').css('margin-bottom'), 10)*2; 2169 2170 if(!$comparee.is(':visible') || $stickyCol.height() >= $comparee.height()) { 2171 return 'should-be-unstuck'; 2172 } else if($stickyCol.hasClass(instance.classes.stickBottom) && $stickyCol.offset().top >= windowTop + instance.offsetTop) { 2173 return 'should-be-stuck'; 2174 } else if((windowTop + $stickyCol.height() + stickyColMarginTop + instance.offsetTop) > ($comparee.offset().top + $comparee.height() - instance.offsetBottom)) { 2175 return 'should-be-stuck-to-bottom'; 2176 } else if($comparee.offset().top <= windowTop + instance.offsetTop) { 2177 return 'should-be-stuck'; 2178 } else { 2179 return 'should-be-unstuck'; 2180 } 2181 }, 2182 2183 updatePosition : function() { 2184 var instance = this, 2185 $el = instance.$el; 2186 2187 if($el.hasClass(instance.classes.stickBottom) || !$el.hasClass(instance.classes.sticky)) { 2188 $el.css('top', ''); 2189 } else { 2190 $el.css('top', jQuery(window).scrollTop() - instance.$comparee.offset().top + instance.offsetTop + 'px'); 2191 } 2192 2193 return instance; 2194 }, 2195 2196 stick : function() { 2197 var instance = this; 2198 2199 instance.$el 2200 .addClass(instance.classes.sticky) 2201 .removeClass(instance.classes.stickBottom); 2202 2203 return instance; 2204 }, 2205 2206 stickToBottom : function() { 2207 var instance = this; 2208 2209 instance.$el 2210 .addClass([ 2211 instance.classes.sticky, 2212 instance.classes.stickBottom 2213 ].join(' ')) 2214 .css('margin-top', ''); 2215 2216 return instance; 2217 }, 2218 2219 unstick : function() { 2220 var instance = this; 2221 2222 instance.$el 2223 .removeClass([ 2224 instance.classes.sticky, 2225 instance.classes.stickBottom 2226 ].join(' ')) 2227 .css('margin-top', ''); 2228 2229 return instance; 2230 }, 2231 2232 watch : function() { 2233 var instance = this; 2234 2235 jQuery(window).on([ 2236 'scroll.', instance.uniqueID, 2237 ' resize.', instance.uniqueID 2238 ].join(''), function() { 2239 var state = instance.determineState(); 2240 2241 switch(state) { 2242 case 'should-be-stuck' : instance.stick(); break; 2243 case 'should-be-stuck-to-bottom' : instance.stickToBottom(); break; 2244 case 'should-be-unstuck' : instance.unstick(); break; 2245 } 2246 2247 instance.updatePosition(); 2248 }).trigger('scroll'); 2249 2250 instance.$el.on('mousewheel', function(e) { 2251 if(instance.determineState() !== 'should-be-stuck') return; 2252 2253 var vector; 2254 2255 if(e.deltaY < 0) { 2256 var bottomDiff = jQuery(window).scrollTop() + jQuery(window).height() - jQuery(this).offset().top - jQuery(this).height(); 2257 2258 if(bottomDiff >= 0) { 2259 return; 2260 } else if(Math.abs(bottomDiff) < 100) { 2261 vector = bottomDiff; 2262 } else { 2263 vector = -100; 2264 } 2265 } else if(e.deltaY > 0) { 2266 var topDiff = jQuery(window).scrollTop() + instance.offsetTop - jQuery(this).offset().top; 2267 2268 if(topDiff === 0) { 2269 return; 2270 } else if(topDiff < 100) { 2271 vector = topDiff; 2272 } else { 2273 vector = 100; 2274 } 2275 } 2276 2277 e.preventDefault(); 2278 instance.$el.css('margin-top', [parseInt(instance.$el.css('margin-top'), 10) + vector, 'px'].join(''));
2279 }); 2280 2281 return instance; 2282 }, 2283 2284 rest : function() { 2285 jQuery(window).off('.' + this.uniqueID); 2286 return this; 2287 } 2288 }); 2289 2290 2291 /*---- Init page ----*/ 2292 2293 FE.initPage(); 2294 2295 /* Run through any queued callbacks if available. */ 2296 if(typeof FE__callbacks === 'object' 2297 && FE__callbacks instanceof Array 2298 && FE__callbacks.length) { 2299 while(FE__callbacks.length) { 2300 FE__callbacks.shift()(); 2301 } 2302 } 2303});
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.