'use strict'; var $ = require("jquery"), yutils = require("yasgui-utils"), utils = require('./utils.js'), imgs = require('./imgs.js'); require("datatables"); require("../lib/colResizable-1.4.js"); /** * Constructor of plugin which displays results as a table * * @param yasr {object} * @param parent {DOM element} * @param options {object} * @class YASR.plugins.table * @return yasr-table (doc) * */ var root = module.exports = function(yasr) { var table = null; var plugin = { name: "Table", getPriority: 10, }; var options = plugin.options = $.extend(true, {}, root.defaults); var tableLengthPersistencyId = (options.persistency ? yasr.getPersistencyId(options.persistency.tableLength) : null); var getRows = function() { var rows = []; var bindings = yasr.results.getBindings(); var vars = yasr.results.getVariables(); var usedPrefixes = null; if (yasr.options.getUsedPrefixes) { usedPrefixes = (typeof yasr.options.getUsedPrefixes == "function" ? yasr.options.getUsedPrefixes(yasr) : yasr.options.getUsedPrefixes); } for (var rowId = 0; rowId < bindings.length; rowId++) { var row = []; row.push(""); //row numbers var binding = bindings[rowId]; for (var colId = 0; colId < vars.length; colId++) { var sparqlVar = vars[colId]; if (sparqlVar in binding) { if (options.getCellContent) { row.push(options.getCellContent(yasr, plugin, binding, sparqlVar, { 'rowId': rowId, 'colId': colId, 'usedPrefixes': usedPrefixes })); } else { row.push(""); } } else { row.push(""); } } rows.push(row); } return rows; }; var eventId = yasr.getPersistencyId('eventId') || "yasr_" + $(yasr.container).closest('[id]').attr('id'); var addEvents = function() { table.on('order.dt', function() { drawSvgIcons(); }); if (tableLengthPersistencyId) { table.on('length.dt', function(e, settings, len) { yutils.storage.set(tableLengthPersistencyId, len, "month"); }); } $.extend(true, options.callbacks, options.handlers); table.delegate("td", "click", function(event) { if (options.callbacks && options.callbacks.onCellClick) { var result = options.callbacks.onCellClick(this, event); if (result === false) return false; } }).delegate("td", 'mouseenter', function(event) { if (options.callbacks && options.callbacks.onCellMouseEnter) { options.callbacks.onCellMouseEnter(this, event); } var tdEl = $(this); if (options.fetchTitlesFromPreflabel && tdEl.attr("title") === undefined && tdEl.text().trim().indexOf("http") == 0) { addPrefLabel(tdEl); } }).delegate("td", 'mouseleave', function(event) { if (options.callbacks && options.callbacks.onCellMouseLeave) { options.callbacks.onCellMouseLeave(this, event); } }); }; plugin.draw = function() { table = $('
'); $(yasr.resultsContainer).html(table); var dataTableConfig = options.datatable; dataTableConfig.data = getRows(); dataTableConfig.columns = options.getColumns(yasr, plugin); //fetch stored datatables length value var pLength = yutils.storage.get(tableLengthPersistencyId); if (pLength) dataTableConfig.pageLength = pLength; table.DataTable($.extend(true, {}, dataTableConfig)); //make copy. datatables adds properties for backwards compatability reasons, and don't want this cluttering our own drawSvgIcons(); addEvents(); //finally, make the columns dragable: table.colResizable(); }; var drawSvgIcons = function() { var sortings = { "sorting": "unsorted", "sorting_asc": "sortAsc", "sorting_desc": "sortDesc" }; table.find(".sortIcons").remove(); for (var sorting in sortings) { var svgDiv = $("
"); yutils.svg.draw(svgDiv, imgs[sortings[sorting]]); table.find("th." + sorting).append(svgDiv); } }; /** * Check whether this plugin can handler the current results * * @property canHandleResults * @type function * @default If resultset contains variables in the resultset, return true */ plugin.canHandleResults = function() { return yasr.results && yasr.results.getVariables && yasr.results.getVariables() && yasr.results.getVariables().length > 0; }; plugin.getDownloadInfo = function() { if (!yasr.results) return null; return { getContent: function() { return require("./bindingsToCsv.js")(yasr.results.getAsJson()); }, filename: "queryResults.csv", contentType: "text/csv", buttonTitle: "Download as CSV" }; }; return plugin; }; var formatLiteral = function(yasr, plugin, literalBinding) { var stringRepresentation = utils.escapeHtmlEntities(literalBinding.value); if (literalBinding["xml:lang"]) { stringRepresentation = '"' + stringRepresentation + '"@' + literalBinding["xml:lang"] + ''; } else if (literalBinding.datatype) { var xmlSchemaNs = "http://www.w3.org/2001/XMLSchema#"; var dataType = literalBinding.datatype; if (dataType.indexOf(xmlSchemaNs) === 0) { dataType = "xsd:" + dataType.substring(xmlSchemaNs.length); } else { dataType = "<" + dataType + ">"; } stringRepresentation = '"' + stringRepresentation + '"^^' + dataType + ''; } return stringRepresentation; }; var getCellContent = function(yasr, plugin, bindings, sparqlVar, context) { var binding = bindings[sparqlVar]; var value = null; if (binding.type == "uri") { var title = null; var href = binding.value; var visibleString = href; if (context.usedPrefixes) { for (var prefix in context.usedPrefixes) { if (visibleString.indexOf(context.usedPrefixes[prefix]) == 0) { visibleString = prefix + ':' + href.substring(context.usedPrefixes[prefix].length); break; } } } if (plugin.options.mergeLabelsWithUris) { var postFix = (typeof plugin.options.mergeLabelsWithUris == "string" ? plugin.options.mergeLabelsWithUris : "Label"); if (bindings[sparqlVar + postFix]) { visibleString = formatLiteral(yasr, plugin, bindings[sparqlVar + postFix]); title = href; } } value = "" + visibleString + ""; } else { value = "" + formatLiteral(yasr, plugin, binding) + ""; } return "
" + value + "
"; }; var addPrefLabel = function(td) { var addEmptyTitle = function() { td.attr("title", ""); //this avoids trying to fetch the label again on next hover }; $.get("http://preflabel.org/api/v1/label/" + encodeURIComponent(td.text()) + "?silent=true") .success(function(data) { if (typeof data == "object" && data.label) { td.attr("title", data.label); } else if (typeof data == "string" && data.length > 0) { td.attr("title", data); } else { addEmptyTitle(); } }) .fail(addEmptyTitle); }; var openCellUriInNewWindow = function(cell) { if (cell.className.indexOf("uri") >= 0) { window.open(this.innerHTML); } }; /** * Defaults for table plugin * * @type object * @attribute YASR.plugins.table.defaults */ root.defaults = { /** * Draw the cell content, from a given binding * * @property drawCellContent * @param binding {object} * @type function * @return string * @default YASR.plugins.table.getFormattedValueFromBinding */ getCellContent: getCellContent, persistency: { tableLength: "tableLength", }, getColumns: function(yasr, plugin) { var includeVariable = function(variableToCheck) { if (!plugin.options.mergeLabelsWithUris) return true; var postFix = (typeof plugin.options.mergeLabelsWithUris == "string" ? plugin.options.mergeLabelsWithUris : "Label"); if (variableToCheck.indexOf(postFix, variableToCheck.length - postFix.length) !== -1) { //this one ends with a postfix if (yasr.results.getVariables().indexOf(variableToCheck.substring(0, variableToCheck.length - postFix.length)) >= 0) { //we have a shorter version of this variable. So, do not include the .. variable in the table return false; } } return true; }; var cols = []; cols.push({ "title": "" }); //row numbers column yasr.results.getVariables().forEach(function(variable) { cols.push({ "title": "" + variable + "", "visible": includeVariable(variable) }); }); return cols; }, /** * Try to fetch the label representation for each URI, using the preflabel.org services. (fetching occurs when hovering over the cell) * * @property fetchTitlesFromPreflabel * @type boolean * @default true */ fetchTitlesFromPreflabel: true, mergeLabelsWithUris: false, /** * Set a number of handlers for the table * * @property handlers * @type object */ callbacks: { /** * Mouse-enter-cell event * * @property handlers.onCellMouseEnter * @type function * @param td-element * @default null */ onCellMouseEnter: null, /** * Mouse-leave-cell event * * @property handlers.onCellMouseLeave * @type function * @param td-element * @default null */ onCellMouseLeave: null, /** * Cell clicked event * * @property handlers.onCellClick * @type function * @param td-element * @default null */ onCellClick: null }, /** * This plugin uses the datatables jquery plugin (See datatables.net). For any datatables specific defaults, change this object. * See the datatables reference for more information * * @property datatable * @type object */ datatable: { "autoWidth": false, "dom": '<"dtTopHeader"ilf>rtip', "order": [], //disable initial sorting "pageLength": 50, //default page length "lengthMenu": [ [10, 50, 100, 1000, -1], [10, 50, 100, 1000, "All"] ], //possible page lengths "lengthChange": true, //allow changing page length "pagingType": "full_numbers", //how to show the pagination options "drawCallback": function(oSettings) { //trick to show row numbers for (var i = 0; i < oSettings.aiDisplay.length; i++) { $('td:eq(0)', oSettings.aoData[oSettings.aiDisplay[i]].nTr).html(i + 1); } //Hide pagination when we have a single page var activePaginateButton = false; $(oSettings.nTableWrapper).find(".paginate_button").each(function() { if ($(this).attr("class").indexOf("current") == -1 && $(this).attr("class").indexOf("disabled") == -1) { activePaginateButton = true; } }); if (activePaginateButton) { $(oSettings.nTableWrapper).find(".dataTables_paginate").show(); } else { $(oSettings.nTableWrapper).find(".dataTables_paginate").hide(); } }, "columnDefs": [{ "width": "32px", "orderable": false, "targets": 0 } //disable row sorting for first col ], }, }; root.version = { "YASR-table": require("../package.json").version, "jquery": $.fn.jquery, "jquery-datatables": $.fn.DataTable.version };