Difference between revisions of "Team:ZJU-China/js/globe/ZJU-gl-head-scripts"
(Created page with "/* All javascript needed for the global header and footer, and some utility functions. @file gl-head-scripts.en.js @author marionm ======================...") |
|||
Line 211: | Line 211: | ||
// emulate horizontal scroll effect on fixed position element | // emulate horizontal scroll effect on fixed position element | ||
− | + | ||
− | + | ||
− | + | ||
})); | })); | ||
Latest revision as of 11:30, 17 September 2015
/* All javascript needed for the global header and footer, and some utility functions.
@file gl-head-scripts.en.js @author marionm ========================================================================== */
var gl_Wrapper = '#gl-outer-wrapper'; // outer wrapper element selector var gl_Header = '#gl-header'; // header element selector var gl_HeaderFullHeight = 100; // maximum height of header, should always match height of #gl-header in /common/css/gl-styles.en.css var gl_HeaderCompactHeight = 42; // minimum height of header, should always match height of #gl-header.gl-compact in /common/css/gl-styles.en.css var gl_HeaderOffset = 60; // clearance needed to properly offset linked elements from the compact header, should always match height of .gl-anchor in /common/css/gl-styles.en.css var gl_Footer = '#gl-footer'; // footer element selector var gl_FooterLanguages = '#gl-footer-lang'; // footer language menu element selector var gl_FooterLangMessages = '#gl-footer-lang-message'; // footer language message element selector var gl_FooterLangLinks = '#gl-footer-lang-links'; // footer language link wrapper element selector var gl_FooterDifference = '#gl-footer-difference'; // footer difference element selector
/* Return an element's "true" top offset that has been adjusted to account for its margin-top, border-top, and (optionally) the header offset.
@function gl_GetAdjustedOffset @param elem {string} [default=false] the element to assess, can be any standard jQuery selector @param header {boolean} [default=true] optional, if true, subtract gl_HeaderOffset from the offset value @return {integer} or {float} ========================================================================== */
function gl_GetAdjustedOffset(elem, header) {
var elem = elem || false; var header = header || true; if (elem && $(elem).length > 0) { // element found, calculate its top offset var offset = parseFloat($(elem).offset().top); var difference = parseFloat($(elem).css('margin-top')) + parseFloat($(elem).css('border-top-width')); // difference found, subtract it if (difference > 0) offset -= difference; // header found, subtract its designated offset if (header && $(gl_Header).length > 0) offset -= gl_HeaderOffset; } else { // element not found offset = 0; } return offset;
}
/* Return the height of the header or (optionally) subtract the header offset from a specific value.
@function gl_GetHeaderHeight @param top {integer} or {float} [default=0] optional, subtract gl_HeaderOffset from this value and return it @return {integer} or {float} ========================================================================== */
function gl_GetHeaderHeight(top) {
if ($(gl_Header).length > 0) { // header found var top = top || 0; var height = gl_HeaderFullHeight; // top parameter set, subtract the header offset from it if (top > 0 && top >= height) height = top - gl_HeaderOffset; } else { // header not found height = 0; } return height;
}
/* Offset scrollTop on hash urls to compensate for the fixed position header.
@function gl_SetAnchorOffset @bind $('a').on('click') @param href {string} optional, pass an anchor's href attribute @return nothing ========================================================================== */
function gl_SetAnchorOffset(href) {
// determine which url to use; the anchor's href attribute or the current location var url = href || window.location.href;
// search the url for a hash, quit if blank or not found var hash = url.indexOf('#'); var len = url.slice(hash).length; if (hash == -1 || len < 2) return;
// search for identifiers on the page, quit if not found var id = url.slice(hash); var name = 'a[name=' + id.replace('#', ) + ']'; if ($(id).length == 0 && $(name).length == 0) return;
// determine which element selector to use, quit if no eligible elements found if ($(id).length > 0) { // use element id var select = id; } else if ($(id).length == 0 && $(name).length > 0) { // use element name (for anchors) var select = name; } else { return; }
// insert a dummy span in front of the element so we always get an accurate offset $(select).before(''); // get dummy span offset var top = $('.gl-anchor').offset().top; // remove the dummy span now that we have the offset $('.gl-anchor').remove();
// scroll to the new offset (the delay makes it more reliable) setTimeout(function() { $(window).scrollTop(top); }, 1);
return;
}
/* Limit the rate at which a function can trigger, greatly improves performance in some situations.
@function gl_ThrottleFunction @param func {function} function to throttle @param wait {integer} [default=250] maximum rate at which the function is permitted to trigger, in milliseconds @param immediate {boolean} [default=true] if true, trigger the function before the wait; if false, trigger the function after the wait @return function ========================================================================== */
function gl_ThrottleFunction(func, wait, immediate) {
var timeout; var wait = wait || 250; var immediate = immediate || true; return function() { var context = this; var args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { timeout = null; // trigger after the wait if (!immediate) func.apply(context, args); }, wait); // trigger before the wait if (immediate) func.apply(context, args); };
};
/* Move the footer to the bottom of the viewport on short pages.
@variable gl_SetBodyHeightDifference @bind $(window).on('load resize') @return function ========================================================================== */
var gl_SetBodyHeightDifference = gl_ThrottleFunction(function() {
if ($(gl_Wrapper).length > 0 && $(gl_FooterDifference).length > 0) { // remove any existing difference from the calculation $(gl_FooterDifference).hide(); // get the height difference between the viewport and the outer wrapper var difference = parseInt($(window).height() - $(gl_Wrapper).height()); if (difference > 0) { // difference found, set height on a placeholder div (more reliable than a margin) $(gl_FooterDifference).show().height(difference); } else { // no difference found, hide placeholder div $(gl_FooterDifference).hide().height(0); } }
});
/* Prevent javascript errors on this old, obsolete function call.
@function set_domain @return nothing ========================================================================== */
function set_domain() {};
$(document).ready(function() {
/* header
========================================================================== */
// only run if the header has been included if ($(gl_Header).length > 0) {
/* enable fixed header only in js-enabled environments ================== */
$('#gl-header, #gl-header-bg, #gl-header-offset').addClass('gl-header-fixed');
/* adjust header on page scroll ========================================= */
var gl_OffsetLeft = $(gl_Header).offset().left + (0 - $(window).scrollLeft()); var gl_FirstRun = true;
$(window).on('load scroll resize', gl_ThrottleFunction(function() { // create ability to toggle the compact menu if ($('body.gl-header-nocompact').length > 0) { // don't use the compact header (the rest is done with css) gl_FirstRun = false; } else { // switch to compact mode when page is loaded/reloaded while scrolled down if (gl_FirstRun && $(window).scrollTop() > gl_HeaderFullHeight - gl_HeaderCompactHeight) { $('#gl-header, #gl-header-bg, #gl-header-offset').addClass('gl-compact gl-onload'); gl_FirstRun = false; return; } gl_FirstRun = false;
// toggle modes on scroll if ($(window).scrollTop() > gl_HeaderFullHeight - gl_HeaderCompactHeight) { // switch to compact mode when user scrolls down $('#gl-header, #gl-header-bg, #gl-header-offset').addClass('gl-compact'); } else { // use full size mode by default and switch to it when user scrolls up $('#gl-header, #gl-header-bg, #gl-header-offset').removeClass('gl-compact'); } }
// emulate horizontal scroll effect on fixed position element }));
/* adjust header offset on named anchors ================================ */
gl_SetAnchorOffset(); $('a').on('click', function() {
// gl_SetAnchorOffset($(this).prop('href'));
});
/* show submenus ======================================================== */
if ($('body.gl-header-no-submenus').length === 0) { var gl_Doctype = document.doctype; // null in IE9 quirks-mode environments, but not html5 if (!!(('ontouchstart' in window) || (navigator.MaxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0))) { // only show submenu in js-enabled, non-quirks-mode environments if (typeof gl_LTEIE9 === 'undefined' || (typeof gl_LTEIE9 === 'boolean' && gl_Doctype !== null)) { // force touch devices make better use of the :hover and :active pseudo-classes $('body').attr({ ontouchstart: , onmouseover: });
// prevent videos from absorbing click/touch events from the global header var gl_Clicked = false; $('#gl-header a').off('click tap hover').on('click tap', function(e) { if (gl_Clicked === true) { gl_Clicked = false; e.preventDefault(); return; } gl_Clicked = true; // add classes to videos, hiding them so they can't steal focus $('video').addClass('gl-hidden'); // make sure they're hidden before triggering the click event setTimeout(function() { $(this).trigger('click tap'); }, 1); });
// show submenus on click/tap for touch-enabled devices $('#gl-menu .gl-has-submenu > a').off('click tap hover').on('click tap', function(e) { // prevent href from triggering e.preventDefault();
// close submenu when clicked/tapped a 2nd time if ($(this).parent().hasClass('gl-open')) { $(this).parent().removeClass('gl-open'); // prevent videos from absorbing click/touch events from the global header $('video.gl-hidden').removeClass('gl-hidden'); return; }
// close any other submenus that may be open var gl_IsOpen = $('#gl-menu').find('.gl-open'); if (gl_IsOpen) $(gl_IsOpen).removeClass('gl-open');
// toggle the submenu $(this).parent().addClass('gl-open'); if ($(this).parent().attr('id') === 'gl-menu-search') { // focus on the search input field $('#gl-search').focus(); } else { // focus on the submenu $(this).parent().children('.gl-submenu').focus(); }
// add classes to videos and remove their controls $('video').addClass('gl-hidden'); }); } } else { // convert the search link into a div in submenu-enabled, js-enabled, non-quirks-mode environments if (typeof gl_LTEIE9 === 'undefined' || (typeof gl_LTEIE9 === 'boolean' && gl_Doctype !== null)) { // convert search anchor to div so IE9 doesn't try to use it var gl_SearchWrapper = '#gl-menu-search .gl-submenu-wrapper';$(gl_SearchWrapper).replaceWith($(' '));
// hide all submenus when mouse leaves the menu wrapper $('#gl-menu').on('mouseleave', function() { $('#gl-menu.gl-clicked, #gl-menu .gl-open').removeClass('gl-clicked gl-open'); $('body').focus(); });
// enable submenu hover functionality on click $('#gl-menu .gl-has-submenu .gl-submenu-wrapper').on('click', function(e) { // prevent href from triggering e.preventDefault();
// enable hover events on the menu $('#gl-menu').addClass('gl-clicked'); // show the submenu on this tab on click $(this).parent().addClass('gl-open'); if ($(this).parent().attr('id') === 'gl-menu-search') { // focus on the search input field $('#gl-search').focus(); } else { // focus on the submenu $(this).parent().children('.gl-submenu').focus(); } });
// show submenus on timed hover var gl_HoverConfig = { over: function() { if ($('#gl-menu').hasClass('gl-clicked')) { // show the submenu on this tab on mouseenter $(this).addClass('gl-open'); if ($(this).attr('id') === 'gl-menu-search') { // focus on the search input field $('#gl-search').focus(); } else { // focus on the submenu $(this).children('.gl-submenu').focus(); } } }, out: function() { if ($('#gl-menu').hasClass('gl-clicked')) { // hide the submenu on this tab on mouseleave $('#gl-menu .gl-open').removeClass('gl-open'); $('body').focus(); } }, interval: 50, // default = 100 timeout: 50, // default = 0 sensitivity: 500 // default = 6 }; $('#gl-menu .gl-has-submenu, #gl-menu #gl-menu-search.gl-has-submenu').hoverIntent(gl_HoverConfig); } else { // show hover styles instead of submenu in IE9 quirks mode $('#gl-menu .gl-has-submenu > a').hover(function() { $(this).parent().addClass('gl-open'); }, function() { $(this).parent().removeClass('gl-open'); }); } }
/* search input ========================================================= */
// ensure default localized placeholder text is being used // $('#gl-submenu-search form').get(0).reset();
var gl_SearchDefault = $('#gl-search').val();
// change placeholder text to a blank string on submit $('#gl-submenu-search form').submit(function(e) { var gl_CurrentSearchValue = $('#gl-search').val(); if (gl_CurrentSearchValue == gl_SearchDefault || gl_CurrentSearchValue == ) e.preventDefault(); }); $('#gl-search').focus(function() { // deselect input text and change style on focus var gl_SearchCurrent = $(this).val(); if (gl_SearchCurrent !== gl_SearchDefault) { // terms found, un-italicize $(this).removeClass('italics').addClass('normal active'); } else { // no terms found, italicize $(this).val().val(gl_SearchDefault).removeClass('normal active').addClass('italics'); if ($(this)[0].setSelectionRange) $(this)[0].setSelectionRange(0, 0); } }).keydown(function() { // change placeholder text to a blank string and un-italicize on keypress if ($(this).val() == gl_SearchDefault) $(this).val().removeClass('italics').addClass('normal active'); }).focusout(function() { // change everything back to default on focusout when input is blank if ($(this).val() == ) { $(this).val(gl_SearchDefault).removeClass('normal active').addClass('italics'); if ($(this)[0].setSelectionRange) $(this)[0].setSelectionRange(0, 0); } }).click(function() { // refocus on click, fixes bug with flash movies stealing focus in Firefox on OSX $(this).blur().focus(); }); } }
});