// ################# 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));