// ################# SATURNO STARTUP ACTIONS ################# (function ($) { // first-wave actions $(document).ready(function() { addMarkers(); }); // second-wave actions $(window).on("load", function() { // execute lazy-load for mobile elements! if (saturno.getGridStatus().smExact) { $('[data-lazybg-mobile]').satLazyBg('data-lazybg-mobile'); } }); // add satgrid responsive size markers if they do not already exist var addMarkers = function() { var container = $('#sg-markers'); if (container.length == 0) { $('body').append( '
' ); } }; }(jQuery)); // ################# SATURNO UTILITY FUNCTIONS ############### // BEGIN master "saturno" namespace wrapper (function (saturno, $, undefined) { // get an object representing current visibility of responsive grid {sm,md,lg,smExact,mdExact,lgExact} saturno.getGridStatus = function() { var markers = $('#sg-markers'); var smMarkerVis = $('.sg-marker-sm',markers).css('visibility') == 'visible'; var mdMarkerVis = $('.sg-marker-md',markers).css('visibility') == 'visible'; var lgMarkerVis = $('.sg-marker-lg',markers).css('visibility') == 'visible'; var state = { sm: smMarkerVis, md: mdMarkerVis, lg: lgMarkerVis, smExact: smMarkerVis && !mdMarkerVis && !lgMarkerVis, mdExact: mdMarkerVis && !lgMarkerVis, lgExact: lgMarkerVis }; return state; } // get the value of a named parameter from the query string saturno.getQueryParam = function (name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\?&]" + name + "=([^&#]*)", "i"); var results = regex.exec(location.search); return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } // get the value of a named parameter from the location.hash saturno.getHashParam = function (name) { name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); var regex = new RegExp("[\\#&]" + name + "=([^&#]*)", "i"); var results = regex.exec(location.hash); return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " ")); } // dynamically load a javascript file (similar to $.getScript), uses cache saturno.loadScript = function (url, successCallback) { return $.ajax({ url: url, dataType: 'script', cache: true, success: successCallback }); } // attach a stylesheet to the document for the given media type saturno.loadStylesheet = function (url, mediaType) { $('', { rel: 'stylesheet', type: 'text/css', media: mediaType, href: url }).appendTo('head'); } }(window.saturno = window.saturno || {}, jQuery)); // END master "saturno" namespace wrapper // ################# JQUERY PLUGIN DEFINITIONS BELOW HERE ################### // satAutoHint() // auto-hint functionality for textboxes // crafted by Brian 07/26/2010, tweaked 01/14/2011, tested on jQuery 1.4.2 (function ($) { $.fn.satAutoHint = function () { // loop through all our items and set them up one by one $(this).each(function (idx) { var mytextbox = $(this); var myhint = mytextbox.attr('placeholder'); // needs a title attribute to be set to the hint value if (myhint) { // on focus, automatically clear the hint mytextbox.on('focus', function () { if ($(this).val() == myhint) { $(this).val(''); } }); // on blur, replace the hint if we have no text mytextbox.on('blur', function () { if ($(this).val() == '') { $(this).val(myhint); } }); // force focus loss to initialize mytextbox.blur(); } }); return $(this); }; // auto-setup based on existence of 'placeholder' attribute $(document).ready(function () { $('input[placeholder],textarea[placeholder]').satAutoHint(); }) }(jQuery)); // satEnterSubmit() // add enter-to-submit functionality to an element // pass in the element you wish to be 'clicked' when enter is pressed (function ($) { $.fn.satEnterSubmit = function (submitelement) { // loop through all our items and set them up one by one $(this).each(function (idx) { $(this).keypress(function (e) { if (e.keyCode == 13) { if ($(submitelement).is('a') && $(submitelement).attr('href').indexOf('javascript:__doPostBack(') == 0) { // POSTBACK LINK CLICKS! eval($(submitelement).attr('href')); } else { $(submitelement).click(); } return false; } }); }); return $(this); }; }(jQuery)); // satForm() // jQuery plugin for simplistic configuration of a "form" for javascript submission // ----- // call this plugin on a "container" element with the following contents: // - input/textarea/select elements with an attribute dictating the "name" to use for that element's value (default attribute is "name") // - a "submit" element; can be a button, link, input (defaults to the last button found in the container) // // sample call with no options (requires destination url): // $('.myform').satForm('/search.aspx'); // ----- // created by Brian 07/21/2017, tested on jQuery 1.12.0, should support IE8+ (function ($) { // default settings var optionsBase = { fields: null, nameAttribute: 'name', submitButton: null, submitHash: '', textFields: null, }; // our actual plugin action $.fn.satForm = function (submitUrl, options) { // use base options + overrides as our settings var settings = $.extend({}, optionsBase, options); // loop through submitted containers and carry out operations $(this).each(function () { // master container reference var container = $(this); // flesh out default settings for this loop var loopFields = settings.fields != null ? settings.fields : $('[' + settings.nameAttribute + ']', container); var loopSubmitButton = settings.submitButton != null ? settings.submitButton : $('button, input[type="submit"], input[type="button"]', container).last(); var loopTextFields = settings.textFields != null ? settings.textFields : $('input[type="text"]', container); // connect the actual submit activity loopSubmitButton.on('click', function (e) { var params = getFormParams(loopFields, settings.nameAttribute); var targetUrl = getFormTargetUrl(submitUrl, settings.submitHash, params); submitForm(targetUrl); return false; }); // connect events to submit form on 'enter' connectEnterSubmitFields(loopTextFields, loopSubmitButton); }); // send back reference for chaining return $(this); }; // connect submit-on-enter events to the supplied text fields function connectEnterSubmitFields(textFields, submitElement) { $(textFields).each(function () { $(this).on('keypress', function (e) { if (e.keyCode == 13) { $(submitElement).click(); return false; } }); }); } // gather selected search arguments from a specified container function getFormParams(fields, nameAttribute) { var params = []; // collect all the selected search values $(fields).each(function () { var field = $(this); var fieldName = field.attr(nameAttribute); var fieldValue = field.val(); if (field.is('[type=checkbox]') && !field.is(':checked')) { // blank out the checkbox types unless they are checked fieldValue = ''; } if (fieldValue != '' && fieldValue != '-1' && fieldValue != field.attr('placeholder')) { // generate a label to use in filtering var fieldLabel = fieldValue; if (field.is('select')) { fieldLabel = $('option:selected', field).text(); } // actually record the search value for use params.push({ name: fieldName, value: fieldValue, label: fieldLabel }); } }); return params; } // assemble the url components to get the finished url for submission function getFormTargetUrl(submitUrl, submitHash, params) { var targetUrl = submitUrl + (submitUrl.indexOf('?') < 0 ? '?' : '&'); targetUrl += $.param(params, true); targetUrl += (submitHash != '' ? '#' + submitHash : ''); return targetUrl; } // actually submit a collection of form parameters to the given submission target function submitForm(targetUrl) { window.location.href = targetUrl; } }(jQuery)); // satInjectPagedResults() // paging helper for display of search results data - call this from the element that should act as output container // ---- // elements: jQuery collection of the individual result elements // itemsperpage: max number of items to allow on a single page // groupclass: CSS class which will be added to a "wrapper" div around each page group // noresultsmsg: content shown in the pager controls when no results are found // pagingcontainer: jQuery ref for the element where dynamic paging controls should be injected // // sample call: $('result-output').satInjectPagedResults( $('result-item'), 10, $('result-page'), 'No results found.', $('results-pager') ); // ---- // crafted by Brian 04/01/2013 (function ($) { $.fn.satInjectPagedResults = function (elements, itemsperpage, groupclass, noresultsmsg, pagingcontainer) { var outputcontainer = $(this); var totalitems = elements.length; var totalpages = Math.ceil(totalitems / itemsperpage); // wipe the output container outputcontainer.empty(); // run our wrapping operation that groups elements together and pops them into the output area var sliceidx = 0; var pageidx = 0; var group; // keep looping as long as we can make more groups! while ((group = elements.slice(sliceidx, sliceidx += itemsperpage)).length) { var newgroup = group.wrapAll('
').parent(); if (pageidx > 0) { // only first page is visible by default newgroup.hide(); } outputcontainer.append(newgroup); pageidx += 1; } // create an element with our paging info and links pagingcontainer.empty().append(''); var pagingel = pagingcontainer.children('.sat-paging'); if (totalpages == 0) { pagingel.append('' + noresultsmsg + ''); } else if (totalpages > 1) { pagingel.append('Prev'); pagingel.append('Page 1 of ' + totalpages + ""); pagingel.append('Next'); var currpage = pagingel.find('.sat-paging-curr'); var btnprev = pagingel.find('a.sat-paging-prev'); var btnnext = pagingel.find('a.sat-paging-next'); // NEXT click ... advance the page and potentially disable ourselves btnnext.click(function () { var clickedbutton = $(this); if (!clickedbutton.hasClass('sat-paging-disabled')) { var activepage = outputcontainer.children('.' + groupclass).filter(':visible'); var activeindex = activepage.index('.' + groupclass); // hide curr page, show the next one! activepage.hide().next().show(); // see if we need to disable the next button ... if (activeindex + 2 >= totalpages) { clickedbutton.addClass('sat-paging-disabled'); } // enable the PREV button btnprev.removeClass('sat-paging-disabled'); // echo the active page currpage.text(activeindex + 2); outputcontainer.trigger('pagechange'); } return false; }); // PREV click ... show prev page and potentially disable ourselves btnprev.click(function () { var clickedbutton = $(this); if (!clickedbutton.hasClass('sat-paging-disabled')) { var activepage = outputcontainer.children('.' + groupclass).filter(':visible'); var activeindex = activepage.index('.' + groupclass); // hide curr page, show the next one! activepage.hide().prev().show(); // see if we need to disable the prev button ... if (activeindex - 1 <= 0) { clickedbutton.addClass('sat-paging-disabled'); } // enable the NEXT button btnnext.removeClass('sat-paging-disabled'); // echo the active page currpage.text(activeindex); outputcontainer.trigger('pagechange'); } return false; }); } outputcontainer.trigger('load'); // return call for chaining return $(this); } }(jQuery)); // satLazyBg() // Simple lazy-loading functionality for CSS background-images, with proxy support // Background image will be loaded from an attribute, but if a "proxy" selector is supplied, // that proxy element must be visible or the background image load will not take place. // ---- // sample call, first param is the attribute name that holds the delayed bg src value // $('img[data-lazybg]').satLazyBg('data-lazybg'); // // sample call, optional second param is the attribute name for a visibility proxy // $('img[data-lazybg]').satLazyBg('data-lazybg', 'data-lazybg-proxy'); // ---- // created by Brian 02/2014, revised into plugin 08/08/2016 (function ($) { $.fn.satLazyBg = function (lazyattr,proxyattr) { // marker class we use to avoid repeat work var completeattr = 'data-lazybg-complete'; $(this).each(function () { var myel = $(this); if (myel.attr(completeattr) == '1') { // already finished return; } else { var src_lazy = myel.attr(lazyattr); if (src_lazy != '') { // if we have a proxy element, that element must be visible or we won't carry out the operation if (proxyattr != undefined) { var proxyselector = myel.attr(proxyattr); if (proxyselector != undefined && proxyselector != '') { var proxyvisible = ($(proxyselector).css('visibility') == 'visible'); if (!proxyvisible) { // replace true image url with encoded 16x16 transparent PNG src_lazy = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AgFETMWwGUT9wAAABJJREFUOMtjYBgFo2AUjAIIAAAEEAABhT+qcgAAAABJRU5ErkJggg=='; } } } // actually mark job complete and set the value myel.attr(completeattr, '1'); myel.css('background-image', 'url("' + src_lazy + '")'); } } }); return $(this); } // automatic configuration using attribute "data-lazybg" and "data-lazybg-proxy" $(window).on("load", function () { $('[data-lazybg]').satLazyBg('data-lazybg', 'data-lazybg-proxy'); }); }(jQuery)); // satLazySrc() // Simple lazy-loading functionality for images // ---- // sample call, single param is the attribute name that holds the delayed src value // $('img[data-lazysrc]').satLazySrc('data-lazysrc'); // ---- // created by Brian 07/2013, revised into plugin 08/08/2016 (function ($) { $.fn.satLazySrc = function (lazyattr) { // marker class we use to avoid repeat work var completeattr = 'data-lazysrc-complete'; $(this).each(function () { var myimg = $(this); if (myimg.attr(completeattr) == '1') { return; } else { // set the "src" attribute, only if it's non-empty, and a change from the existing src var src_orig = myimg.attr('src'); var src_lazy = myimg.attr(lazyattr); if (src_lazy != '' && src_lazy != src_orig) { // mark this element as processed so we don't do it over and over myimg.attr(completeattr, '1'); myimg.attr('src', src_lazy); } } }); return $(this); } // auto-configuration using attribute "data-lazysrc" $(window).on("load", function () { $('img[data-lazysrc]').satLazySrc('data-lazysrc'); }); }(jQuery)); // satRollup() // content rollup behavior for accordion style header/body collapsing // ---- // Sample call with [optional] third param for class to indicate "start open" (instead of collapsed) // Optional 4th param will close siblings when a header is clicked // Optional 5th param will change the class used to designate a head or body element as active. // $('.sharedrollup').satRollup('.sharedrollup-header','.sharedrollup-body','sharedrollup-startopen', true, '-active'); // ---- // created by Brian 08/08/2016 (function ($) { $.fn.satRollup = function (headerfilter, bodyfilter, startopenclass, accordion, activeclass) { $(this).each(function () { var container = $(this); var headel = $(headerfilter, this); var bodyel = $(bodyfilter, this); activeclass = activeclass || "active"; if (bodyel != null && bodyel.length == 1 && $.trim(bodyel.text()).length == 0) { //Hides the whole accordion section if the content is empty $(this).hide(); } if (headel.length == 1 && bodyel.length == 1) { // configure the 'start open' items if ($(this).hasClass(startopenclass)) { container.addClass(activeclass); bodyel.show(); } else { container.removeClass(activeclass); bodyel.hide(); } // configure some extra dynamic elements for smooth operation bodyel.wrapInner('
') // basic event handlers container.on('click', headerfilter, function () { if (accordion) { var sibs = container.siblings(); sibs.each(function () { $(this).removeClass(activeclass); $(this).find(bodyfilter).slideUp(); }); } var clickedbody = $(bodyfilter, container); if (container.hasClass(activeclass)) { container.removeClass(activeclass); clickedbody.slideUp(); } else { container.addClass(activeclass); clickedbody.slideDown(); } return true; }); } }); return $(this); } }(jQuery)); // satTabs() // jQuery plugin for "tab" behaviors with targeted hashtags that persist with back button. // ----- // Call this plugin on a set of "tab" links with destination id in "href" (ex: "#overview") // Content areas must have a matching attribute "data-sattabs-id" (ex: data-sattabs-id="overview") // Some of the internal functions are overridable with settings // // sample call if your tab links have class "sharedtab": // $('a.sharedtab').satTabs(); // // sample calls if you need multiple tab sets: // $('a.maintab').satTabs({ groupName:'primary' }); // $('a.sidetab').satTabs({ groupName:'secondary' }); // ----- // created by Brian 03/18/2015, tested on jQuery 1.9+, should support IE8+ (function ($) { // default settings var optionsBase = { activeTabClass: 'active', activeBodyClass: 'active', groupName: 'global', getTabName: function(tabEl) { // get the "target name" string for this tab, // default behavior is read it from the href attribute and chop the # var linkhash = $(tabEl).attr('href'); if (linkhash.indexOf('#')==0 && linkhash.length>1) { var cleantargetname = linkhash.substring(1); cleantargetname = cleantargetname.toLowerCase().replace(/[^a-z0-9]/g,""); return cleantargetname; } else { return ''; } }, getTabBody: function (targetName) { // given the target string, lookup the related tab body // default behavior is to look for an item with that ID return $('[data-sattabs-id="' + targetName + '"]'); }, getTabMarkerElement: function (tabEl) { // given a tab element, get the 'element' that should be marked as active // default behavior is it just uses itself as a marker return $(tabEl); }, isTabBodyEmpty: function (body) { // given a tab body element, return true if the element is considered 'empty' var content = ''; if (body != null && body.length == 1) { content = $.trim(body.text()); } return (content.length == 0); } }; // Plugin startup for satTabs, takes possible override options {activeTabClass, activeBodyClass, groupName, getTabName, getTabBody, getTabMarkerElement, isTabBodyEmpty} $.fn.satTabs = function (options) { // use base options + overrides as our settings var settings = $.extend({}, optionsBase, options); // loop through tabs and connect our tabs to their bodies! $(this).each(function (tabindex) { // look up the appropriate tab/body by name var tablink = $(this); var tabname = settings.getTabName(tablink); var tabbody = settings.getTabBody(tabname); // mark the appropriate elements with the group name var fullgroupname = 'data-sattabs-' + settings.groupName; tablink.attr(fullgroupname + '-tab', tabname); tabbody.attr(fullgroupname + '-body', tabname); // automatically hide empty tabs if (settings.isTabBodyEmpty(tabbody)) { tablink.hide(); tabbody.hide(); } }); // bind to the hashchange event to determine if user is requesting a content with hashtag (such as a named tab!) $(window).on('hashchange', function () { // gather all tab markers and bodies for this group var tablinks = $('[data-sattabs-' + settings.groupName + '-tab]'); var tabmarkers = $([]); for (var i = 0; i < tablinks.length; i++) { var looplink = tablinks.get(i); tabmarkers = tabmarkers.add(settings.getTabMarkerElement(looplink)); } var tabbodies = $('[data-sattabs-' + settings.groupName + '-body]'); // read the desired tabname from the browser hash var tabname = ''; // first check in browser hash if (window.location.hash && window.location.hash.length > 1 && window.location.hash.substring(0,1) == '#') { tabname = window.location.hash.substring(1); } // if we found nothing and the hash is part of the possible set of hashes (is check), check for the first visible tab link if (tabname == null || tabname == '' || !$(tablinks).is(function () { return $(this).attr('data-sattabs-'+settings.groupName+'-tab') == tabname; })) { tabname = tablinks.filter(':visible').first().attr('data-sattabs-'+settings.groupName+'-tab'); } // assemble refs to the item we want to mark as "active" var activeTab = tablinks.filter('[data-sattabs-' + settings.groupName + '-tab="' + tabname + '"]'); var activeMarker = settings.getTabMarkerElement(activeTab); var activeBody = tabbodies.filter('[data-sattabs-' + settings.groupName + '-body="' + tabname + '"]'); // on click, we want to mark the active item and un-marks all others in the group activeMarker.addClass(settings.activeTabClass); tabmarkers.not(activeMarker).removeClass(settings.activeTabClass); activeBody.addClass(settings.activeBodyClass); tabbodies.not(activeBody).removeClass(settings.activeBodyClass); }); // trigger a hash change on initial setup to make sure we initialize $(window).trigger('hashchange'); // send back reference for chaining return $(this); }; }(jQuery));