First draft of the schema alignment dialog

This commit is contained in:
Antonin Delpeuch 2017-08-29 18:22:41 +01:00
parent 5853cdf10d
commit 82930da4db
6 changed files with 816 additions and 0 deletions

View File

@ -15,6 +15,7 @@
<ant dir="pc-axis/" target="build" />
<ant dir="database/" target="build" />
<ant dir="wikidata/" target="build" />
<ant dir="rdf-extension/" target="build" />
</target>
<target name="clean">
@ -25,6 +26,7 @@
<ant dir="pc-axis/" target="clean" />
<ant dir="database/" target="clean" />
<ant dir="wikidata/" target="clean" />
<ant dir="rdf-extension/" target="clean" />
</target>
<target name="test">

View File

@ -0,0 +1,137 @@
importPackage(org.openrefine.wikidata.commands);
/*
* Function invoked to initialize the extension.
*/
function init() {
var RefineServlet = Packages.com.google.refine.RefineServlet;
RefineServlet.registerClassMapping(
"org.openrefine.wikidata.operations.SaveWikibaseSchemaOperation$WikibaseSchemaChange",
"org.openrefine.wikidata.operations.SaveWikibaseSchemaOperation$WikibaseSchemaChange");
RefineServlet.cacheClass(Packages.org.openrefine.wikidata.operations.SaveWikibaseSchemaOperation$WikibaseSchemaChange);
/*
* Context Initialization. This is mainly to allow testability. a simple attempt to mimic dependency injection
*/
/*
var initializer = new Packages.org.deri.grefine.rdf.app.InitilizationCommand();
RefineServlet.registerCommand(module, "initialize", initializer);
var ctxt = new Packages.org.deri.grefine.rdf.app.ApplicationContext();
initializer.initRdfExportApplicationContext(ctxt);
*/
/*
* Attach a Wikibase schema to each project.
*/
Packages.com.google.refine.model.Project.registerOverlayModel(
"wikibaseSchema",
Packages.org.openrefine.wikidata.schema.WikibaseSchema);
/*
* Operations
*/
Packages.com.google.refine.operations.OperationRegistry.registerOperation(
module, "save-wikibase-schema", Packages.org.openrefine.wikidata.operations.SaveWikibaseSchemaOperation);
/*
* Exporters
*/
/*
var ExporterRegistry = Packages.com.google.refine.exporters.ExporterRegistry;
var QSV2Exporter = Packages.org.openrefine.wikidata.exporters.QuickStatements2Exporter;
ExporterRegistry.registerExporter("qsv2", new QSV2());
*/
/*
* Resources
*/
ClientSideResourceManager.addPaths(
"project/scripts",
module,
[
"scripts/menu-bar-extension.js",
"scripts/dialogs/schema-alignment-dialog.js",
]);
ClientSideResourceManager.addPaths(
"project/styles",
module,
[
"styles/dialogs/schema-alignment-dialog.less",
]);
}
function process(path, request, response) {
// Analyze path and handle this request yourself.
/*
var loggerFactory = Packages.org.slf4j.LoggerFactory;
var logger = loggerFactory.getLogger("rdf_extension");
var method = request.getMethod();
logger.info('receiving request for ' + path);
if(rdfReconcileExtension.isKnownRequestUrl(path)){
var command = rdfReconcileExtension.getCommand(path, request);
logger.info('command is ' + command);
var serviceName = rdfReconcileExtension.getServiceName(path);
logger.info('command is ' + command + ', while service name is ' + serviceName);
if(command && command !== 'unknown'){
var jsonResponse;
if(command==='metadata'){
jsonResponse = GRefineServiceManager.singleton.metadata(serviceName,request);
}else if(command==='multi-reconcile'){
jsonResponse = GRefineServiceManager.singleton.multiReconcile(serviceName,request);
}else if (command==='suggest-type'){
jsonResponse = GRefineServiceManager.singleton.suggestType(serviceName,request);
}else if (command==='flyout-type'){
jsonResponse = GRefineServiceManager.singleton.previewType(serviceName,request);
}else if (command==='suggest-property'){
jsonResponse = GRefineServiceManager.singleton.suggestProperty(serviceName,request);
}else if (command==='flyout-property'){
jsonResponse = GRefineServiceManager.singleton.previewProperty(serviceName,request);
}else if (command==='suggest-entity'){
jsonResponse = GRefineServiceManager.singleton.suggestEntity(serviceName,request);
}else if (command==='flyout-entity'){
jsonResponse = GRefineServiceManager.singleton.previewEntity(serviceName,request);
}else if (command==='preview-resource-template'){
var htmlResponse = GRefineServiceManager.singleton.getHtmlOfResourcePreviewTemplate(serviceName,request);
if(htmlResponse){
butterfly.sendString(request, response, htmlResponse ,"UTF-8", "text/html");
}else{
butterfly.sendError(request, response, 404, "unknown service");
}
return;
}else if (command==='view-resource'){
var id = request.getParameter('id');
butterfly.redirect(request,response,id);
return;
}else if (command ==='preview-resource'){
logger.info("id is " + request.getParameter("id"));
var htmlResponse = GRefineServiceManager.singleton.previewResource(serviceName,request);
if(htmlResponse){
butterfly.sendString(request, response, htmlResponse ,"UTF-8", "text/html");
}else{
butterfly.sendError(request, response, 404, "unknown service");
}
return;
}
if(jsonResponse){
logger.info(jsonResponse);
butterfly.sendString(request, response, jsonResponse ,"UTF-8", "text/javascript");
return;
}else{
butterfly.sendError(request, response, 404, "unknown service");
}
}
//else it is an unknown command... do nothing
}
if (path == "/" || path == "") {
butterfly.redirect(request, response, "index.html");
} */
}

View File

@ -0,0 +1,3 @@
name = wikidata
description = OpenRefine Wikidata export
requires = core

View File

@ -0,0 +1,45 @@
<div class="dialog-frame" style="width: 800px;">
<div class="dialog-header" bind="dialogHeader">Align to Wikibase</div>
<div class="dialog-body" bind="dialogBody">
<p class="body-text">
The schema alignment skeleton below specifies how your tabular data will be
transformed into Wikibase statements. You can drag and drop columns in statements:
they will be replaced by their values once you export the statements.
</p>
<div id="schema-alignment-tabs" class="refine-tabs">
<ul>
<li><a href="#schema-alignment-tabs-schema">Schema</a></li>
<li><a href="#schema-alignment-tabs-preview-qs">QuickStatements preview</a></li>
</ul>
<div id="schema-alignment-tabs-schema">
<div class="schema-alignment-dialog-canvas">
<p class="schema-alignment-columns-header">Columns:</p>
<div class="schema-alignment-dialog-columns-area">
</div>
<div class="schema-alignment-dialog-statements-area">
<div id="schema-alignment-statements-container">
</div>
<div class="wbs-toolbar"><a class="wbs-add-item" bind="addItemButton">add item</a></div>
</div>
</div>
</div>
<div id="schema-alignment-tabs-preview-qs" style="display: none;">
<div class="schema-alignment-dialog-preview"></div>
</div>
</div>
</div>
<div class="dialog-footer" bind="dialogFooter"><div class="grid-layout layout-normal layout-full"><table><tr>
<td>
<button class="button" bind="resetButton">Reset</button>
</td>
<td style="text-align:right;">
<button class="button button-primary" bind="saveButton">Save</button>
</td>
<td style="text-align:center;" width="30%">
<span class="schema-alignment-status-indicator" bind="statusIndicator"></span>
</td>
<td style="text-align:right;" width="1%">
<button class="button" bind="closeButton">Close</button>
</td>
</tr></table></div></div>
</div>

View File

@ -0,0 +1,409 @@
/*
Copyright 2010, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
var SchemaAlignment = {};
SchemaAlignment.autoAlign = function() {
var protograph = {};
var columns = theProject.columnModel.columns;
var typedCandidates = [];
var candidates = [];
for (var c = 0; c < columns.length; c++) {
var column = columns[c];
var typed = (column.reconConfig) &&
ReconciliationManager.isFreebaseIdOrMid(column.reconConfig.identifierSpace) &&
ReconciliationManager.isFreebaseId(column.reconConfig.schemaSpace);
var candidate = {
status: "unbound",
typed: typed,
index: c,
column: column
};
candidates.push(candidate);
if (typed) {
typedCandidates.push(candidate);
}
}
if (typedCandidates.length > 0) {
} else {
var queries = {};
for (var i = 0; i < candidates.length; i++) {
var candidate = candidates[i];
var name = SchemaAlignment._cleanName(candidate.column.name);
var key = "t" + i + ":search";
queries[key] = {
"query" : name,
"limit" : 10,
"type" : "/type/type,/type/property",
"type_strict" : "any"
};
}
SchemaAlignment._batchSearch(queries, function(result) {
console.log(result);
});
}
};
SchemaAlignment._batchSearch = function(queries, onDone) {
var keys = [];
for (var n in queries) {
if (queries.hasOwnProperty(n)) {
keys.push(n);
}
}
var result = {};
var args = [];
var makeBatch = function(keyBatch) {
var batch = {};
for (var k = 0; k < keyBatch.length; k++) {
var key = keyBatch[k];
batch[key] = queries[key];
}
args.push("http://api.freebase.com/api/service/search?" +
$.param({ "queries" : JSON.stringify(batch) }) + "&callback=?");
args.push(null); // no data
args.push(function(data) {
for (var k = 0; k < keyBatch.length; k++) {
var key = keyBatch[k];
result[key] = data[key];
}
});
};
for (var i = 0; i < keys.length; i += 10) {
makeBatch(keys.slice(i, i + 10));
}
args.push(function() {
onDone(result);
});
Ajax.chainGetJSON.apply(null, args);
};
SchemaAlignment._cleanName = function(s) {
return s.replace(/\W/g, " ").replace(/\s+/g, " ").toLowerCase();
};
SchemaAlignment.createNewRootNode = function() {
var rootNode = null;
var links = [];
var columns = theProject.columnModel.columns;
for (var i = 0; i < columns.length; i++) {
var column = columns[i];
var target = {
nodeType: "cell-as-topic",
columnName: column.name,
createForNoReconMatch: true
};
if ((column.reconConfig) &&
ReconciliationManager.isFreebaseIdOrMid(column.reconConfig.identifierSpace) &&
ReconciliationManager.isFreebaseId(column.reconConfig.schemaSpace) &&
(column.reconConfig.type)) {
target.type = {
id: column.reconConfig.type.id,
name: column.reconConfig.type.name
};
}
if (column.name == theProject.columnModel.keyColumnName) {
rootNode = target;
} else {
links.push({
property: null,
target: target
});
}
}
rootNode = rootNode || { nodeType: "cell-as-topic" };
rootNode.links = links;
return rootNode;
};
var SchemaAlignmentDialog = {};
SchemaAlignmentDialog.launch = function(onDone) {
this._onDone = onDone;
this._hasUnsavedChanges = false;
this._createDialog();
// this._reset(schema, true);
}
SchemaAlignmentDialog._reset = function(protograph, initial) {
this._originalProtograph = protograph || { rootNodes: [] };
this._protograph = cloneDeep(this._originalProtograph); // this is what can be munched on
if (!this._protograph.rootNodes.length) {
this._protograph.rootNodes.push(SchemaAlignment.createNewRootNode());
}
$(this._nodeTable).empty();
this._nodeUIs = [];
for (var i = 0; i < this._protograph.rootNodes.length; i++) {
this._nodeUIs.push(new SchemaAlignmentDialog.UINode(
this,
this._protograph.rootNodes[i],
this._nodeTable,
{
expanded: true,
mustBeCellTopic: true
}
));
}
this.preview(initial);
};
SchemaAlignmentDialog._save = function(onDone) {
var self = this;
var protograph = this.getJSON();
Refine.postProcess(
"freebase",
"save-protograph",
{},
{ protograph: JSON.stringify(protograph) },
{},
{
onDone: function() {
theProject.overlayModels.freebaseProtograph = protograph;
self._elmts.statusIndicator.hide();
self._hasUnsavedChanges = false;
if (onDone) onDone();
}
}
);
};
SchemaAlignmentDialog._createDialog = function() {
var self = this;
var frame = $(DOM.loadHTML("wikidata", "scripts/dialogs/schema-alignment-dialog.html"));
var elmts = this._elmts = DOM.bind(frame);
this._level = DialogSystem.showDialog(frame);
// Init the column area
var columns = theProject.columnModel.columns;
this._columnArea = $(".schema-alignment-dialog-columns-area");
for (var i = 0; i < columns.length; i++) {
var cell = $("<div></div>").addClass('schema-alignment-draggable-column').text(columns[i].name);
this._columnArea.append(cell);
}
$('.schema-alignment-draggable-column').draggable({
helper: "clone",
revert: true,
revertDuration: 200,
});
var dismiss = function() {
DialogSystem.dismissUntil(self._level - 1);
};
elmts.saveButton.click(function() {
self._save();
});
elmts.resetButton.click(function() {
self._reset(null);
});
elmts.closeButton.click(function() {
if (!self._hasUnsavedChanges || window.confirm("There are unsaved changes. Close anyway?")) {
dismiss();
}
});
elmts.addItemButton.click(function() {
self._addItem();
});
$("#schema-alignment-tabs").tabs();
this._previewPanes = $(".schema-alignment-dialog-preview");
this._canvas = $(".schema-alignment-dialog-canvas");
this._nodeTable = $('<table></table>').addClass("schema-alignment-table-layout").appendTo(this._canvas)[0];
SchemaAlignmentDialog._reconService = ReconciliationManager.ensureDefaultServicePresent();
console.log(SchemaAlignmentDialog._reconService);
};
SchemaAlignmentDialog._addItem = function() {
var item = $('<div></div>').addClass('wbs-item');
var input = $('<div></div>').addClass('wbs-item-input').appendTo(item);
SchemaAlignmentDialog._initField(input);
var right = $('<div></div>').addClass('wbs-right').appendTo(item);
$('<div></div>').addClass('wbs-statement-group-container').appendTo(right);
var toolbar = $('<div></div>').addClass('wbs-toolbar').appendTo(right);
$('<a></a>').addClass('wbs-add-statement-group').text('add statement').click(function() {
SchemaAlignmentDialog._addStatementGroup(item);
}).appendTo(toolbar);
SchemaAlignmentDialog._addStatementGroup(item);
$('#schema-alignment-statements-container').append(item);
}
SchemaAlignmentDialog._addStatementGroup = function(item) {
var container = item.find('.wbs-statement-group-container').first();
var statementGroup = $('<div></div>').addClass('wbs-statement-group');
var input = $('<div></div>').addClass('wbs-prop-input').appendTo(statementGroup);
SchemaAlignmentDialog._initField(input);
var right = $('<div></div>').addClass('wbs-right').appendTo(statementGroup);
$('<div></div>').addClass('wbs-statement-container').appendTo(right);
var toolbar = $('<div></div>').addClass('wbs-toolbar').appendTo(right);
$('<a></a>').addClass('wbs-add-statement').text('add value').click(function() {
SchemaAlignmentDialog._addStatement(statementGroup);
}).appendTo(toolbar);
container.append(statementGroup);
SchemaAlignmentDialog._addStatement(statementGroup);
}
SchemaAlignmentDialog._addStatement = function(statementGroup) {
var container = statementGroup.find('.wbs-statement-container').first();
var statement = $('<div></div>').addClass('wbs-statement');
var toolbar1 = $('<div></div>').addClass('wbs-toolbar').appendTo(statement);
$('<a></a>').addClass('wbs-remove-statement').text('x').click(function() {
SchemaAlignmentDialog._removeStatement(statement);
}).appendTo(toolbar1);
var input = $('<div></div>').addClass('wbs-target-input').appendTo(statement);
SchemaAlignmentDialog._initField(input);
var right = $('<div></div>').addClass('wbs-right').appendTo(statement);
$('<div></div>').addClass('wbs-qualifier-container').appendTo(right);
var toolbar2 = $('<div></div>').addClass('wbs-toolbar').appendTo(right);
$('<a></a>').addClass('wbs-add-qualifier').text('add qualifier').appendTo(toolbar2);
container.append(statement);
}
SchemaAlignmentDialog._initField = function(input) {
input.droppable({
accept: ".schema-alignment-draggable-column"
}).on("drop", function (evt, ui) {
input.text(ui.draggable.text());
});
var suggestConfig = $.extend({}, this._reconService.suggest.property);
suggestConfig.key = null;
suggestConfig.query_param_name = "prefix";
input.suggestP(suggestConfig).bind("fb-select", function(evt, data) {
console.log(data); /*
self._addProperty({
id : data.id,
name: data.name,
}); */
});
}
SchemaAlignmentDialog._removeStatement = function(statement) {
var statementGroup = statement.parents('.wbs-statement-group').first();
statement.remove();
var remainingStatements = statementGroup.find('.wbs-statement').length;
if (remainingStatements === 0) {
statementGroup.remove();
}
}
/*
SchemaAlignmentDialog._addStatement = function() {
var newStatement = $('<div></div>').addClass('schema-alignment-statement');
var subject = $('<div></div>').addClass('schema-alignment-subject').appendTo(newStatement);
var prop = $('<div></div>').addClass('schema-alignment-prop').appendTo(newStatement);
var target = $('<div></div>').addClass('schema-alignment-target').appendTo(newStatement);
var qualifiersArea = $('<div></div>').addClass('schema-alignment-qualifiers').appendTo(newStatement);
var addQualifier = $('<p></p>').addClass('schema-alignment-add-qualifier').text('Add qualifier').appendTo(newStatement);
$('#schema-alignment-statements-container').append(newStatement);
}
*/
SchemaAlignmentDialog.getJSON = function() {
var rootNodes = [];
for (var i = 0; i < this._nodeUIs.length; i++) {
var node = this._nodeUIs[i].getJSON();
if (node !== null) {
rootNodes.push(node);
}
}
return {
rootNodes: rootNodes
};
};
SchemaAlignmentDialog.preview = function(initial) {
var self = this;
this._previewPanes.empty();
if (!(initial)) {
this._elmts.statusIndicator.show().text("There are unsaved changes.");
this._hasUnsavedChanges = true;
}
var protograph = this.getJSON();
$.post(
"command/freebase/preview-protograph?" + $.param({ project: theProject.id }),
{ protograph: JSON.stringify(protograph), engine: JSON.stringify(ui.browsingEngine.getJSON()) },
function(data) {
if ("mqllike" in data) {
$(self._previewPanes[0]).text(JSON.stringify(data.mqllike, null, 2));
}
if ("tripleloader" in data) {
$(self._previewPanes[1]).text(data.tripleloader);
}
},
"json"
);
};
SchemaAlignmentDialog._findColumn = function(cellIndex) {
var columns = theProject.columnModel.columns;
for (var i = 0; i < columns.length; i++) {
var column = columns[i];
if (column.cellIndex == cellIndex) {
return column;
}
}
return null;
};

View File

@ -0,0 +1,220 @@
/*
Copyright 2010, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
@import-less url("../theme.less");
.schema-alignment-dialog-canvas {
height: 400px;
overflow: auto;
padding: 0px
margin-top: 3px;
background: white;
}
.schema-alignment-dialog-statements-area {
padding: 10px;
min-height: 200px;
}
.schema-alignment-dialog-columns-area {
border: 1px solid #bcf;
padding: 5px;
}
.schema-alignment-draggable-column {
border: 1px solid #aaa;
padding: 2px;
margin: 2px;
background-color: #eee;
display: inline-block;
}
.wbs-item-input, .wbs-prop-input, .wbs-target-input {
width: 160px;
height: 20px;
border: 1px solid #a2a9b1;
display: inline-block;
background: white;
margin: 5px;
}
.wbs-prop-input {
width: 120px;
}
.wbs-toolbar {
float: right;
width: 100px;
padding: 2px;
}
.wbs-add-item, .wbs-add-statement-group, .wbs-add-statement, .wbs-add-qualifier {
color: #0645ad !important;
font-size: 0.9em;
}
.wbs-statement-group-container, .wbs-statement-container, .wbs-qualifier-container {
width: 100%;
display: block;
overflow: auto;
}
.wbs-item, .wbs-statement-group, .wbs-statement {
display: block;
overflow: auto;
}
.wbs-statement-group {
background: #eaecf0;
margin-bottom: 5px;
}
.wbs-statement {
background: white;
}
.wbs-right {
float: right;
width: 70%;
}
.schema-alignment-columns-header {
margin-bottom: 0.3em;
}
table.schema-alignment-table-layout {
border-collapse: collapse;
margin: 0px;
padding: 0px;
margin-bottom: 1em;
}
table.schema-alignment-table-layout .padded {
padding: 3px 6px;
}
div.schema-alignment-detail-container {
border-left: 3px solid #eee;
}
td.schema-alignment-node-main, td.schema-alignment-link-main {
white-space: pre;
width: 300px;
}
td.schema-alignment-node-toggle, td.schema-alignment-link-toggle {
white-space: pre;
width: 1%;
}
td.schema-alignment-node-toggle img, td.schema-alignment-link-toggle img {
cursor: pointer;
vertical-align: middle;
padding: 2px;
}
td.schema-alignment-node-details, td.schema-alignment-link-details {
width: 90%;
}
a.schema-alignment-node-tag {
padding: 3px 6px;
background: #ddd;
text-decoration: none;
color: black;
-moz-border-radius: 10px;
}
a.schema-alignment-node-tag:hover {
background: #888;
color: white;
}
.schema-alignment-node-column {
font-weight: bold;
}
a.schema-alignment-link-tag {
padding: 3px 6px;
text-decoration: none;
color: black;
}
a.schema-alignment-link-tag:hover {
color: #88f;
}
div.schema-alignment-dialog-preview {
height: 400px;
overflow: auto;
background: white;
padding: 10px;
margin-top: 3px;
white-space: pre;
font-family: monospace;
font-size: 9pt;
}
.schema-alignment-status-indicator {
color: #red;
}
/*--------------------------------------------------
* Node dialog
*--------------------------------------------------
*/
.schema-align-node-dialog-node-type {
padding: 0.25em;
background: #ddf;
}
.schema-align-node-dialog-node-type input {
vertical-align: text-bottom;
}
.schema-alignment-node-dialog-column-list {
height: 300px;
width: 200px;
overflow: auto;
border: 1px solid #ddd;
}
/*--------------------------------------------------
* Link dialog
*--------------------------------------------------
*/
.schema-alignment-link-menu-section {
padding: 8px;
border-bottom: 1px solid #ddd;
}
.schema-alignment-link-menu-section-last {
padding: 8px;
}