//   Display RDF information in tabular form using HTML DOM
// 
// CVS Id: tabulate.js,v 1.345 2006/01/12 14:00:56 timbl Exp $
//
// SVN ID: $Id: tabulate.js 1312 2006-07-14 15:44:54Z alerer $
//
// See Help.html, About.html, tb.html   

//CONFIG

//Smushing: Some properties are considered "Inverse Functional Property".
//If foo->IFP->R and bar->IFP->R, then foo=bar, and they are 'smushed'
//in store.

// var kb = new RDFFormula()    // Using this gives no smushing
var kb = new RDFIndexedFormula()  // This uses indexing and smushing
kb.predicateCallback = AJAR_handleNewTerm
kb.typeCallback = AJAR_handleNewTerm

selection = []  // Array of statements which have been selected

// For offline working, you might want to map URIs to local copies.
var SiteMap = []
SiteMap[ "http://www.w3.org/" ] = "http://localhost/www.w3.org/"  // Salt to taste

// Icons. Must be able to change for platform-consistency,
// color blindness, etc.
icon_expand = 'icons/tbl-expand-trans.png'
// icon_expand = 'icons/clean/icon_expand.png'
icon_collapse = 'icons/tbl-collapse.png'  // icons/clean/icon_menu.png
icon_rows = 'icons/tbl-rows.png'
// icon_columns = 'icons/tbl-columns.png'
icon_unrequested = 'icons/16dot-blue.gif'
// icon_parse = 'icons/18x18-white.gif'
icon_fetched = 'icons/16dot-green.gif'
icon_failed = 'icons/16dot-red.gif'  
icon_requested = 'icons/16dot-yellow.gif'
// icon_maximize = 'icons/clean/icon_con_max.png'
icon_visit = 'icons/document.png'
// actions for sources
icon_retract = 'icons/retract.gif';
icon_refresh = 'icons/refresh.gif';
icon_retracted = icon_unrequested; 

// Special knowledge of properties
tabont = Namespace("http://dig.csail.mit.edu/2005/ajar/ajaw#")
foaf = Namespace("http://xmlns.com/foaf/0.1/")
rdf = Namespace("http://www.w3.org/1999/02/22-rdf-syntax-ns#")
RDFS = Namespace("http://www.w3.org/2000/01/rdf-schema#")
OWL = Namespace("http://www.w3.org/2002/07/owl#")
dc = Namespace("http://purl.org/dc/elements/1.1/")
rss = Namespace("http://purl.org/rss/1.0/")
contact = Namespace("http://www.w3.org/2000/10/swap/pim/contact#")
tabulator_ns = Namespace("http://julius.ath.cx/tabulator/rdf/tabulator.rdf#");

// labels  -- maybe, extend this with a propertyAction
labelPriority = []
labelPriority[foaf('name').uri] = 10
labelPriority[dc('title').uri] = 8
labelPriority[rss('title').uri] = 6   // = dc:title?
labelPriority[contact('fullName').uri] = 4
labelPriority['http://www.w3.org/2001/04/roadmap/org#name'] = 4
labelPriority[RDFS('label').uri] = 2

PropertiesWithRangeImage = []
PropertiesWithRangeImage[foaf('depiction').uri] = true
PropertiesWithRangeImage[foaf('img').uri] = true
PropertiesWithRangeImage[foaf('thumb').uri] = true
PropertiesWithRangeImage[foaf('logo').uri] = true

 //////////  URI space mapping
 //  If the script is being run from localhost, then assume a local
 // server is mapping also all web accesses.  This allows testing on
 // planes.
 //
 // It also could allow one to get over the web access permission
 // problem using a form of proxy.
 //
 //
 function mapURI(uri) {
    var j, j, v
    fyi("document.domain "+document.domain)
    var i = uri.indexOf('//')
    if (document.domain == "localhost") {   // ie script is localhost
        if ( i >= 0) {
        uri = uri.slice(0,i+2) + 'localhost/' + uri.slice(i+2)
/*          j = uri.indexOf('/', i+2)
            if (j >= 0) {
        v =  SiteMap[uri.slice(0,j+1)]
        if (v) {
            fyi("Mapped "+uri+" to "+v + uri.slice(j+1))
            uri = v + uri.slice(j+1)
        }
            }
*/
        }
    }
    fyi("Mapped "+uri)
    return uri
 }
 
 ////////// Document management:

var sources = { 
  status : [ ], 
  index : 0,
  callbacks : {},
  depends   : {} };
documentStatus = sources.status;

/** state of uri (lookup in sources.status) **/
function docState(uri) {
    if (!uri) {
	tdebug("docState "+uri)
    }
    if (uri.slice(0,5) != 'http:') return 'unfetchable' // @@ add ftp, file?
    uri = uri_docpart(uri); //strip frag
    var status = sources.status[uri];
    if (typeof status == 'undefined' || typeof status.state == 'undefined') return 'unrequested';
    return status.state;
} //docState

/** returns true if str starts with pref, case sensitive, space sensitive **/
function string_startswith(str, pref) { // missing library routines
    return (str.slice(0, pref.length) == pref);
}

/** callback handler for new terms **/
function AJAR_handleNewTerm(kb, p, requestedBy) {
    //tdebug("entering AJAR_handleNewTerm w/ kb, p=" + p + ", requestedBy=" + requestedBy);
    if (p.termType != 'symbol') return;
    var docuri;
    if (p.uri.indexOf('#') < 0) {
        if (string_startswith(p.uri, 'http://xmlns.com/foaf/0.1/')) {
            //tinfo("Overriding: Assuming 303 response for anything FOAF");
            docuri = "http://dig.csail.mit.edu/2005/ajar/ajaw/test/foaf" // should give HTTP 303 to ontology
        } else if (string_startswith(p.uri, 'http://purl.org/dc/elements/1.1/') ||
                string_startswith(p.uri, 'http://purl.org/dc/terms/')) {
            //tinfo("overriding: assuming 30x for dublin core elements");
            docuri = "http://dublincore.org/2005/06/13/dcq"; //dc fetched multiple times
        } else if (string_startswith(p.uri, 'http://xmlns.com/wot/0.1/')) {
            //tinfo("web-of-trust isn't http accessible? fixyou.");
            docuri = "http://xmlns.com/wot/0.1/index.rdf";
        } else if (string_startswith(p.uri, 'http://web.resource.org/cc/')) {
            //tinfo("creative commons links to html instead of rdf. doesn't seem to content-negotiate. fixyou.");
            docuri = "http://web.resource.org/cc/schema.rdf";
        } else {
            docuri = p.uri
        }
    } else {
        docuri = uri_docpart(p.uri)
    }
    if (docState(docuri) != 'unrequested') return;
    sources_request_new(kb.sym(docuri), requestedBy);
} //AJAR_handleNewTerm

/////////////////////////  Logging
//
//bitmask levels
TNONE = 0; 
TERROR  = 1;
TWARN = 2;
TMESG  = 4;
TSUCCESS = 8;
TINFO  = 16;
TDEBUG  = 32;
TALL = 63;
logging = {
    ascending : false, 
    level : TERROR | TWARN //default
};

function escapeForXML(str) {
    return str.replace(/&/g, '&amp;').replace(/</g, '&lt;')
}

// t is for tabulator
//log a message where type is mesg|warning|error|debug|info|good
function tmsg(msg)  { tlog(msg, TMESG, 'mesg') };
function tinfo(msg)  { tlog(msg, TINFO, 'info') };
function twarn(msg) { tlog(msg, TWARN, 'warn') };
function terror(msg) { tlog(msg, TERROR, 'eror') };
function tdebug(msg) { tlog(msg, TDEBUG, 'dbug') };
function tsuccess(msg) { tlog(msg, TSUCCESS, 'good') };
function tlog(str, type, typestr)
{
    if (!type) { type = TMESG; typestr = 'mesg'};
    if (!(logging.level & type)) return; //bitmask
    var log_area = document.getElementById('status');
    
    var addendum = document.createElement("span");
    addendum.setAttribute('class', typestr);
    var now = new Date();
    addendum.innerHTML = now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds()
            + " [" + typestr + "] "+ escapeForXML(str) + "<br/>";
    if (logging.ascending)
        log_area.appendChild(addendum);
    else
        log_area.insertBefore(addendum, log_area.firstChild);
} //tlog
fyi = tlog;

/** clear the log window **/
function clearStatus(str) {
    var x = document.getElementById('status');
    if (!x) return;
    //x.innerHTML = "";
    emptyNode(x);
} //clearStatus

/** set the logging level **/
function setLogging(x) {
    logging.level = TALL;
    fyi("Logging "+x);
    logging.level = x;
}

function dumpStore() {
    var l = logging.level;
    logging.level = TALL;
    fyi("\nStore:\n" + kb + "__________________\n");
    tdebug("subject index: " + kb.subjectIndex[0] + kb.subjectIndex[1]);
    tdebug("object index: " + kb.objectIndex[0] + kb.objectIndex[1]);
    logging.level = l;
}

function dumpHTML() {
    var l = logging.level;
    logging.level = TALL;
    fyi(document.innerHTML);
    logging.level = l
}

///////////////// Utility

function emptyNode(node) {
    var nodes = node.childNodes, len = nodes.length, i
    for (i=len-1; i>=0; i--) node.removeChild(nodes[i])
    return node
}

function ArrayContains(a, x) {
    var i, n = a.length
    for (i=0; i<n; i++)
    if (a[i] == x) return true;
    return false
}

/** returns true if argument is an array **/
function isArray(arr) {
    if (typeof arr == 'object') {  
        var criterion = arr.constructor.toString().match(/array/i); 
        return (criterion != null); 
    }
    return false;
} //isArray

/** turns an HTMLCollection into an Array. Why? um, mostly so map & filter will work, though
  * i suspect they work on collections too **/
function HTMLCollection_to_Array(coll) {
    var arr = new Array();
    var len = coll.length;
    for (var e = 0; e < len; e++) arr[e] = coll[e];
    return arr;
} //HTMLCollection_to_Array

/** evaluate expression asynchronously. scoping? **/
function acall(expr) {
    setTimeout(expr, 0); //start off expr
} //acall

/** benchmark a function **/
benchmark.lastkbsize = 0;
function benchmark(f) {
    var args = [];
    for (var i = arguments.length-1; i > 0; i--) args[i-1] = arguments[i];
    //tdebug("BENCHMARK: args=" + args.join());
    var begin = new Date().getTime();
    var return_value = f.apply(f, args);
    var end = new Date().getTime();
    tinfo("BENCHMARK: kb delta: " + (kb.statements.length - benchmark.lastkbsize) 
            + ", time elapsed for " + f + " was " + (end-begin) + "ms");
    benchmark.lastkbsize = kb.statements.length;
    return return_value;
} //benchmark

///////////////////////// Representing data

function findLabelSubProperties() {
    var i,n
    tdebug("rdfs:label subproperties:");
    var labelPredicates = kb.each(undefined, RDFS('subPropertyOf'), RDFS('label'));
    for (i=0, n=labelPredicates.length; i<n; i++) {
	labelPriority[labelPredicates[i].uri] = 1;
	tdebug("rdfs:label subproperty "+ labelPredicates[i]);
	
    }
}


//  String to represent a thing in displays
function label(x) {
    var i,n
    var plist = kb.statementsMatching(x);
    var y, best = 0, lab = ""
    for (i=0, n=plist.length; i<n; i++) {
	var st = plist[i]
	y = labelPriority[st.predicate.uri]
	if (y && (y > best) && (st.object.termType=='literal')) {
	    lab = st.object.value
	    best = y
	}
    }
    if (lab) return lab

    if (x.termType == 'bnode') {
    return "..."
    }
    var hash = x.uri.indexOf("#")
    if (hash >=0) { return x.uri.slice(hash+1) }
    return x.uri 
}

//  As above but escaped for XML and chopped of contains a slash
function labelForXML(x) {
    var lab = escapeForXML(label(x));
    var slash = lab.lastIndexOf("/");
    if (slash >=0) lab = lab.slice(slash+1);
    return lab
}


//  Represent an object in summary form as a table cell


function AJARImage(src, alt) {
    var image = document.createElement('img')
    image.setAttribute('src', src)
    if (typeof alt != 'undefined') image.setAttribute('alt', alt)
    return image
}

function appendAccessIcon(node, term) {
    if (typeof term.termType == 'undefined') terror("??"+ term);
    if (term.termType != 'symbol') return '';
    var doc = uri_docpart(term.uri);
    var state = docState(doc);
    var icon, alt;
//    fyi("State of " + doc + ": " + state)
    switch (state) {
        case 'unrequested': 
            icon = icon_unrequested;
            alt = 'fetch';
        break;
        case 'requested': icon = icon_requested;
            alt = 'fetching';
        break;
        case 'fetched':
            icon = icon_fetched;
            alt = 'loaded';
        break;
        case 'failed': 
            icon = icon_failed;
            alt = 'failed';
        break;
        case 'unpermitted':
            icon = icon_failed;
            alt = 'no perm';
        break;
        case 'unfetchable':
            icon = icon_failed;
            alt = 'cannot fetch';
        break;
        default:
            terror ("?? state = " + state);
        break;
    } //switch
    //The two lines below can be uncommented to remove blue icon_unrequested dots.
    //if (icon != icon_unrequested) {
      var img = AJARImage(icon, alt);
      node.appendChild(img);
    //}
} //appendAccessIcon

/** make the td for an object (grammatical object) 
 *  @param obj - an RDF term
 *  @param view - a VIEW function (rather than a bool asImage)
 **/
function outline_objectTD(obj, view) {
    //tdebug("entering outline_objectTD with " + obj + ", " + view + ", " + obj.termType);
    //set about
  var td = document.createElement('TD');
  if ((obj.termType == 'symbol') || (obj.termType == 'bnode'))
    td.setAttribute('about', obj.toNT());
    td.setAttribute('class', 'obj');      //this is how you find an object
    if ((obj.termType == 'symbol') || (obj.termType == 'bnode')) {
    td.appendChild(AJARImage(icon_expand, 'expand'));
    } //expandable
    if (!view) // view should be a function pointer
        view = VIEWAS_boring_default;
    td.appendChild( view(obj) );
  return td;
} //outline_objectTD

///////////////// Represent an arbirary subject by its properties
//These are public variables
expandedHeaderTR.tr = document.createElement('tr');
expandedHeaderTR.td = document.createElement('td');
expandedHeaderTR.td.setAttribute('colspan', '2');
expandedHeaderTR.td.appendChild(AJARImage(icon_collapse, 'collapse'));
expandedHeaderTR.td.appendChild(document.createElement('strong'));
expandedHeaderTR.tr.appendChild(expandedHeaderTR.td);

function expandedHeaderTR(subject) {
    var tr = expandedHeaderTR.tr.cloneNode(true); //This sets the private tr as a clone of the public tr
    tr.firstChild.setAttribute('about', subject.toNT());
    tr.firstChild.childNodes[1].appendChild(document.createTextNode(label(subject)));
    return tr;
} //expandedHeaderTR

function propertyTable(subject) {
    fyi("Property table for: "+ subject)
    var table = document.createElement('TABLE')
    table.appendChild(expandedHeaderTR(subject))

    var plist = kb.statementsMatching(subject)
    appendPropertyTRs(table, plist, false)

    plist = kb.statementsMatching(undefined, undefined, subject)
    appendPropertyTRs(table, plist, true)

    if ((subject.termType == 'symbol')
	&& subject.uri.indexOf('#') < 0
        && (docState(uri_docpart(subject.uri)) == 'fetched')) {
        appendClassViewTRs(table, subject)
    }

    return table
} /* propertyTable */


///////////// Property list

function appendPropertyTRs(parent, plist, inverse)
{

    fyi("Property list length = " + plist.length)
    if (plist.length == 0) return "";
    var sel
    if (inverse) {
	sel = function(x) {return x.subject}
	plist = plist.sort(RDFComparePredicateSubject)
    } else {
	sel = function(x){return x.object}
	plist = plist.sort(RDFComparePredicateObject)
    }
    var j
    var max = plist.length
    for (j=0; j<max; j++) { //squishing together equivalent properties I think
	var s = plist[j]
    //      if (s.object == parentSubject) continue; // that we knew

	var k;
	var dups = 0;
	for (k=0; (k+j < max) && (plist[j+k].predicate.sameTerm(s.predicate)); k++) {
	    if (k>0 && (sel(plist[j+k]).sameTerm(sel(plist[j+k-1])))) dups++;
	}

	var lab = null;
	if (inverse) { // If we know an inverse predicate, use its label
	    var ip = kb.any(s.predicate, OWL('inverseOf'));
	    if (!ip) ip = kb.any(undefined, OWL('inverseOf'), s.predicate);
	    if (ip) lab = labelForXML(ip)
	}
	if (!lab) {
	    lab = labelForXML(s.predicate)
	    if (inverse) lab = "is "+lab+" of";
	}
	var tr = document.createElement("TR")
	parent.appendChild(tr)
	tr.AJAR_statement = s
	tr.AJAR_inverse = inverse
	tr.AJAR_variable
	var td_p = document.createElement("TD")
	td_p.setAttribute('about', s.predicate.toNT())
	td_p.setAttribute('class', 'pred')
	td_p.innerHTML = lab
	tr.appendChild(td_p)

	var defaultpropview = views.defaults[s.predicate.uri];
	tr.appendChild(outline_objectTD(sel(s), defaultpropview));
	
	if (k-dups != 1) {
	    td_p.setAttribute('rowspan', k-dups)
	    var l
	    for(l=1; l<k; l++) {
		if (!sel(plist[j+l]).sameTerm(sel(plist[j+l-1]))) {
		    s = plist[j+l]
		    tr = document.createElement("TR")
		    tr.appendChild(outline_objectTD(sel(plist[j+l]), defaultpropview)) //property view
		    tr.AJAR_statement = s
		    tr.AJAR_inverse = inverse
		    parent.appendChild(tr)
		}
	    }
	}
    j += k-1  // extra push
    }
}



////////// Views of classes mentioned in a document

function  appendClassViewTRs(parent, subject) {
	
 /*   var statements = kb.statementsMatching(
            undefined, rdf('type'), undefined, subject)
    n = statements.length
    alert(statements[0]);
    for (var x=0; x<n;x++)
    {
    	kb.add(subject,
    		new RDFSymbol('http://dig.csail.mit.edu/2005/ajar/ajaw/ont#mentions'),statements[x].object, subject)
    	//kb.add(subject,rdf('type'),statements[x].object, subject)
    }
    /*var statements = kb.statementsMatching(
            undefined, rdf('type'), undefined, subject)
    var classes = [], i, n = statements.length
    for (i=0; i<n; i++) {
	classes[statements[i].object] = true;
    }

    var c; for (c in classes) {
        var tr = document.createElement('TR')
        var td1 = document.createElement('TD')
        tr.appendChild(td1)
        td1.appendChild(document.createTextNode('mentions'))   // was: 'mentions class'
        tr.appendChild(outline_objectTD(kb.fromNT(c)))
        parent.appendChild(tr)
    }*/
}

//////// Human-readable content of a document

function documentContentTABLE(subject) {
    var table = document.createElement("TABLE")

    table.setAttribute('class', 'docView')
    table.appendChild(expandedHeaderTR(subject))

    var iframe = document.createElement("IFRAME")
    iframe.setAttribute('src', subject.uri)
    iframe.setAttribute('class', 'doc')
    iframe.setAttribute('height', '480')
    iframe.setAttribute('width', '640')
    var tr = document.createElement('TR')
    tr.appendChild(iframe)
    table.appendChild(tr)
    return table
}

////////////////////////////////////////////////////// VALUE BROWSER VIEW


////////////////////////////////////////////////////////// TABLE VIEW

//  Summarize a thing as a table cell

function matrixTD(obj, asImage) {
    var td = document.createElement('TD')
    if (!obj) var obj = new RDFLiteral(".");
    if  ((obj.termType == 'symbol') || (obj.termType == 'bnode')) {
    td.setAttribute('about', obj.toNT());
    td.setAttribute('style', 'color:#4444ff');
    }

    var image
    if (obj.termType == 'literal') {
    td.appendChild(document.createTextNode(obj.value))
    } else if ((obj.termType == 'symbol') || (obj.termType == 'bnode')){
    if (asImage) {
        image = AJARImage(mapURI(obj.uri), label(obj))
        image.setAttribute('class', 'pic')
        td.appendChild(image)   
    } else {
        td.appendChild(document.createTextNode(label(obj)))
    }
    }
    return td
}

/**********************

  query global vars 

***********************/
var NextVariable = 0;
function newVariableName() {
    return 'v' + NextVariable++;
}
function clearVariableNames() { 
    NextVariable = 0;
} //clear


// const doesn't work in Opera
// const BLANK_QUERY = { pat: kb.formula(), vars: [], orderBy: [] };
// @ pat: the query pattern in an RDFIndexedFormula. Statements are in pat.statements
// @ vars: the free variables in the query
// @ orderBy: the variables to order the table


function queryObj()
{ 
	this.pat = kb.formula(), 
	this.vars = []
	this.orderBy = [] 
}

var queries = [];
myQuery=queries[0]=new queryObj();


function query_save() {
    queries.push(queries[0]);
    var choices = document.getElementById('queryChoices');
    var next = document.createElement('option');
    var box = document.createElement('input');
    var index = queries.length-1;
    box.setAttribute('type','checkBox');
    box.setAttribute('value',index);
    choices.appendChild(box);
    choices.appendChild(document.createTextNode("Saved query #"+index));
    choices.appendChild(document.createElement('br'));
	next.setAttribute("value",index);
	next.appendChild(document.createTextNode("Saved query #"+index));
	document.getElementById("queryJump").appendChild(next);
  }

/** tabulate this! **/
function AJAR_Tabulate(event) {
    makeQueryLines();
    matrixTable(myQuery, sortables_init)
    sortables_init(); //make table headers sortable
} //AJAR_Tabulate

function makeQueryLines() {
    var i, n=selection.length, j, m, tr, sel, st;
    for (i=0; i<n; i++) {
        sel = selection[i]
       	tr = sel.parentNode
       	st = tr.AJAR_statement
       	tdebug("Statement "+st)
       	if (sel.getAttribute('class').indexOf('pred') >= 0) {
           	tinfo("   We have a predicate")
           	patternFromTR(tr) //defined in tabulate.js, currently..
       	}
       	if (sel.getAttribute('class').indexOf('obj') >=0) {
       		tinfo("   We have an object")
       		patternFromTR(tr,true)
       	}
    }
    fyi("Vars now: "+myQuery.vars)
    fyi("Query pattern now:\n"+myQuery.pat+"\n")
}


/** add a row to global myQuery using tr **/
function patternFromTR(tr, constraint) {
    var nodes = tr.childNodes, n = tr.childNodes.length, inverse=tr.AJAR_inverse,
        i, hasVar = 0, pattern, v, c, parentVar=null, level;
    
    function makeRDFStatement(freeVar, parent)
    {
    	if (inverse)
	    	return new RDFStatement(freeVar, st.predicate, parent)
	    else
	    	return new RDFStatement(parent, st.predicate, freeVar)
	}
	
    var st = tr.AJAR_statement; 
    for (level=tr.parentNode; level; level=level.parentNode) {
        if (typeof level.AJAR_statement != 'undefined') {
            fyi("Parent TR statement="+level.AJAR_statement + ", var=" + level.AJAR_variable)
            /*for(c=0;c<level.parentNode.childNodes.length;c++) //This makes sure the same variable is used for a subject
            	if(level.parentNode.childNodes[c].AJAR_variable)
            		level.AJAR_variable = level.parentNode.childNodes[c].AJAR_variable;*/
            if (!level.AJAR_variable)
                patternFromTR(level);
            parentVar = level.AJAR_variable
            break;
        }
    }
    var constraintVar = tr.AJAR_inverse? st.subject:st.object; //this is only used for constraints
    var hasParent=true
    if (constraintVar.isBlank && constraint) 
			alert("Warning: you are restraining your query with a blank node. The query will only find entries with the same blank node. Try constraining with a variable inside this node.");
    if (!parentVar)
    {
    	hasParent=false;
    	parentVar = inverse? st.object : st.subject; //if there is no parents, uses the sub/obj
	}
	tdebug('Initial variable: '+tr.AJAR_variable)
	v = tr.AJAR_variable? tr.AJAR_variable : kb.variable(newVariableName());
    myQuery.vars.push(v)
    v.label = hasParent? parentVar.label : label(parentVar);
    v.label += inverse?' is '+ label(st.predicate) +' of':" "+ label(st.predicate);
    pattern = makeRDFStatement(v,parentVar);
    //alert(pattern);
    v.label = v.label.slice(0,1).toUpperCase() + v.label.slice(1)// init cap
    
    if (constraint)   //binds the constrained variable to its selected value
    	myQuery.pat.initBindings[v]=constraintVar;
    	
    tinfo('Pattern: '+pattern);
    pattern.tr = tr
    tr.AJAR_pattern = pattern    // Cross-link UI and query line
    tr.AJAR_variable = v;
    tdebug('Final variable: '+tr.AJAR_variable)
    fyi("Query pattern: "+pattern)
    myQuery.pat.statements.push(pattern)
    return v
} //patternFromTR


function resetQuery(){
	function resetOutliner(pat)
	{
    	var i, n = pat.statements.length, pattern, tr;
    	for (i=0; i<n; i++) {
        	pattern = pat.statements[i];
        	tr = pattern.tr;
       	 	//tdebug("tr: " + tr.AJAR_statement);
        	if (typeof tr!='undefined')
        	{
        		delete tr.AJAR_pattern;
        		delete tr.AJAR_variable;
        	}
    	}
    	for (x in pat.optional)
    		resetOutliner(pat.optional[x])
    }
    resetOutliner(myQuery.pat)
    clearVariableNames();
    queries[0]=myQuery=new queryObj();
}

function AJAR_ClearTable() {
	resetQuery();
    var div = document.getElementById('results');
    var cal = document.getElementById('divCal');
    emptyNode(div);
    emptyNode(cal);
    return false;
} //AJAR_ClearTable


/** build the tabulator table 
  * @param q - a query pattern (RDFFormula) 
  * @param matrixTableCB - function pointer **/
function matrixTable(q, matrixTableCB) {
    function onBinding(bindings) { //Creates a row of the table and sticks all the columns in it
        tinfo("making a row w/ bindings " + bindings);
        var i, tr, td
        tr = document.createElement('tr')
        t.appendChild(tr)
        for (i=0; i<nv; i++) {
            v = q.vars[i]
            tr.appendChild(matrixTD(bindings[v]))
        } //for each query var, make a row
    }
    var i, nv=q.vars.length, td, th, j, v
    var t = document.createElement('table')

    tr = document.createElement('tr')
    t.appendChild(tr)
    t.setAttribute('class', 'results sortable')
    t.setAttribute('id', 'tabulated_data'); //needed to make sortable
    var div = document.getElementById('results')
    emptyNode(div).appendChild(t) // See results as we go
    for (i=0; i<nv; i++) {
    	v = q.vars[i]
//  	fyi("table header cell for " + v + ': '+v.label)
    	th = document.createElement('th')
    	
    	th.appendChild(document.createTextNode(v.label))
    	tr.appendChild(th)
    }
    use_callback = 1
    use_fetcher = 1
    if (use_callback) {
        kb.query(myQuery, onBinding, use_fetcher ? myFetcher : null); 
        //queries myQuery with use_fetcher, creating a callback to onBinding when it is fetched
        //It passes onBinding an association list of all the vars with their associated values in this subgraph
        //kb.query essentially routes to kb.match, which passes all matching subsets of kb, each to onBinding
        //kb.query(myQuery, queryCB)
    } else {
        var nbs = kb.query(myQuery.pat)
        var j, tr, nb, bindings, what
        for (j=0; j<nbs.length; j++) {
            tr = document.createElement('tr')
            t.appendChild(tr)
            bindings = nbs[j][0]  // [bindings, reason]
            for (i=0; i<nv; i++) {
            v = q.vars[i]
            td = document.createElement('td')
            what = bindings[v]
        //      fyi("    table cell "+v+": "+what + " type="+what.termType)
            tr.appendChild(matrixTD(what))
            }
        }
    }
    return t
}

function myFetcher(x, requestedBy) {
    if (x == null) {
        fyi("@@ SHOULD SYNC NOW")
    } else {
        fyi("Fetcher: "+x)
        AJAR_handleNewTerm(kb, x, requestedBy)

	var refs = kb.each(x, kb.sym(
			    'http://www.w3.org/2000/01/rdf-schema#seeAlso'), undefined)  //find all matches of seeAlso
	var j, m=refs.length, ref
	for (j=0; j<m; j++) {
	    ref = refs[j]
	    if (ref.termType != 'symbol') throw "myFetcher: Ooops";
            var doc = uri_docpart(ref.uri)
            if (docState(doc) == 'unrequested') 
                sources_request_new(kb.sym(doc), requestedBy)
        }
    }
}

//////////////////////////////////// User Interface Events

function getAboutLevel(target) {
    var level
    for (level = target; level; level = level.parentNode) {
    fyi("Level "+level)
    var aa = level.getAttribute('about')
    if (aa) return level
    }
    return undefined
}

function ancestor(target, tagName) {
    var level
    for (level = target; level; level = level.parentNode) {
    fyi("looking for "+tagName+" Level: "+level+" "+level.tagName)
    if (level.tagName == tagName) return level;
    }
    return undefined
}

function getAbout(kb, target) {
    var level, aa
    for (level = target; level && (level.nodeType==1); level = level.parentNode) {
        fyi("Level "+level + ' '+level.nodeType)
        aa = level.getAttribute('about')
        if (aa) return kb.fromNT(aa)
    }
    return undefined
}


//   Selection support

function selected(node) {
    var a = node.getAttribute('class')
    if (a && (a.indexOf('selected') >= 0)) return true
    return false
}

function setSelected(node, newValue) {
    if (newValue == selected(node)) return;
    var cla = node.getAttribute('class')
    if (!cla) cla = ""
    if (newValue) {
	    cla += ' selected'
	    selection.push(node)
	    fyi("Selecting "+node)
	    var source
	    if (node.AJAR_statement) source=node.AJAR_statement.why
	    else if (node.parentNode.AJAR_statement) source=node.parentNode.AJAR_statement.why
	    if (source && document.getElementById("Source:"+source.uri)) document.getElementById("Source:"+source.uri).setAttribute('class','sourceHighlight')
    } else {
	    fyi("cla=$"+cla+"$")
	    cla = cla.replace('selected','')
	    RDFArrayRemove(selection, node)
	    fyi("Deselecting "+node)
	    fyi("cla=$"+cla+"$")
	    if (node.AJAR_statement) source=node.AJAR_statement.why
	    else if (node.parentNode.AJAR_statement) source=node.parentNode.AJAR_statement.why
	    if (source && document.getElementById("Source:"+source.uri)) document.getElementById("Source:"+source.uri).setAttribute('class','')
    }
    node.setAttribute('class', cla)
}

function deselectAll() {
    var i, n=selection.length
    for (i=n-1; i>=0; i--) setSelected(selection[i], false);
}

function getTarget(e) {
    var target
    if (!e) var e = window.event
    if (e.target) target = e.target
    else if (e.srcElement) target = e.srcElement
    if (target.nodeType == 3) // defeat Safari bug [sic]
       target = target.parentNode
    fyi("Click on: " + target.tagName)
    return target
}

/////////  Hiding

function AJAR_hideNext(event) {
    var target = getTarget(event)
    fyi("@@ "+target.parentNode)
    fyi("@@ "+target.parentNode.nextSibling)
    var div = target.parentNode.nextSibling
    if (target.src.indexOf('collapse') >= 0) {
    div.setAttribute('class', 'collapse')
    target.src = icon_expand
    } else {
    div.removeAttribute('class')    
    target.src = icon_collapse
    }
}

function TabulatorDoubleClick(event)
{
    var target = getTarget(event)
    var tname = target.tagName
    fyi("TabulatorDoubleClick: " + tname + " in "+target.parentNode.tagName)
    var aa = getAbout(kb, target)
    if (!aa) return;
    // if (aa && aa.termType == 'symbol')
	// GotoURI(aa.uri);
	GotoSubject(aa);
}

function ResultsDoubleClick(event)
{	
	var target = getTarget(event);
	var aa = getAbout(kb, target)
	if (!aa) return;
//    if (aa && aa.termType == 'symbol')
	//GotoURI(aa.uri);
	GotoSubject(aa);
}


function setCookie(name, value, expires, path, domain, secure) {
  var curCookie = name + "=" + escape(value) +
      ((expires) ? "; expires=" + expires.toGMTString() : "") +
      ((path) ? "; path=" + path : "") +
      ((domain) ? "; domain=" + domain : "") +
      ((secure) ? "; secure" : "");
  document.cookie = curCookie;
}


/*
  name - name of the desired cookie
  return string containing value of specified cookie or null
  if cookie does not exist
*/

function getCookie(name) {
  var dc = document.cookie;
  var prefix = name + "=";
  var begin = dc.indexOf("; " + prefix);
  if (begin == -1) {
    begin = dc.indexOf(prefix);
    if (begin != 0) return null;
  } else
    begin += 2;
  var end = document.cookie.indexOf(";", begin);
  if (end == -1)
    end = dc.length;
  return unescape(dc.substring(begin + prefix.length, end));
}

function deleteCookie(name, path, domain) {
  if (getCookie(name)) {
    document.cookie = name + "=" +
    ((path) ? "; path=" + path : "") +
    ((domain) ? "; domain=" + domain : "") +
    "; expires=Thu, 01-Jan-70 00:00:01 GMT";
  }
}

function sources_xml(request) {
    return (new DOMParser()).parseFromString(request.responseText, 'text/xml');
} //sources_xml




function exportTable()
{
	sel=document.getElementById('exportType')
	var type = sel.options[sel.selectedIndex].value
	
	switch (type)
	{
		case 'cv':

			break;
		case 'html':
			var win=window.open('table.html','Save table as HTML');
			var tbl=document.getElementById('tabulated_data');
			win.document.write('<TABLE>');
			for(j=0;j<tbl.childNodes[0].childNodes.length;j++)
			{
				win.document.write('<TH>'+ts_getInnerText(tbl.childNodes[0].cells[j])+'</TH>');
			}
			for(i=1;i<tbl.childNodes.length;i++)
			{
				var r=tbl.childNodes[i]
				win.document.write('<TR>');
				var j
				for(j=0;j<r.childNodes.length;j++)
				{
					var about = "";
					if (r.childNodes[j].attributes['about'])
						about=r.childNodes[j].attributes['about'].value;
					win.document.write('<TD about="'+about+'">');
					win.document.write(ts_getInnerText(r.childNodes[j]));
					win.document.write('</TD>');
				}
				win.document.write('</TR>');
			}
			win.document.write('</TABLE>');
			win.document.uri='table.html'
			win.document.close();
			break;
		case 'sparql':
			//makeQueryLines();
			var spr = document.getElementById('SPARQLText')
			spr.setAttribute('class','expand')
            document.getElementById('SPARQLTextArea').value=queryToSPARQL(myQuery);
			//SPARQLToQuery("PREFIX ajar: <http://dig.csail.mit.edu/2005/ajar/ajaw/data#> SELECT ?v0 ?v1 WHERE { ajar:Tabulator <http://usefulinc.com/ns/doap#developer> ?v0 . ?v0 <http://xmlns.com/foaf/0.1/birthday> ?v1 . }")
			//matrixTable(myQuery, sortables_init)
    		      //sortables_init();
			break;
		case '': 
			alert('Please select a file type');
			break;
	}
}

function QuerySPARQLText ()
{
	var txt=document.getElementById('SPARQLTextArea').value;
	myQuery =  SPARQLToQuery(txt);
	matrixTable(myQuery, sortables_init)
    sortables_init(); //make table headers sortable
}

/** get the target of an event **/
function targetOf(e) {
    var target;
    if (!e) var e = window.event
    if (e.target) 
        target = e.target
    else if (e.srcElement) 
        target = e.srcElement
    else {
        terror("can't get target for event " + e);
        return false;
    } //fail
    if (target.nodeType == 3) // defeat Safari bug [sic]
        target = target.parentNode;
    return target;
} //targetOf

/** things to do onmousedown in outline view **/
// expand
// collapse
// refocus
// select
// visit/open a page
function TabulatorMousedown(e)
{
    var target = targetOf(e);
    if (!target) return;
    var tname = target.tagName;
    //fyi("TabulatorMousedown: " + tname + " shift="+e.shiftKey+" alt="+e.altKey+" ctrl="+e.ctrlKey);
    var p = target.parentNode;
    var about = getAbout(kb, target)
    var source = null;
    if (tname != "IMG") {
        if(about && about.termType == 'symbol') {
            document.getElementById('UserURI').value = about.uri;
        }
        var node = ancestor(target, 'TD');
        if (!node) return;
        var sel = selected(node);
        var cla = node.getAttribute('class')
        fyi("Was node selected before: "+sel)
        if (e.altKey) {
            setSelected(node, !selected(node))
        } else if  (e.shiftKey) {
            setSelected(node, true)
        } else {
            deselectAll()
            setSelected(node, true)
        }
        fyi("Was node selected after: "+selected(node)
            +", count="+selection.length)
	var tr = node.parentNode;
	if (tr.AJAR_statement) {
	    var why = tr.AJAR_statement.why
	    tinfo("Information from "+why);
	}

    } else { // IMG
        var tsrc = target.src
        var outer
        var i = tsrc.indexOf('/icons/')
        if (i >=0 ) tsrc=tsrc.slice(i+1) // get just relative bit we use
        fyi("\nEvent: You clicked on an image, src=" + tsrc)
        if (!about) {
            alert("No about attribute");
            return;
        }
        var subject = about;
        fyi("TabulatorMousedown: subject=" + subject);
        
        switch (tsrc) {
            case icon_expand:
            case icon_collapse:
                    var mode = e.shiftKey ? outline_refocus :
			(tsrc == icon_expand ? outline_expand : outline_collapse);
                    mode(p, subject);
                    break;
            case icon_visit:
                    emptyNode(p).appendChild(documentContentTABLE(subject))
                    document.url = subject.uri   // How to jump to new page?
                    break;
            case icon_unrequested:
            case icon_failed:
                    refreshButtons(subject.uri, icon_requested);
                    sources_refresh(subject.uri);
                    break;
            default: //nothing
        }
    }
} //function

// added source-getting to outline expand 4/27/06
function outline_expand(p, subject) {
    function expand() { emptyNode(p).appendChild(propertyTable(subject)); }
    function getMentionsOntology () {
    	//alert(tabont("#mentionsClass"))
    	var refs = kb.any(subject, kb.sym('http://dig.csail.mit.edu/2005/ajar/ajaw/ont#mentionsClass'), undefined)
    	if (refs)
    	{
    		sources_register_cb("expand_tab_ont",trySeeAlsoAndExpand);
    		sources_request_new(kb.sym('http://dig.csail.mit.edu/2005/ajar/ajaw/ont'),"expand_tab_ont")
    	}
    	else
    		trySeeAlsoAndExpand();
 	}
    function trySeeAlsoAndExpand () {
		var later = false
		var refs = kb.each(subject, kb.sym(
				    'http://www.w3.org/2000/01/rdf-schema#seeAlso'), undefined) 
		var sparqlEndpoints = kb.each(subject, kb.sym(
					'http://web.mit.edu/alerer/www/query#sparqlEndpoint'), undefined)
		var j, m=refs.length, ref
		for (j=0; j<m; j++) {
		    ref = refs[j]
	//	    tdebug("trySeeAlsoAndExpand: checking "+ref)
		    if (ref && ref.uri && sources_pending(ref.uri)) {
			tdebug("trySeeAlsoAndExpand: for "+subject+" fetching seeAlso: "+ref)
			sources_register_cb("expand"+ref.uri, expand);
			sources_request_new(ref, "expand"+ref);
			later = true
		}
	}
	for (var x=0;x<sparqlEndpoints.length;x++)
	{
		ep = sparqlEndpoints[x];
		if (ep && ep.uri) {
			tdebug("Retrieving data for "+subject+" from SPARQL endpoint at "+ep.uri);
			kb.spEndpointIndex[subject]=ep.uri;
		}
	}	
	
	if (later) {
	    var loading = document.createElement("span");
	    tdebug("trySeeAlsoAndExpand: refs= "+refs)
	    loading.innerHTML = " loading seeAlso...";
	    p.appendChild(loading);
	} else {
	    tdebug("trySeeAlsoAndExpand: No seeAlso, expanding "+subject)
	    expand()
	}
    }

    if (subject.uri && sources_pending(subject.uri)) {
	tdebug("outline_expand: dereferencing "+subject)
	sources_register_cb("expand"+subject.uri, getMentionsOntology);
	var loading = document.createElement("span");
	loading.innerHTML = " loading...";
	p.appendChild(loading);
	sources_request_new(subject, "expand"+subject);
    } else {
	tdebug("outline_expand: NO dereferencing "+subject)
	getMentionsOntology()
    }
} //outline_expand

function outline_collapse(p, subject) {
    var row = ancestor(p, 'TR');
    row = ancestor(row.parentNode, 'TR'); //two levels up
    if (row) var statement = row.AJAR_statement;
    var level; //find level
    for (level=p.parentNode; level.tagName != "TD";
            level=level.parentNode) {
        if (typeof level == 'undefined') {
            alert("Not enclosed in TD!")
            return
        }
    }
    fyi("Collapsing subject "+subject);
    var myview;
    if (statement) {
        tdebug("looking up pred " + statement.predicate.uri + "in defaults");
        myview = views.defaults[statement.predicate.uri];
    }
    tdebug("view= " + myview);
    level.parentNode.replaceChild(outline_objectTD(subject, myview), emptyNode(level));
} //outline_collapse

function outline_refocus(p, subject) { // Shift-expand or shift-collapse: Maximize
    var outer = null
    for (var level=p.parentNode; level; level=level.parentNode) {
        fyi("level "+ level.tagName)
        if (level.tagName == "TD") outer = level
    } //find outermost td
    emptyNode(outer).appendChild(propertyTable(subject));
    document.title = label(subject);
    outer.setAttribute('about', subject.toNT());
} //outline_refocus

// Inversion is turning the outline view inside-out

function outline_inversion(p, subject) { // re-root at subject

    function move_root(rootTR, childTR) { // swap root with child
    // @@
    }

}

function GotoFormURI(event) { GotoURI(document.getElementById('UserURI').value); }

function GotoURI(uri){
	var subject = kb.sym(uri)
	GotoSubject(subject);
}

function GotoSubject(subject) {
    var table = document.getElementById('browser')
    var tr = document.createElement("TR")
	
    table.appendChild(tr)
    var td = outline_objectTD(subject)
    tr.appendChild(td)
    outline_expand(td, subject)  // Always expand
    document.title = label(subject)
    return subject;
}

function GotoURIAndOpen(uri) {
   var sbj = GotoURI(uri);
//   outline_expand(document.getElementById('browser'), sbj);  Wrong element
}


////////////////////////////////////////////////////////
//
//
//                    VIEWS
//
//
////////////////////////////////////////////////////////

var views = {
    properties                          : [],
    defaults                                : [],
    classes                                 : []
}; //views

// not functional at the moment. 
///** find applicable views for the subject 
//    @returns array of javascript functions **/
//function views_find_applicable(subject) {
//  //find class-specific views
//  //i.e.: foaf:Person tabulator:view <http://MYDOMAIN/MYVIEW.js> }
//  var classes = kb.each(subject, rdf('type'));
//  var views;
//  for (var c in classes) classes[c] = classes[c].object;
//  for (var c in classes)
//      views = views.concat(views, views_findClassViews(c));
//
//  //@@fresnel views
//  //@@views if fulfills some functions
//  return views;
//} //find
//
///** find views that apply to the given class to the array **/
//function views_findClassViews(clss)
//{
//  var classviews = kb.each(clss, tabulator_ns('view'));
//  for (var cv in classviews)
//      classviews[cv] = classview[cv].object;
//  return classviews;
//} //views_add_classviews

/** add a property view function **/
function views_addPropertyView(property, pviewfunc, isDefault) {
    if (!views.properties[property]) 
        views.properties[property] = [];
    views.properties[property].push(pviewfunc);
    if(isDefault) //will override an existing default!
        views.defaults[property] = pviewfunc;
} //addPropertyView

//view that applies to items that are objects of certain properties.
//views_addPropertyView(property, viewjsfile, default?)
views_addPropertyView(foaf('depiction').uri, VIEWAS_image, true);
views_addPropertyView(foaf('img').uri, VIEWAS_image, true);
views_addPropertyView(foaf('thumb').uri, VIEWAS_image, true);
views_addPropertyView(foaf('logo').uri, VIEWAS_image, true);
views_addPropertyView(foaf('aimChatID').uri, VIEWAS_aim_IMme, true);
views_addPropertyView(foaf('mbox').uri, VIEWAS_mbox, true);
//views_addPropertyView(foaf('based_near').uri, VIEWAS_map, true);
views_addPropertyView(foaf('birthday').uri, VIEWAS_cal, true);

/** some builtin simple views **/
function VIEWAS_boring_default(obj) {
    //tdebug("entered VIEWAS_boring_default...");
    var rep; //representation in html

    if (obj.termType == 'literal')
    {
        rep = document.createTextNode(obj.value);
    } else if (obj.termType == 'symbol' || obj.termType == 'bnode') {
        rep = document.createElement('span');
        rep.setAttribute('about', obj.toNT());
        appendAccessIcon(rep, obj);
        rep.appendChild(document.createTextNode(label(obj)));
        if ((obj.termType == 'symbol') &&
                (obj.uri.indexOf("#") <0) &&
                (obj.uri.slice(0,5)=='http:')) { // a web page @@ file, ftp;
                rep.appendChild(AJARImage(icon_visit, 'open'));
        }
    } else {
        terror ("unknown term type: " + obj.termType);
        rep = document.createTextNode("[unknownTermType:" + obj.termType +"]");
    } //boring defaults.
    tdebug("contents: "+rep.innerHTML);
    return rep;
}  //boring_default!
function VIEWAS_image(obj) {
    return AJARImage(obj.uri, label(obj));
}

function VIEWAS_mbox(obj) {
    var anchor = document.createElement('a');
    // previous implementation assumed email address was Literal. fixed.
    var address = (obj.termType=='symbol') ? obj.uri : obj.value; // this way for now
    var index = address.indexOf('mailto:');
    address = (index >= 0) ? address.slice(index + 7) : address;
    anchor.setAttribute('href', 'mailto:'+address);
    anchor.appendChild(document.createTextNode(address));
    return anchor;
}

/* need to make unique calendar containers and names
 * YAHOO.namespace(namespace) returns the namespace specified 
 * and creates it if it doesn't exist
 * function 'uni' creates a unique namespace for a calendar and 
 * returns number ending
 * ex: uni('cal') may create namespace YAHOO.cal1 and return 1
 *
 * YAHOO.namespace('foo.bar') makes YAHOO.foo.bar defined as an object,
 * which can then have properties
 */

function uni(prefix){
    var n = counter();
    var name = prefix + n;
    YAHOO.namespace(name);
    return n;
}

// counter for calendar ids, 
counter = function(){
	var n = 0;
	return function(){
		n+=1;
		return n;
	}
}() // *note* those ending parens! I'm using function scope

var renderHoliday = function(workingDate, cell) { 
	YAHOO.util.Dom.addClass(cell, "holiday");
} 


/* toggles whether element is displayed
 * if elt.getAttribute('display') returns null, 
 * it will be assigned 'block'
 */
function toggle(eltname){
	var elt = document.getElementById(eltname);
	elt.style.display = (elt.style.display=='none')?'block':'none'
}

/* Example of calendar Id: cal1
 * 42 cells in one calendar. from top left counting, each table cell has
 * ID: YAHOO.cal1_cell0 ... YAHOO.cal.1_cell41
 * name: YAHOO.cal1__2006_3_2 for anchor inside calendar cell 
 * of date 3/02/2006
 * 
 */	
function VIEWAS_cal(obj) {
	prefix = 'cal';
	var cal = prefix + uni(prefix);

	var containerId = cal + 'Container';
	var table = document.createElement('table');
	
	
	// create link to hide/show calendar
	var a = document.createElement('a');
	// a.appendChild(document.createTextNode('[toggle]'))
	a.innerHTML="<small>mm-dd: " + obj.value + "[toggle]</small>"
	a.setAttribute('href',"javascript:toggle('"+containerId+"')")
	table.appendChild(a);

	var dateArray = obj.value.split("-");
	var m = dateArray[0];
	var d = dateArray[1];
	var yr = (dateArray.length>2)?dateArray[2]:(new Date()).getFullYear();

	// hack: calendar will be appended to divCal at first, but will
	// be moved to new location
	document.getElementById('divCal').appendChild(table);
	var div = table.appendChild(document.createElement('DIV'));
	div.setAttribute('id', containerId);
	// default hide calendar
	div.style.display = 'none';
	div.setAttribute('tag','calendar');
	YAHOO[cal] = new YAHOO.widget.Calendar("YAHOO." + cal, containerId, m+"/"+yr);

	YAHOO[cal].addRenderer(m+"/"+d, renderHoliday); 

	YAHOO[cal].render();
	// document.childNodes.removeChild(table);
	return table;
}


// test writing something to calendar cell


function VIEWAS_aim_IMme(obj) {
    var anchor = document.createElement('a');
    anchor.setAttribute('href', "aim:goim?screenname=" + obj.value + "&message=hello");
    anchor.setAttribute('title', "IM me!");
    anchor.appendChild(document.createTextNode(obj.value));
    return anchor;
} //aim_IMme

/*function VIEWAS_map(obj) {
	var lat = kb.each(obj, kb.sym('http://www.w3.org/2003/01/geo/wgs84_pos#lat'), undefined).toString();
	var lng = kb.each(obj, kb.sym('http://www.w3.org/2003/01/geo/wgs84_pos#long'), undefined).toString();
	if (parseFloat(lat) && parseFloat(lng))
	{
	
	    var disp = document.createElement('table')
	    var cola=disp.appendChild(document.createElement('tr'))
	    cola.appendChild(document.createTextNode("Latitude: "+lat+", Longitude: "+lng));
	    var colb=disp.appendChild(document.createElement('tr'))
	    var anchor=colb.appendChild(document.createElement('div'));
	    anchor.setAttribute('style','width:400px; height: 400px');
		anchor.setAttribute('id','mapContainer'+obj.toString);
		if (GBrowserIsCompatible()) {
			var map = new GMap2(anchor);
			map.setCenter(new GLatLng(parseFloat(lat), parseFloat(lng)), 9);
			map.panTo(new GLatLng(parseFloat(lat), parseFloat(lng)))
	        map.addOverlay(new GMarker(new GLatLng(parseFloat(lat), parseFloat(lng))));
	    }
	 
    //var myPoint = new YGeoPoint (parseFloat(lat.toString()),parseFloat(lng.toString));
    //var myPoint = new YGeoPoint(42.3601,-71.8783);
    //var map = new YMap(anchor);
    //map.drawZoomAndCenter(myPoint,8);
    //map.addTypeControl();
    //map.setMapType(YAHOO_MAP_REG);
    //var marker = new YMarker(myPoint);
    //var owner = kb.each(undefined,kb.sym(foaf('based_near')),obj)
    //var owner_name = kb.each(owner,kb.sym(foaf('name')),undefined)
    //marker.addLabel('Adam')
    
		return disp;
	}
	else
	{
		return VIEWAS_boring_default(obj);
	}
}*/

/*<div id="mapContainer"></div>

<script type="text/javascript">
// Create a lat/lon object
var myPoint = new YGeoPoint(37.4041960114344,-122.008194923401);
// Create a map object 
var map = new YMap(document.getElementById('mapContainer'));
// Display the map centered on a latitude and longitude 
map.drawZoomAndCenter(myPoint, 3);

// Add map type control
map.addTypeControl();

// Set map type to either of: YAHOO_MAP_SAT YAHOO_MAP_HYB YAHOO_MAP_REG
map.setMapType(YAHOO_MAP_SAT);

//Get valid map types, returns array [YAHOO_MAP_REG, YAHOO_MAP_SAT, YAHOO_MAP_HYB]
var myMapTypes = map.getMapTypes(); */

///////////////////////////////////////////////////
//
//
//
//        simplify event handling
//
//
//
///////////////////////////////////////////////////
/** add event to elm of type evType executing fn **/
function addEvent(elm, evType, fn, useCapture) {
    if (elm.addEventListener) {
        elm.addEventListener(evType, fn, useCapture);
        return true;
    }
    else if (elm.attachEvent) {
        var r = elm.attachEvent('on' + evType, fn);
        return r;
    }
    else {
        elm['on' + evType] = fn;
    }
} //addEvent

/** add event on page load **/
function addLoadEvent(func) {
    var oldonload = window.onload;
    if (typeof window.onload != 'function') {
        window.onload = func;
    }
    else {
        window.onload = function() {
            oldonload();
            func();
        }
    }
} //addLoadEvent

function test() {
    tmsg("DEPENDENCIES: ");
    for (var d in sources.depends) tmsg("d=" + d + ", sources.depends[d]=" + sources.depends[d]);
    tmsg("CALLBACKS: ");
    for (var c in sources.callbacks) tmsg("c=" + c + ", sources.callbacks[c]=" + sources.callbacks[c]);
} //test
//end;

/** nicked from http://www.safalra.com/programming/javascript/getdata.html 
 *  initializes a global GET_DATA array **/
var GET_DATA; //global
function initialiseGetData(){
    GET_DATA=new Array();
    var getDataString=new String(window.location);
    var questionMarkLocation=getDataString.search(/\?/);
    if (questionMarkLocation!=-1){
        getDataString=getDataString.substr(questionMarkLocation+1);
        var getDataArray=getDataString.split(/&/g);
        for (var i=0;i<getDataArray.length;i++){
            var nameValuePair=getDataArray[i].split(/=/);
            GET_DATA[unescape(nameValuePair[0])]=unescape(nameValuePair[1]);
        }
    }
}

/** if the get ?uri= is set, begin with that; otherwise, display the suggested
 * starting points **/
function AJAR_initialisePage()
{
    initialiseGetData();
    if (GET_DATA['uri']) {
        //clear browser
        var browser = document.getElementById('browser');
        emptyNode(browser);
	document.getElementById('UserURI').value = GET_DATA['uri'];
        GotoURIAndOpen(GET_DATA['uri']);
    } //go to an initial uri
} //initialize page


///and at the end
//addLoadEvent(function() { sources_request_new(tabulator_ns('')) });
//addLoadEvent(function () { sources_request_new(kb.sym('http://www.w3.org/People/Connolly/#me')); });
addLoadEvent(AJAR_initialisePage)
//ends
//sources_register_cb("<http://www.w3.org/People/Berners-Lee/card>", function() {alert("callback!"); });
