Finished vc builder (although the value change needs finetuning and there are no tests for the menu)
diff --git a/Gruntfile.js b/Gruntfile.js
index 7e9edf8..acf027f 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -11,9 +11,7 @@
* for assets.
* http://yui.github.io/yuidoc/
* TODO: Use https://www.npmjs.com/package/grunt-contrib-yuidoc
- * TODO: Use RequireJS
- * http://addyosmani.com/writing-modular-js/
- * http://qnundrum.com/question/393866
+ * TODO: Implement a LaTeX generator for a pdf of the dokumentation
*/
module.exports = function(grunt) {
grunt.initConfig({
@@ -101,7 +99,9 @@
files: ['dev/scss/{util,base,fonts,kalamar,media}.scss',
'dev/scss/footer/footer.scss',
'dev/scss/header/{header,hint,menu,searchbar,vc}.scss',
- 'dev/scss/main/{alertify,highlight,kwic,logos,main,matchinfo,pagination,query,resultinfo,sidebar,tutorial}.scss'
+ 'dev/scss/main/{alertify,highlight,kwic,logos,' +
+ 'main,matchinfo,pagination,query,'+
+ 'resultinfo,sidebar,tutorial}.scss'
],
tasks: ['sass'],
options: {
diff --git a/dev/demo/vcdemo.js b/dev/demo/vcdemo.js
index d136cef..ff93f27 100644
--- a/dev/demo/vcdemo.js
+++ b/dev/demo/vcdemo.js
@@ -15,13 +15,13 @@
"operands":[
{
"@type":"koral:doc",
- "key":"Titel",
+ "key":"title",
"value":"Der Birnbaum",
"match":"match:eq"
},
{
"@type":"koral:doc",
- "key":"Veröffentlichungsort",
+ "key":"pubPlace",
"value":"Mannheim",
"match":"match:eq"
},
@@ -31,13 +31,13 @@
"operands":[
{
"@type":"koral:doc",
- "key":"Untertitel",
+ "key":"subTitle",
"value":"Aufzucht und Pflege",
"match":"match:eq"
},
{
"@type":"koral:doc",
- "key":"Untertitel",
+ "key":"subTitle",
"value":"Gedichte",
"match":"match:eq",
"rewrites" : [
@@ -54,7 +54,7 @@
},
{
"@type":"koral:doc",
- "key":"Veröffentlichungsdatum",
+ "key":"pubDate",
"type":"type:date",
"value":"2015-03-05",
"match":"match:geq"
@@ -63,22 +63,34 @@
};
require(['vc','lib/domReady'], function (vcClass, domReady) {
- KorAP.Locale.AND = 'und';
- KorAP.Locale.OR = 'oder';
+
+ var loc = KorAP.Locale;
+
+ loc.AND = 'und';
+ loc.OR = 'oder';
+ loc.VC_subTitle = 'Untertitel';
+ loc.VC_title = 'Titel';
+ loc.VC_pubDate = 'Veröffentlichungsdatum';
+ loc.VC_pubPlace = 'Veröffentlichungsort';
domReady(function() {
+
+ // Create a new virtual collection by passing a based json object and
+ // field menu information
var vc = vcClass.render(json, [
- ['Titel', 'title', 'string'],
- ['Untertitel', 'subTitle', 'string'],
- ['Veröffentlichungsdatum', 'pubDate', 'date'],
- ['Autor', 'author', 'string']
+ ['title', 'string'],
+ ['subTitle', 'string'],
+ ['pubDate', 'date'],
+ ['author', 'string']
]);
document.getElementById('vc').appendChild(vc.element());
+ // show the current JSON serialization
KorAP.showJSON = function () {
document.getElementById("json").innerHTML = JSON.stringify(vc.root().toJson());
};
+ // show the current query serialization
KorAP.showQuery = function () {
document.getElementById("query").innerHTML = vc.root().toQuery();
};
diff --git a/dev/js/src/vc.js b/dev/js/src/vc.js
index 2587d1c..80f6627 100644
--- a/dev/js/src/vc.js
+++ b/dev/js/src/vc.js
@@ -1,5 +1,9 @@
/**
* Create virtual collections with a visual user interface.
+ * This resembles the collection type objects of a KoralQuery
+ * "collection" object.
+ *
+ * KoralQuery v0.3 is expected.
*
* @author Nils Diewald
*/
@@ -35,6 +39,7 @@
OR : 'or',
DELETE : 'x'
}
+ and various field names with the prefix 'VC_'
*/
define([
@@ -95,7 +100,28 @@
// Create key menu
KorAP._vcKeyMenu = menuClass.create(keyList);
- KorAP._vcKeyMenu.limit(5);
+ KorAP._vcKeyMenu.limit(6);
+
+ // Create match menus ....
+ KorAP._vcMatchopMenu = {
+ 'string' : menuClass.create([
+ ['eq', null],
+ ['ne', null],
+ ['contains', null],
+ ['excludes', null]
+ ]),
+ 'date' : menuClass.create([
+ ['eq', null],
+ ['geq', null],
+ ['leq', null]
+ ]),
+ 'regex' : menuClass.create([
+ ['eq', null],
+ ['ne', null],
+ ['contains', null],
+ ['excludes', null]
+ ])
+ };
};
return this;
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index 436f9ef..10a49a8 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -1,45 +1,50 @@
/**
- * Document criterion
+ * A new document criterion
*/
define([
- 'vc/jsonld', 'vc/rewritelist'], function (jsonldClass, rewriteListClass) {
+ 'vc/jsonld',
+ 'vc/rewritelist',
+ 'util'
+], function (jsonldClass, rewriteListClass) {
-/*
- var fieldMenu = menuClass.create([
- ['Titel', 'title', 'string'],
- ['Untertitel', 'subTitle', 'string'],
- ['Veröffentlichungsdatum', 'pubDate', 'date'],
- ['Autor', 'author', 'string']
- ]);
-
- fieldMenu.limit(5);
-*/
+ /*
+ var fieldMenu = menuClass.create([
+ ['Titel', 'title', 'string'],
+ ['Untertitel', 'subTitle', 'string'],
+ ['Veröffentlichungsdatum', 'pubDate', 'date'],
+ ['Autor', 'author', 'string']
+ ]);
+
+ fieldMenu.limit(5);
+ */
_validRegexMatchRE = new RegExp("^(?:eq|ne)$");
- /**
- * Criterion in a KorAP.Doc
- */
- function _changeKey () {
- var doc = this.parentNode.refTo;
- var key = doc.element().firstChild;
- key.appendChild(fieldMenu.element());
- fieldMenu.show();
- fieldMenu.focus();
- // key, matchop, type, value
- };
+ var loc = KorAP.Locale;
+ loc.EMPTY = loc.EMPTY || '⋯';
return {
+
+ // The JSON-LD type
_ldType : "doc",
+
+ // The object ... maybe not important
_obj : function () { return '???'; /*KorAP.Doc*/ },
+ /**
+ * Create a new document criterion
+ * by passing the parent object and a json construct.
+ */
create : function (parent, json) {
+
+ // Create the object, inheriting from Json-LD class
var obj = Object(jsonldClass).
create().
upgradeTo(this).
fromJson(json);
-
+
+ // Bind the parent
if (parent !== undefined)
obj._parent = parent;
@@ -47,6 +52,9 @@
return obj;
},
+ /**
+ * Update the elements content.
+ */
update : function () {
if (this._element === undefined)
return this.element();
@@ -66,39 +74,58 @@
};
// Added key
- var key = document.createElement('span');
- key.setAttribute('class', 'key');
+ this._keyE = document.createElement('span');
+ this._keyE.setAttribute('class', 'key');
// Change key
- key.addEventListener('click', _changeKey, false);
+ this._keyE.addEventListener('click', this._changeKey.bind(this));
- if (this.key())
- key.appendChild(document.createTextNode(this.key()));
-
+ if (this.key()) {
+ var k = this.key();
+ if (loc['VC_' + k] !== undefined)
+ k = loc['VC_' + k];
+ this._keyE.appendChild(document.createTextNode(k));
+ };
+
// Added match operator
- var matchop = document.createElement('span');
- matchop.setAttribute('data-type', this.type());
- matchop.setAttribute('class', 'match');
- matchop.appendChild(
+ this._matchopE = document.createElement('span');
+ this._matchopE.setAttribute('data-type', this.type());
+ this._matchopE.setAttribute('class', 'match');
+ this._matchopE.appendChild(
document.createTextNode(this.matchop())
);
- // Added match operator
- var value = document.createElement('span');
- value.setAttribute('data-type', this.type());
- value.setAttribute('class', 'value');
- if (this.value())
- value.appendChild(
- document.createTextNode(this.value())
- );
+ // Change matchop
+ this._matchopE.addEventListener(
+ 'click',
+ this._changeMatchop.bind(this)
+ );
+
+ // Added value operator
+ this._valueE = document.createElement('span');
+ this._valueE.setAttribute('data-type', this.type());
+ this._valueE.setAttribute('class', 'value');
+ if (this.value()) {
+ this._valueE.appendChild(document.createTextNode(this.value()));
+ }
+ else {
+ this._valueE.appendChild(document.createTextNode(loc.EMPTY));
+ };
+
+ // Change value
+ this._valueE.addEventListener(
+ 'click',
+ this._changeValue.bind(this)
+ );
+
// Remove all element children
_removeChildren(e);
// Add spans
- e.appendChild(key);
- e.appendChild(matchop);
- e.appendChild(value);
+ e.appendChild(this._keyE);
+ e.appendChild(this._matchopE);
+ e.appendChild(this._valueE);
this.__changed = false;
};
@@ -122,6 +149,10 @@
return e;
},
+
+ /**
+ * Get the associated element
+ */
element : function () {
if (this._element !== undefined)
return this._element;
@@ -133,7 +164,9 @@
return this._element;
},
- // Wrap a new operation around the doc element
+ /**
+ * Wrap a new operation around the doc element
+ */
wrap : function (op) {
var parent = this.parent();
var group = require('vc/docgroup').create(parent);
@@ -143,7 +176,9 @@
return parent.replaceOperand(this, group).update();
},
- // Deserialize from json
+ /**
+ * Deserialize from json
+ */
fromJson : function (json) {
if (json === undefined)
return this;
@@ -250,6 +285,9 @@
return this;
},
+ /**
+ * Get or set the key
+ */
key : function (value) {
if (arguments.length === 1) {
this._key = value;
@@ -259,15 +297,93 @@
return this._key;
},
+ // Click on the key, show me the menu
+ _changeKey : function (e) {
+ var menu = KorAP._vcKeyMenu;
+
+ // Insert menu
+ this._element.insertBefore(
+ menu.element(),
+ this._keyE
+ );
+
+ // Release event
+ var that = this;
+ menu.released(function (key, type) {
+ var doc = that.key(key).type(type);
+
+ // This may not be compatible - then switch to default
+ doc.matchop(doc.matchop());
+ doc.value(doc.value());
+
+ // Update the doc
+ doc.update();
+
+ // hide!
+ this.hide();
+ });
+
+ // TODO: Highlight the old value!
+ menu.show();
+ menu.focus();
+ },
+
+ /**
+ * Get or set the match operator
+ */
matchop : function (match) {
if (arguments.length === 1) {
- this._matchop = match.replace(/^match:/, '');
+ var m = match.replace(/^match:/, '');
+ if (
+ (
+ (this._type === 'string' || this._type === 'regex') &&
+ KorAP._validStringMatchRE.test(m)
+ ) ||
+ (this._type === 'date' && KorAP._validDateMatchRE.test(m))
+ ) {
+ this._matchop = m;
+ }
+ else {
+ this._matchop = "eq";
+ };
+
this._changed();
return this;
};
return this._matchop || "eq";
},
+
+ // Click on the match operator, show me the menu
+ _changeMatchop : function (e) {
+ var menu = KorAP._vcMatchopMenu[this.type()];
+
+ if (menu === undefined) {
+ KorAP.log(0, "Unable to init menu for " + this.type());
+ return;
+ };
+
+ // Insert menu
+ this._element.insertBefore(
+ menu.element(),
+ this._matchopE
+ );
+
+ // Release event
+ var that = this;
+ menu.released(function (mo) {
+ that.matchop(mo).update();
+ this.hide();
+ });
+
+ menu.show();
+ menu.focus();
+ },
+
+
+ /**
+ * Get or set the type
+ */
type : function (type) {
if (arguments.length === 1) {
this._type = type;
@@ -277,15 +393,33 @@
return this._type || "string";
},
+
+ /**
+ * Get or set the value
+ */
value : function (value) {
if (arguments.length === 1) {
- this._value = value;
+ if (this._type === 'date' && !KorAP._validDateRE.test(value)) {
+ delete this._value;
+ }
+ else {
+ this._value = value;
+ };
this._changed();
return this;
};
return this._value;
},
+
+ // Click on the match operator, show me the menu
+ _changeValue : function (e) {
+ // TODO: Just kidding - this is temporary!
+ this.value(window.prompt('Enter new value'));
+ this.update();
+ },
+
+
rewrites : function () {
return this._rewrites;
},
@@ -303,6 +437,7 @@
this._element.classList.remove("rewritten");
},
+
toJson : function () {
if (!this.matchop() || !this.key())
return {};
@@ -316,6 +451,7 @@
};
},
+
toQuery : function () {
if (!this.matchop() || !this.key())
return "";
diff --git a/dev/js/src/vc/item.js b/dev/js/src/vc/item.js
index 7922bbd..c05fbad 100644
--- a/dev/js/src/vc/item.js
+++ b/dev/js/src/vc/item.js
@@ -1,43 +1,79 @@
// Field menu item
-define(['menu/item'], function (itemClass) {
+define(['menu/item', 'util'], function (itemClass) {
+
+ var loc = KorAP.Locale;
+
return {
+
+ /**
+ * Create new menu item.
+ * Pass two parameters: value and type.
+ * the value may be localized by a name in
+ * KorAP.Locale with the prefix 'VC_',
+ * e.g. 'VC_subTitle'.
+ */
create : function (params) {
return Object.create(itemClass)
.upgradeTo(this)
._init(params);
},
+ // Initialize item object
_init : function (params) {
if (params[0] === undefined)
throw new Error("Missing parameters");
- this._name = params[0];
- this._value = params[1];
- this._type = params[2];
+ this._key = params[0];
+ this._type = params[1];
+
+ var k = this._key;
+ this._name = loc["VC_" + k] ? loc["VC_" + k] : k;
this._lcField = ' ' + this._name.toLowerCase();
return this;
},
+ /**
+ * Override click event by passing all clicks
+ * to the menu object.
+ */
onclick : function (e) {
this.menu().release(
- this._name,
- this._value,
+ this._key,
this._type
);
e.halt();
},
+ /**
+ * Get the name of the item.
+ * This is a potential localized version
+ * of the value.
+ */
name : function () {
return this._name;
},
+ /**
+ * Get the type of the item.
+ */
type : function () {
return this._type;
},
+ /**
+ * Get the key of the item.
+ */
+ key : function () {
+ return this._key;
+ },
+
+ /**
+ * Get the HTML element associated with the item.
+ */
element : function () {
+
// already defined
if (this._element !== undefined)
return this._element;
@@ -45,7 +81,7 @@
// Create list item
var li = document.createElement("li");
li.setAttribute("data-type", this._type);
- li.setAttribute("data-value", this._value);
+ li.setAttribute("data-key", this._key);
// Connect action
li["onclick"] = this.onclick.bind(this);
diff --git a/dev/js/src/vc/menu.js b/dev/js/src/vc/menu.js
index 2cdc0e8..d305960 100644
--- a/dev/js/src/vc/menu.js
+++ b/dev/js/src/vc/menu.js
@@ -1,4 +1,6 @@
-// Field menu
+/**
+ * Menu showing all key fields.
+ */
define(['menu', 'vc/item'], function (menuClass, itemClass) {
return {
create : function (params) {
@@ -6,12 +8,20 @@
.upgradeTo(this)
._init(itemClass, undefined, params)
},
+
+ /**
+ * Register callback for click event.
+ */
released : function (cb) {
this._cb = cb;
},
- release : function (name, value, type) {
+
+ /**
+ * A click event was released
+ */
+ release : function (key, type) {
if (this._cb !== undefined)
- this._cb(name, value, type);
+ this._cb(key, type);
}
};
});
diff --git a/dev/js/src/vc/unspecified.js b/dev/js/src/vc/unspecified.js
index f10dcf7..31101ef 100644
--- a/dev/js/src/vc/unspecified.js
+++ b/dev/js/src/vc/unspecified.js
@@ -117,9 +117,11 @@
* Click on the unspecified object
*/
onclick : function () {
+
+ // Get the key menu
var menu = KorAP._vcKeyMenu;
- // Add keyelement at the correct position
+ // Add key menu element at the correct position
this._element.insertBefore(
menu.element(),
this._element.firstChild
@@ -128,9 +130,9 @@
var that = this;
// Set released method
- menu.released(function (name, value, type) {
- that.key(name);
- // + ' # ' + type);
+ menu.released(function (key, type) {
+ // Set chosen key and type - will return a doc
+ that.key(key).type(type).update();
this.hide();
});
diff --git a/dev/scss/header/vc.scss b/dev/scss/header/vc.scss
index 16f62c3..d6650a4 100644
--- a/dev/scss/header/vc.scss
+++ b/dev/scss/header/vc.scss
@@ -101,7 +101,8 @@
*/
.doc {
padding-left: $left-padding;
- > span + span {
+ > span + span,
+ > span + .menu + span{
margin-left: 5pt;
}
> span.key,
@@ -145,8 +146,11 @@
}
}
- .unspecified > span {
+ .doc > span {
cursor: pointer;
+ &:hover {
+ color: $dark-green;
+ }
}
/**
@@ -200,6 +204,9 @@
}
}
+ .menu {
+ display: inline-block;
+ }
}