forked from GitHub/gf-core
411 lines
13 KiB
JavaScript
411 lines
13 KiB
JavaScript
/*
|
|
GF RGL Browser
|
|
John J. Camilleri, 2012
|
|
*/
|
|
$(document).ready(function() {
|
|
|
|
// ===== State information =====
|
|
|
|
state = {
|
|
index: undefined,
|
|
lookup: {},
|
|
loadCount: 0,
|
|
language: undefined, // lang of drop-down
|
|
current: { // current file
|
|
language: undefined,
|
|
module: undefined,
|
|
set: function(lang, module) {
|
|
state.current.language = lang;
|
|
state.current.module = module;
|
|
},
|
|
equals: function(a, b) {
|
|
if (!b)
|
|
return (a == state.current.module);
|
|
else
|
|
return (a == state.current.language) && (b == state.current.module);
|
|
}
|
|
},
|
|
title: "RGL Source Browser",
|
|
urlPrefix: "/",
|
|
defaultLangs: ['abstract','api','common','prelude']
|
|
} ;
|
|
|
|
var lookupModuleLanguage = function(module) {
|
|
var l = state.lookup[module];
|
|
if (l==undefined || l.length==0)
|
|
return null;
|
|
else if (l.length==1)
|
|
return l[0];
|
|
else {
|
|
for (i in l) {
|
|
if ($.inArray(l[i], state.defaultLangs))
|
|
return l[i];
|
|
}
|
|
return l[0]; // no preferred default, just return first...
|
|
}
|
|
}
|
|
var lookupAllModuleLanguages = function(module) {
|
|
return state.lookup[module];
|
|
}
|
|
|
|
|
|
// ===== Utility/UI functions =====
|
|
|
|
var showLoading = function(){
|
|
state.loadCount++;
|
|
$("#loading").show();
|
|
}
|
|
var hideLoading = function(){
|
|
state.loadCount = Math.max(state.loadCount-1, 0);
|
|
if (state.loadCount == 0)
|
|
$("#loading").hide();
|
|
}
|
|
|
|
var scrollToTop = function() {
|
|
$("html, body").animate({ scrollTop: 0 }, "slow");
|
|
}
|
|
var scrollToCodeLine = function(lineNo) {
|
|
showPanel("#code", function() {
|
|
// Find exact line, using the classes generated by google prettify
|
|
var obj = $("#code pre li.L"+(lineNo%10)+":eq("+Math.floor(lineNo/10)+")").prev();
|
|
var y = Math.max(obj.offset().top - obj.parent().offset().top - 75, 0);
|
|
$("#code pre").animate({ scrollTop: y }, "slow", function(){
|
|
highlight(obj);
|
|
});
|
|
});
|
|
}
|
|
var highlight = function(obj) {
|
|
obj.css('background-color', "yellow");
|
|
setTimeout(function(){
|
|
obj.css('background-color', "");
|
|
}, 1500);
|
|
}
|
|
|
|
var clearScope = function(msg) {
|
|
$('#scope_list').empty();
|
|
updateScopeCount();
|
|
if (msg) {
|
|
$('#scope_list').html("<em>"+msg+"</em>");
|
|
}
|
|
}
|
|
var setScope = function(code) {
|
|
$('#scope_list').html(code);
|
|
}
|
|
var clearCode = function(msg) {
|
|
$('#code pre').empty();
|
|
if (msg) {
|
|
$('#codes pre').html("<em>"+msg+"</em>");
|
|
}
|
|
}
|
|
var setCode = function(code) {
|
|
$('#code pre').html(code);
|
|
prettyPrint();
|
|
}
|
|
var updateScopeCount = function(){
|
|
$('#scope_count').text( $("#scope_list tr:visible").length );
|
|
}
|
|
|
|
var setLanguage = function(lang){
|
|
state.language = lang;
|
|
$("#languages select").val(lang);
|
|
initModules(lang);
|
|
}
|
|
|
|
// obj can be just a plain selector or a jQuery object
|
|
var showPanel = function(obj, callback){
|
|
showLoading();
|
|
setTimeout(function(){
|
|
$(".panel:visible").hide();
|
|
$(obj).show(0, callback);
|
|
recalculateHeights();
|
|
updateScopeCount();
|
|
hideLoading();
|
|
}, 500); // this ensures the loading displays
|
|
}
|
|
var getPanel = function() {
|
|
return $('.panel:visible').first();
|
|
}
|
|
|
|
var setTitle = function(s){
|
|
$('#module_name').html(s);
|
|
$('title').html(state.title + ": " + s);
|
|
}
|
|
|
|
|
|
// ===== Initialization =====
|
|
|
|
// Load the index file and populate language & module lists
|
|
$.ajax({
|
|
url: "index.json",
|
|
dataType: "json",
|
|
type: "GET",
|
|
success: function(data) {
|
|
state.index = data;
|
|
if (data['urlprefix']) state.urlPrefix = data['urlprefix'];
|
|
|
|
// Build language lookup index
|
|
for (var lang in data['languages']) {
|
|
for (var i in data['languages'][lang]) {
|
|
var module = data['languages'][lang][i];
|
|
if (!module) continue;
|
|
if (!state.lookup[module]) state.lookup[module] = [];
|
|
state.lookup[module].push(lang);
|
|
}
|
|
}
|
|
|
|
// Initialize the language list
|
|
var lang_select = $("<select>")
|
|
.attr('id', 'language_select')
|
|
.change(function(){
|
|
setLanguage($(this).val());
|
|
})
|
|
.appendTo("#languages")
|
|
var language_list = data['languages'];
|
|
for (var i in language_list) {
|
|
if (!i) continue;
|
|
var lang = i;
|
|
$('<option>')
|
|
.html(lang)
|
|
.appendTo(lang_select);
|
|
}
|
|
setLanguage("english");
|
|
hideLoading();
|
|
},
|
|
error: function(){
|
|
hideLoading();
|
|
alert("Error getting index. Try reloading page, or just give up.");
|
|
}
|
|
});
|
|
|
|
// Initialize the panels, tabs
|
|
$("a.tab").click(function(){
|
|
var panel = $(this).attr("href");
|
|
showPanel(panel);
|
|
return false;
|
|
});
|
|
showPanel(".panel:first");
|
|
|
|
|
|
// ===== URL history =====
|
|
|
|
$.History.bind(function(hash){
|
|
var s = hash.split("/");
|
|
var lang = s[0];
|
|
var module = stripExt(s[1]);
|
|
var parseLineNo = s[1].match(/:(\d+)(-(\d+))?$/);
|
|
|
|
if (state.current.equals(lang, module)) {
|
|
if (parseLineNo) {
|
|
scrollToCodeLine(parseInt(parseLineNo[1]));
|
|
}
|
|
// else there's nothing to do!
|
|
} else {
|
|
if (parseLineNo != undefined)
|
|
loadFile(lang, module, parseInt(parseLineNo[1]));
|
|
else
|
|
loadFile(lang, module);
|
|
}
|
|
});
|
|
|
|
var stripExt = function(s) {
|
|
var i = s.lastIndexOf('.');
|
|
return (i>-1) ? s.substr(0, i) : s;
|
|
};
|
|
|
|
|
|
// ===== Loading functionality =====
|
|
|
|
// Initialize the module list
|
|
var initModules = function(lang){
|
|
state.index['languages'][lang] = state.index['languages'][lang].sort();
|
|
$("#modules").empty();
|
|
for (var i in state.index['languages'][lang]) {
|
|
var module = state.index['languages'][lang][i];
|
|
if (!module) continue;
|
|
$('<a>')
|
|
.html(module)
|
|
.attr('href', "#"+lang+"/"+module+".gf")
|
|
.appendTo("#modules");
|
|
}
|
|
};
|
|
|
|
// Load both scope & source for a file
|
|
var loadFile = function(lang, module, lineNo){
|
|
setTitle(lang+"/"+module);
|
|
state.current.set(lang, module);
|
|
loadTagsFile(module);
|
|
loadSourceFile(lang, module, lineNo);
|
|
}
|
|
|
|
// Load a tags file
|
|
var loadTagsFile = function(module) {
|
|
clearScope();
|
|
showLoading();
|
|
$.ajax({
|
|
url: "tags/"+module+".gf-tags",
|
|
type: "GET",
|
|
dataType: "text",
|
|
success: function(data){
|
|
data = data.replace(/^(\S+)\s(\S+)\s(.+)?$/gm, function(a,b,c,d){
|
|
var s = d.split("\t");
|
|
if (c == "indir") {
|
|
var module = s[2].substring(s[2].lastIndexOf('/')+1, s[2].lastIndexOf('.'));
|
|
var lang = lookupModuleLanguage(module);
|
|
var name = lang+"/"+module;
|
|
var url = "#"+lang+"/"+module;
|
|
var anchor = '<a href="'+url+'">'+name+'</a>';
|
|
return '<tr class="indir" name="'+b+'"><th>'+b+'</th><td>'+c+'</td><td>'+s[0]+'</td><td>'+s[1]+'</td><td>'+anchor+'</td><td></td></tr>'
|
|
} else {
|
|
var bits = s[0].split("/"); // ["lib", "src", "english", "AdjectiveEng.gf:43-46"]
|
|
var name = bits[3]+"/"+bits[4];
|
|
var url = "#"+bits[3]+"/"+bits[4];
|
|
var anchor = '<a href="'+url+'">'+name+'</a>';
|
|
return '<tr class="local" name="'+b+'"><th>'+b+'</th><td>'+c+'</td><td></td><td></td><td>'+anchor+'</td><td>'+s[1]+'</td></tr>'
|
|
}
|
|
});
|
|
setScope(data);
|
|
runFilter();
|
|
hideLoading();
|
|
},
|
|
error: function(data){
|
|
clearScope("No scope available");
|
|
hideLoading();
|
|
},
|
|
});
|
|
}
|
|
|
|
// Load a source module
|
|
var loadSourceFile = function(lang, module, lineNo) {
|
|
clearCode();
|
|
showLoading();
|
|
$.ajax({
|
|
url: state.urlPrefix + "lib/src/"+lang+"/"+module+".gf",
|
|
type: "GET",
|
|
dataType: "text",
|
|
success: function(data, status, xhr){
|
|
setCode(data);
|
|
hideLoading();
|
|
if (lineNo) {
|
|
scrollToCodeLine(lineNo);
|
|
}
|
|
},
|
|
error: function(data){
|
|
clearCode("No code available");
|
|
hideLoading();
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
// ===== Filtering of scope info =====
|
|
|
|
// Custom selector
|
|
$.expr[':'].match = function(a,b,c) {
|
|
var obj = $(a);
|
|
var needle = c[3];
|
|
var haystack = obj.attr('name');
|
|
if (haystack == undefined)
|
|
return false;
|
|
if ($("#case_sensitive").is(":checked"))
|
|
return haystack.indexOf(needle)>=0;
|
|
else
|
|
return haystack.toLowerCase().indexOf(needle.toLowerCase())>=0;
|
|
};
|
|
|
|
var runFilter = function() {
|
|
showLoading();
|
|
var s = $("#search").val();
|
|
try {
|
|
if (s) {
|
|
$("#scope_list tr:match(\""+s+"\")").show();
|
|
$("#scope_list tr:not(:match(\""+s+"\"))").hide();
|
|
} else {
|
|
$("#scope_list tr").show();
|
|
}
|
|
if ($("#show_local").is(":checked") ) {
|
|
$("#scope_list tr.indir").hide();
|
|
}
|
|
} catch (error) {
|
|
alert(error.message);
|
|
}
|
|
updateScopeCount();
|
|
hideLoading();
|
|
}
|
|
|
|
// Instant results
|
|
var prevSearch = $("#search").val();
|
|
$("#search").keyup(function(){
|
|
var s = $("#search").val();
|
|
if (s!=prevSearch) {
|
|
runFilter();
|
|
prevSearch = s;
|
|
}
|
|
});
|
|
$("#submit").hide();
|
|
|
|
// Filter & clear buttons
|
|
// $("#submit").click(runFilter);
|
|
|
|
$("#search").keypress(function(e){
|
|
var code = (e.keyCode ? e.keyCode : e.which);
|
|
if(code == 13) { // Enter
|
|
runFilter();
|
|
}
|
|
});
|
|
$("#clear").click(function(){
|
|
$("#search")
|
|
.val('')
|
|
.focus()
|
|
runFilter();
|
|
});
|
|
$("#case_sensitive").change(runFilter);
|
|
$("#show_all").change(runFilter);
|
|
$("#show_local").change(runFilter);
|
|
|
|
|
|
// ===== Window resizing stuff =====
|
|
|
|
// refer: http://paulirish.com/2009/throttled-smartresize-jquery-event-handler/
|
|
(function($,sr){
|
|
// debouncing function from John Hann
|
|
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
|
|
var debounce = function (func, threshold, execAsap) {
|
|
var timeout;
|
|
return function debounced () {
|
|
var obj = this, args = arguments;
|
|
function delayed () {
|
|
if (!execAsap)
|
|
func.apply(obj, args);
|
|
timeout = null;
|
|
};
|
|
|
|
if (timeout)
|
|
clearTimeout(timeout);
|
|
else if (execAsap)
|
|
func.apply(obj, args);
|
|
|
|
timeout = setTimeout(delayed, threshold || 100);
|
|
};
|
|
}
|
|
// smartresize
|
|
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
|
|
})(jQuery,'smartresize');
|
|
|
|
// usage:
|
|
$(window).smartresize(function() {
|
|
recalculateHeights();
|
|
});
|
|
|
|
var recalculateHeights = function() {
|
|
var x = 55; // just found empirically
|
|
$('body').height( $(window).height()-x);
|
|
$('.maxheight').each(function(){
|
|
var obj = $(this);
|
|
var parent = obj.parent();
|
|
obj.height(parent.height() - (obj.offset().top - parent.offset().top) - parseInt(parent.css('padding-bottom')));
|
|
});
|
|
}
|
|
|
|
recalculateHeights();
|
|
});
|