loadJQuery();
addPolyfills();
addExtensions();
// --- Get infos about this script file ---
var scriptElement = document.querySelector('script[src$="static/content.js"]');
var scriptDir = scriptElement.src.substr(0, scriptElement.src.lastIndexOf('/'));
// --- User data ---
var user = {
fontSize: 1.0,
clickTab: 0,
displaySidebar: true,
colorScheme: null,
collapseQuickRef: false
}
// --- Cached data ---
// To have the data remain while navigating through the docs, it'll be stored into
// sessionStorage. Fallbacks to window.name if sessionStorage is not supported.
// Note: CHM doesn't support window.localStorage/sessionStorage or cookies.
var cache = {
colorScheme: user.colorScheme,
scriptDir: scriptDir,
fontSize: user.fontSize,
forceNoFrame: false,
forceNoScript: false,
clickTab: user.clickTab,
displaySidebar: user.displaySidebar,
sidebarWidth: '18em',
collapseQuickRef: user.collapseQuickRef,
RightIsFocused: true,
toc_clickItem: null,
toc_clickItemTemp: null,
toc_scrollPos: 0,
index_filter:-1,
index_input: "",
index_clickItem: 0,
index_scrollPos: 0,
search_data: {},
search_highlightWords: false,
search_input: "",
search_clickItem: 0,
search_scrollPos: 0,
load: function() {
if (window.sessionStorage)
{
var data = JSON.parse(window.sessionStorage.getItem('data'));
if (!data)
return false;
}
else try {
var data = JSON.parse(window.name);
} catch(e) {
return false;
}
if (data.scriptDir != scriptDir)
return false;
$.extend(this, data);
return true;
},
save: function() {
if (window.sessionStorage)
window.sessionStorage.setItem('data', JSON.stringify(this));
else
window.name = JSON.stringify(this);
},
set: function(prop, value) {
this[prop] = value;
try {
postMessageToFrame('updateCache', [prop, value])
} catch(e) {}
return value;
}
};
// --- Main Execute Area ---
// Set global variables:
var forceNoScript = forceNoScript || false;
var isCacheLoaded = cache.load();
var workingDir = getWorkingDir();
var relPath = getRelativePath(location.href, workingDir);
var equivPath = $('meta[name|="ahk:equiv"]').prop('content');
var isInsideCHM = (location.href.search(/::/) > 0) ? 1 : 0;
var supportsHistory = (history.replaceState) && !isInsideCHM;
var isFrameCapable = !cache.forceNoFrame && (isInsideCHM || supportsHistory);
var isInsideFrame = (window.self !== window.top);
var isSearchBot = navigator.userAgent.match(/googlebot|bingbot|slurp/i);
var isTouch = !!("ontouchstart" in window) || !!(navigator.msMaxTouchPoints);
// http://stackoverflow.com/a/9851769
var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0; // Opera 8.0+
var isFirefox = typeof InstallTrigger !== 'undefined'; // Firefox 1.0+
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0; // At least Safari 3+: "[object HTMLElementConstructor]"
var isIE = /*@cc_on!@*/false || !!document.documentMode; // Internet Explorer 6-11
var isIE8 = !-[1,]; // Internet Explorer 8 or below
var isIE9 = navigator.userAgent.match(/MSIE 9/); // Internet Explorer 9
var isEdge = !isIE && !!window.StyleMedia; // Edge 20+
var isChrome = !!window.chrome && (!!window.chrome.webstore || !!window.chrome.csi); // Chrome 1+
var isBlink = (isChrome || isOpera) && !!window.CSS; // Blink engine detection
var structure = new ctor_structure;
var toc = new ctor_toc;
var index = new ctor_index;
var search = new ctor_search;
var features = new ctor_features;
var translate = {dataPath: scriptDir + '/source/data_translate.js'};
var deprecate = {dataPath: scriptDir + '/source/data_deprecate.js'};
scriptElement.insertAdjacentHTML('afterend', structure.metaViewport);
var isPhone = (document.documentElement.clientWidth <= 600);
(function() {
// Exit the script if the user is a search bot. This is done because we want
// to prevent the search bot from parsing the elements added via javascript,
// otherwise the search results would be distorted:
if (isSearchBot)
return;
// Get user data:
if (!isCacheLoaded) {
if (isInsideCHM) {
var m = scriptDir.match(/mk:@MSITStore:(.*?)\\[^\\]+\.chm/i);
if (m[1])
loadScript(decodeURI(m[1]) + '\\chm_config.js', function () {
try {
$.extend(cache, overwriteProps(user, config));
setInitialSettings();
} catch (e) {}
});
}
else if (window.localStorage) {
config = JSON.parse(window.localStorage.getItem('config'));
$.extend(cache, overwriteProps(user, config));
setInitialSettings();
}
else if (navigator.cookieEnabled) {
config = document.cookie.match(/config=([^;]+)/);
config && (config = JSON.parse(config[1]));
$.extend(cache, overwriteProps(user, config));
setInitialSettings();
}
}
else
setInitialSettings();
function setInitialSettings() {
// font size
if (!isFrameCapable && cache.fontSize != 1)
$('head').append('');
// color scheme
structure.setScheme(cache.colorScheme);
}
// Exit the script on sites which doesn't need the sidebar:
if (forceNoScript || cache.forceNoScript)
return;
// Special treatments for pages inside a frame:
if (isFrameCapable)
{
if (isInsideFrame)
{
if (cache.fontSize != 1)
$('head').append('');
normalizeParentURL = function() {
var url = getUrlParameter('url');
postMessageToParent('normalizeURL', [url ? {"href": url} : $.extend({}, window.location), document.title, supportsHistory ? history.state : null, equivPath]);
if (cache.toc_clickItemTemp)
if (supportsHistory)
history.replaceState($.extend(history.state, {toc_clickItemTemp: cache.toc_clickItemTemp}), null, null);
cache.set('toc_clickItemTemp', null);
}
normalizeParentURL(); $(window).on('hashchange', normalizeParentURL);
structure.setScheme(cache.colorScheme);
structure.addShortcuts();
structure.addAnchorFlash();
structure.saveCacheBeforeLeaving();
if (!isIE) {
structure.hideFrameBeforeLeaving();
postMessageToParent('unhideFrame', []);
}
$(document).ready(function() {
$('html').attr({ id: 'right'});
features.add();
});
$(window).on('message onmessage', function(event) {
var data = parseJSON(event.originalEvent.data);
if (Array.isArray(data) === false)
return;
switch(data[0]) {
case 'updateCache':
if(typeof data[1] === 'object')
$.extend(cache, data[1]);
else
cache[data[1]] = data[2];
break;
case 'highlightWords':
search.highlightWords(data[1]);
break;
case 'scrollToMatch':
search.scrollToMatch(data[1]);
break;
case 'setScheme':
structure.setScheme(data[1]);
break;
}
});
return;
}
else
{
$(window).on('message onmessage', function(event) {
var data = parseJSON(event.originalEvent.data);
if (Array.isArray(data) === false)
return;
switch(data[0]) {
case 'normalizeURL':
var relPath = getRelativePath(data[1].href, workingDir);
try {
if (history.replaceState)
history.replaceState(null, null, data[1].href);
}
catch(e) {
if (history.replaceState)
history.replaceState(null, null, "?frame=" + encodeURI(relPath).replace(/#/g, '%23'));
}
document.title = data[2];
if (structure.modifyTools)
structure.modifyTools(relPath, data[4]);
if ($('#left > div.toc li > span.selected a').attr('href') == data[1].href)
break;
else if (data[3] && data[3].toc_clickItemTemp) {
toc.deselect($('#left > div.toc'));
$('#left > div.toc li > span').eq(data[3].toc_clickItemTemp).trigger('select');
}
else
toc.preSelect($('#left > div.toc'), data[1]);
break;
case 'pressKey':
structure.pressKey(data[1]);
break;
case 'updateQuickRef':
structure.updateQuickRef(data[1], data[2]);
break;
case 'hideFrame':
$('#right .load').hide().show(0); // reload animation
document.getElementById('frame').className = 'hidden';
break;
case 'unhideFrame':
document.getElementById('frame').className = 'visible';
break;
}
});
$(window).on('hashchange', function() {
structure.openSite(location.href);
});
}
}
// Add elements for sidebar:
structure.build();
// Load current URL into frame:
if (isFrameCapable)
$(document).ready(function() {
if (window.sessionStorage)
window.sessionStorage.setItem('data', JSON.stringify(cache));
else
document.getElementById('frame').contentWindow.name = JSON.stringify(cache);
structure.openSite(scriptDir + '/../' + (getUrlParameter('frame') || relPath));
});
// Modify the site:
document.getElementsByTagName('body')[0].className += ' body';
structure.modify();
if (!isFrameCapable)
$(document).ready(features.add);
toc.modify();
index.modify();
search.modify();
})();
// --- Constructor: Table of content ---
function ctor_toc()
{
var self = this;
self.dataPath = scriptDir + '/source/data_toc.js';
self.create = function(input) { // Create and add TOC items.
var ul = document.createElement("ul");
for(var i = 0; i < input.length; i++)
{
var text = input[i][0];
var path = input[i][1];
var subitems = input[i][2];
if (path != '')
{
var el = document.createElement("a");
el.href = workingDir + path;
if (cache.deprecate_data[path])
el.className = "deprecated";
}
else
var el = document.createElement("button");
if (isIE8)
el.innerHTML = text;
else
{
el.setAttribute("data-content", text);
el.setAttribute("aria-label", text);
}
var span = document.createElement("span");
span.innerHTML = el.outerHTML;
var li = document.createElement("li");
li.title = text;
if (cache.deprecate_data[path])
li.title += "\n\n" + T("Deprecated. New scripts should use {0} instead.").format(cache.deprecate_data[path]);
if (subitems != undefined && subitems.length > 0)
{
li.className = "closed";
li.innerHTML = span.outerHTML;
li.innerHTML += self.create(subitems).outerHTML;
}
else
li.innerHTML = span.outerHTML;
ul.innerHTML += li.outerHTML;
}
return ul;
};
// --- Modify the elements of the TOC tab ---
self.modify = function() {
if (!retrieveData(self.dataPath, "toc_data", "tocData", self.modify))
return;
if (!retrieveData(deprecate.dataPath, "deprecate_data", "deprecateData", self.modify))
return;
if (!retrieveData(translate.dataPath, "translate_data", "translateData", self.modify))
return;
$toc = $('#left div.toc').html(self.create(cache.toc_data));
$tocList = $toc.find('li > span');
// --- Fold items with subitems ---
$toc.find('li > ul').hide();
// --- Hook up events ---
// Select the item on click:
registerEvent($toc, 'click', 'li > span', function() {
$this = $(this);
cache.set('toc_clickItem', $tocList.index(this));
cache.set('toc_scrollPos', $toc.scrollTop());
// Fold/unfold item with subitems:
if ($this.parent().has("ul").length) {
$this.siblings("ul").slideToggle(100);
$this.closest("li").toggleClass("closed opened");
}
// Higlight and open item with link:
if ($this.has("a").length) {
self.deselect($toc); $this.trigger('select');
structure.openSite($this.children('a').attr('href'));
structure.focusContent();
return false;
}
});
// Highlight the item and parents on select:
registerEvent($toc, 'select', 'li > span', function() {
$this = $(this);
// Highlight the item:
$this.addClass("selected");
// Highlight its parents:
$this.parent("li").has('ul').addClass('highlighted');
$this.parent().parents('li').addClass('highlighted');
// Unfold parent items:
$this.parents("ul").show();
$this.parents("ul").closest("li").removeClass('closed').addClass('opened');
});
// --- Show scrollbar on mouseover ---
if (!isTouch) // if not touch device.
{
$toc.css("overflow", "hidden").hover(function() {
$(this).css("overflow", "auto");
}, function() {
$(this).css("overflow", "hidden");
});
}
self.preSelect($toc, location);
if (!isFrameCapable || cache.search_input)
$(document).ready(function() {
setTimeout( function() { self.preSelect($toc, location); }, 0);
});
};
self.preSelect = function($toc, url) { // Apply stored settings.
var tocList = $toc.find('li > span');
var clicked = tocList.eq(cache.toc_clickItem);
var found = null;
var foundList = [];
var foundNoHashList = [];
var url_href = (url.href.slice(-1) == '/') ? url.href + 'index.htm' : url.href;
for (var i = 0; i < tocList.length; i++) {
var href = tocList[i].firstChild.href;
if (!href)
continue;
// Search for items matching the address:
if (href == url_href)
foundList.push($(tocList[i]));
// Search for items matching the address without anchor:
else if (href == url_href.substring(0, url_href.length - url.hash.length))
foundNoHashList.push($(tocList[i]));
}
if (foundList.length)
found = $(foundList).map($.fn.toArray);
else if (foundNoHashList.length)
found = $(foundNoHashList).map($.fn.toArray);
var el = found;
// If the last clicked item can be found in the matches, use it instead:
if (clicked.is(found))
el = clicked;
else
cache.set('toc_scrollPos', ""); // Force calculated scrolling.
// If items are found:
if (el) {
// Highlight items and parents:
self.deselect($toc); el.trigger('select');
// Scroll to the last match:
if (cache.toc_scrollPos != "" || cache.toc_scrollPos != 0)
$toc.scrollTop(cache.toc_scrollPos);
if (!isScrolledIntoView(el, $toc)) {
el[el.length-1].scrollIntoView(false);
$toc.scrollTop($toc.scrollTop()+100);
}
}
}
self.deselect = function($toc) { // Deselect all items.
$toc.find("span.selected").removeClass("selected");
$toc.find(".highlighted").removeClass("highlighted");
}
}
// --- Constructor: Keyword search ---
function ctor_index()
{
var self = this;
self.dataPath = scriptDir + '/source/data_index.js';
self.create = function(input, filter) { // Create and add the index links.
var output = '', label, path, type;
var type_name = {2: T("function"), 4: T("operator"), 6: T("class")};
var lang = T('en');
var collator = window.Intl ? new Intl.Collator(lang) : null;
if (collator)
input.sort(function(a, b) { return collator.compare(a[0], b[0]); });
else
input.sort(function(a, b) { return a[0].localeCompare(b[0], lang); });
for (var i = 0, len = input.length; i < len; i++)
{
label = input[i][0];
path = input[i][1];
type = input[i][2];
if (filter != -1 && type != filter)
continue;
// Append type name for ambiguities:
if (filter == -1 && type && type_name[type])
if (input[i-1] && input[i-1][0] == label || input[i+1] && input[i+1][0] == label)
label += ' (' + type_name[type] + ')';
var a = document.createElement("a");
a.href = workingDir + path;
a.setAttribute("tabindex", "-1");
if (isIE8)
a.innerHTML = label;
else
{
a.setAttribute("data-content", label);
a.setAttribute("aria-label", label);
}
output += a.outerHTML;
}
return output;
};
self.modify = function() { // Modify the elements of the index tab.
if (!retrieveData(self.dataPath, "index_data", "indexData", self.modify))
return;
if (!retrieveData(translate.dataPath, "translate_data", "translateData", self.modify))
return;
var $index = $('#left div.index');
var $indexSelect = $index.find('.select select');
var $indexInput = $index.find('.input input');
var $indexList = $index.find('div.list');
// --- Hook up events ---
// Filter list on change:
$indexSelect.on('change', function(e) {
cache.set('index_filter', this.value);
if(this.value == -1)
$(this).addClass('empty');
else
$(this).removeClass('empty');
$indexList.html(self.create(cache.index_data, this.value));
structure.addEventsForListBoxItems($indexList);
});
// Select closest index entry and show color indicator on input:
$indexInput.on('keyup input', function(e, noskip) {
var $this = $(this);
var prevInput = cache.index_input; // defaults to undefined
var input = cache.set('index_input', $this.val().toLowerCase());
// if no input, remove color indicator and return:
if (!input) {
$this.removeAttr('class');
return;
}
// Skip subsequent index-matching if we have the same query as the last search, to prevent double execution:
if (!noskip && input == prevInput)
return;
// Otherwise find the first item which matches the input value:
var indexListChildren = $indexList.children();
var match = self.findMatch(indexListChildren, input);
// Select the found item, scroll to it and add color indicator:
if (match.length) {
match.click();
// Scroll to 5th item below the match to improve readability:
scrollTarget = match.next().next().next().next().next();
if (!scrollTarget.length) { scrollTarget = indexListChildren.last(); };
scrollTarget[0].scrollIntoView(false);
$this.attr('class', 'match'); // 'items found'
}
else
$this.attr('class', 'mismatch'); // 'items not found'
});
$indexSelect.val(cache.index_filter).trigger('change');
self.preSelect($indexList, $indexInput);
if (!isFrameCapable || cache.index_input)
$(document).ready(function() {
setTimeout( function() { self.preSelect($indexList, $indexInput); }, 0);
});
};
self.findMatch = function(indexListChildren, input) {
var match = {};
if (!input)
return match;
for (var i = 0; i < indexListChildren.length; i++) {
var text = isIE8 ? indexListChildren[i].innerText : indexListChildren[i].getAttribute('data-content');
var listitem = text.substr(0, input.length).toLowerCase();
if (listitem == input) {
match = indexListChildren.eq(i);
break;
}
}
return match;
};
self.preSelect = function($indexList, $indexInput) { // Apply stored settings.
var clicked = $indexList.children().eq(cache.index_clickItem);
$indexInput.val(cache.index_input);
if (cache.index_scrollPos == null)
$indexInput.trigger('keyup', true);
else
{
$indexList.scrollTop(cache.index_scrollPos);
clicked.click();
}
};
}
// --- Constructor: Full text search ---
function ctor_search()
{
var self = this;
self.dataPath = scriptDir + '/source/data_search.js';
self.modify = function() { // Modify the elements of the search tab.
if (!retrieveData(self.dataPath, "search_index", "SearchIndex", self.modify))
return;
if (!retrieveData(self.dataPath, "search_files", "SearchFiles", self.modify))
return;
if (!retrieveData(self.dataPath, "search_titles", "SearchTitles", self.modify))
return;
var $search = $('#left div.search');
var $searchList = $search.find('div.list');
var $searchInput = $search.find('.input input');
var $searchCheckBox = $search.find('.checkbox input');
// --- Hook up events ---
// Refresh the search list and show color indicator on input:
$searchInput.on('keyup input', function(e, noskip) {
var $this = $(this);
var prevInput = cache.search_input; // defaults to undefined
var input = cache.set('search_input', $this.val());
// if no input, empty the search list, remove color indicator and return:
if (!input) {
$searchList.empty();
$this.removeAttr('class');
return;
}
// Skip subsequent search if we have the same query as the last search, to prevent double execution:
if (!noskip && input == prevInput)
return;
// Otherwise fill the search list:
cache.set('search_data', self.create(input));
$searchList.html(cache.search_data);
structure.addEventsForListBoxItems($searchList);
// Select the first item and add color indicator:
var searchListChildren = $searchList.children();
if (searchListChildren.length) {
searchListChildren.first().click();
cache.set('search_clickItem', 0);
$this.attr('class', 'match'); // 'items found'
}
else
$this.attr('class', 'mismatch'); // 'items not found'
});
self.preSelect($searchList, $searchInput, $searchCheckBox);
if (!isFrameCapable)
setTimeout( function() { self.preSelect($searchList, $searchInput, $searchCheckBox); }, 0);
};
self.preSelect = function($searchList, $searchInput, $searchCheckBox) { // Apply stored settings.
$searchInput.val(cache.search_input);
if (cache.search_scrollPos == null)
$searchInput.trigger('keyup', true);
else
{
$searchList.html(cache.search_data);
structure.addEventsForListBoxItems($searchList);
$searchList.scrollTop(cache.search_scrollPos);
$searchList.children().eq(cache.search_clickItem).click();
}
$searchCheckBox.prop('checked', cache.search_highlightWords);
};
self.convertToArray = function(SearchText) { // Convert text to array.
// Normalize whitespace:
SearchText = SearchText.toLowerCase().replace(/^ +| +$| +(?= )|\+/, '');
if (SearchText == '')
return '';
else // split and remove undefined or empty strings
return $(SearchText.split(' ')).filter(function(){return (!!this)});
}
self.highlightWords = function(words) {
var content = $(isInsideFrame ? 'body' : '#right .area');
if(words)
{
var qry = self.convertToArray(words);
for (var i = 0; i < qry.length; i++) {
content.highlight(qry[i]);
}
self.scrollToMatch(); // Scroll to first match.
}
else
content.removeHighlight();
}
self.scrollToMatch = function(direction) {
var matches = $(isInsideFrame ? 'body' : '#right .area').find('span.search_highlight');
if (!matches.length)
return;
var currMatch = matches.filter('.current');
if (currMatch.length)
{
index = matches.index(currMatch) + (direction == 'next' ? 1 : -1);
if (index > matches.length - 1)
index = 0;
else if (index < 0)
index = matches.length - 1;
currMatch.removeClass('current');
}
else
index = 0;
matches.eq(index).addClass('current');
matches.eq(index)[0].scrollIntoView(isIE8 ? true : {block: 'center'});
}
self.create = function(qry) { // Create search list.
var PartialIndex = {};
var RESULT_LIMIT = 50;
qry = self.convertToArray(qry);
if (qry == '')
return
function file_has_all_words(file_index, words, start) {
for ( ; start < words.length; ++start) {
var iw = index_partial(words[start])
if (!iw || iw.indexOf(file_index) == -1)
return false
}
return true
}
// Get each word from index and clone for modification below:
var all_results = []
for (var i = 0; i < qry.length; ++i) {
var t = qry[i].replace(/(\(|\(\))$/,'') // special case for page names ending with ()
var w = index_partial(t)
w = w ? w.slice() : []
all_results[i] = get_results(w)
}
var ranked = rank_results(all_results,qry)
return append_results(ranked);
// Get normal results for each term:
function get_results(w) {
var c = 0
var ret = []
for (var i = 0; i < w.length && c < RESULT_LIMIT; ++i) {
if (!file_has_all_words(w[i], qry, 1))
continue // Skip files which don't have all the words.
c++
var f = cache.search_files[w[i]]
// data.files excludes '.htm' to save space, so add it back:
if (f.indexOf('#') != -1)
f = f.replace('#', '.htm#')
else
f = f + '.htm'
// var ret_i = { u: location.href.replace(/\/docs\/.*/, "/docs/" + f),
var ret_i = { u: f,
t: (cache.search_titles[w[i]] || f) }
ret.push(ret_i)
}
return ret
}
function rank_results(aro,terms) {
// Organize the info:
var aro_k = []
var aro_ua = []
var aro_ka = []
for (var i = 0; i < aro.length; ++i) {
aro_k[i] = []
if (aro[i] === undefined) { continue; }
for (var j = 0; j < aro[i].length; ++j) {
aro_k[i].push(aro[i][j].t)
aro_ka.push(aro[i][j].t)
aro_ua[aro[i][j].t] = aro[i][j].u
}
}
// Assemble list of unique results:
var ukeys = []
for (var i = 0; i < aro_ka.length; ++i)
if (ukeys.indexOf(aro_ka[i]) == -1)
ukeys.push(aro_ka[i])
// The lower the rank the better
// normal ranking (based on page contents):
var uranks = []
for (var i = 0; i < ukeys.length; ++i) {
uranks[ukeys[i]] = []
for (var j = 0; j < aro_k.length; ++j) {
uranks[ukeys[i]][j] = ukeys[i].indexOf(aro_k[j])
}
}
// Added ranking (based on page names)
// and calculate the ranks:
for (var i = 0; i < ukeys.length; ++i){
var name = ukeys[i]
var tmp = uranks[name]
// If the name contains any of the search terms:
if ( anyContains(name.toLowerCase(),terms) ) {
tmp.push(0) // Give it a better rank.
} else {
tmp.push(1,8) // Give it a worse rank, Tweakable!
}
uranks[name] = array_avg(tmp)
}
// Sort results by rank average and finalize:
var ret = []
for (var i = 0; i < ukeys.length; ++i){
var name = ukeys[i]
var url = aro_ua[name]
var avg = uranks[name]
ret.push({n:name,u:url,a:avg})
}
ret.sort(function(a,b){return (a.a - b.a)})
ret.slice(0,RESULT_LIMIT)
return ret
}
function array_avg(arr) {
var sum = 0
var total = arr.length
for (var j = 0; j < arr.length; ++j){
if ( (arr[j] < 0) || (arr[j-1] == arr[j]) )
total -= 1 // Give a worse rank, duplicate ranks are ignored.
else
sum += arr[j]
}
return (sum/total)
}
function anyContains(str, arr) { // http://stackoverflow.com/a/5582640/883015
for (var i = 0, len = arr.length; i < len; ++i) {
if (str.indexOf(arr[i]) != -1)
return 1
}
return 0
}
function append_results(ro) {
var output = '';
for (var t = 0; t < ro.length && t < RESULT_LIMIT; ++t) {
var a = document.createElement("a");
a.href = workingDir + ro[t].u;
a.setAttribute("tabindex", "-1");
if (isIE8)
a.innerHTML = ro[t].n;
else
{
a.setAttribute("data-content", ro[t].n);
a.setAttribute("aria-label", ro[t].n);
}
output += a.outerHTML;
}
return output;
}
function decode_numbers(a) {
// Decode a string of [a-zA-Z] based 'numbers'
var n = []
for (i = 0; i < a.length; i += 2)
n.push(decode_number(a.substr(i, 2)))
return n
}
function decode_number(a) {
// Decode a number encoded by encode_number() in build_search.ahk:
var n = 0, c
for (var i = 0; i < a.length; ++i) {
c = a.charCodeAt(i)
n = n*52 + c - ((c >= 97 && c <= 122) ? 97 : 39)
}
return n
}
function index_whole(word) {
// Return a word from the index, decoding the list of files
// if it hasn't been decoded already:
var files = cache.search_index[word]
if (typeof(files) == "string") {
files = decode_numbers(files)
cache.search_index[word] = files
}
return files
}
function index_partial(word) {
if (word[0] == '+') // + prefix disables partial matching.
return index_whole(word.substr(1))
// Check if we've already indexed this partial word.
var files = PartialIndex[word]
if (files !== undefined)
return files
// Find all words in search.index which *contain* this word
// and cache the result.
files = []
var files_low = []
for (iw in cache.search_index) {
var p = iw.indexOf(word)
if (p != -1)
files.push.apply(p == 0 ? files : files_low, index_whole(iw))
}
files = files.concat(files_low)
var unique = []
for (var i = 0; i < files.length; ++i)
if (unique.indexOf(files[i]) == -1)
unique.push(files[i])
PartialIndex[word] = unique
return unique
}
};
}
// --- Constructor: Navigation structure ---
function ctor_structure()
{
var self = this;
self.metaViewport = '';
self.template = '