1/** 2 * Global main.js file 3 * For all pages. 4 */ 5 6 7/** 8 * Namespace 9 * All objects & methods are attached to this global object SF. 10 * Any JS files for this theme should start by scoping this object. 11 */ 12var SF = window.SF || {}; 13 14 15/** 16 * Scoping jQuery $ 17 */ 18(function($) { 19 20 21 /** 22 * (window) READY SET GO 23 */ 24 $(window).load(function() { 25 26 // Initialise our RoyalSliders after 27 // everything else has been loaded, 28 // to keep initial load light. 29 SF.initRoyalSliders(); 30 }); 31 // ------------------------------------------------------ 32 33 34 /** 35 * (domready) READY SET GO 36 */ 37 $(function() { 38 39 SF.fancyboxVideos(); 40 41 SF.initMegaDropdowns(); 42 43 SF.bindAccordionEvents(); 44 45 SF.bindTabEvents(); 46 47 SF.jRespond(); 48 49 $('.meeting-info').hide(); 50 51 $('.info-meeting .meeting-loc').click(function() { 52 /* Act on the event */ 53 $(this).parent().find('.meeting-info').toggle(); 54 if ($(this).find('i').hasClass('icon-caret-right')) { 55 $(this).find('i').removeClass('icon-caret-right') 56 $(this).find('i').addClass('icon-caret-down'); 57 } else { 58 $(this).find('i').removeClass('icon-caret-down') 59 $(this).find('i').addClass('icon-caret-right'); 60 } 61 ; 62 63 }); 64 65 $('#student_email').focusout(function() { 66 if (validateMail($(this).val())) { 67 console.log("this val" + $(this).val()); 68 69 var _url = '/site/query_mail'; 70 var _mail = $(this).val(); 71 console.log(" _mail" + _mail); 72 $.ajax({ 73 type: 'GET', 74 url: _url, 75 data: { 76 mail: _mail 77 }, 78 success: function(response) { 79 $('#student_message').html('Your account already exists, please <a href="' + response + '" title="MySummerfuel" target="_blank">Log In</a>.'); 80 $(".applyForm2 :input").attr("disabled", true); 81 $('#submit_continue').css('background-image', 'url(../img/legacy/submit-continue-disabled.png)'); 82 $("#student_email").attr("disabled", false); 83 }, 84 error: function(jqXHR, textStatus, errorThrown) { 85 if (jqXHR.status == 404) { 86 $('#student_message').html(''); 87 $(".applyForm2 :input").attr("disabled", false); 88 $('#submit_continue').css('background-image', 'url(../img/legacy/submit-continue.png)'); 89 } else { 90 var message = '<h4>Some errors prohibited this email from being evaluated:</h4><ul>'; 91 message += '<li>' + jqXHR.responseText + '</li>'; 92 message += '</ul>'; 93 $('#student_message').html(message); 94 } 95 } 96 }); 97 } else if ($(this).val() == '') { 98 $('#student_message').html(''); 99 $(".applyForm2 :input").attr("disabled", false); 100 $('#student_first_name').val(''); 101 $('#student_last_name').val(''); 102 } 103 }); 104 var _parent = null; 105 $('#parent_email').focusout(function() { 106 if (validateMail($(this).val())) { 107 var _url = '/site/query_mail'; 108 var _mail = $(this).val(); 109 $.ajax({ 110 type: 'GET', 111 url: _url, 112 dataType: 'json', 113 data: { 114 mail: _mail 115 }, 116 success: function(response) { 117 _parent = response; 118 $('#parent_message').html('Parent already exists, a connection with the student will be created.'); 119 $('#parent_first_name').val(_parent.first_name); 120 $('#parent_last_name').val(_parent.last_name); 121 $('select[name="relationship_to_student"]').find('option[value="' + _parent.relation + '"]').attr("selected", true); 122 }, 123 error: function(jqXHR, textStatus, errorThrown) { 124 if (jqXHR.status == 404) { 125 $('#parent_message').html(''); 126 console.log(_parent); 127 if (typeof _parent !== 'undefined' && _parent) { 128 $('#parent_first_name').val(''); 129 $('#parent_last_name').val(''); 130 $('option:selected', 'select[name="relationship_to_student"]').removeAttr('selected'); 131 _parent = null; 132 } 133 } else { 134 var message = '<h4>Some errors prohibited this email being evaluated:</h4><ul>'; 135 message += '<li>' + jqXHR.responseText + '</li>'; 136 message += '</ul>'; 137 $('#parent_message').html(message); 138 } 139 } 140 }); 141 } else if ($(this).val() == '') { 142 $('#parent_first_name').val(''); 143 $('#parent_last_name').val(''); 144 $('option:selected', 'select[name="relationship_to_student"]').removeAttr('selected'); 145 $('#parent_message').html(''); 146 _parent = null; 147 } 148 }); 149 150 }); 151 // ------------------------------------------------------ 152 153 154 /** 155 * Handle video links by popping them into a fancybox 156 */ 157 SF.fancyboxVideos = function() { 158 // open youtube in lightbox 159 var $fancyboxMe = $('a[href*="vimeo.com"],a[href*="youtube.com"],a[href*="youtu.be"]'); 160 161 $.each($fancyboxMe, function(i) { 162 var $slf = $(this), 163 $rsBlockParent = $slf.parents('.rsBlock'), 164 $heroImageParent = $slf.parents('.hero-image'); 165 166 $slf.addClass('playCta') 167 .fancybox({ 168 padding: 0, 169 helpers: { 170 media: { 171 youtube: { 172 params: { 173 autoplay: 0 174 } 175 } 176 } 177 }, 178 width: 960, 179 height: 540, 180 aspectRatio: true, 181 scrolling: 'no', 182 beforeLoad: function() { 183 184 // track a video play event
185 _gaq.push(['_trackEvent', 'Videos', 'Play', $(this).attr('href')]); 186 187 $('.royalSlider').each(function(i, e) { 188 var slider = $(this).data('royalSlider'); 189 slider.stopAutoPlay(); 190 }); 191 }, 192 afterClose: function() { 193 $('.royalSlider').each(function(i, e) { 194 var slider = $(this).data('royalSlider'); 195 slider.startAutoPlay(); 196 }); 197 } 198 }); 199 200 if ($rsBlockParent.length > 0) 201 { 202 $rsBlockParent.addClass('rsBlockHasPlayCta'); 203 } 204 205 if ($heroImageParent.length > 0) 206 { 207 $heroImageParent.addClass('heroImageHasPlayCta'); 208 } 209 }); 210 }; 211 212 213 /** 214 * refreshUI() 215 * Refresh JS components like Scrollspy, Affix. 216 * Will be called by other methods here. 217 */ 218 SF.refreshUI = function() { 219 // this is temporarily commented out due to an issue with our location-navbar losing its affix/waypoint on window resize 220 // see this issue https://github.com/imakewebthings/jquery-waypoints/issues/64#issuecomment-5450902 221 //$.waypoints('refresh'); 222 223 if ($('body').data('scrollspy')) 224 { 225 $('body').scrollspy('refresh'); 226 } 227 }; 228 229 // ------------------------------------------------------ 230 231 /** 232 * jRespond() 233 * JS to trigger when entering/exiting responsive breakpoints 234 */ 235 SF.jRespond = function() { 236 // refresh our UI on breakpoints 237 var jRes = jRespond([ 238 { 239 label: 'handheld', 240 enter: 0, 241 exit: 767 242 }, { 243 label: 'tablet', 244 enter: 768, 245 exit: 979 246 }, { 247 label: 'desktop', 248 enter: 980, 249 exit: 10000 250 } 251 ]); 252 253 // redraw UI on every breakpoint entry 254 jRes.addFunc({ 255 breakpoint: '*', 256 enter: function(b) { 257 SF.refreshUI(); 258 } 259 }); 260 }; 261 // ------------------------------------------------------ 262 263 264 /** 265 * initRoyalSliders() 266 * init our various royalsliders 267 */ 268 SF.initRoyalSliders = function() { 269 270 // create our default options object 271 function defaultOptions(obj) { 272 var options = { 273 slidesSpacing: 0, 274 imageScalePadding: 0, 275 imageScaleMode: 'fill', 276 autoScaleSlider: true, 277 fadeinLoadedSlide: false, 278 loop: true, 279 imagesToPreload: 1, 280 transitionSpeed: 800, 281 autoPlay: { 282 enabled: true, 283 pauseOnHover: true, 284 delay: 10000 285 } 286 }; 287 288 // Disable CSS3 animations for Chrome due to bug 289 // http://help.dimsemenov.com/discussions/problems/1153-chrome-scrolling-choppylags-when-a-lot-of-images-in-slideshow-plus-position-fixed 290 // Currently we are addressing this through a second option, in sf-content-wrapper.less 291 // But if that turns out to not help enough, fallback this route! 292 if ($.browser.chrome) { 293 options.allowCSS3 = false; 294 } 295 296 // have we be given a width & height 297 // to help with aspect ratio during resize? 298 if ($(obj).data('rsWidth')) 299 { 300 options.autoScaleSliderWidth = $(obj).data('rsWidth'); 301 } 302 303 if ($(obj).data('rsHeight')) 304 { 305 options.autoScaleSliderHeight = $(obj).data('rsHeight'); 306 } 307 308 // no navigation 'cause only one? 309 if ($(obj).find('.royalSlider').children().length < 2) 310 { 311 options.controlNavigation = ''; 312 } 313 314 return options; 315 } 316 317 // our "default" royalsliders 318 // Full-width slideshows with bullets for navigation 319 // and optional "global" caption element 320 $('.rsSummerfuelDefault').each(function(i) { 321 322 var options = defaultOptions(this); 323 324 var first = $(this).find('.royalSlider').children().first(); 325 326 if (first.find('.rsCaption').length > 0) 327 { 328 options.globalCaption = true; 329 $(this).addClass('globalCaption'); 330 } 331 332 $(this).find('.royalSlider').royalSlider(options); 333 }); 334 335 // "thumbnail" royalsliders on homepage 336 // Tiny slideshows, only bullets for navigation 337 $('.rsSummerfuelThumb').each(function(i) { 338 339 var options = defaultOptions(this); 340 341 options.transitionSpeed = 400; 342 343 $(this).find('.royalSlider').royalSlider(options); 344 }); 345 346 // "timeline" royalsliders on program pages 347 // Rather complex - uses a separate UL dom 348 // as custom navigation. 349 // What's more confusing is that the first slide 350 // has no corresponding navigation element; it is
351 // shown as the first slide, and each navigation element 352 // slides to the others. Hence why we play with the index & offset. 353 $('.rsSummerfuelTimeline').each(function(i) { 354 355 var options = defaultOptions(this); 356 357 var second = $(this).find('.royalSlider').children().eq(1); 358 359 if (second.find('.rsCaption').length > 0) 360 { 361 options.globalCaption = true; 362 $(this).addClass('globalCaption'); 363 } 364 365 $(this).find('.royalSlider').royalSlider(options); 366 367 // grab the RoyalSlider API 368 var sliderAPI = $(this).find('.royalSlider').data('royalSlider'), 369 // grab our thumbnails 370 timelineThumbs = $(this).find('.nav li'); 371 372 // when clicked, go to the corresponding slide 373 timelineThumbs.on('click', function(e) { 374 e.preventDefault(); 375 376 // don't forget to go +1 377 sliderAPI.goTo(timelineThumbs.index(this) + 1); 378 }); 379 380 // after a slide, set correct navigation element to "active" 381 sliderAPI.ev.on('rsAfterSlideChange', function() { 382 if (sliderAPI.currSlideId > 0) 383 { 384 // don't forget to go -1 385 timelineThumbs.eq(sliderAPI.currSlideId - 1).addClass('active'); 386 } 387 }); 388 389 // before any slide, remove all "active" class values 390 sliderAPI.ev.on('rsBeforeAnimStart', function() { 391 timelineThumbs.removeClass('active'); 392 }); 393 394 // move our caption! 395 $(this).find('.rsGCaption').detach().appendTo($(this)); 396 SF.refreshUI(); 397 }); 398 399 400 // "hero" slideshow on homepage 401 // UPon first glance it looks like our "default" slideshow, 402 // without captions. However we manually "lazy load" our images, 403 // because we need to achieve a fluid width, fixed height design. 404 $('.rsSummerfuelHero').each(function(i) { 405 406 var options = defaultOptions(this); 407 408 options.autoScaleSlider = false;
409 options.autoPlay = { 410 // autoplay options go gere 411 enabled: true, 412 pauseOnHover: true, 413 delay: 10000 414 }; 415 416 // fit our headline text 417 $(this).find('h1').fitText(1.1, { 418 minFontSize: 40, 419 maxFontSize: 100 420 }); 421 422 // now lazyload our background images 423 $(this).find('.rsContent').each(function(i) { 424 425 var self = this, 426 background = $(self).data('background'); 427 428 if (background) 429 { 430 var img = $('<img />', { 431 src: background 432 }); 433 434 // once our image has loaded, add it as the background image to our slide 435 $(img).imagesLoaded(function() { 436 $(self).css('background-image', 'url(' + background + ')'); 437 }); 438 } 439 440 }); 441 442 // init RoyalSlider 443 $(this).find('.royalSlider').royalSlider(options); 444 445 // trigger a window resize to ensure fittext runs 446 var rsApi = $(this).find('.royalSlider').data('royalSlider'); 447 rsApi.ev.on('rsBeforeAnimStart', function(event) { 448 $(window).trigger('resize'); 449 }); 450 451 }); 452 }; 453 // ------------------------------------------------------ 454 455 456 /** 457 * bindAccordionEvents() 458 * Attach to accordion events 459 * This is basically to give us our toggling carats (down = open, right = closed) 460 * 461 * Nov 17, 2012 - Temporarily have moved hide & show events into hidden & shown, 462 * to work around conflict with Prototype. 463 */ 464 SF.bindAccordionEvents = function() { 465 466 // for each element, do... 467 $('.accordion-body').each(function() { 468 $(this) 469 .on('hide', function() { 470 //$(this).closest('.accordion-group').find('.accordion-heading').removeClass('open'); 471 }) 472 .on('show', function() { 473 //$(this).closest('.accordion-group').find('.accordion-heading').addClass('open'); 474 }) 475 .on('hidden', function() { 476 $(this).closest('.accordion-group').find('.accordion-heading').removeClass('open'); 477 SF.refreshUI(); 478 }) 479 .on('shown', function() { 480 $(this).closest('.accordion-group').find('.accordion-heading').addClass('open'); 481 SF.refreshUI(); 482 }); 483 }); 484 }; 485 // ------------------------------------------------------ 486 487 488 /** 489 * bindTabEvents() 490 * Enable forward/back 491 */ 492 SF.bindTabEvents = function() { 493 494 // if we have support of history, let's bind to it 495 if (Modernizr.history && $('.nav-tabs').length > 0) 496 { 497 // add a hash to the URL when the user clicks on a tab 498 $('a[data-toggle="tab"]').on('click', function(e) { 499 500 e.preventDefault(); 501 502 history.pushState(null, null, $(this).attr('href')); 503 504 $(this).tab('show'); 505 506 }); 507 508 // navigate to a tab when the history changes 509 $(window).on('popstate', function(e) { 510 var activeTab = $('[href=' + location.hash + ']'); 511 if (activeTab.length) { 512 activeTab.tab('show'); 513 } else { 514 $('.nav-tabs a:first').tab('show'); 515 } 516 }).trigger('popstate'); 517 } 518 }; 519 // ------------------------------------------------------ 520 521 522 /** 523 * initMegaDropdowns() 524 */ 525 SF.initMegaDropdowns = function() { 526 527 // sanity check 528 SF.programNavbar = $('#program-navbar'); 529 if (SF.programNavbar.length == 0) 530 return; 531 532 // our containing dom 533 SF.megaPopover = $('<div id="mega-dropdown-popover" class="hidden-phone" />');
534 SF.megaPopover 535 // determine top position for our mega dropdowns 536 .css('top', SF.programNavbar.offset().top + SF.programNavbar.innerHeight()) 537 // inject 538 .appendTo($('body')); 539 540 // Our internal flag tells us which dropdown may be open 541 SF.megaDropdownOpen = false; 542 SF.programNavs = SF.programNavbar.find('.nav').first().children(); 543 SF.activePage = SF.programNavs.filter('.active'); 544 545 // "IN" function 546 SF.megaDropdownIn = function(el) { 547 548 clearTimeout(SF.megaDropdownOutTimeout); 549 550 SF.megaDropdownOpen = $(el).data('target'); 551 var parent = $(el).closest('li'); 552 553 // determine top position for our mega dropdowns 554 SF.megaPopover.css('top', $(el).offset().top + $(el).closest('.navbar').innerHeight()); 555 556 // temp remove active state 557 if (SF.activePage.length > 0) { 558 SF.activePage.removeClass('active'); 559 } 560 561 // move open class 562 SF.programNavs.filter('.open').not(parent).removeClass('open'); 563 $(parent).addClass('open'); 564 565 var clone = $(SF.megaDropdownOpen).clone(); 566 SF.megaPopover.empty(); 567 clone.appendTo(SF.megaPopover); 568 569 SF.megaPopover.height(clone.height()); 570 clone.addClass('open'); 571 }; 572 573 // "OUT" function 574 SF.megaDropdownOut = function() { 575 SF.megaDropdownOpen = false; 576 SF.programNavs.filter('.open').removeClass('open'); 577 578 // restore page active? 579 if (SF.activePage.length > 0) 580 { 581 SF.activePage.addClass('active'); 582 } 583 584 SF.megaPopover.height(0); 585 }; 586 587 // Bind to clicks 588 SF.programNavbar.find('.mega-dropdown-trigger').on('click', function(e) { 589 590 e.preventDefault(); 591 592 if (Modernizr.touch) 593 { 594 if (!SF.megaDropdownOpen || SF.megaDropdownOpen != $(this).data('target')) 595 { 596 SF.megaDropdownIn(this); 597 } 598 else 599 { 600 SF.megaDropdownOut(); 601 } 602 } 603 }); 604 605 if (!Modernizr.touch) 606 { 607 // Bind to hovering 608 SF.programNavbar.find('.mega-dropdown-trigger').hoverIntent({ 609 over: function(e) { 610 SF.megaDropdownIn(this); 611 }, 612 out: function(e) { 613 } 614 }); 615 616 SF.programNavbar.on('mouseover', function(e) { 617 clearTimeout(SF.megaDropdownOutTimeout); 618 }); 619 620 SF.megaPopover.on('mouseover', function(e) { 621 clearTimeout(SF.megaDropdownOutTimeout); 622 }); 623 624 SF.programNavbar.on('mouseout', function(e) { 625 SF.megaDropdownOutTimeout = setTimeout(function() { 626 SF.megaDropdownOut(); 627 }, 300); 628 }); 629 630 SF.megaPopover.on('mouseout', function(e) { 631 SF.megaDropdownOutTimeout = setTimeout(function() { 632 SF.megaDropdownOut(); 633 }, 300); 634 }); 635 } 636 }; 637 // ------------------------------------------------------ 638 639})(jQuery); 640// ------------------------------------------------------ 641 642// PRIVATE 643 644function validateMail(mailText) { 645 var pattern = new RegExp(/^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i); 646 return pattern.test(mailText); 647}
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.