diff --git a/platform/src/Button.js b/platform/src/Button.js new file mode 100644 index 0000000..2d817ee --- /dev/null +++ b/platform/src/Button.js @@ -0,0 +1,80 @@ + +var buttonTypes= { + BUTTON_ACTION: 'BUTTON_ACTION', + BUTTON_HELP: 'BUTTON_HELP' +} + +class Button { + id; + icon; + hint; + action; + type; + + /** + * Create a Button + * @param {object} buttonConfigObject + * @param {string} parentPanel + */ + constructor(buttonConfigObject, parentPanel){ + this.id= buttonConfigObject.id; + this.icon= buttonConfigObject.icon; + this.hint= buttonConfigObject.hint; + this.action = buttonConfigObject.action; + + // Set button's onclick action + if (buttonConfigObject["url"] != undefined) { + this.type = buttonTypes.BUTTON_HELP; + this.action = "window.open('" + buttonConfigObject.url + "');"; + + } else if (buttonConfigObject["actionfunction"] != undefined) { + this.type = buttonTypes.BUTTON_ACTION; + this.action = "runAction( '" + parentPanel + "', '" + buttonConfigObject.id +"' )"; + + } else if (buttonConfigObject["internal"] != undefined) { + this.action = buttonConfigObject.internal; + + } else { + console.log( "Button '" + buttonConfigObject.id + "' with uknown key."); + } + } + + + buttonHtml() { + return ""; + } + + /** + * Get a string representation of the button for its display + * @returns {String} DOM object with html, cls and onclick properties + */ + getView() { + var buttonData={}; + + buttonData.html= this.buttonHtml(); + buttonData.cls= "sys-button"; + buttonData.onclick= this.action; + + return buttonData; + } + + + /** + * Create an array of buttons from an array of configurations + * @param {object[]} buttonConfigs + * @param {string} parentPanel + * @returns {Button[]} the Button objects + */ + static createButtons(buttonConfigs, parentPanel){ + + let buttons= []; + + buttonConfigs.forEach((config)=>{ + buttons.push( new Button(config, parentPanel) ); + }) + + return buttons; + } +} + +export {Button} \ No newline at end of file diff --git a/platform/src/ConsolePanel.js b/platform/src/ConsolePanel.js index ddd4c13..13e82e0 100644 --- a/platform/src/ConsolePanel.js +++ b/platform/src/ConsolePanel.js @@ -1,5 +1,6 @@ import { Panel } from "./Panel.js"; import { define } from "ace-builds"; +import { Button } from "./Button.js"; class ConsolePanel extends Panel { @@ -7,19 +8,22 @@ class ConsolePanel extends Panel { super(id); this.editor.setReadOnly(true); this.editor.setValue("", 1); - this.element.dataset.customButtons = JSON.stringify(this.getButtons()); + + let buttons = []; + let clearButton = new Button( + { id:"clear", + hint:"Clear the console", + internal: `panels.find((p) => p.id==="${this.id}").editor.setValue('')`, + icon: "clear" }, + this.id + ); + buttons.push(clearButton); + this.addButtons(buttons); + this.detectHyperlinks(this.editor); this.setTitleAndIcon("Console", "console"); } - getButtons() { - return [{ - html: this.buttonHtml("clear", "Clear the console"), - cls: "sys-button", - onclick: "consolePanel.setValue('')" - }]; - } - setOutput(str) { document.getElementById(this.id + "Editor").style.color = "black"; this.editor.getSession().setUseWrapMode(false); diff --git a/platform/src/MetamodelPanel.js b/platform/src/MetamodelPanel.js index 918b6a7..03a1ca4 100644 --- a/platform/src/MetamodelPanel.js +++ b/platform/src/MetamodelPanel.js @@ -3,7 +3,6 @@ import { ModelPanel } from './ModelPanel.js'; class MetamodelPanel extends ModelPanel { constructor(id) { super(id, true, null); - this.element.dataset.customButtons = JSON.stringify(this.getButtons()); this.setTitleAndIcon("Metamodel", "emfatic"); } @@ -11,22 +10,6 @@ class MetamodelPanel extends ModelPanel { this.editor.getSession().setMode("ace/mode/emfatic"); } - getButtons() { - return [{ - html: this.buttonHtml("help", "Emfatic language reference"), - cls: "sys-button", - onclick: "window.open('https://www.eclipse.org/epsilon/doc/articles/playground/#emfatic-metamodels-in-the-playground');" - },{ - html: this.buttonHtml("refresh", "Render the metamodel class diagram"), - cls: "sys-button", - onclick: this.id + "Panel.refreshDiagram()" - },{ - html: this.buttonHtml("diagram", "Show/hide the metamodel class diagram"), - cls: "sys-button", - onclick: "toggle('" + this.id + "Diagram', function(){" + this.id + "Panel.refreshDiagram();})" - }]; - } - refreshDiagram() { this.refreshDiagramImpl(backend.getEmfaticToPlantUMLService(), this.id + "Diagram", "metamodel", null, this.getEditor()); } diff --git a/platform/src/ModelPanel.js b/platform/src/ModelPanel.js index 2192025..968ea53 100644 --- a/platform/src/ModelPanel.js +++ b/platform/src/ModelPanel.js @@ -13,7 +13,6 @@ class ModelPanel extends Panel { this.editable = editable; this.metamodelPanel = metamodelPanel; this.setupSyntaxHighlighting(); - this.element.dataset.customButtons = JSON.stringify(this.getButtons()); this.setTitleAndIcon("Model", "flexmi"); } @@ -62,22 +61,6 @@ class ModelPanel extends Panel { } } - getButtons() { - return this.editable ? [{ - html: this.buttonHtml("help", "Flexmi language reference"), - cls: "sys-button", - onclick: "window.open('https://www.eclipse.org/epsilon/doc/flexmi');" - }, { - html: this.buttonHtml("refresh", "Render the model object diagram"), - cls: "sys-button", - onclick: this.id + "Panel.refreshDiagram()" - }, { - html: this.buttonHtml("diagram", "Show/hide the model object diagram"), - cls: "sys-button", - onclick: "toggle('" + this.id + "Diagram', function(){" + this.id + "Panel.refreshDiagram();})" - }] : []; - } - /* TODO: Rename to something more sensible */ refreshDiagramImpl(url, diagramId, diagramName, modelEditor, metamodelEditor) { var xhr = new XMLHttpRequest(); diff --git a/platform/src/OutputPanel.js b/platform/src/OutputPanel.js index b4beaee..a6df7c2 100644 --- a/platform/src/OutputPanel.js +++ b/platform/src/OutputPanel.js @@ -1,5 +1,7 @@ + import { ModelPanel } from "./ModelPanel.js"; import { language } from "./Playground.js"; +import { Button } from "./Button.js"; class OutputPanel extends ModelPanel { @@ -13,20 +15,25 @@ class OutputPanel extends ModelPanel { this.outputType = outputType; this.outputLanguage = outputLanguage; this.language = language; - this.element.dataset.customButtons = JSON.stringify(this.getButtons()); + + let buttons = []; + if (this.outputType == "code"){ + let highlightButton = new Button( + { id:"highlight", + hint:"Set generated text language", + internal: `panels.find((p) => p.id==="${this.id}").editor.setOutputLanguage()`, + icon: "highlight" }, + this.id + ); + buttons.push(highlightButton); + } + this.addButtons(buttons); + this.getEditor().getSession().setMode("ace/mode/" + outputLanguage.toLowerCase()); - //this.getEditor().getSession().setUseWrapMode(false); } setupSyntaxHighlighting() {} - getButtons() { - return (this.outputType == "code") ? [{ - html: this.buttonHtml("highlight", "Set generated text language"), - cls: "sys-button", - onclick: this.id + "Panel.setOutputLanguage()" - }] : []; - } getSelect() { return Metro.getPlugin("#generatedFiles", 'select'); diff --git a/platform/src/Panel.js b/platform/src/Panel.js index 00e95a3..af624df 100644 --- a/platform/src/Panel.js +++ b/platform/src/Panel.js @@ -97,32 +97,20 @@ class Panel { return this.type; } - buttonHtml(icon, hint) { - return ""; - } - - /** * Add the buttons to the page - * @param {object[]} buttons Objects with attributes: icon, hint, action - * - * TODO Support image files for icon + * @param {Button[]} buttons - The Button objects to add. */ addButtons(buttons){ + if (buttons.length > 0){ + var buttonViewData= buttons.map( (btn) => { + return btn.getView(); + }); - var buttonViewData= buttons.map( (btn) => { - var buttonData={}; - - buttonData.html= this.buttonHtml(btn.icon, btn.hint); - buttonData.cls= "sys-button"; - buttonData.onclick= btn.action; + buttonViewData.reverse(); // So they are displayed in the order they are defined - return buttonData; - }); - - buttonViewData.reverse(); // So they are displayed in the order they are defined - - this.element.dataset.customButtons = JSON.stringify(buttonViewData); + this.element.dataset.customButtons = JSON.stringify(buttonViewData); + } } diff --git a/platform/src/Playground.js b/platform/src/Playground.js index 6c8e34a..af24dde 100644 --- a/platform/src/Playground.js +++ b/platform/src/Playground.js @@ -21,6 +21,7 @@ import { OutputPanel } from "./OutputPanel.js"; import { TestPanel } from './TestPanel.js'; import { BlankPanel } from './BlankPanel .js'; import { XtextEditorPanel } from './XtextEditorPanel.js'; +import { Button } from './Button.js'; import { Preloader } from './Preloader.js'; import { Backend } from './Backend.js'; @@ -262,8 +263,8 @@ function initialisePanels() { /** * Create a panel for a given panel config entry * - * @param {string} panel - * @return {Panel} + * @param {Object} panel - The activity config panel definition. + * @return {Panel} the platform Panel */ function createPanelForDefinitionId(panel){ const panelDefinition = panel.ref; @@ -278,7 +279,6 @@ function initialisePanels() { newPanel = new ProgramPanel(newPanelId); // Set from the tool panel definition - newPanel.setIcon(panelDefinition.icon); newPanel.setEditorMode(panelDefinition.language); newPanel.setType(panelDefinition.language); @@ -301,8 +301,6 @@ function initialisePanels() { const panelDef = toolsManager.getPanelDefinition(newPanelId); newPanel = new OutputPanel(newPanelId, panelDefinition.language, outputType, outputLanguage); - - newPanel.setIcon(panelDefinition.icon); newPanel.hideEditor(); newPanel.showDiagram(); @@ -314,7 +312,6 @@ function initialisePanels() { newPanel = new XtextEditorPanel(newPanelId, editorUrl, panel.extension); - newPanel.setIcon(panelDefinition.icon); newPanel.setType(panelDefinition.language); break; @@ -327,44 +324,38 @@ function initialisePanels() { // Add elements common to all panels newPanel.setTitle(panel.name); - if (panelDefinition.buttons != null){ - - var buttons = panel.ref.buttons.map( (btn) => { - var buttonData = {}; - - buttonData.icon = btn.icon; - buttonData.hint = btn.hint; - buttonData.action = generateButtonOnclickHtml(btn, panel.id); - - return buttonData; + if(panel.icon != null){ + newPanel.setIcon(panel.icon); + } else { + newPanel.setIcon(panelDefinition.icon); + } + + if (panel.buttons == null && panelDefinition.buttons != null){ + // No activity defined buttons + newPanel.addButtons( Button.createButtons( panelDefinition.buttons, panel.id)); + + } else if (panel.buttons != null && panelDefinition.buttons != null) { + // The activity has defined the buttons + let resolvedButtonConfigs = panel.buttons.map(btn =>{ + let resolvedButton; + + if (btn.ref){ + // button reference so resolve + resolvedButton= panelDefinition.buttons.find((pdBtn)=> pdBtn.id===btn.ref); + } else { + // activity defined button + resolvedButton= btn; + } + return resolvedButton; }); - - newPanel.addButtons(buttons); - } - - + panel.buttons = resolvedButtonConfigs; + newPanel.addButtons( Button.createButtons( resolvedButtonConfigs, panel.id)); + } + return newPanel; } -function generateButtonOnclickHtml(button, panelId){ - - var onclickHtml; - - if (button["url"] != undefined) { - onclickHtml = "window.open('" + button.url + "');"; - - } else if (button["actionfunction"] != undefined) { - onclickHtml = "runAction( '" + panelId + "', '" + button.id +"' )"; - - } else { - console.log( "Button '" + button.id + "' with uknown key."); - } - - return onclickHtml; -} - - function copyToClipboard(str) { var el = document.createElement('textarea'); el.value = str; @@ -816,7 +807,14 @@ function runAction(source, sourceButton) { // Get the action var action = activityManager.getActionForCurrentActivity(source, sourceButton); - const buttonConfig = action.source.ref.buttons.find( btn => btn.id == sourceButton ); + let buttonConfig; + if(action.source.buttons){ + //Buttons defined by activity + buttonConfig= action.source.buttons.find( btn => btn.id == sourceButton ); + } else { + //Buttons defined by tool + buttonConfig= action.source.ref.buttons.find( btn => btn.id == sourceButton ); + } const toolActionFunction = toolsManager.getActionFunction( buttonConfig.actionfunction ); // TODO tidy up by resolving tool references // Create map containing panel values diff --git a/platform/test/spec/testButtonSpec.js b/platform/test/spec/testButtonSpec.js new file mode 100644 index 0000000..405e995 --- /dev/null +++ b/platform/test/spec/testButtonSpec.js @@ -0,0 +1,51 @@ + +import {Button} from "../../src/Button.js" + + +describe("Button", () => { + + const btnConfigAf = { + id: "1", + icon: "ico", + actionfunction: "af", + hint: "hn" + } + + it("can be created", () => { + let btn = new Button(btnConfigAf, "pid"); + expect(btn instanceof Button).toBe(true); + }) + + it("has an id set by a config object", () => { + let btn = new Button(btnConfigAf, "pid"); + expect(btn.id).toBe("1"); + }) + + it("has an icon set by a config object", () => { + let btn = new Button(btnConfigAf, "pid"); + expect(btn.icon).toBe("ico"); + }) + + it("has an hint set by a config object", () => { + let btn = new Button(btnConfigAf, "pid"); + expect(btn.hint).toBe("hn"); + }) + + it("getView - outputs a DOM representation for customButtons properties", () => { + const expectedDomObject = { + "html": "", + "cls": "sys-button", + "onclick": "runAction( 'pid', '1' )" + } + + let btn = new Button(btnConfigAf, "pid"); + expect(btn.getView()).toEqual(expectedDomObject); + }) + + it("createButtons - creates multiple buttons from an array of button objects", () => { + let btns = Button.createButtons([btnConfigAf, btnConfigAf, btnConfigAf, btnConfigAf], "pid"); + + expect(btns.length).toBe(4); + expect(btns[0]).toEqual(new Button(btnConfigAf, "pid")); + }) +})