PageSourceSearch

https://www.macfound.org/js/fellows-crownpeak.searchg2-1.0.3.js?v=17646368

js macfound.org collected 2026-09-24 08:32:21 UTC 25,221 bytes, 651 lines download raw bytes

1/*!
2// ------------------------------------------------------------------------
3// (C) CROWNPEAK TECHNOLOGY.
4// ------------------------------------------------------------------------
5// THIS SOFTWARE IS PROVIDED "AS-IS," WITHOUT ANY EXPRESS OR IMPLIED WARRANTY.
6// IN NO EVENT SHALL CROWNPEAK TECHNOLOGY BE HELD LIABLE FOR ANY DAMAGES ARISING FROM THE USE OF THE SOFTWARE.
7// THIS SOFTWARE IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL, BUT WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8// THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS (INCLUDING BOTH CROWN PEAK TECHNOLOGY
9// AND THE VARIOUS THIRD PARTY COPYRIGHT HOLDERS PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
10// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU, THE USER.
11// SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
12// WILL ANY COPYRIGHT HOLDER, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE
13// (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER PROGRAMS),
14// EVEN IF SUCH HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
15// Version: 1.0.3
16*/
17// NOTE: This is the same as the CP CrownPeakSearch Object with some exceptions: _maxRows changed from 200 to 2500
18	//_maxRows changed from 200 to 2500
19	//mergeParameters correctly handles custom params containing "=" sign
20/*HEADERSTART
21   Name: felows-cp-global.js
22   Desc: A clone of CPs SOLR library
23   Notes: We overrode the max rows returned by SOLR
24HEADER END*/
25function FellowsCrownPeakSearch(options) {
26	var self = this;
27	// Default configuration for properties
28	var protocol = location.protocol == "file:" ? "http:" : location.protocol;
29	/* Search proxy allows all queries to be sent via a proxy url - for security reasons
30		 * Search proxy supports the following macros
31		 * %%PROTOCOL%% - "https:" if this script was loaded via https, or "http:" if not
32		 * %%COLLECTION%% - the collection being requested
33		 * %%HANDLER%% - the handler being used
34		 * %%URL%% - the entire url for the search
35		 * %%URLENCODED%% - the entire url for the search, URI encoded
36		 * Leave the proxy blank (or null, or undefined, etc.) if you don't want to use one
37		 */
38	var _searchProxy = "";
39	/* Endpoint supports the following macros
40	 * %%PROTOCOL%% - "https:" if this script was loaded via https, or "http:" if not
41	 * %%COLLECTION%% - the collection being requested
42	 * %%HANDLER%% - the handler being used
43	 * %%QUERY%% - the querystring extension (do not include the "?")
44	 */
45	var _endpoint = "%%PROTOCOL%%//searchg2.crownpeak.net/%%COLLECTION%%/%%HANDLER%%?%%QUERY%%";
46	var _maxRows = 2500; // Don't let the user specify more than this number of results
47	var _rows = 10;
48	var _collection = "www.crownpeak.com";
49	var _params = "";
50	var _facets = [];
51	var _maxFacets = 5;
52	var _sort = [];
53	var _spellcheck = false;
54	var _highlight = false;
55	var _highlightSeparator = "...";
56	var _resultProxy = "";
57	var _language = "";
58	var _handler = "select";
59	var _queryText = "";
60	var _timeout = 5000;
61	var _geo = null;
62	parseArguments(options);
63	// Fill for missing console.log for oldIE
64	if (typeof console === "undefined") console = { log: function() { } }
65	else if (!console.log) { console.log = function () { }; }
66	function parseArguments(options) {
67		if (options) {
68			if (typeof options === "string") {
69				// Single string is collection
70				_collection = options;
71			} else {
72				// Otherwise look for properties on the thing they passed in
73				if (options.collection) _collection = options.collection;
74				if (options.endpoint) _endpoint = options.endpoint;
75				if (options.facets) _facets = options.facets;
76				if (options.handler) _handler = options.handler;
77				if (options.highlight || options.highlight == false) _highlight = options.highlight;
78				if (options.highlightSeparator) _highlightSeparator = options.highlightSeparator;
79				if (options.language) _language = options.language;
80				if (options.maxFacets || options.maxfacets == 0) _maxFacets = options.maxFacets;
81				if (options.parameters) _params = options.parameters;
82				if (options.resultProxy) _resultProxy = options.resultProxy;
83				if (options.rows || options.rows == 0) _rows = options.rows;
84				if (options.searchProxy) _searchProxy = options.searchProxy;
85				if (options.sort) _sort = options.sort;
86				if (options.spellcheck || options.spellcheck == false) _spellcheck = options.spellcheck;
87				if (options.timeout || options.timeout == 0) _timeout = options.timeout;
88			}
89		}
90	}
91	/// <summary>
92	/// Extend the document that matches this id with some extra properties
93	/// </summary>
94	function extendDoc(docs, id, props) {
95		for (var i = 0, len = docs.length; i < len; i++) {
96			var doc = docs[i];
97			if (doc.id === id) {
98				// Copy the properties over to the document
99				var keys = Object.keys(props);
100				for (var k = 0, lenK = keys.length; k < lenK; k++) {
101					var key = keys[k];
102					doc[key] = props[key];
103				}
104				break;
105			}
106		}
107	}
108	/// <summary>
109	/// Copy some properties from the highlighting object into the response documents
110	/// </summary>
111	function copyHighlights(data) {
112		// Copy the highlights into the response model
113		var highlights = data.highlighting;
114		var ids = Object.keys(highlights);
115		// Loop through each document that has highlights
116		for (var i = 0, len = ids.length; i < len; i++) {
117			var id = ids[i];
118			var hh = highlights[id];
119			var fields = Object.keys(hh);
120			// Create an object that we'll use to extend the response document
121			var o = {};
122			var lenF = fields.length;
123			if (lenF > 0) {
124				// Loop through each field within the document
125				for (var j = 0; j < lenF; j++) {
126					// Make a new property - e.g. title_hl - with highlights
127					var fieldName = fields[j];
128					o[fieldName + "_hl"] = _highlightSeparator + hh[fieldName].join(_highlightSeparator) + _highlightSeparator;
129				}
130				// Copy these new properties to the document that has this id
131				extendDoc(data.response.docs, id, o);
132			}
133		}
134	}
135	/// <summary>
136	/// Clean the types property to make it easier to use
137	/// </summary>
138	function cleanTypes(data) {
139		if (data.response && data.response.docs && data.response.docs.length) {
140			var docs = data.response.docs;
141			for (var i = 0, len = docs.length; i < len; i++) {
142				var t = docs[i].type;
143				if (t && t.indexOf && t.indexOf("/") >= 0) {
144					docs[i].type_short = t.split("/")[1];
145				}
146			}
147		}
148	}
149	/// <summary>
150	/// Clean the suggestions collection to make it easier to use
151	/// </summary>
152	function cleanSuggestions(data) {
153		if (data.spellcheck && data.spellcheck.suggestions && data.spellcheck.suggestions.length) {
154			var len = data.spellcheck.suggestions.length;
155			var suggestions = data.spellcheck.suggestions[len - 1].suggestion;
156			if (suggestions.length) {
157				var results = [];
158				for (var i = 0, len = suggestions.length; i < len; i++) {
159					results.push(suggestions[i]);
160				}
161				data.suggestions = results;
162			}
163		};
164	}
165	/// <summary>
166	/// Add a proxy_url property to the result, for ease of analytics
167	/// </summary>
168	function addResultProxy(data) {
169		if (_resultProxy && data.response && data.response.docs && data.response.docs.length) {
170			var proxy = _resultProxy;
171			if (proxy.indexOf("%%QUERY%%")) proxy = proxy.replace(/%%QUERY%%/g, encodeURIComponent(_queryText));
172			var docs = data.response.docs;
173			for (var i = 0, len = docs.length; i < len; i++) {
174				docs[i].proxy_url = proxy.replace(/%%INDEX%%/g, i) + docs[i].url;
175			}
176		}
177	}
178	/// <summary>
179	/// Add a pager property to make it easier to render one
180	/// </summary>
181	function addPager(data) {
182		if (data && data.response && (data.response.start || data.response.start === 0)
183			&& (data.response.numFound || data.response.numFound === 0)) {
184			// Add a simple pager to the output - the user can customise it if they like
185			data.pager = {
186				page: data.response.start / _rows + 1,
187				pages: [],
188				total: Math.ceil(data.response.numFound / _rows)
189			};
190			if (_rows > 0) {
191				// Simple pager just has one entry for each page
192				for (var i = 0; i < data.response.numFound / _rows; i++) {
193					data.pager.pages.push(i + 1);
194				}
195			}
196		}
197	}
198	/// <summary>
199	/// Add a score property to each result, normalized based on the highest score
200	/// </summary>
201	function addNormalizedScores(data) {
202		if (data.response && data.response.maxScore && data.response.docs && data.response.docs.length) {
203			var maxScore = data.response.maxScore || 1.0;
204			var docs = data.response.docs;
205			for (var i = 0, len = docs.length; i < len; i++) {
206				docs[i].normalizedScore = docs[i].score / maxScore;
207			}
208		}
209	}
210	/// <summary>
211	/// Add a didYouMean property to the main, to make suggestions easy to provide later
212	/// </summary>
213	function addDidYouMean(data) {
214		if (data.spellcheck && data.spellcheck.suggestions && data.spellcheck.suggestions.length) {
215			var didYouMean = [];
216			// Results come in name,value pairs
217			for (var i = 0, len = data.spellcheck.suggestions.length; i < len; i += 2) {
218				var name = data.spellcheck.suggestions[i];
219				var value = data.spellcheck.suggestions[i + 1];
220				if (name == "collation") {
221					// Collations also come in name,value pairs
222					var query = "";
223					var html = "";
224					for (var j = 0, lenC = value.length; j < lenC; j += 2) {
225						var cname = value[j];
226						var cvalue = value[j + 1];
227						if (cname == "collationQuery") {
228							query = html = cvalue;
229						}
230						else if (cname == "hits") {
231							html += " (" + cvalue + ")";
232						}
233						else if (cname == "misspellingsAndCorrections") {
234							// Corrections also come in (what else) name,value pairs
235							for (var k = 0, lenM = cvalue.length; k < lenM; k += 2) {
236								var original = cvalue[k];
237								var replacement = cvalue[k + 1];
238								var re = new RegExp("\\b" + replacement + "\\b", "i");
239								html = html.replace(re, "<em>" + replacement + "</em>");
240							}
241						}
242					}
243					// Add this to our collection
244					didYouMean.push({ query: query, display: html });
245				}
246			}
247			if (didYouMean.length > 0) {
248				// Add this to our output
249				data.didYouMean = didYouMean;
250			}
251		}
252	}
253	/// <summary>
254	/// Get the full url to be used to execute a query against Search G2
255	/// </summary>
256	function getSearchUrl(collection, handler, query) {
257		var url = _endpoint
258			.replace(/%%PROTOCOL%%/ig, protocol)
259			.replace(/%%COLLECTION%%/ig, _collection)
260			.replace(/%%HANDLER%%/ig, handler)
261			.replace(/%%QUERY%%/ig, query);
262		if (_searchProxy && _searchProxy.replace) {
263			return _searchProxy
264				.replace(/%%PROTOCOL%%/ig, protocol)
265				.replace(/%%COLLECTION%%/ig, _collection)
266				.replace(/%%HANDLER%%/ig, handler)
267				.replace(/%%URL%%/ig, url)
268				.replace(/%%URLENCODED%%/ig, encodeURIComponent(url));
269		}
270		return url;
271	}
272	/// <summary>
273	/// Execute the provided query against the configured url and collection
274	/// </summary>
275	function internalRawQuery(query, handler) {
276		// We'll return the promise from this object later
277		var dfd = new Deferred();
278		handler = handler || _handler || "select";
279		// Append our JSONP callback
280		query += "&json.wrf=%%CALLBACK%%";
281		var thisUrl = getSearchUrl(_collection, handler, query);
282		//console.log("Querying " + thisUrl.replace("&json.wrf=%%CALLBACK%%", ""));
283		// Make a JSONP request to our Solr collection
284		getUrl(thisUrl, _timeout)
285			.done(function(data) {
286				if (_highlight && data.highlighting) {
287					copyHighlights(data);
288				}
289				if (handler == "suggest") {
290					cleanSuggestions(data);
291				}
292				else {
293					cleanTypes(data);
294					addResultProxy(data);
295					addDidYouMean(data);
296					addNormalizedScores(data);
297					addPager(data);
298				}
299				// Search complete - resolve the promise
300				dfd.resolve(data);
301			})
302			.fail(function(status, url, error) { //jqXHR, textStatus, errorThrown) {
303				dfd.reject(status, url, error);
304			});
305		// Return a promise that we'll resolve later when the search has completed
306		return dfd.promise();
307	}
308	/// <summary>
309	/// Build a query using the text and page number and execute it
310	/// </summary>
311	function internalQuery(text, page, filterQueries) {
312		_queryText = text;
313		var data = "q=" + encodeURIComponent(text)
314			+ (!text ? "&q.alt=*" : "")
315			+ "&echoParams=explicit" // change to none when finished debugging
316			+ "&fl=*,score" // we want the score field in order to be able to rate results
317			+ "&defType=edismax" // handles more complex queries and gracefully fails if it doesn't understand
318			+ "&wt=json&start=" + page * _rows + "&rows=" + Math.min(_rows, _maxRows)
319			+ (_language !== "" ? "&fq=language:" + _language : "")
320			+ (_spellcheck ? "&spellcheck=true&spellcheck.collate=true&spellcheck.collateExtendedResults=true" : "")
321			+ (filterQueries && filterQueries.length > 0 ? "&fq=" + filterQueries.join("&fq=") : "")
322			+ (_facets.length > 0 ? "&facet=true&facet.mincount=1&facet.field=" + _facets.join("&facet.field=") : "")
323			+ (_facets.length > 0 && _maxFacets > 0 ? "&facet.limit=" + _maxFacets : "")
324			+ (_sort.length > 0 ? "&sort=" + _sort.join(",") : "")
325			+ (_highlight ? "&hl=true&hl.fl=*&hl.snippets=3&hl.simple.pre=" + escape("<b>") + "&hl.simple.post=" + escape("</b>") + "&f.title.hl.fragsize=50000&f.url.hl.fragsize=50000" : "");
326		var geoPostParams = [];
327		if (_geo) {
328			var geoParams = [];
329			if (_geo.location) {
330				if (typeof _geo.location === "string")
331					geoParams.push("pt=" + _geo.location);
332				else
333					geoParams.push("pt=" + (_geo.location.latitude || _geo.location.lat || 0) + "," +
334						(_geo.location.longitude || _geo.location.lon || 0));
335			} else {
336				geoParams.push("pt=0,0");
337			}
338			// It won't work without a field
339			geoParams.push("sfield=" + (_geo.field || "custom_p_location"));
340			// If they didn't already sort by something else, assume they want by ascending distance
341			if (_sort.length == 0) geoParams.push("sort=geodist()%20asc");
342			// What do they want it called on the output?
343			geoPostParams.push("fl=" + encodeURI(_geo.outputfield || "_dist_") + ":geodist()");
344			// Filter results by by range
345			if (_geo.range) {
346				var range = _geo.range;
347				if (typeof _geo.range != "number" && _geo.range.distance) {
348					range = _geo.range.distance;
349					var unit = _geo.range.units || _geo.range.unit;
350					if (unit && unit.toLowerCase) {
351						switch (unit.toLowerCase()) {
352							case "mile": case "miles":
353								range *= 1.609344;
354								break;
355							case "metre": case "metres": case "meter": case "meters":
356								range /= 1000;
357								break;
358							default: // KM = nothing to do
359						}
360					}
361				}
362				if (!isNaN(range)) geoParams.push("fq={!geofilt%20d%3d" + (range) + "}");
363			}
364			data += "&" + geoParams.join("&");
365		}
366		// If they provided additional parameters, merge them into our set
367		if (_params) data = mergeParameters(data, _params.replace(/^[&]/, ""));
368		// If they asked for highlighting and didn't include the id field, add it now
369		if (data.indexOf("&hl=true") > 0) {
370			if (data.indexOf("&fl=") >= 0) {
371				var start = data.indexOf("&fl=") + 1;
372				var end = data.indexOf("&", start);
373				if (end < 0) end = data.length;
374				var i = data.indexOf(",id,", start);
375				if (i < 0 || i > end) {
376					i = data.indexOf(",id&", start);
377					if (i < 0 || i > end) {
378						i = data.indexOf("=id", start);
379						if (i < 0 || i > end) {
380							data = data.replace(/&fl=/, "&fl=id,");
381						}
382					}
383				}
384			}
385		}
386		if (geoPostParams && geoPostParams.length)
387			data += "&" + geoPostParams.join("&");
388		return internalRawQuery(data);
389	}
390	/// <summary>
391	/// Merge together two sets of parameters and return the result, with s2 taking precendence
392	/// </summary>
393	function mergeParameters(s1, s2) {
394		var PARAMS_TO_IGNORE = ",fq,hl.fl,"; // NOTE: keep the leading/trailing commas
395		var result = [];
396		// Immediate exit conditions
397		if (!s1) return s2;
398		if (!s2) return s1;
399		var s1Items = s1.split("&"), s2Items = s2.split("&");
400		for (var i = 0, len = s1Items.length; i < len; i++) {
401			var item = s1Items[i].split("=");
402			var key = item[0], value = item[1];
403			// Is this in string 2?
404			var s2Index = indexOfKey(s2Items, key);
405			if (s2Index >= 0) {
406				// Yes, so process it
407				//var item2 = s2Items[s2Index].split("="); //does not work with values containing = sign, like fl=*,content:[value v=""]
408				var key2 = key;
409				var value2 = s2Items[s2Index].replace(key + "=", "");
410				if (PARAMS_TO_IGNORE.indexOf("," + key + ",") >= 0) {
411					// push key 1
412					result.push(key + "=" + value);
413				}
414				// Push key 2
415				result.push(key2 + "=" + value2);
416				// Remove this item from string 2
417				s2Items.splice(s2Index, 1);
418			} else {
419				// No, so just append it
420				result.push(key + "=" + value);
421			}
422		}
423		// Append anything left in string 2
424		result = result.concat(s2Items);
425		return result.join("&");
426	}
427	/// <summary>
428	/// Find the index of a key in an array of key=value pairs
429	/// </summary>
430	function indexOfKey(array, key) {
431		if (!array || !array.length) return -1;
432		for (var i = 0, len = array.length; i < len; i++) {
433			if (array[i].split("=")[0] === key) return i;
434		}
435		return -1;
436	}
437	/// <summary>
438	/// Build a suggest query using the text and execute it
439	/// </summary>
440	function internalAutocompleteQuery(text) {
441		_queryText = text;
442		var data = "q=" + encodeURIComponent(text)
443			+ "&wt=json&spellcheck.collate=false"
444			+ (_params ? "&" + _params.replace(/^[&]/, "") : "");
445		return internalRawQuery(data, "suggest");
446	}
447	function getUrl(url, timeout) {
448		var deferred = new Deferred();
449		function addEvent(element, eventName, fn) {
450			if (element.addEventListener)
451				element.addEventListener(eventName, fn);
452			else if (element.attachEvent)
453				element.attachEvent("on" + eventName, function () { fn.call(element); });
454		}
455		function tidytimer() {
456			if (timer) {
457				clearTimeout(timer);
458				timer = null;
459			}
460		}
461		function tidyscript() {
462			window[cb] = overwritten;
463			if (script) {
464				script.parentElement.removeChild(script);
465				script = null;
466			}
467		}
468		try {
469			var cb = "searchg2_" + Math.random().toString().substr(2);
470			var overwritten = window[cb];
471			var timer = null;
472			if (timeout && !isNaN(timeout)) {
473				timer = setTimeout(function () {
474					try {
475						tidytimer();
476					}
477					catch (ex) {
478						// Ignore errors here
479					}
480					finally {
481						deferred.reject("timeout", url);
482					}
483				}, timeout);
484			}
485			window[cb] = function (data) {
486				tidytimer();
487				deferred.resolve(data, url);
488			}
489			var script = document.createElement("script");
490			var loadOrError = function (event) {
491				tidytimer();
492				tidyscript();
493				if (event.type === "error") {
494					deferred.reject("error", url);
495				}
496			}
497			addEvent(script, "load", loadOrError);
498			addEvent(script, "error", loadOrError);
499			script.src = url.replace(/%%CALLBACK%%|%25%25CALLBACK%25%25/ig, cb);
500			document.getElementsByTagName("head")[0].appendChild(script);
501		} catch (e) {
502			deferred.reject(url, "error", e);
503		}
504		return deferred.promise();
505	}
506	function Deferred() {
507		var deferred = {
508			args: null,
509			state: "pending",
510			doneList: [],
511			failList: [],
512			alwaysList: []
513		},
514			trigger = function (list, args) {
515				for (var i = 0, len = list.length; i < len; i++) {
516					list[i].apply(list[i], args);
517				}
518			};
519		return {
520			promise: function () {
521				return {
522					status: function () {
523						return state;
524					},
525					done: function (fn) {
526						if (deferred.state == "resolved") {
527							fn.apply(fn, deferred.args);
528						} else {
529							deferred.doneList.push(fn);
530						}
531						return this;
532					},
533					fail: function (fn) {
534						if (deferred.state == "rejected") {
535							fn.apply(fn, deferred.args);
536						} else {
537							deferred.failList.push(fn);
538						}
539						return this;
540					},
541					always: function (fn) {
542						if (deferred.state == "rejected" || deferred.state == "resolved") {
543							fn.apply(fn, deferred.args);
544						} else {
545							deferred.alwaysList.push(fn);
546						}
547						return this;
548					}
549				}
550			},
551			resolve: function () {
552				if (deferred.state === "pending") {
553					deferred.state = "resolved";
554					deferred.args = arguments;
555					trigger(deferred.doneList, arguments);
556					trigger(deferred.alwaysList, arguments);
557				}
558			},
559			reject: function () {
560				if (deferred.state === "pending") {
561					deferred.state = "rejected";
562					deferred.args = arguments;
563					trigger(deferred.failList, arguments);
564					trigger(deferred.alwaysList, arguments);
565				}
566			}
567		}
568	}
569	// Getters and setters would be nice, but in the meantime...
570	this.rows = function (value) { if (value !== undefined) _rows = value; return _rows; };
571	this.collection = function (value) { if (value !== undefined) _collection = value; return _collection; };
572	this.endpoint = function (value) { if (value !== undefined) _endpoint = value; return _endpoint; };
573	this.facets = function (value) { if (value !== undefined) _facets = (typeof value === "string" ? [value] : value); return _facets; };
574	this.maxFacets = function (value) { if (value !== undefined) _maxFacets = value; return _maxFacets; };
575	this.handler = function (value) { if (value !== undefined) _handler = value; return _handler; };
576	this.highlight = function (value) { if (value !== undefined) _highlight = value; return _highlight; };
577	this.highlightSeparator = function (value) { if (value !== undefined) _highlightSeparator = value; return _highlightSeparator; };
578	this.language = function (value) { if (value !== undefined) _language = value; return _language; };
579	this.searchProxy = function (value) { if (value !== undefined) _searchProxy = value; return _searchProxy; };
580	this.spellcheck = function (value) { if (value !== undefined) _spellcheck = value; return _spellcheck; };
581	this.sort = function (value) { if (value !== undefined) _sort = (typeof value === "string" ? [value] : value); return _sort; };
582	this.timeout = function (value) { if (value !== undefined) _timeout = value; return _timeout; };
583	this.parameters = function (value) { if (value !== undefined) _params = value; return _params; };
584	this.resultProxy = function (value) { if (value !== undefined) _resultProxy = value; return _resultProxy; };
585	this.geo = function (value) { if (value !== undefined) _geo = value; return _geo; };
586	/// <summary>
587	/// Run a query against the configured CrownPeakSearch object
588	/// </summary>
589	/// <param name="text">The query text. See https://wiki.apache.org/solr/SolrQuerySyntax for more information</param>
590	/// <param name="page">The page number (starting at 1) of results that you wish to retrieve</param>
591	/// <param name="filterQueries">An array of strings in the format "field:value" to apply to the query</param>
592	/// <returns>A Deferred object that will be resolved when the query is complete</returns>
593	this.query = function(text, page, filterQueries) {
594		if (!page) page = 1;
595		return internalQuery(text, page - 1, filterQueries);
596	};
597	/// <summary>
598	/// Run an autocomplete (suggest) query against the configured CrownPeakSearch object
599	/// </summary>
600	/// <param name="text">The query text. See https://wiki.apache.org/solr/SolrQuerySyntax for more information</param>
601	/// <returns>A Deferred object that will be resolved when the query is complete</returns>
602	this.autocomplete = function (text) {
603		return internalAutocompleteQuery(text);
604	};
605	/// <summary>
606	/// Run a raw query against the Solr instance that is configured in the CrownPeakSearch object
607	/// </summary>
608	/// <param name="query">The query, sent in an HTTP GET request. See http://lucene.apache.org/solr/3_6_2/doc-files/tutorial.html for more information</param>
609	/// <returns>A Deferred object that will be resolved when the query is complete</returns>
610	this.raw = function(query) {
611		return internalRawQuery(query);
612	};
613}
614// Old IE support
615// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
616if (!Object.keys) {
617	Object.keys = (function () {
618		'use strict';
619		var hasOwnProperty = Object.prototype.hasOwnProperty,
620				hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'),
621				dontEnums = [
622					'toString',
623					'toLocaleString',
624					'valueOf',
625					'hasOwnProperty',
626					'isPrototypeOf',
627					'propertyIsEnumerable',
628					'constructor'
629				],
630				dontEnumsLength = dontEnums.length;
631		return function (obj) {
632			if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
633				throw new TypeError('Object.keys called on non-object');
634			}
635			var result = [], prop, i;
636			for (prop in obj) {
637				if (hasOwnProperty.call(obj, prop)) {
638					result.push(prop);
639				}
640			}
641			if (hasDontEnumBug) {
642				for (i = 0; i < dontEnumsLength; i++) {
643					if (hasOwnProperty.call(obj, dontEnums[i])) {
644						result.push(dontEnums[i]);
645					}
646				}
647			}
648			return result;
649		};
650	}());
651}

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.