/* Part of SWISH Author: Jan Wielemaker E-mail: J.Wielemaker@cs.vu.nl WWW: http://www.swi-prolog.org Copyright (C): 2014-2018, 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 * This file deals with tabbed panes. It implements dynamic tabs on top * if Bootstrap. * * @version 0.2.0 * @author Jan Wielemaker, J.Wielemaker@vu.nl * @requires jquery */ define(["jquery", "form", "config", "preferences", "modal", "laconic", "search", "chatbell", "sourcelist" ], function($, form, config, preferences, modal) { var the_iframe_tab; var modes = []; /** * Suggests a mode based on the file extension present in the given path * @param {string} path The path to the file * @returns {object} Returns an object containing information about the * suggested mode. */ function getModeForPathAlt(path) { var mode = aceModesByNameOrExt.text; var fileName = path.split(/[\/\\]/).pop(); for (var i = 0; i < modes.length; i++) { if (modes[i].supportsFile(fileName)) { mode = modes[i]; break; } } return mode; } var Mode = function(name, caption, extensions) { this.name = name; this.caption = caption; this.mode = "ace/mode/" + name; this.extensions = extensions; var re; if (/\^/.test(extensions)) { re = extensions.replace(/\|(\^)?/g, function(a, b) { return "$|" + (b ? "^" : "^.*\\."); }) + "$"; } else { re = "^.*\\.(" + extensions + ")$"; } this.extRe = new RegExp(re, "gi"); }; Mode.prototype.supportsFile = function(filename) { return filename.match(this.extRe); }; // todo firstlinematch var supportedAceModesInit = { ABAP: ["abap"], ABC: ["abc"], ActionScript: ["as"], ADA: ["ada|adb"], Apache_Conf: ["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"], AsciiDoc: ["asciidoc|adoc"], Assembly_x86: ["asm|a"], AutoHotKey: ["ahk"], BatchFile: ["bat|cmd"], Bro: ["bro"], C_Cpp: ["cpp|c|cc|cxx|h|hh|hpp|ino"], C9Search: ["c9search_results"], Cirru: ["cirru|cr"], Clojure: ["clj|cljs"], Cobol: ["CBL|COB"], coffee: ["coffee|cf|cson|^Cakefile"], ColdFusion: ["cfm"], CSharp: ["cs"], CSS: ["css"], Curly: ["curly"], D: ["d|di"], Dart: ["dart"], Diff: ["diff|patch"], Dockerfile: ["^Dockerfile"], Dot: ["dot"], Drools: ["drl"], Dummy: ["dummy"], DummySyntax: ["dummy"], Eiffel: ["e|ge"], EJS: ["ejs"], Elixir: ["ex|exs"], Elm: ["elm"], Erlang: ["erl|hrl"], Forth: ["frt|fs|ldr|fth|4th"], Fortran: ["f|f90"], FTL: ["ftl"], Gcode: ["gcode"], Gherkin: ["feature"], Gitignore: ["^.gitignore"], Glsl: ["glsl|frag|vert"], Gobstones: ["gbs"], golang: ["go"], GraphQLSchema: ["gql"], Groovy: ["groovy"], HAML: ["haml"], Handlebars: ["hbs|handlebars|tpl|mustache"], haskell: ["hs|haskell|curry"], Haskell_Cabal: ["cabal"], haXe: ["hx"], Hjson: ["hjson"], // Add themissingmode"Django"toext-modelist Django: ["html"], HTML: ["html|htm|xhtml"], HTML_Elixir: ["eex|html.eex"], HTML_Ruby: ["erb|rhtml|html.erb"], INI: ["ini|conf|cfg|prefs"], Io: ["io"], Jack: ["jack"], Jade: ["jade|pug"], Java: ["java"], JavaScript: ["js|jsm|jsx"], JSON: ["json"], JSONiq: ["jq"], JSP: ["jsp"], JSX: ["jsx"], Julia: ["jl"], Kotlin: ["kt|kts"], LaTeX: ["tex|latex|ltx|bib"], LESS: ["less"], Liquid: ["liquid"], Lisp: ["lisp|pddl|l|subl|cl|clif|lsp|kif|cycl"], // dmiles: + pddl + clif + lsp + kif + cycl LiveScript: ["ls"], LogiQL: ["logic|lql"], LSL: ["lsl"], Lua: ["lua"], LuaPage: ["lp"], Lucene: ["lucene"], Makefile: ["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"], Markdown: ["md|markdown"], Mask: ["mask"], MATLAB: ["matlab"], Maze: ["mz"], MEL: ["mel"], MUSHCode: ["mc|mush"], MySQL: ["mysql"], Nix: ["nix"], NSIS: ["nsi|nsh"], ObjectiveC: ["m|mm"], OCaml: ["ml|mli"], Pascal: ["pas|p"], Perl: ["perl|pm"], // dmiles pl -> perl pgSQL: ["pgsql"], PHP: ["php|phtml|shtml|php3|php4|php5|phps|phpt|aw|ctp|module"], Pig: ["pig"], Powershell: ["ps1"], Praat: ["praat|praatscript|psc|proc"], Razor: ["cshtml|asp"], Logtalk: ["lgt"], Prolog: ["plg|pro|pfc|plt|prolog|pl|chr|lgt|P|cpl|csp|dlv|asp|ec|lps|lpsw|lpst"], // dmiles: + pl + chr Properties: ["properties"], Protobuf: ["proto"], Python: ["py"], R: ["r"], RDoc: ["Rd"], RHTML: ["Rhtml"], RST: ["rst"], Ruby: ["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"], Rust: ["rs"], SASS: ["sass"], SCAD: ["scad"], Scala: ["scala"], Scheme: ["scm|sm|rkt|oak|scheme"], SCSS: ["scss"], SH: ["sh|bash|^.bashrc"], SJS: ["sjs"], Smarty: ["smarty|tpl"], snippets: ["snippets"], Soy_Template: ["soy"], Space: ["space"], SQL: ["sql"], SQLServer: ["sqlserver"], Stylus: ["styl|stylus"], SVG: ["svg"], Swift: ["swift"], Tcl: ["tcl"], Tex: ["tex"], Text: ["txt"], Textile: ["textile"], Toml: ["toml"], TSX: ["tsx"], Twig: ["twig|swig"], Typescript: ["ts|typescript|str"], Vala: ["vala"], VBScript: ["vbs|vb"], Velocity: ["vm"], Verilog: ["v|vh|sv|svh"], VHDL: ["vhd|vhdl"], Wollok: ["wlk|wpgm|wtest"], XML: ["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl|xaml|owl"], // dmiles: + owl XQuery: ["xq"], YAML: ["yaml|yml"] }; var nameOverrides = { ObjectiveC: "Objective-C", CSharp: "C#", golang: "Go", C_Cpp: "C and C++", coffee: "CoffeeScript", HTML_Ruby: "HTML (Ruby)", HTML_Elixir: "HTML (Elixir)", FTL: "FreeMarker" }; var aceModesByNameOrExt = {}; for (var name in supportedAceModesInit) { var data = supportedAceModesInit[name]; var dataExt = data[0].split("|"); var displayName = (nameOverrides[name] || name).replace(/_/g, " "); var filename = name.toLowerCase(); var mode = new Mode(filename, displayName, data[0]); aceModesByNameOrExt[filename] = mode; for (var ext in dataExt) { aceModesByNameOrExt[ext] = mode; } modes.push(mode); } var recurse = 0; // array containing all the editors we will create var aceEditors = []; var tabbed = { tabTypes: {}, type: function(from) { recurse++; if (recurse > 10) { debugger; recurse = 0; return null; } if (from == "prolog") from = "pl"; if (from == "program") from = "pl"; var ret = null; var ext = from.split('.').pop(); for (var k in tabbed.tabTypes) { if (tabbed.tabTypes.hasOwnProperty(k) && tabbed.tabTypes[k].dataType == ext) { ret = tabbed.tabTypes[k]; if (ret != undefined) { recurse--; return ret; } // return tabbed.tabTypes[k]; } } // debugger; ret = tabbed.tabTypes[ext]; if (ret != undefined) { recurse--; return ret; } var mode = getModeForPathAlt(from).mode var extUpC = ext.slice(0, 1).toUpperCase() + ext.slice(1, ext.length); if (!mode.endsWith("/prolog")) { // if (true) return undefined; // if (true) return tabbed.tabTypes.program; } tabbed.tabTypes[ext] = ret = { dataType: ext, typeName: ext, label: (extUpC + " Program"), contentType: "text/x-prolog", order: false, create: function(dom, options) { $(dom).addClass("prolog-editor") .prologEditor($.extend({ placeholder: "Your " + extUpC + " program rules and facts go here ...", codeType: ext, save: true }, options)) .prologEditor('makeCurrent', options); } }; recurse--; return ret; } }; tabbed.tabTypes.permalink = { dataType: "lnk", typeName: "program", label: "Program", create: function(dom, options) { $(dom).addClass("prolog-editor printable") .prologEditor($.extend({ save: true }, options)) .prologEditor('makeCurrent'); } }; function clone(obj) { var copy; // Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj; // Handle Date if (obj instanceof Date) { copy = new Date(); copy.setTime(obj.getTime()); return copy; } // Handle Array if (obj instanceof Array) { copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = clone(obj[i]); } return copy; } // Handle Object if (obj instanceof Object) { copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]); } return copy; } throw new Error("Unable to copy obj! Its type isn't supported."); } (function($) { var pluginName = 'tabbed'; var tabid = 0; /** @lends $.fn.tabbed */ var methods = { /** * Turn the current element into a Bootstrap tabbed pane. All * children of the current element are changed into tabs. The * child can control the mapping using: * * - `data-label = "Label"` * - `data-close = "disabled"` */ _init: function(options) { options = options || {}; return this.each(function() { var elem = $(this); var data = {}; /* private data */ data.newTab = options.newTab; data.tabTypes = options.tabTypes || tabbed.tabTypes; elem.data(pluginName, data); /* store with element */ elem.addClass("tabbed unloadable"); elem.tabbed('makeTabbed'); elem.on("trace-location", function(ev, prompt) { elem.tabbed('showTracePort', prompt); }); elem.on("data-is-clean", function(ev, clean) { var tab = $(ev.target).closest(".tab-pane"); var a = elem.tabbed('navTab', tab.attr('id')); if (a) { if (clean) a.removeClass("data-dirty"); else a.addClass("data-dirty"); } }); elem.on("unload", function(ev) { if (ev.target == elem[0] && elem.closest(".swish").swish('preserve_state')) { var state = elem[pluginName]('getState'); localStorage.setItem("tabs", JSON.stringify(state)); } }); elem.on("restore", function(ev) { var state; if (ev.target == elem[0]) { try { var str = localStorage.getItem("tabs"); if (str) state = JSON.parse(str); } catch (err) {} if (state && typeof(state) == "object") { elem[pluginName]('setState', state); } } }); elem.on("preference", function(ev, pref) { if (pref.name == "preserve-state" && pref.value == false) { localStorage.removeItem("tabs"); } }); }); }, /** * Turn the pane into a tabbed pane */ makeTabbed: function() { var children = this.children(); var ul = $.el.ul({ class: "nav nav-tabs", role: "tablist" }); var contents = $.el.div({ class: "tab-content" }); this.prepend(contents); this.prepend(ul); $(ul).on("click", "span.xclose", function(ev) { var id = $(ev.target).parent().attr("data-id"); $(ev.target).parents(".tabbed").first().tabbed('removeTab', id); ev.preventDefault(); }); $(ul).on("click", "a", function(ev) { $(ev.target).closest("a").tab('show'); ev.preventDefault(); }); /* Turn children into tabs */ for (var i = 0; i < children.length; i++) { var child = $(children[i]); var id = genId(); var label = child.attr("data-label") || "Unknown"; var close = child.attr("data-close") != "disabled"; var active = (i == children.length - 1); /* activate last */ var li = this.tabbed('tabLabel', id, label, close); if (active) $(li).addClass("active"); $(ul).append(li); $(contents).append(wrapInTab($(children[i]), id, active)); } /* Create and handle "+" button */ var create = $.el.a({ class: "tab-new compact", title: "Open a new tab" }, glyphicon("plus")); $(ul).append($.el.li({ class: "tab-new", role: "presentation" }, create)); $(create).on("click", function(ev) { var tabbed = $(ev.target).parents(".tabbed").first(); tabbed.tabbed('newTab'); ev.preventDefault(); return false; }); /* Handle tab-switching */ $(ul).on("shown.bs.tab", "a", function(ev) { var newContentID = $(ev.target).data("id"); $("#" + newContentID + " .swish-event-receiver").trigger("activate-tab"); var s = $("#" + newContentID + " .storage") if(s.storage) { $("#" + newContentID + " .storage").storage("activate"); } }); if (this.tabbed('navContent').children().length == 0) { this.tabbed('newTab'); } }, /** * Add an empty new tab from the "+" button. This calls * options.newTab() to return a DOM element for the new * tab. * @param {HTMLElement} [content] Content for the new tab * If omitted, it calls `options.newTab` or uses the method * `tabSelect`. * @return {jQuery} object representing the created tab */ newTab: function(dom, active) { var data = this.data(pluginName); if (dom == undefined) { if (data.newTab) { dom = data.newTab(); } else { var sl; dom = this.tabbed('tabSelect'); $(dom).append(this.tabbed('profileForm'), $.el.hr(), //this.tabbed('searchForm'), sl = $.el.div({ class: "sourcelist" })); // BEGIN FILE-EXPLORER var ss = $('