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