[[BackLinksMenu]] [[TicketQuery(summary=GROUP_SCRIPTING_R0, format=table, col=summary|owner|status|type|component|priority|effort|importance, rows=description|analysis_owners|analysis_reviewers|analysis_score|design_owners|design_reviewers|design_score|implementation_owners|implementation_reviewers|implementation_score|test_owners|test_reviewers|test_score|)]] = Analysis = == Overview == General Scripting overview: The users should be able to type and execute custom scripts. * A script is a resource. * Execute button should be present inside the resource pallete. * Scripts could be edited inside a document window. Again execute button should be available. While editing the resource is saved automatically. * Scripts could also be executed via links. * The resource preview for scripts should allow editing. * There should be ability to import/export script files. * The user could create/edit/delete scripts. == Task requirements == Create prototype for scripting that includes the following functionality: * Ability to add/edit/delete script files. * Add 'Insert Script File' button inside 'Insert' menu * Create 'Insert Script' button inside 'Insert' menu that creates new resource. * Provide document window that allows editing for scripts as well as executing them. * Add 'Execute' button inside resource details pallete, that is visible only when script resource is selected. * Create 'Hello world' example via scripting in Sophie. * OPTIONAL: Provide functionality that allows scripts to be evoked from links. * Provide functionality that allows for script resource to be exported to files. Again in the resource pallete. Also while editing it from the file menu save and save as should be functional. (NOTE: Saving the resource should be automatically, but exporting it to file is not). == Task result == * The result should be code. == Implementation idea == * Use the Rhino library: [http://www.mozilla.org/rhino/]. * The output for the script could be Eclipse console or popup window. == Related == ^(Add links to related tasks that could be useful or helpful.)^ == How to demo == * Click Insert -> Script. A new window should be opened. * Write a JavaScript code that outputs "Hello world". * Click the Run button. = Design = * Create a Sophie module {{{org.sophie2.extra.func.scripting}}}. * Create a class {{{org.sophie2.extra.func.scripting.ScriptingModule}}} that extends {{{SophieModule}}}. * Create subpackages model, view and logic. * model: * class ScriptResource that extends Resource. * kind is "script" * RwProp scriptText - the javascript source code text * subpackage {{{persistence}}} * class {{{ScriptResPersister}}} extends {{{Persister}}} * schema "resource:script|storage|r3" * the storage should have one child for the script text * this persister is needed when the parent book is persisted. * register the persister as an extension * view: * menu items InsertScriptItem and InsertScriptFileItem in Insert menu * enum EventIds that contains the identifiers of all events that can be fired by any UI component in this package * INSERT_SCRIPT * INSERT_SCRIPT_FILE * RUN_SCRIPT * TEXT_CHANGED * OPEN_WINDOW * document window for resource viewing, editing and running: * class {{{ScriptDocumentWindow}}} extends {{{DefaultDocumentWindow}}} implements {{{DocumentView}}} * In DocumentWindow add Prop modelDocument() * RwProp modelDocument - the script resource that is displayed * resource property {{{swingFrameSync}}} that adds the following components to {{{swingComponent}}}: * a text area that fires TEXT_CHANGED * "Run" {{{LogicR3Button}}} that fires RUN_SCRIPT * a text area for the script result or error messages * class {{{ScriptDocumentWindowProvider}}} implements {{{DocumentWindowProvider}}} (see last section) * getWindow should return a new {{{ScriptDocumentWindow}}} for the given script resource * register it as an extension * resource preview of scripts (in Resource Preview Palette): * class {{{ScriptResourcePreviewProvider}}} implements {{{ResourcePreviewProvider}}} * {{{getResourceKind()}}} should return the kind in {{{ScriptResource}}} * {{{filterKind()}}} should return "Script" * nested static class {{{ScriptResourcePreview}}} which is a {{{SwingVisualElement}}} * constructor with argument of type {{{ScriptResource}}} * in {{{swingComponent}}} create a new JPanel with: * read-only JTextArea that displays the script content * "Open" LogicR3Button that fires OPEN_WINDOW * {{{getVisualElement(Resource element)}}} should return a new instance of {{{ScriptResourcePreview}}} * In ResourceDetailsPalette create a property RwProp previewVisualElement * use @Own annotation because the parent is required in the logic of OPEN_WINDOW * register the class as an extension * logic: * enum ScriptingLogic that implements OperationDef: * all of the following items should listen for an event with the corresponding id * INSERT_SCRIPT * listens for the event in {{{InsertScriptItem}}} * creates a new empty {{{ScriptResource}}} in current book (use {{{CurrentBookUtil.getCurrentBook}}}) * adds it to {{{App.documents}}} (see last section for details) * sets it as current * INSERT_SCRIPT_FILE * listens for the event in {{{InsertScriptFileItem}}} * displays a file dialog for "JavaScript files (*.js)" with "Insert" button * using the persisters, creates a new {{{ScriptResource}}} in current book * adds it to {{{App.documents}}} (see last section) * sets it as current * RUN_SCRIPT * write the following temporary solution: * create a new context using {{{Context cx = Context.enter();}}} * initialize the standard objects (Object, Function, etc.) using {{{Scriptable scope = cx.initStandardObjects();}}} * evaluate the resource's text: {{{Object result = cx.evaluateString(scope, s, "", 1, null);}}} * display the result in the result text area. * if org.mozilla.javascript.EcmaError or org.mozilla.javascript.EvaluatorException is thrown, display the message in the result text area * exit from the context with {{{Context.exit();}}}. Call this from a finally block. * TEXT_CHANGED * the following solution can be optimized: * create a new AutoChange that changes the text in the resource with that from the text area. Register the change to the script resource. * OPEN_WINDOW * get the source which is of type {{{ScriptResourcePreview}}} * add its model to the list {{{App.documents}}}, but only if it is not already added * set the document as current * register the enum as an extension * Currently all open documents are books. Sophie's main window should display also images, scripts and so on. * Rename the {{{books}}} property in {{{App}}} to {{{documents}}}. * Create an interface {{{org.sophie2.main.app.commons.windows.DocumentWindowProvider}}} * String getResourceKind() - Gets the resource kind of the resource for which this provider should create appropriate document window. * DocumentWindowProvider getWindowProvider(ResourceRef ref) - Creates a new concrete implementation of {{{DocumentWindowProvider}}} for the given resource. * Create an extension point in {{{MainAppModule}}} for document window providers * Class {{{org.sophie2.main.app.commons.windows.BookDocumentWindowProvider}}} implements {{{DocumentWindowProvider}}} and creates book document windows * Register it as an extension * Class {{{org.sophie2.main.app.commons.windows.DocumentWindowProviderFactory}}} * static DocumentWindowProvider getWindowProvider(ResourceRef ref) * iterates over all extensions in the extension point * if an appropriate provider is found, returns it * Refactor {{{AppDocumentsDesktop.windows}}}: * create a class DocumentInfo that has two fields - ResourceRef ref and DocumentWindowProvider provider * create a hash map with keys of type DocumentInfo and values of type DocumentWindow * override equals and hashCode methods * create a hashCode in ResourceRef too * replace the TrackingProList with ComposingProList * iterate over all open documents and use the factory to find the appropriate provider * if the cache (the hash map) contains DocumentInfo with these resource reference and provider, return the window * if not, create a new window with the factory and store it in the cache * Source code: [source:/branches/private/deni/scripting/] * Tests: [source:/branches/private/deni/scripting/modules/org.sophie2.extra.func.scripting/src/test/java/org/sophie2/extra/func/scripting] = Implementation = * Source code: [source:/branches/private/deni/scripting/] = Testing = ^(Place the testing results here.)^ = Comments = * The goal of this task is mainly to provide interface for scripting