Jonas Kress (WMDE) has uploaded a new change for review.
https://gerrit.wikimedia.org/r/287987
Change subject: [WIP] Refactor Visual Editor
......................................................................
[WIP] Refactor Visual Editor
Change-Id: Id31057b8c5726ce5be45a6293c8ea90fda5ebb26
---
M index.html
M wikibase/queryService/ui/App.js
A wikibase/queryService/ui/visualEditor/SparqlQueryVerbalizer.js
M wikibase/queryService/ui/visualEditor/VisualEditor.js
M wikibase/tests/VisualEditor.html
5 files changed, 598 insertions(+), 437 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/wikidata/query/gui
refs/changes/87/287987/1
diff --git a/index.html b/index.html
index 8373f9a..0d25b5c 100644
--- a/index.html
+++ b/index.html
@@ -242,6 +242,7 @@
<script src="wikibase/queryService/ui/editor/Editor.js"></script>
<script
src="wikibase/queryService/ui/visualEditor/VisualEditor.js"></script>
<script
src="wikibase/queryService/ui/visualEditor/SparqlQuery.js"></script>
+ <script
src="wikibase/queryService/ui/visualEditor/SparqlQueryVerbalizer.js"></script>
<script
src="wikibase/queryService/ui/visualEditor/SelectorBox.js"></script>
<script src="wikibase/queryService/ui/QueryExampleDialog.js"></script>
<script
src="wikibase/queryService/ui/resultBrowser/helper/FormatterHelper.js"></script>
diff --git a/wikibase/queryService/ui/App.js b/wikibase/queryService/ui/App.js
index 8d5579d..8df5dd0 100644
--- a/wikibase/queryService/ui/App.js
+++ b/wikibase/queryService/ui/App.js
@@ -173,7 +173,7 @@
this._visualEditor = new
wikibase.queryService.ui.visualEditor.VisualEditor();
}
this._visualEditor.setChangeListener( function( ve ) {
- self._editor.setValue( ve.getQuery() );
+ self._editor.setValue( self._visualEditor.getQuery() );
} );
if ( this._editor ) {
diff --git a/wikibase/queryService/ui/visualEditor/SparqlQueryVerbalizer.js
b/wikibase/queryService/ui/visualEditor/SparqlQueryVerbalizer.js
new file mode 100644
index 0000000..72b9c51
--- /dev/null
+++ b/wikibase/queryService/ui/visualEditor/SparqlQueryVerbalizer.js
@@ -0,0 +1,567 @@
+var wikibase = wikibase || {};
+wikibase.queryService = wikibase.queryService || {};
+wikibase.queryService.ui = wikibase.queryService.ui || {};
+wikibase.queryService.ui.visualEditor = wikibase.queryService.ui.visualEditor
|| {};
+
+wikibase.queryService.ui.visualEditor.SparqlQueryVerbalizer = ( function( $,
wikibase ) {
+ 'use strict';
+
+ var FILTER_PREDICATES = {
+ 'http://www.w3.org/2000/01/rdf-schema#label': true,
+ 'http://schema.org/description': true,
+ 'http://www.bigdata.com/queryHints#optimizer': true,
+ 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type': true
+ };
+
+ /**
+ * A SPARQL verbalizer - creates verbal output of a SPARQL query
+ *
+ * @class wikibase.queryService.ui.visualEditor.SparqlQueryVerbalizer
+ * @license GNU GPL v2+
+ *
+ * @author Jonas Kress
+ * @constructor
+ * @param {function} i18n
+ * @param {wikibase.queryService.api.Wikibase} api
+ * @param {wikibase.queryService.ui.visualEditor.SelectorBox}
selectorBox
+ */
+ function SELF( i18n, api, selectorBox ) {
+
+ this._i18n = i18n;
+
+ this._api = api;
+ if ( !this._api ) {
+ this._api = new wikibase.queryService.api.Wikibase();
+ }
+
+ this._selectorBox = selectorBox;
+ if ( !this._selectorBox ) {
+ this._selectorBox = new
wikibase.queryService.ui.visualEditor.SelectorBox( this._api );
+ }
+ }
+
+ /**
+ * @property {wikibase.queryService.api.Wikibase}
+ * @private
+ */
+ SELF.prototype._api = null;
+
+ /**
+ * @property {wikibase.queryService.ui.visualEditor.SelectorBox}
+ * @private
+ */
+ SELF.prototype._selectorBox = null;
+
+ /**
+ * @property {function}
+ * @private
+ */
+ SELF.prototype._i18n = null;
+
+ /**
+ * @property {Function}
+ * @private
+ */
+ SELF.prototype._changeListener = null;
+
+ /**
+ * @property {wikibase.queryService.ui.visualEditor.SparqlQuery}
+ * @private
+ */
+ SELF.prototype._query = null;
+
+ /**
+ * Set the SPARQL query
+ *
+ * @param {wikibase.queryService.ui.visualEditor.SparqlQuery} query
+ */
+ SELF.prototype.setQuery = function( query ) {
+ this._query = query;
+ };
+
+ /**
+ * Set the change listener
+ *
+ * @param {Function} listener a function called when query changed
+ */
+ SELF.prototype.setChangeListener = function( listener ) {
+ this._changeListener = listener;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype.getHtml = function() {
+
+ this._triples = this._query.getTriples();
+ var subqueries = this._query.getSubQueries();
+ while ( subqueries.length > 0 ) {
+ var q = subqueries.pop();
+ this._triples = this._triples.concat( q.getTriples() );
+ subqueries.concat( q.getSubQueries() );
+ }
+
+ this._isSimpleMode = this._isSimpleQuery();
+
+ return this._getHtml();
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getHtml = function() {
+ var self = this;
+ var $html = $( '<div>' ), $find = this._getFindSection(), $show
= this._getShowSection(), $spacer = $(
+ '<div>' ).addClass( 'spacer' );
+
+ $html.append( $find, $spacer.clone(), $show, $spacer.clone(),
this._getLimitSection() );
+
+ $.each( this._triples, function( k, triple ) {
+ if ( self._isNotRelevant( triple.triple ) ) {
+ return;
+ }
+
+ if ( self._isInShowSection( triple.triple ) ) {
+ if ( $show.children().length > 1 ) {
+ $show.append( $( '<span>' ).text( ', '
) );
+ }
+ $show.append( self._getTripleHtml( triple ) );
+ return;
+ }
+ if ( $find.children().length > 1 ) {
+ if ( $find.children().length === 2 ) {
+ $find.append( $( '<span>' ).text(
self._i18n( 'with' ) + ' ' ) );
+ } else {
+ $find.append( $( '<span>' ).text(
self._i18n( 'and' ) + ' ' ) );
+ }
+ }
+ $find.append( self._getTripleHtml( triple ) );
+
+ } );
+
+ if ( $find.children().length === 1 ) {
+ $find.append( $( '<span>' ).text( this._i18n(
'anything' ) + ' ' ) );
+ }
+
+ return $html;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isNotRelevant = function( triple ) {
+
+ if ( FILTER_PREDICATES[triple.predicate] ) {
+ return true;
+ }
+
+ if ( this._isSimpleMode && this._isInShowSection( triple ) &&
+ ( this._query.hasVariable( triple.object ) ===
false &&
+ this._query.hasVariable(
triple.object + 'Label' ) === false ) ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isInShowSection = function( triple ) {
+
+ // Must match ?value wdt:Pxx ?item
+ if ( this._isVariable( triple.subject ) && this._isVariable(
triple.object ) ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isVariable = function( entity ) {
+ if ( typeof entity === 'string' && entity.startsWith( '?' ) ) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isSimpleQuery = function() {
+ var boundVariables = {};
+
+ var self = this;
+ $.each( this._triples, function( k, t ) {
+ // Must match ?value wdt:Pxx ?item
+ if ( self._isVariable( t.triple.subject ) &&
+ self._isVariable( t.triple.object ) ===
false ) {
+ boundVariables[t.triple.subject] = true;
+ }
+
+ } );
+
+ if ( Object.keys( boundVariables ).length > 1 ) {
+ return false;
+ }
+
+ return true;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getLimitSection = function() {
+ var $limitSection = $( '<div>' ), $limit = $( '<a
data-type="number">' ).attr( 'href', '#' )
+ .text( 'Limit' ).data( 'value',
this._query.getLimit() ), $value = $( '<span>' )
+ .text( this._query.getLimit() ?
this._query.getLimit() : '' );
+
+ var self = this;
+ this._selectorBox.add( $limit, function( value ) {
+ if ( value === '0' ) {
+ value = null;
+ }
+
+ $value.text( value ? value : '' );
+ self._query.setLimit( value );
+
+ if ( self._changeListener ) {
+ self._changeListener( self );
+ }
+ }, {
+ trash: function() {
+ self._query.setLimit( null );
+ $limit.data( 'value', '' );
+ $value.text( '' );
+ if ( self._changeListener ) {
+ self._changeListener( self );
+ }
+ return true;//close popover
+ }
+ } );
+
+ return $limitSection.append( $limit.append( ' ', $value ) );
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getFindSection = function() {
+ var $findSection = $( '<div>' );
+ // Show link
+ var $link = $( '<a class="btn btn-default">' ).text(
this._i18n( 'find' ) );
+ $link.attr( 'href', '#' ).prepend(
+ '<span class="glyphicon glyphicon-search"
aria-hidden="true"></span>', ' ' )
+ .tooltip( {
+ title: 'Click to add new item'
+ } ).attr( 'data-type', 'item' ).attr(
'data-auto_open', true );
+
+ // SelectorBox
+ var self = this;
+ this._selectorBox.add( $link, function( id, name ) {
+ var entity = 'http://www.wikidata.org/entity/' + id;//
FIXME technical debt
+
+ var variable = self._query.getBoundVariables().shift();
+ if ( !variable ) {
+ variable = '?' + '_' + name.replace( /(
|[^a-z0-9])/gi, '_' );
+ }
+
+ var prop = 'http://www.wikidata.org/prop/direct/P31';//
FIXME technical debt
+ var triple = self._query.addTriple( variable, prop,
entity, false );
+ if ( !self._query.hasVariable( variable ) ) {
+ self._query.addVariable( variable );
+ }
+
+ if ( $findSection.children().length >= 3 ) {
+ if ( $findSection.children().length === 3 ) {
+ $findSection.append( $( '<span>'
).text( self._i18n( 'with' ) + ' ' ) );
+ } else {
+ $findSection.append( $( '<span>'
).text( self._i18n( 'and' ) + ' ' ) );
+ }
+ }
+ $findSection.append( self._getTripleHtml( triple ) );
+
+ if ( self._changeListener ) {
+ self._changeListener( self );
+ }
+ } );
+
+ $findSection.append( $link, ' ' );
+ return $findSection;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getShowSection = function() {
+ var $showSection = $( '<div>' );
+ // Show link
+ var $link = $( '<a class="btn btn-default">' ).text(
this._i18n( 'show' ) );
+ $link.attr( 'href', '#' ).prepend(
+ '<span class="glyphicon glyphicon-eye-open"
aria-hidden="true"></span>', ' ' )
+ .tooltip( {
+ title: 'Click to add new property'
+ } ).attr( 'data-type', 'property' ).attr(
'data-auto_open', true );
+
+ // SelectorBox
+ var self = this;
+ this._selectorBox.add( $link, function( id, name ) {
+ var prop = 'http://www.wikidata.org/prop/direct/' +
id;// FIXME technical debt
+
+ var subject = self._query.getBoundVariables().shift();
+ if ( !subject ) {
+ return;
+ }
+ var variable2 = '?_' + name.replace( /( |[^a-z0-9])/gi,
'_' );// FIXME technical debt
+
+ var triple = self._query.addTriple( subject, prop,
variable2, true );
+ self._query.addVariable( variable2 );
+
+ if ( $showSection.children().length > 2 ) {
+ $showSection.append( $( '<span>' ).text( ', ' )
);
+ }
+ $showSection.append( self._getTripleHtml( triple ) );
+
+ if ( self._changeListener ) {
+ self._changeListener( self );
+ }
+ } );
+
+ return $showSection.append( $link, ' ' );
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isNotRelevant = function( triple ) {
+
+ if ( FILTER_PREDICATES[triple.predicate] ) {
+ return true;
+ }
+
+ if ( this._isSimpleMode && this._isInShowSection( triple ) &&
+ ( this._query.hasVariable( triple.object ) ===
false &&
+ this._query.hasVariable(
triple.object + 'Label' ) === false ) ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isInShowSection = function( triple ) {
+
+ // Must match ?value wdt:Pxx ?item
+ if ( this._isVariable( triple.subject ) && this._isVariable(
triple.object ) ) {
+ return true;
+ }
+
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getTripleHtml = function( triple ) {
+ var self = this;
+
+ var $triple = $( '<span>' );
+ $.each( triple.triple, function( k, entity ) {
+
+ if ( self._isSimpleMode && self._isVariable( entity ) )
{
+ return;
+ }
+
+ if ( entity.type && entity.type === 'path' ) {
+ $triple.append( self._getTripleEntityPathHtml(
entity, triple, k ), ' ' );
+ } else {
+ $triple.append( self._getTripleEntityHtml(
entity, triple, k ), ' ' );
+ }
+ } );
+
+ return $triple;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isVariable = function( entity ) {
+ if ( typeof entity === 'string' && entity.startsWith( '?' ) ) {
+ return true;
+ }
+ return false;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._isSimpleQuery = function() {
+ var boundVariables = {};
+
+ var self = this;
+ $.each( this._triples, function( k, t ) {
+ // Must match ?value wdt:Pxx ?item
+ if ( self._isVariable( t.triple.subject ) &&
+ self._isVariable( t.triple.object ) ===
false ) {
+ boundVariables[t.triple.subject] = true;
+ }
+
+ } );
+
+ if ( Object.keys( boundVariables ).length > 1 ) {
+ return false;
+ }
+
+ return true;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getTripleEntityPathHtml = function( path, triple ) {
+ var self = this, $path = $( '<span>' );
+ $.each( path.items, function( k, v ) {
+ if ( v.type && v.type === 'path' ) {
+ $path.append( self._getTripleEntityPathHtml( v,
triple ) );
+ return;
+ }
+
+ if ( k > 0 && path.pathType === '/' ) {
+ $path.append( ' ' + self._i18n( 'or' ) + ' ' +
self._i18n( 'subtype' ) + ' ' );
+ }
+ if ( path.pathType === '*' ) {
+ $path.append( ' ' + self._i18n( 'any' ) + ' ' );
+ }
+
+ // FIXME: Do not fake triple here
+ var newTriple = path.items.reduce( function( o, v, i ) {
+ o[i] = v;
+ return o;
+ }, {} );
+ newTriple = $.extend( newTriple, triple.triple );
+ triple = $.extend( {}, triple );
+ triple.triple = newTriple;
+
+ $path.append( self._getTripleEntityHtml( v, triple, k )
);
+
+ } );
+
+ return $path;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getTripleEntityHtml = function( entity, triple, key ) {
+ var $label = $( '<span>' );
+
+ var self = this;
+ this._getLabel( entity ).done( function( label, id,
description, type ) {
+ var $link = $( '<a>' ).attr( 'href', '#' );
+ $link.text( label );
+ $link.attr( 'data-type', type );
+ $link.attr( 'data-id', id );
+ $link.appendTo( $label );
+
+ $label.tooltip( {
+ 'title': '(' + id + ') ' + description
+ } );
+ $( $label ).on( 'show.bs.tooltip', function() {
+ if ( $( '.tooltip' ).is( ':visible' ) ) {
+ $( '.tooltip' ).not( this ).hide();
+ }
+ } );
+
+ //TODO: refactor method
+ self._selectorBox.add( $link, function( selectedId ) {
+ var newEntity = entity.replace( new RegExp( id
+ '$' ), '' ) + selectedId;// TODO: technical debt
+
+ $label.replaceWith( self._getTripleEntityHtml(
newEntity, triple, key ) );
+ triple.triple[key] = newEntity;
+
+ if ( self._changeListener ) {
+ self._changeListener();
+ }
+ }, {
+ trash: function() {
+ triple.remove();
+
+ var variable = triple.triple.object;
+ if ( triple.triple.object === entity ||
+ (
triple.triple.object.startsWith( '?' ) === false && triple.triple.predicate ===
entity ) ) {
+ variable =
triple.triple.subject;
+ }
+ if ( $label.parent().next( 'span'
).length ) {
+ $label.parent().next( 'span'
).remove();
+ } else {
+ $label.parent().prev( 'span'
).remove();
+ }
+ $label.parent().remove();
+
+ self._query.removeVariable( variable );
+ self._query.removeVariable( variable +
'Label' );
+
+ if ( self._changeListener ) {
+ self._changeListener();
+ }
+ $( '.tooltip' ).hide();
+ return true;//close popover
+ },
+ tag: function() {
+ if ( triple.triple.object.startsWith(
'?' ) ) {
+ self._query
+ .addVariable(
triple.triple.object +
+
'Label' );
+ } else {
+ self._query
+ .addVariable(
triple.triple.subject +
+
'Label' );
+ }
+ if ( self._changeListener ) {
+ self._changeListener();
+ }
+ return true;
+ }
+ }
+ );
+ } ).fail( function() {
+ $label.text( entity );
+ } );
+
+ return $label;
+ };
+
+ /**
+ * @private
+ */
+ SELF.prototype._getLabel = function( url ) {
+ var deferred = $.Deferred();
+
+ var entity = url.match( /(Q|P)([0-9]+)/ );// TODO: make use of
Rdf namespaces
+ if ( !entity ) {
+ return deferred.reject().promise();
+ }
+
+ var type = {
+ P: 'property',
+ Q: 'item'
+ };
+ type = type[entity[1]];
+ var term = entity[0];
+
+ this._api.searchEntities( term, type ).done( function( data ) {
+ $.each( data.search, function( key, value ) {
+ deferred.resolve( value.label, value.id,
value.description, type );
+ return false;
+ } );
+ } );
+
+ return deferred.promise();
+ };
+
+ return SELF;
+}( jQuery, wikibase ) );
diff --git a/wikibase/queryService/ui/visualEditor/VisualEditor.js
b/wikibase/queryService/ui/visualEditor/VisualEditor.js
index 507e016..c7931ab 100644
--- a/wikibase/queryService/ui/visualEditor/VisualEditor.js
+++ b/wikibase/queryService/ui/visualEditor/VisualEditor.js
@@ -6,14 +6,20 @@
wikibase.queryService.ui.visualEditor.VisualEditor = ( function( $, wikibase )
{
'use strict';
- var FILTER_PREDICATES = {
- 'http://www.w3.org/2000/01/rdf-schema#label': true,
- 'http://schema.org/description': true,
- 'http://www.bigdata.com/queryHints#optimizer': true,
- 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type': true
- };
+ var PACKAGE = wikibase.queryService.ui.visualEditor;
var I18N_PREFIX = 'wdqs-ve-';
+
+ var DEFAULT_LABELS = {
+ find: 'Find',
+ show: 'Show',
+ anything: 'anything',
+ 'with': 'with',
+ and: 'and',
+ any: 'any',
+ or: 'or',
+ subtype: 'subtype'
+ };
/**
* A visual SPARQL editor for the Wikibase query service
@@ -35,10 +41,12 @@
this._selectorBox = selectorBox;
if ( !this._selectorBox ) {
- this._selectorBox = new
wikibase.queryService.ui.visualEditor.SelectorBox( this._api );
+ this._selectorBox = new PACKAGE.SelectorBox( this._api
);
}
- this._query = new
wikibase.queryService.ui.visualEditor.SparqlQuery();
+ this._query = new PACKAGE.SparqlQuery();
+
+ this._verbalizer = new PACKAGE.SparqlQueryVerbalizer(
this._i18n, api, selectorBox );
}
/**
@@ -54,10 +62,10 @@
SELF.prototype._selectorBox = null;
/**
- * @property {Function}
+ * @property
{wikibase.queryService.ui.visualEditor.SparqlQueryVerbalizer}
* @private
*/
- SELF.prototype._changeListener = null;
+ SELF.prototype._verbalizer = null;
/**
* @property {wikibase.queryService.ui.visualEditor.SparqlQuery}
@@ -84,18 +92,15 @@
SELF.prototype._isSimpleMode = false;
/**
- * @property {Object}
* @private
*/
- SELF.prototype._labels = {
- find: 'Find',
- show: 'Show',
- anything: 'anything',
- 'with': 'with',
- and: 'and',
- any: 'any',
- or: 'or',
- subtype: 'subtype'
+ SELF.prototype._i18n = function( key ) {
+
+ if ( !$.i18n ) {
+ return DEFAULT_LABELS[key];
+ }
+
+ return $.i18n( I18N_PREFIX + key );
};
/**
@@ -165,17 +170,8 @@
* @param {jQuery} $element
*/
SELF.prototype.draw = function( $element ) {
-
- this._triples = this._query.getTriples();
- var subqueries = this._query.getSubQueries();
- while ( subqueries.length > 0 ) {
- var q = subqueries.pop();
- this._triples = this._triples.concat( q.getTriples() );
- subqueries.concat( q.getSubQueries() );
- }
-
- this._isSimpleMode = this._isSimpleQuery();
- $element.html( this._getHtml() );
+ this._verbalizer.setQuery( this._query );
+ $element.html( this._verbalizer.getHtml() );
};
/**
@@ -184,411 +180,7 @@
* @param {Function} listener a function called when query changed
*/
SELF.prototype.setChangeListener = function( listener ) {
- this._changeListener = listener;
- };
-
- /**
- * @private
- */
- SELF.prototype._i18n = function( key ) {
-
- if ( !$.i18n ) {
- return this._labels[key];
- }
-
- return $.i18n( I18N_PREFIX + key );
- };
-
- /**
- * @private
- */
- SELF.prototype._getHtml = function() {
- var self = this;
- var $html = $( '<div>' ), $find = this._getFindSection(), $show
= this._getShowSection(), $spacer = $(
- '<div>' ).addClass( 'spacer' );
-
- $html.append( $find, $spacer.clone(), $show, $spacer.clone(),
this._getLimitSection() );
-
- $.each( this._triples, function( k, triple ) {
- if ( self._isNotRelevant( triple.triple ) ) {
- return;
- }
-
- if ( self._isInShowSection( triple.triple ) ) {
- if ( $show.children().length > 1 ) {
- $show.append( $( '<span>' ).text( ', '
) );
- }
- $show.append( self._getTripleHtml( triple ) );
- return;
- }
- if ( $find.children().length > 1 ) {
- if ( $find.children().length === 2 ) {
- $find.append( $( '<span>' ).text(
self._i18n( 'with' ) + ' ' ) );
- } else {
- $find.append( $( '<span>' ).text(
self._i18n( 'and' ) + ' ' ) );
- }
- }
- $find.append( self._getTripleHtml( triple ) );
-
- } );
-
- if ( $find.children().length === 1 ) {
- $find.append( $( '<span>' ).text( this._i18n(
'anything' ) + ' ' ) );
- }
-
- return $html;
- };
-
- /**
- * @private
- */
- SELF.prototype._getLimitSection = function() {
- var $limitSection = $( '<div>' ), $limit = $( '<a
data-type="number">' ).attr( 'href', '#' )
- .text( 'Limit' ).data( 'value',
this._query.getLimit() ), $value = $( '<span>' )
- .text( this._query.getLimit() ?
this._query.getLimit() : '' );
-
- var self = this;
- this._selectorBox.add( $limit, function( value ) {
- if ( value === '0' ) {
- value = null;
- }
-
- $value.text( value ? value : '' );
- self._query.setLimit( value );
-
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- }, {
- trash: function() {
- self._query.setLimit( null );
- $limit.data( 'value', '' );
- $value.text( '' );
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- return true;//close popover
- }
- } );
-
- return $limitSection.append( $limit.append( ' ', $value ) );
- };
-
- /**
- * @private
- */
- SELF.prototype._getFindSection = function() {
- var $findSection = $( '<div>' );
- // Show link
- var $link = $( '<a class="btn btn-default">' ).text(
this._i18n( 'find' ) );
- $link.attr( 'href', '#' ).prepend(
- '<span class="glyphicon glyphicon-search"
aria-hidden="true"></span>', ' ' )
- .tooltip( {
- title: 'Click to add new item'
- } ).attr( 'data-type', 'item' ).attr(
'data-auto_open', true );
-
- // SelectorBox
- var self = this;
- this._selectorBox.add( $link, function( id, name ) {
- var entity = 'http://www.wikidata.org/entity/' + id;//
FIXME technical debt
-
- var variable = self._query.getBoundVariables().shift();
- if ( !variable ) {
- variable = '?' + '_' + name.replace( /(
|[^a-z0-9])/gi, '_' );
- }
-
- var prop = 'http://www.wikidata.org/prop/direct/P31';//
FIXME technical debt
- var triple = self._query.addTriple( variable, prop,
entity, false );
- if ( !self._query.hasVariable( variable ) ) {
- self._query.addVariable( variable );
- }
-
- if ( $findSection.children().length >= 3 ) {
- if ( $findSection.children().length === 3 ) {
- $findSection.append( $( '<span>'
).text( self._i18n( 'with' ) + ' ' ) );
- } else {
- $findSection.append( $( '<span>'
).text( self._i18n( 'and' ) + ' ' ) );
- }
- }
- $findSection.append( self._getTripleHtml( triple ) );
-
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- } );
-
- return $findSection.append( $link, ' ' );
- };
-
- /**
- * @private
- */
- SELF.prototype._getShowSection = function() {
- var $showSection = $( '<div>' );
- // Show link
- var $link = $( '<a class="btn btn-default">' ).text(
this._i18n( 'show' ) );
- $link.attr( 'href', '#' ).prepend(
- '<span class="glyphicon glyphicon-eye-open"
aria-hidden="true"></span>', ' ' )
- .tooltip( {
- title: 'Click to add new property'
- } ).attr( 'data-type', 'property' ).attr(
'data-auto_open', true );
-
- // SelectorBox
- var self = this;
- this._selectorBox.add( $link, function( id, name ) {
- var prop = 'http://www.wikidata.org/prop/direct/' +
id;// FIXME technical debt
-
- var subject = self._query.getBoundVariables().shift();
- if ( !subject ) {
- return;
- }
- var variable2 = '?_' + name.replace( /( |[^a-z0-9])/gi,
'_' );// FIXME technical debt
-
- var triple = self._query.addTriple( subject, prop,
variable2, true );
- self._query.addVariable( variable2 );
-
- if ( $showSection.children().length > 2 ) {
- $showSection.append( $( '<span>' ).text( ', ' )
);
- }
- $showSection.append( self._getTripleHtml( triple ) );
-
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- } );
-
- return $showSection.append( $link, ' ' );
- };
-
- /**
- * @private
- */
- SELF.prototype._isNotRelevant = function( triple ) {
-
- if ( FILTER_PREDICATES[triple.predicate] ) {
- return true;
- }
-
- if ( this._isSimpleMode && this._isInShowSection( triple ) &&
- ( this._query.hasVariable( triple.object ) ===
false &&
- this._query.hasVariable(
triple.object + 'Label' ) === false ) ) {
- return true;
- }
-
- return false;
- };
-
- /**
- * @private
- */
- SELF.prototype._isInShowSection = function( triple ) {
-
- // Must match ?value wdt:Pxx ?item
- if ( this._isVariable( triple.subject ) && this._isVariable(
triple.object ) ) {
- return true;
- }
-
- return false;
- };
-
- /**
- * @private
- */
- SELF.prototype._getTripleHtml = function( triple ) {
- var self = this;
-
- var $triple = $( '<span>' );
- $.each( triple.triple, function( k, entity ) {
-
- if ( self._isSimpleMode && self._isVariable( entity ) )
{
- return;
- }
-
- if ( entity.type && entity.type === 'path' ) {
- $triple.append( self._getTripleEntityPathHtml(
entity, triple, k ), ' ' );
- } else {
- $triple.append( self._getTripleEntityHtml(
entity, triple, k ), ' ' );
- }
- } );
-
- return $triple;
- };
-
- /**
- * @private
- */
- SELF.prototype._isVariable = function( entity ) {
- if ( typeof entity === 'string' && entity.startsWith( '?' ) ) {
- return true;
- }
- return false;
- };
-
- /**
- * @private
- */
- SELF.prototype._isSimpleQuery = function() {
- var boundVariables = {};
-
- var self = this;
- $.each( this._triples, function( k, t ) {
- // Must match ?value wdt:Pxx ?item
- if ( self._isVariable( t.triple.subject ) &&
- self._isVariable( t.triple.object ) ===
false ) {
- boundVariables[t.triple.subject] = true;
- }
-
- } );
-
- if ( Object.keys( boundVariables ).length > 1 ) {
- return false;
- }
-
- return true;
- };
-
- /**
- * @private
- */
- SELF.prototype._getTripleEntityPathHtml = function( path, triple ) {
- var self = this, $path = $( '<span>' );
- $.each( path.items, function( k, v ) {
- if ( v.type && v.type === 'path' ) {
- $path.append( self._getTripleEntityPathHtml( v,
triple ) );
- return;
- }
-
- if ( k > 0 && path.pathType === '/' ) {
- $path.append( ' ' + self._i18n( 'or' ) + ' ' +
self._i18n( 'subtype' ) + ' ' );
- }
- if ( path.pathType === '*' ) {
- $path.append( ' ' + self._i18n( 'any' ) + ' ' );
- }
-
- // FIXME: Do not fake triple here
- var newTriple = path.items.reduce( function( o, v, i ) {
- o[i] = v;
- return o;
- }, {} );
- newTriple = $.extend( newTriple, triple.triple );
- triple = $.extend( {}, triple );
- triple.triple = newTriple;
-
- $path.append( self._getTripleEntityHtml( v, triple, k )
);
-
- } );
-
- return $path;
- };
-
- /**
- * @private
- */
- SELF.prototype._getTripleEntityHtml = function( entity, triple, key ) {
- var $label = $( '<span>' );
-
- var self = this;
- this._getLabel( entity ).done( function( label, id,
description, type ) {
- var $link = $( '<a>' ).attr( 'href', '#' );
- $link.text( label );
- $link.attr( 'data-type', type );
- $link.attr( 'data-id', id );
- $link.appendTo( $label );
-
- $label.tooltip( {
- 'title': '(' + id + ') ' + description
- } );
- $( $label ).on( 'show.bs.tooltip', function() {
- if ( $( '.tooltip' ).is( ':visible' ) ) {
- $( '.tooltip' ).not( this ).hide();
- }
- } );
-
- //TODO: refactor method
- self._selectorBox.add( $link, function( selectedId ) {
- var newEntity = entity.replace( new RegExp( id
+ '$' ), '' ) + selectedId;// TODO: technical debt
-
- $label.replaceWith( self._getTripleEntityHtml(
newEntity, triple, key ) );
- triple.triple[key] = newEntity;
-
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- }, {
- trash: function() {
- triple.remove();
-
- var variable = triple.triple.object;
- if ( triple.triple.object === entity ||
- (
triple.triple.object.startsWith( '?' ) === false && triple.triple.predicate ===
entity ) ) {
- variable =
triple.triple.subject;
- }
- if ( $label.parent().next( 'span'
).length ) {
- $label.parent().next( 'span'
).remove();
- } else {
- $label.parent().prev( 'span'
).remove();
- }
- $label.parent().remove();
-
- self._query.removeVariable( variable );
- self._query.removeVariable( variable +
'Label' );
-
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- $( '.tooltip' ).hide();
- return true;//close popover
- },
- tag: function() {
- if ( triple.triple.object.startsWith(
'?' ) ) {
- self._query
- .addVariable(
triple.triple.object +
-
'Label' );
- } else {
- self._query
- .addVariable(
triple.triple.subject +
-
'Label' );
- }
- if ( self._changeListener ) {
- self._changeListener( self );
- }
- return true;
- }
- }
- );
- } ).fail( function() {
- $label.text( entity );
- } );
-
- return $label;
- };
-
- /**
- * @private
- */
- SELF.prototype._getLabel = function( url ) {
- var deferred = $.Deferred();
-
- var entity = url.match( /(Q|P)([0-9]+)/ );// TODO: make use of
Rdf namespaces
- if ( !entity ) {
- return deferred.reject().promise();
- }
-
- var type = {
- P: 'property',
- Q: 'item'
- };
- type = type[entity[1]];
- var term = entity[0];
-
- this._api.searchEntities( term, type ).done( function( data ) {
- $.each( data.search, function( key, value ) {
- deferred.resolve( value.label, value.id,
value.description, type );
- return false;
- } );
- } );
-
- return deferred.promise();
+ this._verbalizer.setChangeListener( listener );
};
return SELF;
diff --git a/wikibase/tests/VisualEditor.html b/wikibase/tests/VisualEditor.html
index c04cf8f..2c4c557 100644
--- a/wikibase/tests/VisualEditor.html
+++ b/wikibase/tests/VisualEditor.html
@@ -25,6 +25,7 @@
<script src="../queryService/ui/visualEditor/VisualEditor.js"></script>
<script src="../queryService/ui/visualEditor/SelectorBox.js"></script>
<script src="../queryService/ui/visualEditor/SparqlQuery.js"></script>
+ <script
src="../queryService/ui/visualEditor/SparqlQueryVerbalizer.js"></script>
<!-- Tests -->
<script
src="queryService/ui/visualEditor/VisualEditor.test.js"></script>
<script src="queryService/ui/visualEditor/SparqlQuery.test.js"></script>
--
To view, visit https://gerrit.wikimedia.org/r/287987
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Id31057b8c5726ce5be45a6293c8ea90fda5ebb26
Gerrit-PatchSet: 1
Gerrit-Project: wikidata/query/gui
Gerrit-Branch: master
Gerrit-Owner: Jonas Kress (WMDE) <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits