You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by ij...@apache.org on 2013/11/07 17:59:55 UTC
svn commit: r1539711 [5/5] - in /jena/branches/jena-fuseki-new-ui/pages: ./
admin/ assets/ css/ images/ js/ js/app/ js/app/controllers/ js/app/models/
js/lib/ js/lib/codemirror/
Added: jena/branches/jena-fuseki-new-ui/pages/js/lib/qonsole.js
URL: http://svn.apache.org/viewvc/jena/branches/jena-fuseki-new-ui/pages/js/lib/qonsole.js?rev=1539711&view=auto
==============================================================================
--- jena/branches/jena-fuseki-new-ui/pages/js/lib/qonsole.js (added)
+++ jena/branches/jena-fuseki-new-ui/pages/js/lib/qonsole.js Thu Nov 7 16:59:54 2013
@@ -0,0 +1,678 @@
+/* Copyright (c) 2012-2013 Epimorphics Ltd. Released under Apache License 2.0 http://www.apache.org/licenses/ */
+
+var qonsole = function() {
+ "use strict";
+
+ /* JsLint */
+ /*global sprintf, testCSS, loadConfig, bindEvents, $, onConfigLoaded, updatePrefixDeclaration, _,
+ showCurrentQuery, setCurrentEndpoint, setCurrentFormat, elementVisible, runQuery, onLookupPrefix,
+ startTimingResults, onAddPrefix, initQuery, CodeMirror, renderCurrentPrefixes, onQuerySuccess,
+ onQueryFail, ajaxDataType, checkForceTextFormat, resetResults, checkForceJsonP, XMLSerializer,
+ showTableResult, showCodeMirrorResult
+ */
+
+ /* --- module vars --- */
+ /** The loaded configuration */
+ var _config = {};
+ var _query_editor = null;
+ var _startTime = 0;
+ var _outstandingQueries = 0;
+
+ /* --- utils --- */
+
+ /** Return the string representation of the given XML value, which may be a string or a DOM object */
+ var xmlToString = function( xmlData ) {
+ var xs = _.isString( xmlData ) ? xmlData : null;
+
+ if (!xs && window.ActiveXObject && xmlData.xml) {
+ xs = xmlData.xml;
+ }
+
+ if (!xs) {
+ xs = new XMLSerializer().serializeToString( xmlData );
+ }
+
+ return xs;
+ };
+
+ /** Browser sniffing */
+ var isOpera = function() {return !!(window.opera && window.opera.version);}; // Opera 8.0+
+ var isFirefox = function() {return testCSS('MozBoxSizing');}; // FF 0.8+
+ var isSafari = function() {return Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;}; // At least Safari 3+: "[object HTMLElementConstructor]"
+ var isChrome = function() {return !isSafari() && testCSS('WebkitTransform');}; // Chrome 1+
+ var isIE = function() {return /*@cc_on!@*/false || testCSS('msTransform');}; // At least IE6
+
+ var testCSS = function(prop) {
+ return document.documentElement.style.hasOwnProperty( prop );
+ };
+
+ /* Shorten a URI to qname form, if possible */
+ var toQName = function( prefixes, uri ) {
+ var result = uri, qname, u = uri;
+
+ if (u.substring( 0, 1 ) === '<') {
+ u = u.substring( 1, u.length - 1 );
+ }
+
+ $.each( prefixes, function( prefix, prefURI ) {
+ if (u.indexOf( prefURI ) === 0) {
+ qname = sprintf( "%s:%s", prefix, u.substring( prefURI.length ) );
+
+ if (qname.length < result.length) {
+ result = qname;
+ }
+ }
+ } );
+ return result;
+ };
+
+ /* --- application code --- */
+
+ /** Initialisation - only called once */
+ var init = function( config ) {
+ loadConfig( config );
+ bindEvents();
+
+ $.ajaxSetup( {
+ converters: {"script json": true}
+ } );
+ };
+
+ /** Load the configuration definition */
+ var loadConfig = function( config ) {
+ if (config.configURL) {
+ $.getJSON( config.configURL, onConfigLoaded );
+ }
+ else {
+ onConfigLoaded( config );
+ }
+ };
+
+ /** Return the current config object */
+ var config = function() {
+ return _config;
+ };
+
+ /** Bind events that we want to manage */
+ var bindEvents = function() {
+ $("ul.prefixes").on( "click", "a.btn", function( e ) {
+ var elem = $(e.currentTarget);
+ updatePrefixDeclaration( $.trim( elem.text() ), elem.data( "uri" ), !elem.is(".active") );
+ } );
+ $("ul.examples").on( "click", "a", function( e ) {
+ var elem = $(e.currentTarget);
+ $("ul.examples a").removeClass( "active" );
+ _.defer( function() {showCurrentQuery();} );
+ } );
+ $(".endpoints").on( "click", "a", function( e ) {
+ var elem = $(e.currentTarget);
+ setCurrentEndpoint( $.trim( elem.text() ) );
+ } );
+ $("ul.formats").on( "click", "a", function( e ) {
+ var elem = $(e.currentTarget);
+ setCurrentFormat( elem.data( "value" ), $.trim( elem.text() ) );
+ } );
+
+ $("a.run-query").on( "click", runQuery );
+
+ $(document)
+ .ajaxStart(function() {
+ elementVisible( ".loadingSpinner", true );
+ startTimingResults();
+ })
+ .ajaxStop(function() {
+ elementVisible( ".loadingSpinner", false );
+ });
+
+ // dialogue events
+ $("#prefixEditor").on( "click", "#lookupPrefix", onLookupPrefix )
+ .on( "keyup", "#inputPrefix", function( e ) {
+ var elem = $(e.currentTarget);
+ $("#lookupPrefix span").text( sprintf( "'%s'", elem.val() ));
+ } );
+ $("#addPrefix").on( "click", onAddPrefix );
+ };
+
+ /** List the current defined prefixes from the config */
+ var initPrefixes = function( config ) {
+ var prefixAdd = $("ul.prefixes li:last" );
+ $.each( config.prefixes, function( key, value ) {
+ var html = sprintf( "<li><a class='btn btn-custom2 btn-sm active' data-toggle='button' data-uri='%s'>%s</a></li>", value, key );
+ $(html).insertBefore( prefixAdd);
+ } );
+ };
+
+ /** List the example queries from the config */
+ var initExamples = function( config ) {
+ var examples = $("ul.examples");
+
+ examples.empty();
+
+ $.each( config.queries, function( i, queryDesc ) {
+ var html = sprintf( "<li><a class='btn btn-custom2 btn-sm' data-toggle='button'>%s</a></li>",
+ queryDesc.name );
+ examples.append( html );
+
+ if (queryDesc.queryURL) {
+ loadRemoteQuery( queryDesc.name, queryDesc.queryURL );
+ }
+ } );
+
+ setFirstQueryActive();
+ };
+
+ /** Set the default active query */
+ var setFirstQueryActive = function() {
+ if (_outstandingQueries === 0) {
+ $("ul.examples").find("a").first().addClass( "active" );
+ showCurrentQuery();
+ }
+ };
+
+ /** Load a remote query */
+ var loadRemoteQuery = function( name, url ) {
+ _outstandingQueries++;
+
+ var options = {
+ success: function( data, xhr ) {
+ namedExample( name ).query = data;
+
+ _outstandingQueries--;
+ setFirstQueryActive();
+ },
+ failure: function() {
+ namedExample( name ).query = "Not found: " + url;
+
+ _outstandingQueries--;
+ setFirstQueryActive();
+ },
+ dataType: "text"
+ };
+
+ $.ajax( url, options );
+ };
+
+ /** Set up the drop-down list of end-points */
+ var initEndpoints = function( config ) {
+ var endpoints = $("ul.endpoints");
+ endpoints.empty();
+
+ $.each( config.endpoints, function( key, url ) {
+ var html = sprintf( "<li role='presentation'><a role='menuitem' tabindex='-1' href='#'>%s</a></li>",
+ url );
+ endpoints.append( html );
+ } );
+
+ setCurrentEndpoint( config.endpoints["default"] );
+ };
+
+ /** Successfully loaded the configuration */
+ var onConfigLoaded = function( config, status, jqXHR ) {
+ _config = config;
+ initPrefixes( config );
+ initExamples( config );
+ initEndpoints( config );
+ };
+
+ /** Set the current endpoint text */
+ var setCurrentEndpoint = function( url ) {
+ $("[id=sparqlEndpoint]").val( url );
+ };
+
+ /** Return the current endpoint text */
+ var currentEndpoint = function( url ) {
+ return $("[id=sparqlEndpoint]").val();
+ };
+
+ /** Return the query definition with the given name */
+ var namedExample = function( name ) {
+ return _.find( config().queries, function( ex ) {return ex.name === name;} );
+ };
+
+ /** Return the currently active named example */
+ var currentNamedExample = function() {
+ return namedExample( $.trim( $("ul.examples a.active").first().text() ) );
+ };
+
+ /** Return the DOM node representing the query editor */
+ var queryEditor = function() {
+ if (!_query_editor) {
+ _query_editor = new CodeMirror( $("#query-edit-cm").get(0), {
+ lineNumbers: true,
+ mode: "sparql"
+ } );
+ }
+ return _query_editor;
+ };
+
+ /** Return the current value of the query edit area */
+ var currentQueryText = function() {
+ return queryEditor().getValue();
+ };
+
+ /** Set the value of the query edit area */
+ var setCurrentQueryText = function( text ) {
+ queryEditor().setValue( text );
+ };
+
+ /** Display the given query, with the currently defined prefixes */
+ var showCurrentQuery = function() {
+ var query = currentNamedExample();
+ displayQuery( query );
+ };
+
+ /** Display the given query */
+ var displayQuery = function( query ) {
+ if (query) {
+ var queryBody = query.query ? query.query : query;
+ var prefixes = assemblePrefixes( queryBody, query.prefixes )
+
+ var q = sprintf( "%s\n\n%s", renderPrefixes( prefixes ), stripLeader( queryBody ) );
+ setCurrentQueryText( q );
+
+ syncPrefixButtonState( prefixes );
+ }
+ };
+
+ /** Return the currenty selected output format */
+ var selectedFormat = function() {
+ return $("a.display-format").data( "value" );
+ };
+
+ /** Update the user's format selection */
+ var setCurrentFormat = function( val, label ) {
+ $("a.display-format").data( "value", val ).find("span").text( label );
+ };
+
+ /** Assemble the set of prefixes to use when initially rendering the query */
+ var assemblePrefixes = function( queryBody, queryDefinitionPrefixes ) {
+ if (queryBody.match( /^prefix/ )) {
+ // strategy 1: there are prefixes encoded in the query body
+ return assemblePrefixesFromQuery( queryBody );
+ }
+ else if (queryDefinitionPrefixes) {
+ // strategy 2: prefixes given in query def
+ return _.map( queryDefinitionPrefixes, function( prefixName ) {
+ return {name: prefixName, uri: config().prefixes[prefixName] };
+ } );
+ }
+ else {
+ return assembleCurrentPrefixes();
+ }
+ };
+
+ /** Return an array comprising the currently selected prefixes */
+ var assembleCurrentPrefixes = function() {
+ var l = $("ul.prefixes a.active" ).map( function( i, elt ) {
+ return {name: $.trim( $(elt).text() ),
+ uri: $(elt).data( "uri" )};
+ } );
+ return $.makeArray(l);
+ };
+
+ /** Return an array of the prefixes parsed from the given query body */
+ var assemblePrefixesFromQuery = function( queryBody ) {
+ var leader = queryLeader( queryBody )[0].trim();
+ var pairs = _.compact( leader.split( "prefix" ) );
+ var prefixes = [];
+
+ _.each( pairs, function( pair ) {
+ var m = pair.match( "^\\s*(\\w+)\\s*:\\s*<([^>]*)>\\s*$" );
+ prefixes.push( {name: m[1], uri: m[2]} );
+ } );
+
+ return prefixes;
+ };
+
+ /** Ensure that the prefix buttons are in sync with the prefixes used in a new query */
+ var syncPrefixButtonState = function( prefixes ) {
+ $("ul.prefixes a" ).each( function( i, elt ) {
+ var name = $.trim( $(elt).text() );
+
+ if (_.find( prefixes, function(p) {return p.name === name;} )) {
+ $(elt).addClass( "active" );
+ }
+ else {
+ $(elt).removeClass( "active" );
+ }
+ } );
+ };
+
+ /** Split a query into leader (prefixes and leading blank lines) and body */
+ var queryLeader = function( query ) {
+ var pattern = /(prefix[^>]+>[\s\n]*)/;
+ var queryBody = query;
+ var i = 0;
+ var m = queryBody.match( pattern );
+
+ while (m) {
+ i += m[1].length;
+ queryBody = queryBody.substring( i );
+ m = queryBody.match( pattern );
+ }
+
+ return [query.substring( 0, query.length - queryBody.length), queryBody];
+ };
+
+ /** Remove the query leader */
+ var stripLeader = function( query ) {
+ return queryLeader( query )[1];
+ };
+
+ /** Return a string comprising the given prefixes */
+ var renderPrefixes = function( prefixes ) {
+ return _.map( prefixes, function( p ) {
+ return sprintf( "prefix %s: <%s>", p.name, p.uri );
+ } ).join( "\n" );
+ };
+
+ /** Add or remove the given prefix declaration from the current query */
+ var updatePrefixDeclaration = function( prefix, uri, added ) {
+ var query = currentQueryText();
+ var lines = query.split( "\n" );
+ var pattern = new RegExp( "^prefix +" + prefix + ":");
+ var found = false;
+ var i;
+
+ for (i = 0; !found && i < lines.length; i++) {
+ found = lines[i].match( pattern );
+ if (found && !added) {
+ lines.splice( i, 1 );
+ }
+ }
+
+ if (!found && added) {
+ for (i = 0; i < lines.length; i++) {
+ if (!lines[i].match( /^prefix/ )) {
+ lines.splice( i, 0, sprintf( "prefix %s: <%s>", prefix, uri ) );
+ break;
+ }
+ }
+ }
+
+ setCurrentQueryText( lines.join( "\n" ) );
+ };
+
+ /** Perform the query */
+ var runQuery = function( e ) {
+ e.preventDefault();
+ resetResults();
+
+ var url = currentEndpoint();
+ var query = currentQueryText();
+ var format = selectedFormat();
+
+ var options = {
+ data: {query: query, output: format},
+ success: function( data, xhr ) {
+ onQuerySuccess( data, format );
+ },
+ error: onQueryFail,
+ dataType: ajaxDataType( format )
+ };
+
+ checkForceTextFormat( format, options );
+ checkForceJsonP( options, format );
+
+ $.ajax( url, options );
+ };
+
+ var ajaxDataType = function( format ) {
+ return {
+ tsv: "html",
+ csv: "html",
+ }[format] || format;
+ };
+
+
+ /** Hide or reveal an element using Bootstrap .hidden class */
+ var elementVisible = function( elem, visible ) {
+ if (visible) {
+ $(elem).removeClass( "hidden" );
+ }
+ else {
+ $(elem).addClass( "hidden" );
+ }
+ };
+
+ /** Prepare to show query time taken */
+ var startTimingResults = function() {
+ _startTime = new Date().getTime();
+ elementVisible( ".timeTaken" );
+ };
+
+ /** Show results count and time */
+ var showResultsTimeAndCount = function( count ) {
+ var duration = new Date().getTime() - _startTime;
+ var ms = duration % 1000;
+ duration = Math.floor( duration / 1000 );
+ var s = duration % 60;
+ var m = Math.floor( duration / 60 );
+ var suffix = (count !== 1) ? "s" : "";
+
+ var html = sprintf( "%s result%s in %d min %d.%03d s", count, suffix, m, s, ms );
+
+ $(".timeTaken").html( html );
+ elementVisible( ".timeTaken", true );
+ };
+
+ /** For display purposes, we want the browser to not parse some formats for us */
+ var checkForceTextFormat = function( format, options ) {
+ if (format === "xml" || format === "json"){
+ // options.data["force-accept"] = "text/plain";
+ }
+ };
+
+ /** Can we use CORS, or do we need to force the use of JsonP? */
+ var checkForceJsonP = function( options, format ) {
+ if (isIE()) {
+ // TODO: currently disabled, because jQuery insists on parsing non-Json
+ // content as JSON. So rendering XML via JSONp causes an error at the moment
+ // options.dataType = "jsonp " + ajaxDataType( format );
+ }
+ };
+
+ /** Reset the results display */
+ var resetResults = function() {
+ $("#results").empty();
+ elementVisible( ".timeTaken", false );
+ };
+
+ /** Report query failure */
+ var onQueryFail = function( jqXHR, textStatus, errorThrown ) {
+ showResultsTimeAndCount( 0 );
+ var text = jqXHR.valueOf().responseText || sprintf( "Sorry, that didn't work because: '%s'", jqXHR.valueOf().statusText );
+ $("#results").html( sprintf( "<pre class='text-danger'>%s</pre>", _.escape(text) ) );
+ };
+
+ /** Return options for display query results as text */
+ var showTextResult = function( data ) {
+ return {
+ count: data.split('\n').length - 5,
+ data: data,
+ mime: "text/plain"
+ };
+ };
+
+ /** Return options for displaying query results as JSON */
+ var showJsonResult = function( data ) {
+ var count, json;
+
+ if (_.isString( data )) {
+ json = data;
+ data = JSON.parse(data);
+ }
+ else {
+ // en bas le Internet Explorer
+ json = JSON.stringify( data, null, 2 );
+ }
+
+ return {
+ count: data.results.bindings.length,
+ data: json,
+ mime: "application/json"
+ };
+ };
+
+ /** Return options for displaying results as XML */
+ var showXmlResult = function( data ) {
+ var count, xml;
+
+ if (_.isString( data )) {
+ xml = data;
+ data = $.parseXML( data );
+ }
+ else {
+ xml = xmlToString( data );
+ }
+
+ return {
+ count: $( data ).find("results").children().length,
+ data: xml,
+ mime: "application/xml"
+ };
+ };
+
+ /** Query succeeded - use display type to determine how to render */
+ var onQuerySuccess = function( data, format ) {
+ var options = null;
+
+ switch (format) {
+ case "text":
+ options = showTextResult( data );
+ break;
+ case "json":
+ options = showJsonResult( data );
+ break;
+ case "xml":
+ options = showXmlResult( data );
+ break;
+ case "tsv":
+ showTableResult( data );
+ break;
+ }
+
+ if (options) {
+ showCodeMirrorResult( options );
+ }
+ };
+
+ /** Show the given text value in a CodeMirror block with the given language mode */
+ var showCodeMirrorResult = function( options ) {
+ showResultsTimeAndCount( options.count );
+
+ var editor = new CodeMirror( $("#results").get(0), {
+ value: options.data,
+ mode: options.mime,
+ lineNumbers: true,
+ extraKeys: {"Ctrl-Q": function(cm){ cm.foldCode(cm.getCursor()); }},
+ gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
+ foldGutter: true,
+ readOnly: true
+ } );
+ };
+
+ /** Format a value for display in the table view */
+ var dataTableValue = function( v ) {
+ var f, parts;
+
+ if (_.isNumber( v )) {
+ f = parseFloat( v );
+ }
+ else if (v.match( /\^\^/ )) {
+ parts = v.match( /^"*([^\\^\\""]*)"*\^\^<*(.*)>*$/m )
+ f = sprintf( "<span title='Type: %s'>%s</span>", parts[2], parts[1])
+ }
+ else if (v.match( /@/ )) {
+ parts = v.match( /^"(.*)"@([^@]*)/ );
+ f = sprintf( "<span title='Language: %s'>%s</span>", parts[2], parts[1] );
+ }
+ else {
+ f = toQName( config().prefixes, v );
+
+ if (f.match( /^</ )) {
+ f = f.slice( 1, -1 );
+ }
+
+ f = _.escape(f );
+ }
+
+ return f;
+ };
+
+ /** Show the result using jQuery dataTables */
+ var showTableResult = function( data ) {
+ var lines = _.compact(data.split( "\n" ));
+ showResultsTimeAndCount( lines.length - 1 );
+
+ var aoColumns = _.map( lines.shift().split("\t"), function( header) {
+ return {sTitle: header};
+ } );
+ var aaData = _.map( lines, function( line ) {
+ var values = _.flatten( [line.split("\t")] );
+ return _.map( values, dataTableValue );
+ } );
+
+ $("#results").empty()
+ .append( '<div class="auto-overflow"></div>')
+ .children()
+ .append( '<table cellpadding="0" cellspacing="0" border="0" class="display"></table>' )
+ .children()
+ .dataTable( {aoColumns: aoColumns,
+ aaData: aaData
+ } );
+ };
+
+ /** Lookup a prefix on prefix.cc */
+ var onLookupPrefix = function( e ) {
+ e.preventDefault();
+
+ var prefix = $.trim( $("#inputPrefix").val() );
+ $("#inputURI").val("");
+
+ if (prefix) {
+ $.getJSON( sprintf( "http://prefix.cc/%s.file.json", prefix ),
+ function( data ) {
+ $("#inputURI").val( data[prefix] );
+ }
+ );
+ }
+ };
+
+ /** User wishes to add the prefix */
+ var onAddPrefix = function( e ) {
+ var prefix = $.trim( $("#inputPrefix").val() );
+ var uri = $.trim( $("#inputURI").val() );
+
+ if (uri) {
+ _config.prefixes[prefix] = uri;
+ }
+ else {
+ delete _config.prefixes[prefix];
+ }
+
+ // remember the state of current user selections, then re-create the list
+ var selections = {};
+ $("ul.prefixes a.btn").each( function( i, a ) {selections[$(a).text()] = $(a).hasClass("active");} );
+
+ $("ul.prefixes li[class!=keep]").remove();
+ initPrefixes( _config );
+
+ // restore selections state
+ $.each( selections, function( k, v ) {
+ if (!v) {
+ $(sprintf("ul.prefixes a.btn:contains('%s')", k)).removeClass("active");
+ }
+ } );
+
+ var lines = currentQueryText().split("\n");
+ lines = _.reject( lines, function( line ) {return line.match( /^prefix/ );} );
+ var q = sprintf( "%s\n%s", renderCurrentPrefixes(), lines.join( "\n" ) );
+ setCurrentQueryText( q );
+ };
+
+ return {
+ init: init
+ };
+}();
+
Modified: jena/branches/jena-fuseki-new-ui/pages/validation.html
URL: http://svn.apache.org/viewvc/jena/branches/jena-fuseki-new-ui/pages/validation.html?rev=1539711&r1=1539710&r2=1539711&view=diff
==============================================================================
--- jena/branches/jena-fuseki-new-ui/pages/validation.html (original)
+++ jena/branches/jena-fuseki-new-ui/pages/validation.html Thu Nov 7 16:59:54 2013
@@ -34,7 +34,7 @@
<li class=""><a href="documentation.html">Documentation</a></li>
</ul>
<ul class="nav navbar-nav navbar-right">
- <li><img src="assets/jena-logo-notext-small.png" alt="Apache Jena logo" title="Apache Jena" /></li>
+ <li><img src="images/jena-logo-notext-small.png" alt="Apache Jena logo" title="Apache Jena" /></li>
</ul>
</div><!-- /.navbar-collapse -->
</div><!-- /row -->