PageSourceSearch

https://hardwoodbiofuels.org/wp-content/themes/spine/js/spine.js?ver=2.0.4-2.0.1-44766

js hardwoodbiofuels.org collected 2026-09-29 08:55:35 UTC 51,145 bytes, 1,667 lines download raw bytes

1/*
2	TermTemplate:"<strong> <%this.term%> </strong>",
3	this.options.relatedHeader
4
5	termTemplate
6	autoSearchObj.options.provider.termTemplate
7*/
8
9/* jshint onevar: false */
10( function( $ ) {
11	"use strict";
12	$.widget( "ui.autosearch", $.ui.autocomplete, {
13		_renderMenu: function( ul, items ) {
14			var that	= this;
15			var related	= $.grep( items, function( obj ) {//, item) {
16				return obj.related !== "false";
17			} );
18			var unrelated = $.grep( items, function( obj ) {//, item) {
19				return obj.related === "false";
20			} );
21			$.each( unrelated, function( i, item ) {
22				that._renderItemData( ul, item );
23			} );
24
25			if ( this.options.showRelated && related.length ) {
26				if ( this.options.relatedHeader ) {
27					that._renderHeader( ul, this.options.relatedHeader );
28				}
29				$.each( related, function( i, item ) {
30					that._renderItemData( ul, item );
31				} );
32			}
33		},
34		_renderItemData: function( ul, item ) {
35			return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
36		},
37		_renderItem: function( ul, item ) {
38			var text	= item.label;
39
40			return $( "<li></li>" ).data( "item.autocomplete", item ).append( text ).appendTo( ul );
41		},
42		_renderHeader: function( ul, text ) {
43			return $( "<li></li>" ).append( "<a href=''>" + text + "</a>" ).appendTo( ul );
44		}
45	} );
46} )( jQuery );
47
48/** Intended usage is
49*	$.spine({
50*		"option":"value"
51*	});
52**/
53/*jshint -W054 */
54;( function( $, window, document, undefined ) {
55	/**
56	 * Strip one or more classes from a class attribute matching a given prefix.
57	 *
58	 * @param {string} partialMatch The class partial to match against, like `btn-` to match `btn-danger btn-active`, but not `btn`.
59	 * @param {string} endOrBegin   Omit to match the beginning. Provide a truthy value to only find classes ending with a match.
60	 * @returns {jQuery}
61	 */
62	$.fn.stripClass = function( partialMatch, endOrBegin ) {
63		var x;
64		x = new RegExp( ( !endOrBegin ? "\\b" : "\\S+" ) + partialMatch + "\\S*", "g" );
65
66		// http://stackoverflow.com/a/2644364/1037948
67		this.attr( "class", function( i, c ) {
68			if ( !c ) {
69				return; // Protect against no class
70			}
71			return c.replace( x, "" );
72		} );
73		return this;
74	};
75
76	/**
77	 * Refresh a snapshot of stored jQuery selector data.
78	 *
79	 * Not all stored object properties would normally be reflected when
80	 * the original selector is modified. This ensures we capture the
81	 * latest version.
82	 *
83	 * @returns {*}
84	 */
85	$.fn.refresh = function() {
86		var elems;
87		elems = $( this.selector );
88		this.splice( 0, this.length );
89
90		try {
91			this.push.apply( this, elems );
92		}
93		catch ( err ) {
94			if ( $( this.selector ).html() !== "" ) {
95				return $( this.selector );
96			}else {
97				return $( "<div>" );
98			}
99		}
100		return this;
101	};
102
103	/**
104	 * A small templating engine for processing HTML with given data.
105	 *
106	 * @see TemplateEngine via MIT Licensed https://github.com/krasimir/absurd/
107	 *
108	 * @param {string} html
109	 * @param {Object} options
110	 * @returns {*}
111	 */
112	$.runTemplate = function( html, options ) {
113		var re, add, match, cursor, code, reExp, result;
114
115		re = /<%(.+?)%>/g;
116		reExp = /(^( )?(var|if|for|else|switch|case|break|{|}|;))(.*)?/g;
117		code = "var r=[];\n";
118		cursor = 0;
119
120		add = function( line, js ) {
121			if ( js ) {
122				code += line.match( reExp ) ? line + "\n" : "r.push(" + line + ");\n";
123			}else {
124				code += line !== "" ? "r.push('" + line.replace( /'/g, "\"" ) + "');\n" : "";
125			}
126			return add;
127		};
128
129		while ( ( match = re.exec( html ) ) ) {
130			add( html.slice( cursor, match.index ) )( match[ 1 ], true );
131			cursor = match.index + match[ 0 ].length;
132		}
133
134		add( html.substr( cursor, html.length - cursor ) );
135		code = ( code + "return r.join('');" ).replace( /[\r\t\n]/g, "" );
136		result = new Function( code ).apply( options );
137
138		return result;
139	};
140
141	/**
142	 * Unused in Spine.
143	 *
144	 * @todo Consider deprecating.
145	 *
146	 * @returns {*}
147	 */
148	$.whenAll = function() {
149		return $.when.apply( $, arguments );
150	};
151
152	/**
153	 * Determine if the current view is an iOS device.
154	 *
155	 * @returns {boolean}
156	 */
157	$.is_iOS = function() {
158		return ( window.navigator.userAgent.match( /(iPad|iPhone|iPod)/ig ) ? true : false );
159	};
160
161	/**
162	 * Determine if the current view is an Android device.
163	 *
164	 * @returns {boolean}
165	 */
166	$.is_Android = function() {
167		return ( window.navigator.userAgent.match( /(Android)/ig ) ? true : false );
168	};
169
170	/**
171	 * Detect browser support for SVG images.
172	 *
173	 * @returns {boolean}
174	 */
175	$.svg_enabled = function() {
176		return document.implementation.hasFeature( "http://www.w3.org/TR/SVG11/feature#Image", "1.1" );
177	};
178
179	/**
180	 * Use MutationObserver to watch for any changes to a specific DOM element and trigger
181	 * the passed callback when a change is made.
182	 *
183	 * This is currently only used within the Spine to watch `#glue` for changes such as
184	 * menu expansion, etc...
185	 *
186	 * @param obj
187	 * @param callback
188	 */
189	$.observeDOM = function( obj, callback ) {
190		var config, mutationObserver;
191
192		if ( window.MutationObserver ) {
193			config = {
194				childList: true,
195				attributes: true,
196				subtree: true,
197				attributeOldValue: true,
198				attributeFilter: [ "class", "style" ]
199			};
200
201			mutationObserver = new MutationObserver( function( mutationRecords ) {
202				var fire_callback = false; // Assume no callback is needed.
203
204				$.each( mutationRecords, function( index, mutationRecord ) {
205					if ( mutationRecord.type === "childList" ) {
206						if ( mutationRecord.addedNodes.length > 0 ) {
207							fire_callback = true;
208						} else if ( mutationRecord.removedNodes.length > 0 ) {
209							fire_callback = true;
210						}
211					} else if ( mutationRecord.type === "attributes" ) {
212						if ( mutationRecord.attributeName === "class" ) {
213							fire_callback = true;
214						}
215					}
216				} );
217
218				// If one of our matched mutations has been observed, fire the callback.
219				if ( fire_callback ) {
220					callback();
221				}
222			} );
223			mutationObserver.observe( obj[ 0 ], config );
224		} else {
225
226			// Set a fallback function to fire every 200ms and watch for DOM changes.
227			window.setTimeout( function() {
228				var current_obj = obj.refresh();
229
230				if ( typeof window.obj_watch === "undefined" ) {
231					window.obj_watch = current_obj[ 0 ];
232				}
233
234				/**
235				 * If the current object does not match the object we're watching, assume
236				 * a DOM mutation has occurred and fire the callback.
237				 */
238				if ( window.obj_watch !== current_obj[ 0 ] ) {
239					callback();
240				}
241
242				window.obj_watch = current_obj[ 0 ];
243
244				// Reset observation on the current object.
245				$.observeDOM( current_obj, callback );
246			}, 200 );
247		}
248	};
249
250	/**
251	 * Setup the plugin's prototype.
252	 *
253	 * @param {string} name
254	 * @param {object} prototype
255	 */
256	$.s = function( name, prototype ) {
257		var namespace;
258
259		namespace = name.split( "." )[ 0 ];
260		name = name.split( "." )[ 1 ];
261
262		$[ namespace ] = $[ namespace ] || {};
263
264		$[ namespace ][ name ] = function( options, element ) {
265			if ( arguments.length ) {
266				this._setup( options, element );
267			}
268		};
269
270		$[ namespace ][ name ].prototype = $.extend( {
271			namespace: namespace,
272			pluginName: name
273		}, prototype );
274
275		$.fn[ name ] = function( context ) {
276			var isMethodCall, context_options, args, returnValue;
277
278			context_options = {};
279
280			if ( arguments[ 1 ] ) {
281				context_options = arguments[ 1 ];
282			}
283
284			context = context || {};
285
286			this.options = $.extend( {}, context );
287
288			isMethodCall = ( typeof context === "string" );
289			args = Array.prototype.slice.call( arguments, 1 );
290			returnValue = this;
291
292			if ( isMethodCall && context.substring( 0, 1 ) === "_" ) {
293				return returnValue;
294			}
295
296			this.each( function() {
297				var instance;
298
299				instance = $.data( this, name );
300
301				if ( !instance ) {
302					instance = $.data( this, name, new $[ namespace ][ name ]( context, this ) );
303				}
304
305				if ( instance[ context + "_init" ] !== undefined ) {
306					if ( instance[ context + "_init" ] ) {
307						instance[ context + "_init" ]( context_options );
308					}
309				}
310
311				if ( isMethodCall && instance[ context ] !== undefined ) {
312					returnValue = instance[ context ].apply( instance, args );
313				}
314			} );
315			return returnValue;
316		};
317	};
318
319	/**
320	 * Configure and create the jQuery.ui.spine plugin.
321	 *
322	 * Based on a fork of MIT Licensed jquery-ui-map
323	 * See: https://code.google.com/p/jquery-ui-map/source/browse/trunk/ui/jquery.ui.map.js
324	 */
325	$.s( "ui.spine", {
326
327		globals: {
328			version: "0.1.0",
329			current_url:window.location.href
330		},
331
332		options: {},
333
334		/**
335		 * Setup plugin basics.
336		 *
337		 * @param {object}      options
338		 * @param {HTMLElement} element
339		 */
340		_setup: function( options, element ) {
341			this.el = element;
342			options = options || {};
343			$.extend( this.options, options, {} );
344			this._create();
345		},
346
347		/**
348		 * Instantiate the object
349		 */
350		_create: function() {
351			var self;
352
353			self = this;
354			this.instance = {
355				spine: self.options,
356				framework: [],
357				search: [],
358				social: []
359			};
360
361			self._call( self.options.callback, self.instance.spine );
362		},
363
364		/**
365		 * Add objects to the global spine object.
366		 *
367		 * Note: Context is not yet implemented.
368		 *
369		 * @param {object} obj     e.g. {'foo':'bar'}
370		 * @param {string} context e.g. 'search', 'social', 'framework'
371		 */
372		_set_globals: function( obj, context ) {
373			context = null; // Avoiding jshint error temporarily.
374
375			if ( typeof( obj ) !== "object" ) {
376				return;
377			}
378			$.extend( this.globals, obj );
379		},
380
381		/**
382		 * Retrieve a context's objects from the global spine object.
383		 *
384		 * @param {string} context e.g. 'search', 'social', 'framework'
385		 * @returns {*}
386		 * @private
387		 */
388		_get_globals: function( context ) {
389			return this.globals[ context ];
390		},
391
392		/**
393		 * Clears an object of a context.
394		 *
395		 * @param {string} context e.g. 'search', 'social', 'framework'
396		 */
397		clear: function( context ) {
398			this._c( this.get( context ) );
399			this.set( context, [] );
400
401			return this;
402		},
403
404		/**
405		 * Clears an object of its properties.
406		 *
407		 * @param {object} obj
408		 */
409		_c: function( obj ) {
410			for ( var property in obj ) {
411				if ( obj.hasOwnProperty( property ) ) {
412					obj[ property ] = null;
413				}
414			}
415		},
416
417		/**
418		 * Returns objects with a specific context.
419		 *
420		 * @param {string} context In what context, e.g. 'search', 'social', 'framework'
421		 * @param {object} options Contains string property, string value, string operator (AND/OR).
422		 * @param callback:function(search:jObj, isFound:boolean)
423		 */
424		find: function( context, options, callback ) {
425			var obj, isFound, property, value;
426			obj = this.get( context );
427			options.value = $.isArray( options.value ) ? options.value : [ options.value ];
428			for ( property in obj ) {
429				if ( obj.hasOwnProperty( property ) ) {
430					isFound = false;
431					for ( value in options.value ) {
432						if ( $.inArray( options.value[ value ], obj[ property ][ options.property ] ) > -1 ) {
433							isFound = true;
434						} else {
435							if ( options.operator && options.operator === "AND" ) {
436								isFound = false;
437								break;
438							}
439						}
440					}
441					callback( obj[ property ], isFound );
442				}
443			}
444			return this;
445		},
446
447		/**
448		 * Returns an instance property by key. Has the ability to set an object if the property does not exist
449		 * @param key:string
450		 * @param value:object(optional)
451		 */
452		get: function( key, value ) {
453			var instance, e, i;
454			instance = this.instance;
455			if ( !instance[ key ] ) {
456				if ( key.indexOf( ">" ) > -1 ) {
457					e = key.replace( / /g, "" ).split( ">" );
458					for ( i = 0; i < e.length; i++ ) {
459						if ( !instance[ e[ i ] ] ) {
460							if ( value ) {
461								instance[ e[ i ] ] = ( ( i + 1 ) < e.length ) ? [] : value;
462							} else {
463								return null;
464							}
465						}
466						instance = instance[ e[ i ] ];
467					}
468					return instance;
469				} else if ( value && !instance[ key ] ) {
470					this.set( key, value );
471				}
472			}
473			return instance[ key ];
474		},
475
476		/**
477		 * Sets an instance property
478		 * @param key:string
479		 * @param value:object
480		 */
481		set: function( key, value ) {
482			this.instance[ key ] = value;
483			return this;
484		},
485
486		/**
487		 * Helper method for unwrapping jQuery/DOM/string elements
488		 * @param obj:string/node/jQuery
489		 */
490		_unwrap: function( obj ) {
491			return ( !obj ) ? null : ( ( obj instanceof jQuery ) ? obj[ 0 ] : ( ( obj instanceof Object ) ? obj : $( "#" + obj )[ 0 ] ) );
492		},
493
494		/**
495		 * Helper method for calling a function
496		 * @param callback
497		 */
498		_call: function( callback ) {
499			if ( callback && $.isFunction( callback ) ) {
500				callback.apply( this, Array.prototype.slice.call( arguments, 1 ) );
501			}
502		},
503
504		/**
505		 * Destroys spine elements and options.
506		 */
507		clear_spine: function() {
508			this.clear( "search" ).clear( "framework" ).clear( "social" );
509		},
510
511		/**
512		 * Destroys the plugin.
513		 */
514		destroy: function( callback ) {
515			this.clear( "search" ).clear( "framework" ).clear( "social" )._c( this.instance );
516			$.removeData( this.el, this.name );
517			this._call( callback, this );
518		}
519	} );
520
521	/**
522	 * The primary Spine method used to start things up.
523	 *
524	 * @param {object} options
525	 * @returns {*}
526	 */
527	$.spine = function( options ) {
528		var targ;
529
530		targ = this.jquery === undefined ? $( "body" ) : this;
531
532		return $.each( targ, function() {
533			var targ;
534			targ = $( this );
535
536			// Initialize the Spine plugin.
537			targ.spine( {} );
538
539			options = $.extend( { "framework":{}, "search":{}, "social":{} }, options );
540
541			// Setup each of the extensions.
542			$.each( options, function( i, v ) {
543				targ.spine( i, v );
544			} );
545		} );
546	};
547
548} )( jQuery, window, document );
549
550 /*!
551 *
552 * Depends:
553 *		jquery.ui.v.js
554 */
555/*jshint multistr: true */
556( function( $ ) {
557	$.extend( $.ui.spine.prototype, {
558		/**
559		 * Initialize the Spine framework. Fired automatically in `$.s`, found
560		 * in ui.spine.js.
561		 *
562		 * @param {object} options
563		 */
564		framework_init: function( options ) {
565			$.extend( this.framework_options, options );
566			this._set_globals( this.framework_globals );
567			this.framework_create();
568		},
569
570		/**
571		 * Global framework options for the Spine framework.
572		 */
573		framework_options: {
574			viewport_offset: 0,
575			equalizer_filter: ".skip*",
576			contact_template: "<address itemscope itemtype='http://schema.org/Organization' class='hcard'>" +
577								"<% if (typeof(this.department) != 'undefined') { %><div class='organization-unit fn org'>" +
578									"<% if (typeof(this.url) != 'undefined') { %><a href='<%this.url%>' class='url'><% } %>" +
579										"<%this.department%>" +
580									"<% if (typeof(this.url) != 'undefined') { %></a><% } %>" +
581								"</div><% } %> " +
582								"<% if (typeof(this.name) != 'undefined') { %><div class='organization-name'><%this.name%></div><% } %>" +
583								"<div class='address'>" +
584									"<% if (typeof(this.streetAddress) != 'undefined') { %><div class='street-address'><%this.streetAddress%></div><% } %>" +
585									"<% if (typeof(this.addressLocality) != 'undefined' || typeof(this.postalCode) != 'undefined') { %><div class='locality'>" +
586										"<% if (typeof(this.addressLocality) != 'undefined' ) { %><%this.addressLocality%><% } %>" +
587										"<% if (typeof(this.addressRegion) != 'undefined' ) { %>, <%this.addressRegion%><% } %>" +
588										"<% if (typeof(this.postalCode) != 'undefined' ) { %> <span class='postalcode'><%this.postalCode%></span><% } %>" +
589									"</div><% } %>" +
590								"</div>" +
591								"<% if (typeof(this.telephone) != 'undefined' ) { %><div class='tel'><%this.telephone%></div><% } %>" +
592								"<% if (typeof(this.email) != 'undefined' ) { %><div class='email' rel='email'><a href='mailto:<%this.email%>'>Email us</a></div><% } %>" +
593								"<% if (typeof(this.ContactPoint) != 'undefined' && typeof(this.ContactPointTitle) != 'undefined') { %>" +
594									"<div class='more'><a href='<%this.ContactPoint%>'><%this.ContactPointTitle%></a></div>" +
595								"<% } %>" +
596								"</address>"
597		},
598
599		/**
600		 * Global objects that are part of the Spine framework.
601		 */
602		framework_globals: {
603			spine: $( "#spine" ),
604			glue: $( "#glue" ),
605			main: $( "main" ),
606			wsu_actions: $( "#wsu-actions" )
607		},
608
609		/**
610		 * Data on the current state of navigation for use when calculating
611		 * sizes and placement of other elements.
612		 */
613		nav_state:{
614			viewport_ht: 0,
615			scroll_dif: 0,
616			positionLock: 0,
617			scroll_top: 0,
618			spine_ht: 0,
619			glue_ht: 0,
620			height_dif: 0
621		},
622
623		/**
624		 * Setup a scroll container for use with iOS.
625		 */
626		setup_nav_scroll: function() {
627			$( "#glue" ).wrapInner( "<div id='scroll'>" );
628			$( "#spine header" ).insertBefore( $( "#glue" ) );
629		},
630
631		/**
632		 * Determine if the page view is in a mobile state, defined as less than 990px;
633		 */
634		is_mobile_view: function() {
635			if ( window.matchMedia ) {
636				return window.matchMedia( "(max-width: 989px)" ).matches;
637			} else if ( window.styleMedia ) {
638
639				// Fallback for IE 9. IE 8 and below do not support media queries anyway.
640				return window.styleMedia.matchMedium( "(max-width: 989px)" );
641			}
642
643			return false;
644		},
645
646		/**
647		 * Set the Spine to a given state, mobile or full.
648		 *
649		 * @param {string} state The state of the Spine to set.
650		 */
651		set_spine_state: function( state ) {
652			if ( "mobile" === state ) {
653				$( "html" ).removeClass( "spine-full" ).addClass( "ios spine-mobile" );
654				this.setup_nav_scroll();
655			} else {
656				$( "html" ).removeClass( "ios spine-mobile spine-mobile-open" ).addClass( "spine-full" );
657				if ( $( "#scroll" ).length ) {
658					$( "#wsu-actions" ).unwrap();
659					$( "#spine header" ).prependTo( "#glue" );
660				}
661			}
662		},
663
664		/**
665		 * Determine if the Spine is already in a mobile state.
666		 *
667		 * @returns {boolean}
668		 */
669		has_mobile_state: function() {
670			if ( $( "html" ).hasClass( "spine-mobile" ) ) {
671				return true;
672			}
673
674			return false;
675		},
676
677		/**
678		 * On a resize event, adjust pieces of the Spine framework accordingly.
679		 */
680		framework_adjust_on_resize: function() {
681			var self, spread, verso, page, para, recto, recto_margin, verso_width,
682				viewport_ht, spine, glue, main;
683
684			self = this;
685
686			// Refresh data for global elements.
687			spine = self._get_globals( "spine" ).refresh();
688			glue = self._get_globals( "glue" ).refresh();
689			main = self._get_globals( "main" ).refresh();
690
691			if ( self.is_mobile_view() && !self.has_mobile_state() ) {
692				self.set_spine_state( "mobile" );
693			} else if ( !self.is_mobile_view() && self.has_mobile_state() ) {
694				self.set_spine_state( "full" );
695			}
696
697			self.sizing();
698			self.equalizing();
699
700			if ( self.is_mobile_view() ) {
701				self.mainheight();
702			}
703
704			// Only run function if an unbound element exists
705			if ( $( ".unbound, #binder.broken" ).length ) {
706				spread = $( window ).width();
707				verso = self._get_globals( "main" ).offset().left;
708				page = self._get_globals( "main" ).width();
709				recto = spread - self._get_globals( "main" ).offset().left;
710				recto_margin = "";
711
712				if ( recto >= page ) {
713					recto_margin = recto - page;
714				} else {
715					recto_margin = 0;
716				}
717
718				/* Broken Binding */
719				if ( $( "#binder" ).is( ".broken" ) ) {
720					self._get_globals( "main" ).css( "width", recto );
721				}
722
723				verso_width = verso + self._get_globals( "main" ).width();
724
725				$( ".unbound:not(.element).recto" ).css( "width", recto ).css( "margin-right", -( recto_margin ) );
726				$( ".unbound.element.recto" ).each( function() {
727					para = $( this ).width();
728					$( this ).css( "width", para + recto_margin ).css( "margin-right", -( recto_margin ) );
729				} );
730				$( ".unbound.verso" ).css( "width", verso_width ).css( "margin-left", -( verso ) );
731				$( ".unbound.verso.recto" ).css( "width", spread );
732			}
733
734			viewport_ht = $( window ).height() - this.framework_options.viewport_offset;
735
736			if ( !self.is_mobile_view() ) {
737				glue.css( "min-height", viewport_ht );
738				spine.css( "min-height", viewport_ht );
739
740				$( document ).trigger( "scroll" );
741			} else {
742				glue.css( "min-height", "" );
743				spine.css( "min-height", "" );
744			}
745		},
746
747		/**
748		 * Create the Spine framework and setup basic events based on information present in the DOM.
749		 */
750		framework_create: function() {
751			var self, contactHtml, propmap = {}, svg_imgs;
752
753			self = this; // Preserve scope.
754
755			// Generate the contact section.
756			if ( !$( "#wsu-contact" ).length ) {
757				contactHtml = "<section id='wsu-contact' class='spine-contact spine-action closed'>";
758				propmap = {};
759
760				$.each( $( "[itemtype='http://schema.org/Organization']" ), function() {
761					var tar = this;
762					$.each( $( tar ).find( "[itemprop]" ), function( i, v ) {
763						var tmp = {};
764						tmp[ $( v ).attr( "itemprop" ) ] = $( v ).attr( "content" );
765						$.extend( propmap, tmp );
766					} );
767					contactHtml += $.runTemplate( self.framework_options.contact_template, propmap );
768				} );
769				contactHtml += "</section>";
770				self.setup_tabs( "contact", contactHtml );
771			}
772
773			self.setup_nav();
774
775			// Set the initial state of the Spine on page load. Mobile is defined as less than 990px.
776			if ( self.is_mobile_view() ) {
777				self.set_spine_state( "mobile" );
778			} else {
779				$( "html" ).addClass( "spine-full" );
780			}
781
782			// If SVG is not supported, add a class and replace Spine SVG files with PNG equivalents.
783			if ( !$.svg_enabled() ) {
784				$( "html" ).addClass( "nosvg" );
785				svg_imgs = $( "img[src$='.svg']" );
786
787				if ( svg_imgs.length ) {
788					$.each( svg_imgs, function() {
789						$( this ).attr( "src", $( this ).attr( "src" ).replace( ".svg", ".png" ) );
790					} );
791				}
792			}
793
794			self.setup_spine();
795			self.setup_printing();
796
797			$( window ).on( "resize orientationchange", function() { self.framework_adjust_on_resize(); } ).trigger( "resize" );
798
799			if ( !self.is_mobile_view() ) {
800				$( document ).trigger( "scroll" );
801			}
802		},
803
804		/**
805		 * Label `#jacket` with the current window size.
806		 *
807		 * @param {HTMLelement} jacket
808		 */
809		sizing: function( jacket ) {
810			var current_width, jacket_classes, px_width, size_intermediate, size_medium, size_large;
811
812            jacket = jacket || $( "#jacket" );
813
814            current_width = $( window ).width();
815
816			size_intermediate = "size-intermediate size-smallish size-lt-medium size-lt-large size-lt-xlarge size-gt-small";
817			size_medium = "size-medium size-lt-xlarge size-lt-large size-gt-intermediate size-gt-smallish size-gt-small";
818			size_large = "size-large size-lt-xlarge size-gt-small size-gt-intermediate size-gt-smallish size-gt-medium";
819
820			px_width = "";
821
822			if ( current_width >= 1188 ) {
823				jacket_classes = "size-xlarge size-gt-small size-gt-intermediate size-gt-smallish size-gt-medium size-gt-large";
824			} else if ( current_width >= 990 ) {
825				jacket_classes = size_large;
826			} else if ( ( current_width < 990 ) && current_width >= 792 ) {
827				px_width = "size-lt-990";
828				jacket_classes = $( "#binder" ).is( ".fluid" ) ? size_large : size_medium;
829			} else if ( current_width < 792 && current_width >= 694 ) {
830				px_width = "size-lt-792";
831				jacket_classes = $( "#binder" ).is( ".fixed" ) ? size_intermediate : size_medium;
832			} else if ( current_width < 694 && current_width >= 396 ) {
833				jacket_classes = "size-small size-lt-intermediate size-lt-smallish size-lt-medium size-lt-large size-lt-xlarge";
834			} else {
835				jacket_classes = "size-small size-lt-small size-lt-intermediate size-lt-smallish size-lt-medium size-lt-large size-lt-xlarge";
836			}
837
838			jacket.stripClass( "size-" ).addClass( jacket_classes + " " + px_width );
839		},
840
841		/**
842		 * Equalize columns in a layout.
843		 */
844		equalizing: function() {
845			var obj;
846
847			if ( $( ".equalize" ).length ) {
848				obj = $( ".equalize" );
849				obj.find( ".column" ).css( "min-height", "" );
850
851				$.each( obj, function() {
852					var tallestBox = 0;
853					$.each( $( ".column", this ), function() {
854						tallestBox = ( $( this ).outerHeight() > tallestBox ) ? $( this ).outerHeight() : tallestBox;
855					} );
856
857					if ( ( $( window ).width() <= 792 && !obj.is( ".equalize-medium" ) ) || ( $( window ).width() <= 694 && !obj.is( ".equalize-small" ) ) ) {
858						$( ".column", this ).not( ".unequaled" ).css( "min-height", "1" );
859					} else {
860						$( ".column", this ).not( ".unequaled" ).css( "min-height", tallestBox );
861					}
862					$( "section.equalize .column", this ).css( "min-height", "auto" );
863				} );
864			}
865		},
866
867		/**
868		 * Apply a minimum height to the `main` element.
869		 */
870		mainheight: function() {
871			var main, window_height, main_height;
872
873			main = this._get_globals( "main" ).refresh();
874
875			if ( main.offset() ) {
876				window_height = $( window ).height();
877				main_height = window_height;
878				if ( $( "#binder" ).is( ".size-lt-large" ) ) {
879					main_height -= 50;
880				}
881				$( "main:not(.height-auto)" ).css( "min-height", main_height );
882			}
883		},
884
885		/**
886		 * Sets up framework html and other DOM attributes
887		 */
888		setup_jacket: function() {},
889
890		/**
891		 * Sets up framework html and other DOM attributes
892		 */
893		setup_binder: function() {},
894
895		 /**
896		 * Sets up framework html and other DOM attributes
897		 */
898		setup_content: function() {},
899
900		/**
901		 * Toggle the display and removal of the mobile navigation.
902		 *
903		 * @param e
904		 */
905		toggle_mobile_nav: function( e ) {
906			var html, body, shelve, spine, glue, transitionEnd;
907
908			if ( typeof e !== "undefined" ) {
909				e.preventDefault();
910			}
911
912			html = $( "html" );
913			body = $( "body" );
914			shelve = $( "#shelve" );
915			spine = $.ui.spine.prototype._get_globals( "spine" ).refresh();
916			glue = $.ui.spine.prototype._get_globals( "glue" ).refresh();
917
918			/* Cross browser support for CSS "transition end" event */
919			transitionEnd = "transitionend webkitTransitionEnd otransitionend MSTransitionEnd";
920
921			// Whether opening or closing, the Spine will be animating from this point forward.
922			body.addClass( "spine-animating" );
923
924			// Tell the browser and stylesheet what direction the Spine is animating.
925			if ( html.hasClass( "spine-mobile-open" ) ) {
926				body.addClass( "spine-move-left" );
927				shelve.attr( "aria-expanded", "false" );
928			} else {
929				body.addClass( "spine-move-right" );
930				shelve.attr( "aria-expanded", "true" );
931			}
932
933			glue.on( transitionEnd, function() {
934				body.removeClass( "spine-animating spine-move-left spine-move-right" );
935
936				if ( html.hasClass( "spine-mobile-open" ) ) {
937					html.removeClass( "spine-mobile-open" );
938
939					$( "#scroll" ).off( "touchstart" );
940					$( document ).off( "touchmove touchend touchstart" );
941				} else {
942					html.addClass( "spine-mobile-open" );
943
944					var scroll_element = document.querySelector( "#scroll" );
945
946					scroll_element.addEventListener( "touchstart", function() {
947						var top = scroll_element.scrollTop, totalScroll = scroll_element.scrollHeight, currentScroll = top + scroll_element.offsetHeight;
948
949						if ( top === 0 ) {
950							scroll_element.scrollTop = 1;
951						} else if ( currentScroll === totalScroll ) {
952							scroll_element.scrollTop = top - 1;
953						}
954					} );
955
956					// Prevent scrolling on mobile outside of `#scroll` while the mobile menu is open.
957					$( document ).on( "touchmove touchend touchstart", function( evt ) {
958						if ( $( evt.target ).parents( "#scroll" ).length > 0 || $( evt.target ).is( "#scroll" ) ) {
959							return true;
960						}
961
962						evt.stopPropagation();
963						evt.preventDefault();
964					} );
965				}
966				glue.off( transitionEnd );
967			} );
968		},
969
970		/**
971		 * Sets up the spine area
972		 */
973		setup_spine: function() {
974			var self, spine, glue, main, viewport_ht, spine_ht, height_dif, positionLock;
975
976			$( "#spine .spine-header" ).prepend( "<button id='shelve' type='button' aria-label='Site Navigation' aria-haspopup='true' aria-controls='glue' aria-expanded='false' />" );
977
978			self = this;
979
980			spine = self._get_globals( "spine" ).refresh();
981			glue = self._get_globals( "glue" ).refresh();
982			main = self._get_globals( "main" ).refresh();
983
984			self.nav_state.scroll_top = 0;
985			self.nav_state.scroll_dif = 0;
986			self.nav_state.positionLock = 0;
987
988			// The menu button should always trigger a toggle of the mobile navigation.
989			$( ".spine-header" ).on( "click touchend", "#shelve", self.toggle_mobile_nav );
990
991			// Tapping anything outside of the Spine should trigger a toggle if the menu is open.
992			main.on( "click", function( e ) {
993				if ( $( "html" ).hasClass( "spine-mobile-open" ) ) {
994					self.toggle_mobile_nav( e );
995				}
996			} );
997
998			if ( !self.is_mobile_view() ) {
999
1000				// Watch for DOM changes and resize the Spine to match.
1001				$.observeDOM( glue, function() {
1002					self.apply_nav_func( self );
1003				} );
1004
1005				// Fixed/Sticky Horizontal Header
1006				$( document ).on( "scroll", function() {
1007					self.apply_nav_func( self );
1008				} );
1009
1010				$( document ).keydown( function( e ) {
1011					if ( e.which === 35 || e.which === 36 ) {
1012						viewport_ht	= $( window ).height();
1013						spine_ht	= spine[ 0 ].scrollHeight;
1014						height_dif	= viewport_ht - spine_ht;
1015
1016						if ( e.which === 35 ) {
1017							positionLock = height_dif;
1018						} else if ( e.which === 36 ) {
1019							positionLock = 0;
1020						}
1021
1022						spine.css( { "position": "fixed", "top": positionLock + "px" } );
1023						self.nav_state.positionLock = positionLock;
1024					}
1025				} );
1026
1027				// Apply the `.skimmed` class to the Spine on non mobile views after 148px.
1028				$( document ).scroll( function() {
1029					var top;
1030					top = $( document ).scrollTop();
1031					if ( top > 148 ) {
1032						$( "#spine" ).addClass( "skimmed" );
1033					} else {
1034						$( "#spine" ).removeClass( "skimmed" );
1035					}
1036				} );
1037			}
1038
1039			/**
1040			 * When the navigation area is shorter than the available window, add a margin to the
1041			 * Spine footer so that the scroll container becomes active. This avoids issues on
1042			 * mobile devices when overflow is not applied.
1043			 */
1044			if ( self.is_mobile_view() ) {
1045				var nav_height = $( ".spine-header" ).height() + $( "#wsu-actions" ).height() + $( "#spine-navigation" ).height();
1046				var spine_footer = $( ".spine-footer" );
1047				var footer_height = spine_footer.height();
1048				if ( window.innerHeight - nav_height >= footer_height ) {
1049					var margin = window.innerHeight - nav_height - footer_height;
1050					spine_footer.css( "margin-top", margin );
1051				}
1052			}
1053		},
1054
1055		/**
1056		 * Ensure Spine navigation is properly positioned and sized to snap to the top
1057		 * and bottom of the document.
1058		 *
1059		 * @param self
1060		 */
1061		apply_nav_func: function( self ) {
1062			var spine, glue, main, top, scroll_top, positionLock, scroll_dif, glue_ht;
1063
1064			spine = self._get_globals( "spine" ).refresh();
1065			glue = self._get_globals( "glue" ).refresh();
1066			main = self._get_globals( "main" ).refresh();
1067
1068			scroll_top   = self.nav_state.scroll_top;
1069			positionLock = self.nav_state.positionLock;
1070
1071			top          = $( document ).scrollTop();
1072			scroll_dif   = scroll_top - top;
1073			scroll_top   = top;
1074			glue_ht		 = glue.height();
1075
1076			self.nav_state.scroll_top = scroll_top;
1077
1078			// Main should always be at least as high as `#glue`.
1079			main.css( { "min-height": glue_ht } );
1080
1081			/**
1082			 * When the content in `main` is larger than the content in `#glue`, maintain a
1083			 * fixed top position on `#spine` for smooth and predictable navigation scrolling.
1084			 */
1085			if ( main.outerHeight( true ) > glue_ht ) {
1086				var upper_bound = glue_ht - window.innerHeight;
1087
1088				/**
1089				 * Assume fluid movement by default. As long as we are within the upper bounds
1090				 * we can calculate the position based on scroll location whether the scroll
1091				 * goes up or down.
1092				 */
1093				positionLock = positionLock + scroll_dif;
1094
1095				/**
1096				 * If the position is ever greater than 0, we've scrolled too far up and can
1097				 * reset the position to 0.
1098				 */
1099				if ( positionLock > 0 ) {
1100					positionLock = 0;
1101				}
1102
1103				/**
1104				 * If we ever scroll to a place where the new position would be calculated
1105				 * outside of the upper bound, then reset it to the upper bound. This prevents
1106				 * from scrolling too far down.
1107				 */
1108				if ( positionLock < ( -1 * upper_bound ) ) {
1109					positionLock = ( -1 * upper_bound ) - this.framework_options.viewport_offset;
1110				}
1111
1112				spine.css( { "position": "fixed", "top": positionLock + "px" } );
1113				self.nav_state.positionLock = positionLock;
1114			} else if ( spine.is( "#spine[style]" ) ) {
1115				spine.removeAttr( "style" );
1116			}
1117		},
1118
1119		/**
1120		 * Toggle a Spine action item and the data associated with it between an
1121		 * open and closed state on touch.
1122		 *
1123		 * @param evt
1124		 * @private
1125		 */
1126		_toggle_spine_action_item: function( evt ) {
1127			var tab, action_ht;
1128
1129			evt.preventDefault();
1130
1131			tab = $( evt.target ).parent( "li" ).attr( "id" ).split( "-" )[ 1 ];
1132
1133			$( "#wsu-actions" ).find( "*.opened, #wsu-" + tab + ", #wsu-" + tab + "-tab" ).toggleClass( "opened closed" );
1134
1135			if ( $( evt.target ).parent( "li" ).hasClass( "opened" ) ) {
1136				$( "#spine-navigation, .spine-footer" ).css( "visibility", "hidden" );
1137			} else {
1138				$( "#spine-navigation, .spine-footer" ).css( "visibility", "initial" );
1139			}
1140
1141			action_ht = window.innerHeight - $( ".spine-header" ).outerHeight() - $( "#wsu-actions-tabs" ).outerHeight();
1142
1143			$( ".spine-action.opened" ).css( "min-height", action_ht );
1144			$( evt.target ).off( "mouseup touchend", $.ui.spine.prototype._toggle_spine_action_item );
1145		},
1146
1147		/**
1148		 * Process a WSU action tab (mail, sharing, etc...) and setup the
1149		 * structure accordingly.
1150		 */
1151		setup_tabs: function( tab, html ) {
1152			var self, wsu_actions, action_ht;
1153
1154			html = html || "";
1155			self = this;
1156
1157			wsu_actions = self._get_globals( "wsu_actions" ).refresh();
1158
1159			$( "#wsu-" + tab + "-tab" ).append( html );
1160
1161			if ( self.is_mobile_view() ) {
1162				$( "#wsu-" + tab + "-tab > button" ).on( "mousedown touchstart", function( e ) {
1163					$( e.target ).on( "mouseup touchend", $.ui.spine.prototype._toggle_spine_action_item );
1164					$( e.target ).on( "mousemove touchmove", function( e ) {
1165						$( e.target ).off( "mouseup touchend", $.ui.spine.prototype._toggle_spine_action_item );
1166					} );
1167				} );
1168			} else {
1169				$( "#wsu-" + tab + "-tab > button" ).on( "click", function( e ) {
1170					e.preventDefault();
1171					wsu_actions.find( "*.opened,#wsu-" + tab + ",#wsu-" + tab + "-tab" ).toggleClass( "opened closed" );
1172
1173					// Hide the Spine navigation from screen readers when action tabs are in focus.
1174					if ( $( "#wsu-" + tab + "-tab" ).hasClass( "opened" ) ) {
1175						$( "#spine-navigation, .spine-footer" ).css( "visibility", "hidden" );
1176					} else {
1177						$( "#spine-navigation, .spine-footer" ).css( "visibility", "initial" );
1178					}
1179
1180					action_ht = window.innerHeight - $( ".spine-header" ).outerHeight() - $( "#wsu-actions-tabs" ).outerHeight();
1181
1182					$( ".spine-action.opened" ).css( "min-height", action_ht );
1183				} );
1184			}
1185		},
1186
1187		/**
1188		 * Toggle a parent list item and its children between an open
1189		 * and closed state on touch.
1190		 *
1191		 * @param evt
1192		 * @private
1193		 */
1194		_toggle_spine_nav_list: function( evt ) {
1195			var target = $( evt.target );
1196
1197			evt.preventDefault();
1198			target.closest( "li" ).toggleClass( "opened" );
1199
1200			// Remove the toggle event, as it will be added again on the next touchstart.
1201			target.off( "mouseup touchend", $.ui.spine.prototype._toggle_spine_nav_list );
1202		},
1203
1204		/**
1205		 * Sets up navigation system
1206		 */
1207		setup_nav: function() {
1208			var self = this;
1209
1210			// Apply the `parent` class to each parent list item of an unordered list in the navigation.
1211			$( "#spine nav ul, #spine ul" ).parents( "li" ).addClass( "parent" );
1212
1213			var couplets = $( "#spine nav li.parent > a" );
1214
1215			// Assign active elements a class of dogeared unless those elements contain other active elements.
1216			$( "#spine .active:not(:has(.active))" ).addClass( "dogeared" );
1217
1218			/**
1219			 * Walk through each of the anchor elements in the navigation to establish when "Overview"
1220			 * items should be added and what the text should read.
1221			 */
1222			couplets.each( function() {
1223				var tar, title, url;
1224				tar = $( this );
1225				url = tar.attr( "href" );
1226
1227				// "Overview" anchors are only added for parents with URLs.
1228				if ( "#" === url ) {
1229					return;
1230				}
1231
1232				var classes = "overview";
1233
1234				// If a generated overview's parent is marked as dogeared, do the same with the overview.
1235				if ( tar.closest( ".parent" ).is( ".dogeared" ) ) {
1236					classes += " dogeared";
1237				}
1238
1239				title = ( tar.is( "[title]" )  ) ? tar.attr( "title" ) : "Overview";
1240				title = ( tar.is( "[data-overview]" ) ) ? tar.data( "overview" ) : title;
1241				title = title.length > 0 ? title : "Overview"; // This is just triple checking that a value made it here.
1242
1243				tar.parent( "li" ).children( "ul" ).prepend( "<li class='" + classes + "'></li>" );
1244				tar.clone( true, true ).appendTo( tar.parent( "li" ).find( "ul .overview:first" ) );
1245				tar.parent( "li" ).find( "ul .overview:first a" ).html( title );
1246
1247				// When the overview page is active, that area of the navigation should be opened.
1248				if ( tar.parent( "li" ).hasClass( "active" ) ) {
1249					tar.parents( "li" ).removeClass( "active" ).addClass( "opened dogeared" );
1250				}
1251			} );
1252
1253			/**
1254			 * Account for historical markup in the WSU ecosystem and add the `active` and `dogeared` classes
1255			 * to any list items that already have classes similar to `current` or `active`. Also apply the
1256			 * `opened` and `dogeared` classes to any parent list items of these active elements.
1257			 *
1258			 * `active` and `dogeared` are both used for the styling of active menu items in the navigation.
1259			 */
1260			$( "#spine nav li[class*=current], #spine nav li[class*=active]" ).addClass( "active dogeared" ).parents( "li" ).addClass( "opened dogeared" );
1261
1262			/**
1263			 * Also look for any anchor elements using a similar method and apply `active` and `dogeared` classes to
1264			 * all parent list items.
1265			 */
1266			$( "#spine nav li a[class*=current], #spine nav li a[class*=active]" ).parents( "li" ).addClass( "active dogeared" );
1267
1268			/**
1269			 * Setup navigation events depending on what the screen size is when the document first
1270			 * loads. If mobile, we use touch events for navigation. If not mobile, we rely on
1271			 * standard click events.
1272			 *
1273			 * Some additional handling is necessary on mobile to properly handle the sequence of
1274			 * touchstart, touchmove, and touchend without confusion.
1275			 */
1276			if ( self.is_mobile_view() ) {
1277				couplets.on( "mousedown touchstart", function( e ) {
1278					$( e.target ).on( "mouseup touchend", $.ui.spine.prototype._toggle_spine_nav_list );
1279					$( e.target ).on( "mousemove touchmove", function( e ) {
1280						$( e.target ).off( "mouseup touchend", $.ui.spine.prototype._toggle_spine_nav_list );
1281					} );
1282				} );
1283			} else {
1284
1285				// Disclosure
1286				couplets.on( "click", function( e ) {
1287					e.preventDefault();
1288					$( e.target ).closest( "li" ).toggleClass( "opened" );
1289				} );
1290
1291				// Trigger a scroll action when an anchor link is used.
1292				$( "main a[href*='#']:not([href*='://'])" ).on( "mouseup", function() {
1293					$( document ).trigger( "scroll" );
1294				} );
1295			}
1296
1297			// Mark external URLs in the nav menu.
1298			$( ".spine-navigation a[href^='http']:not([href*='://" + window.location.hostname + "'])" ).addClass( "external" );
1299		},
1300
1301		/**
1302		 * Handle printing action when selected in the Spine.
1303		 */
1304		setup_printing: function() {
1305			var self, spine, wsu_actions, print_controls;
1306
1307			self = this;
1308			spine = self._get_globals( "spine" ).refresh();
1309			wsu_actions = self._get_globals( "wsu_actions" ).refresh();
1310
1311			// Print & Print View
1312			print_controls = "<span class='print-controls'><button id='print-invoke'>Print</button><button id='print-cancel'>Cancel</button></span>";
1313
1314			function printPage() {
1315				window.print();
1316			}
1317
1318			function print_cancel() {
1319				$( "html" ).toggleClass( "print" );
1320				$( ".print-controls" ).remove();
1321			}
1322
1323			function print( e ) {
1324				if ( undefined !== e ) {
1325					e.preventDefault();
1326				}
1327				wsu_actions.find( ".opened" ).toggleClass( "opened closed" );
1328				$( "html" ).toggleClass( "print" );
1329				spine.find( "header" ).prepend( print_controls );
1330				$( ".unshelved" ).removeClass( "unshelved" ).addClass( "shelved" );
1331				$( "#print-invoke" ).on( "click", function() { window.print(); } );
1332				$( "#print-cancel" ).on( "click", print_cancel );
1333				window.setTimeout( function() { printPage(); }, 400 );
1334			}
1335			$( "#wsu-print-tab button" ).click( print );
1336
1337			// Shut a tool section
1338			$( "button.shut" ).on( "click touchend", function( e ) {
1339				e.preventDefault();
1340				wsu_actions.find( ".opened" ).toggleClass( "opened closed" );
1341			} );
1342		}
1343	} );
1344}( jQuery ) );
1345
1346 /*!
1347 *
1348 * Depends:
1349 *		jquery.ui.v.js
1350 */
1351/*jshint multistr: true */
1352( function( $ ) {
1353	"use strict";
1354	$.extend( $.ui.spine.prototype, {
1355		search_init: function( options ) {
1356			var self;
1357			self = this;//Hold to preserve scop
1358			$.extend( options, self.search_options, options );
1359			this._set_globals( this.search_globals );
1360			this.create_search();
1361		},
1362
1363		search_options:{
1364			data:[],
1365			providers:{
1366				nav:{
1367					name:"From Navigation",
1368					nodes: ".spine-navigation",
1369					dataType: "html",
1370					maxRows: 12,
1371					urlSpaces:"%20"
1372				},
1373				atoz:{
1374					name:"WSU A to Z index",
1375					url: "https://search.wsu.edu/2013service/searchservice/search.asmx/AZSearch",
1376					urlSpaces:"+",
1377					dataType: "jsonp",
1378					featureClass: "P",
1379					style: "full",
1380					maxRows: 12,
1381					termTemplate:"<strong><%this.term%></strong>",
1382					resultTemplate:""
1383				}
1384			},
1385			search:{
1386				minLength: 2,
1387				maxRows: 12,
1388				getRelated:true,
1389				urlSpaces:"+",
1390				tabTemplate: "<section id='wsu-search' class='spine-search spine-action closed'>" +
1391								"<form id='default-search'>" +
1392									"<input name='term' type='text' value='' placeholder='search' title='Type search term here'>" +
1393									"<button type='submit'>Submit</button>" +
1394								"</form>" +
1395								"<div id='spine-shortcuts' class='spine-shortcuts'></div>" +
1396							"</section>"
1397			},
1398			result:{
1399				appendTo: "#spine-shortcuts",
1400				showRelated:true,
1401				target:"_blank",
1402				relatedHeader:"<b class='related_sep'>Related</b><hr/>",
1403				providerHeader:"<b class='provider_header'><%this.provider_name%></b><hr/>",
1404				termTemplate:"<b><%this.term%></b>",
1405				template:"<li><%this.searchitem%></li>"
1406			}
1407		},
1408		search_globals: {
1409			wsu_search: $( "#wsu-search" ),
1410			search_input:$( "#wsu-search input[type=text]" )
1411		},
1412		create_search: function() {
1413			var self, wsu_search, tabhtml;
1414			self = this;//Hold to preserve scop
1415			wsu_search = self._get_globals( "wsu_search" ).refresh();
1416			if ( !wsu_search.length ) {
1417				tabhtml = $.runTemplate( self.search_options.search.tabTemplate, {} );
1418			}else {
1419				tabhtml = "<section id='wsu-search' class='spine-search spine-action closed'>" + wsu_search.html() + "</section>";
1420				wsu_search.remove();
1421			}
1422			this.setup_tabs( "search", tabhtml );
1423
1424			if ( $( "#spine-shortcuts" ).length <= 0 ) {
1425				$( "#wsu-search" ).append( "<div id='spine-shortcuts' class='spine-shortcuts'></div>" );
1426			}
1427
1428			$( "#wsu-search-tab button" ).on( "click touchend", function() {
1429				self._get_globals( "search_input" ).refresh().focus();
1430			} );
1431			this.setup_search();
1432		},
1433
1434		start_search:function( request, callback ) {
1435			var self, term, queries = [];
1436			self = this;//Hold to preserve scop
1437
1438			term = $.trim( request.term );
1439			self.search_options.data = [];
1440			$.each( self.search_options.providers, function( i, provider ) {
1441				$.ui.autocomplete.prototype.options.termTemplate = ( typeof( provider.termTemplate ) !== undefined && provider.termTemplate !== "" ) ? provider.termTemplate : undefined;
1442				queries.push( self.run_query( term, provider ) );
1443			} );
1444
1445			$.when.apply( $, queries ).done(
1446			function() {
1447				$.each( arguments, function( i, v ) {
1448					var data, proData;
1449					if ( v !== undefined ) {
1450						data = v[ 0 ];
1451						if ( data !== undefined && data.length > 0 ) {
1452							proData = self.setup_result_obj( term, data );
1453							$.merge( self.search_options.data, proData );
1454						}
1455					}
1456				} );
1457				self._call( callback, self.search_options.data );
1458			} );
1459		},
1460
1461		run_query:function( term, provider ) {
1462			var self, result = [], tmpObj = [], nodes;
1463			self = this;//Hold to preserve scop
1464			result = [];
1465
1466			if ( typeof( provider ) !== undefined && typeof( provider.url ) !== undefined && provider.nodes === undefined ) {
1467				return $.ajax( {
1468					url: provider.url,
1469					dataType: provider.dataType,
1470					data: {
1471						featureClass: provider.featureClass,
1472						style: provider.style,
1473						maxRows: provider.maxRows,
1474						name_startsWith: term,
1475						related:self.search_options.search.getRelated
1476					}
1477				} );
1478			}else if ( typeof( provider ) !== undefined && typeof( provider.nodes ) !== undefined ) {
1479				nodes = $( provider.nodes ).find( "a" );
1480				$.each( nodes, function( i, v ) {
1481					var obj, text, localtmpObj;
1482					obj = $( v );
1483					text = obj.text();
1484					if ( text.toLowerCase().indexOf( term.toLowerCase() ) > -1 && obj.attr( "href" ) !== "#" ) {
1485						localtmpObj = {
1486							label:text,
1487							value:obj.attr( "href" ),
1488							related:"false",
1489							searchKeywords:""
1490						};
1491						tmpObj.push( localtmpObj );
1492					}
1493				} );
1494				return [ tmpObj ];
1495			}
1496		},
1497
1498		format_result_text:function( term, text, value ) {
1499			var self, termTemplate, regex;
1500			self = this;//Hold to preserve scope
1501
1502			termTemplate = "<strong>$1</strong>"; //Typeof($.ui.autocomplete.prototype.options.termTemplate)!==undefined ? $.ui.autocomplete.prototype.options.termTemplate : "<strong>$1</strong>";
1503
1504			regex	= "(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex( term ) + ")(?![^<>]*>)(?![^&;]+;)";
1505			text	= "<a href='" + value + "' target='" + self.search_options.result.target + "'>" + text.replace( new RegExp( regex, "gi" ), termTemplate ) + "</a>";
1506
1507			return text;
1508		},
1509
1510		setup_result_obj:function( term, data ) {
1511			var self, matcher;
1512			self = this;//Hold to preserve scop
1513			matcher = new RegExp( $.ui.autocomplete.escapeRegex( term ), "i" );
1514			return $.map( data, function( item ) {
1515				var text, value, resultObj;
1516				text = item.label;
1517				value	= item.value;
1518				if ( ( item.value && ( !term || matcher.test( text ) ) || item.related === "true" ) ) {
1519					text = self.format_result_text( term, text, value );
1520					resultObj = {
1521						label: text,
1522						value: item.value,
1523						searchKeywords: item.searchKeywords !== undefined ? item.searchKeywords : "false",
1524						related: item.related !== undefined ? item.related : "false"
1525					};
1526					return resultObj;
1527				}
1528			} );
1529		},
1530
1531		setup_search: function() {
1532			var self, wsu_search, search_input, focuseitem = {};
1533
1534			self = this;//Hold to preserve scop
1535			wsu_search = self._get_globals( "wsu_search" ).refresh();
1536			search_input = self._get_globals( "search_input" ).refresh();
1537			focuseitem = {};
1538
1539			search_input.autosearch( {
1540
1541				appendTo:			self.search_options.result.appendTo,
1542				showRelated:		self.search_options.result.showRelated,
1543				relatedHeader:		self.search_options.result.relatedHeader,
1544				minLength:			self.search_options.search.minLength,
1545
1546				source: function( request, response )  {
1547					self.start_search( request, function( data ) {
1548						response( data );
1549					} );
1550				},
1551				search: function( ) {
1552					focuseitem = {};
1553				},
1554				select: function( e, ui ) {
1555					var id, term;
1556					id = ui.item.searchKeywords;
1557					term = $( ui.item.label ).text();
1558					search_input.val( term );
1559					search_input.autosearch( "close" );
1560					return false;
1561				},
1562				focus: function( e, ui ) {
1563					search_input.val( $( ui.item.label ).text() );
1564					focuseitem = {
1565						label:ui.item.label
1566					};
1567					e.preventDefault();
1568				},
1569				open: function( ) {},
1570				close: function( e ) {
1571					e.preventDefault();
1572					return false;
1573				}
1574			} ).data( "autosearch" );
1575
1576			$( "#wsu-search form" ).submit( function() {
1577				var scope, site, cx, cof, search_term, search_url;
1578				scope = wsu_search.attr( "data-default" );
1579				site = " site:" + window.location.hostname;
1580				if ( scope === "wsu.edu" ) {
1581					site = "";
1582				}
1583				cx = "cx=004677039204386950923:xvo7gapmrrg";
1584				cof = "cof=FORID%3A11";
1585				search_term = search_input.val();
1586				search_url = "https://search.wsu.edu/default.aspx?" + cx + "&" + cof + "&q=" + search_term + site;
1587				window.location.href = search_url;
1588				return false;
1589			} );
1590		}
1591	} );
1592}( jQuery ) );
1593
1594/*!
1595*
1596* Depends:
1597*		jquery.ui.spine.js
1598*/
1599/*jshint multistr: true */
1600( function( $ ) {
1601	$.extend( $.ui.spine.prototype, {
1602		social_init: function( options ) {
1603			$.extend( this.social_options, options );
1604
1605			this._set_globals( this.social_globals );
1606			this.social_create();
1607		},
1608		/**
1609		 * These default options can be overridden with an object before
1610		 * the Spine framework is started and with `$('body').spine( spineoptions )`.
1611		 *
1612		 * NOTE: The structure of these social options **will** change and could be
1613		 * deprecated in a future release. Please communicate via the WSU Spine repository
1614		 * when using these so that we can reach out before a transition in the future.
1615		 *
1616		 * https://github.com/washingtonstateuniversity/WSU-spine/issues/230
1617		 */
1618		social_options:{
1619			share_text:"You should know ...",
1620			twitter_text:"You should know...",
1621			twitter_handle:"wsupullman",
1622			linkedin_source:"wsu.edu"
1623		},
1624		social_globals: {
1625			share_block: $( "#wsu-share" )
1626		},
1627		social_create: function() {
1628			var self, share_block, share_text, current_url, wsu_actions, sharehtml, twitter_text, twitter_handle, linkedin_source;
1629			self = this;//Hold to preserve scope
1630			share_block = self._get_globals( "share_block" ).refresh();
1631			if ( !share_block.length ) {
1632				share_text = encodeURIComponent( this.social_options.share_text );
1633				twitter_text = encodeURIComponent( this.social_options.twitter_text );
1634				twitter_handle = encodeURIComponent( this.social_options.twitter_handle );
1635				current_url = self._get_globals( "current_url" );
1636				wsu_actions = self._get_globals( "wsu_actions" ).refresh();
1637
1638				sharehtml  = "<section id='wsu-share' class='spine-share spine-action closed'>";
1639				sharehtml += "<ul>";
1640				sharehtml += "<li class='by-facebook'><a href='https://www.facebook.com/sharer/sharer.php?u=" + current_url + "'><span class='screen-reader-text'>Share this URL on </span>Facebook</a></li>";
1641
1642				sharehtml += "<li class='by-twitter'><a href='https://twitter.com/intent/tweet?text=" + twitter_text + "&url=" + current_url;
1643				sharehtml += "&via=" + twitter_handle + "' target='_blank'><span class='screen-reader-text'>Share this URL on </span>Twitter</a></li>";
1644
1645				sharehtml += "<li class='by-googleplus'><a href='https://plus.google.com/share?url=" + current_url + "'><span class='screen-reader-text'>Share this URL on </span>Google+</a></li>";
1646
1647				sharehtml += "<li class='by-linkedin'><a href='https://www.linkedin.com/shareArticle?mini=true&url=" + current_url + "&summary=";
1648				sharehtml += share_text + "&source=" + linkedin_source + "' target='_blank'><span class='screen-reader-text'>Share this URL on </span>Linkedin</a></li>";
1649
1650				sharehtml += "<li class='by-email'><a href='mailto:?subject=" + share_text + "&body=" + current_url + "'><span class='screen-reader-text'>Share this URL with </span>Email</a></li>";
1651				sharehtml += "</ul>";
1652				sharehtml += "</section>";
1653
1654				self.setup_tabs( "share", sharehtml );
1655			} // End Share Generation
1656		}
1657	} );
1658}( jQuery ) );
1659
1660( function( $ ) {
1661	"use strict";
1662	$( document ).ready( function() {
1663		$( "html" ).removeClass( "no-js" ).addClass( "js" );
1664		var spineoptions = window.spineoptions || {};
1665		$.spine( spineoptions );
1666	} );
1667} )( jQuery );

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.