/* Part of SWISH Author: Jan Wielemaker E-mail: J.Wielemaker@cs.vu.nl WWW: http://www.swi-prolog.org Copyright (C): 2014-2016, VU University Amsterdam CWI Amsterdam All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. 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. 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. */ /** * @fileOverview * Render a single Prolog answer. * * @version 0.2.0 * @author Jan Wielemaker, J.Wielemaker@vu.nl * @requires jquery */ define([ "jquery", "preferences", "laconic" ], function($, preferences) { /******************************* * RENDER AN ANSWER * *******************************/ (function($) { var pluginName = 'prologAnswer'; /** @lends $.fn.prologAnswer */ var methods = { /** * Represent the binding of one or more variables to exactly the * same (==) Prolog term. * * @typedef {Object} Binding * @property {Array.String} variables represents the names of the * variables. This array is at least one long. * @property {String} value contains the HTML that describes the * binding of the variable. */ /** * Represent the binding of a single variable used to represent * sharing, an in particular cyclic terms * * @typedef {Object} Subsitution * @property {String} var name of the variable * @property {String} value contains the HTML that describes the * binding of the variable. */ /** * Represent an answer as represented by the pengines `json-html` * format. * @typedef {Object} Answer * @property {Array.Binding} variables represents the variable * bindings. * @property {Array.Subsitution} [substitutions] represents substitutions * needed to break cyclic terms. * @property {Array.String} [residuals] represents residual goals as HTML * strings. */ /** * Render a single answer as returned by pengines `json-html` format * as an HTML string. * * to HTML escaping issues * @param {Answer} answer represents an answer to a Prolog query */ _init: function(answer) { return this.each(function() { var elem = $(this); if ( answerHasOutput(answer) ) { if ( elem.is("table") ) { var row = $.el.tr(); elem.append(row); row.innerHTML = renderTabledAnswer(answer, elem); evalScripts($(row)); $(row).find(".render-multi").renderMulti(); } else { elem[0].innerHTML = renderAnswer(answer); evalScripts(elem); elem.find(".render-multi").renderMulti(); if ( preferences.getVal("auto-binding-layout") ) elem.find(".pl-binding").pl_term('layout', 'auto'); } } else elem.append($.el.span({class: "prolog-true"}, "true")); }); } }; function answerHasOutput(answer) { return ( answer.variables.length > 0 || answer.residuals || answer.wfs_residual_program || answer.scasp_model || answer.scasp_justification ); } function renderSubstitutions(substs, html) { html.push(', % where
'); for (var s = 0; s < substs.length; s++) { html.push('', "", substs[s].var+" = ", substs[s].value, ''); if (s < substs.length - 1) html.push(",
"); } } function renderAnswer(answer) { var html = []; var bindings = answer.variables; var wfshelp = "http://www.swi-prolog.org/pldoc/man?section=WFS"; if ( answer.wfs_residual_program ) { html.push("
", "
", "WFS residual program (", "", "help", "", ")
", answer.wfs_residual_program, "
"); } for (var i = 0; i < bindings.length; i++) { var vars = bindings[i].variables; for (var v = 0; v < vars.length - 1; v++) { html.push("", vars[v], " = ", "", vars[v + 1], ", "); } html.push("", "", vars[vars.length - 1], " = ", bindings[i].value, ""); if (bindings[i].substitutions) { renderSubstitutions(bindings[i].substitutions, html); } if (i < bindings.length - 1 || answer.residuals) html.push(",
"); } var residuals; if ((residuals = answer.residuals)) { for (var i = 0; i < residuals.length; i++) { html.push(residuals[i]); if (i < residuals.length - 1) html.push(",
"); } } // s(CASP) handling. if ( answer.scasp_model || answer.scasp_justification ) { html.push("
\n", answer.scasp_model || "", answer.scasp_justification || "", "
\n", "\n"); } return html.join(""); } /** * Render answer as a new row to the answer table. * @param {Answer} answer represents an answer to a Prolog query * @param {Table} table is the jQuery table to which the answer must * be added. */ function renderTabledAnswer(answer, table) { var html = []; function findBinding(name) { var bindings = answer.variables; for (var i = 0; i < bindings.length; i++) { var vars = bindings[i].variables; for (var v = 0; v < vars.length; v++) { if ( vars[v] == name ) return bindings[i]; } } return null; } for(var i = 0; i"); if ( binding ) { html.push(binding.value); if ( binding.substitutions ) renderSubstitutions(binding.substitutions, html); } else { html.push("", vname, ""); } html.push(""); } function ensureResidualColumn() { if ( table.find("tr.projection th.residuals").length == 0 ) { $("Residual goals").insertBefore( table.find("tr.projection th.answer-nth")); $("").insertBefore( table.find("tr td.answer-nth")); } } var residuals; if ((residuals = answer.residuals)) { ensureResidualColumn(); html.push(""); for (var i = 0; i < residuals.length; i++) { html.push(residuals[i]); if (i < residuals.length - 1) html.push(",
"); } html.push(""); } if ( answer.nth ) html.push("", answer.nth, ""); return html.join(""); } /** * Execute scripts that are embedded in the jQuery object elem. * While executing a script, the property `$.ajaxScript` points * to the executing script to enable the script to find elements * in the ajax DOM extension in which the script is embedded. * @param {jQuery} elem is the set in which scripts are searched * and executed. */ function evalScripts(elem) { elem.find("script").each(function() { var type = this.getAttribute('type')||"text/javascript"; if ( type == "text/javascript" ) { $.ajaxScript = $(this); eval(this.textContent); } }); if ( $.ajaxScript ) delete $.ajaxScript; } /** * Render a single Prolog answer. This class is the entry point for * more flexible answer rendering. * * @class prologAnswer * @tutorial jquery-doc * @memberOf $.fn * @param {String|Object} answer Either a method name or the jQuery * plugin initialization object, which is the answer to a Prolog query * in pengines "json-html" format * @param [...] Zero or more arguments passed to the jQuery `method` */ $.fn.prologAnswer = function(method) { if ( methods[method] ) { return methods[method] .apply(this, Array.prototype.slice.call(arguments, 1)); } else if ( typeof method === 'object' || !method ) { return methods._init.apply(this, arguments); } else { $.error('Method ' + method + ' does not exist on jQuery.' + pluginName); } }; }(jQuery)); /******************************* * RENDER TERMS * *******************************/ (function($) { var pluginName = 'renderMulti'; var timeout = 0; var hovering = false; /** @lends $.fn.renderMulti */ var methods = { _init: function(options) { return this.each(function() { var elem = $(this); var data = {current: 0}; /* private data */ var display = []; var selector = $.el.div({class: "render-multi-active"}); var i = 0; elem.children().each(function() { var how = $(this).css("display"); display.push(how); if ( i++ == 0 ) { elem.css("display", how); $(this).attr('draggable', false); } else { $(this).hide(); } }); data.display = display; elem.append(selector); $(selector).hover(function(ev) { elem.renderMulti('showSelect', ev); }, function(ev) { elem.renderMulti('hideSelect', ev); }); elem.attr('draggable', true) .bind('dragstart', dragStart); elem.data(pluginName, data); /* store with element */ }); }, /** * @returns {String} holding HTML with a radio button to select a * rendering */ selectMenu: function() { var data = this.data(pluginName); var select = ["
"]; var children = this.children(); function downloadButton(i, name) { var title, glyph; if ( name == "Prolog term" ) { title = "Copy"; glyph = "copy"; } else { title = "Download"; glyph = "download"; } btn = '' + ''; return btn; } var i = 0; for(var i=0; i", downloadButton(i, name), " ", name, ""); } select.push("")[0].download != undefined; } if ( node.hasClass("export-dom") ) { var r = {}; node = node.trigger("export-dom", r); if ( r.element ) { data = r.element.outerHTML; ext = r.extension||"html"; type = r.contentType||"text/html"; } else { alert("Failed to export rendered result"); } } else if ( node.find("svg").length == 1 ) { var svg = node.find("svg"); if ( !svg.attr("xmlns") ) svg.attr("xmlns", "http://www.w3.org/2000/svg"); data = svg[0].outerHTML ext = "svg"; type = "image/svg+xml"; } else { data = node.html(); type = "text/html"; } if ( !aSupportsDownload() ) type = "application/octet-stream"; console.log(type); var blob = new Blob([data], {type:type}); var href = URL.createObjectURL(blob); var filename = "swish-rendered."+ext; var a, input, btn; var span = $.el.div({class:"download"}, btn = $.el.button({ type:"button", class:"close" }), a = $.el.a({ href:href, target:"_blank", download:filename }, "Right click me to download as "), $.el.br(), input = $.el.input({value:filename})); this.append(span); $(btn) .html("×") .on("click", function(ev) { $(span).remove(); }); $(input).on("change keyup paste", function(ev) { $(a).attr("download", $(input).val()); ev.preventDefault(); return false; }); return this; }, /** * @return {String} native Prolog text for a multi-rendered block */ prologText: function() { return this.find("span.render-as-prolog").text(); } }; // methods function selectMenu() { var menu = $("#render-select"); if ( !menu[0] ) { menu = $($.el.form({ id:"render-select", style:"display:none" })); menu.on("click", "a", function(ev) { var a = $(ev.target).closest("a"); var i = a.data("nr"); menu.data("target").renderMulti(a.data("action"), i); return false; }); menu.on("click", function() { var r = $("input[name=render]:checked", $(this)).val(); menu.data("target").renderMulti('select', parseInt(r)); }); menu.hover(function() { hovering = true; startMenuTimeout(); }, function() { resetHover(); }); $("body").append(menu); } return menu; } function closeSelectMenu() { if ( !hovering ) { var menu = selectMenu(); var target = menu.data("target"); if ( target ) { target.removeClass("render-selecting"); menu.data("target", null); } menu.hide(400); } } function startMenuTimeout() { timeout = setTimeout(function() { closeSelectMenu(); }, 400); } function resetHover() { hovering = false; startMenuTimeout(); } function dragStart(ev) { var dt = ev.originalEvent.dataTransfer; dt.setData("Text", $(ev.target).renderMulti('prologText')); return true; } /** * * * @class renderMulti * @tutorial jquery-doc * @memberOf $.fn * @param {String|Object} [method] Either a method name or the jQuery * plugin initialization object. * @param [...] Zero or more arguments passed to the jQuery `method` */ $.fn.renderMulti = function(method) { if ( methods[method] ) { return methods[method] .apply(this, Array.prototype.slice.call(arguments, 1)); } else if ( typeof method === 'object' || !method ) { return methods._init.apply(this, arguments); } else { $.error('Method ' + method + ' does not exist on jQuery.' + pluginName); } }; }(jQuery)); });