{"_id":"54d5635732d98b0d00384b1b","project":"54d53c7b23010a0d001aca0c","__v":7,"category":{"_id":"54d5635632d98b0d00384afc","__v":5,"pages":["54d5635732d98b0d00384b18","54d5635732d98b0d00384b19","54d5635732d98b0d00384b1a","54d5635732d98b0d00384b1b","54d5635732d98b0d00384b1c","54d5635732d98b0d00384b1d","54d5635732d98b0d00384b1e","54d5635732d98b0d00384b1f","54d5689d7e05890d006f14ea","54d8243a0100e20d00ddd57b","5516e49f5a2a4f1700d4f5f8","552d5437d048dd0d00f36e68"],"version":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-02-06T23:43:10.191Z","from_sync":false,"order":0,"slug":"getting-started","title":"Getting Started"},"parentDoc":null,"user":"54cfa8e1a8a4fd0d00b7fd1d","version":{"_id":"54d5635532d98b0d00384afb","project":"54d53c7b23010a0d001aca0c","__v":10,"forked_from":"54d53c7c23010a0d001aca0f","createdAt":"2015-02-07T00:59:01.934Z","releaseDate":"2015-02-07T00:59:01.934Z","categories":["54d5635632d98b0d00384afc","54d5635632d98b0d00384afd","54d5635632d98b0d00384afe","54d5635632d98b0d00384aff","54d5635632d98b0d00384b00","54d5635632d98b0d00384b01","54d5635632d98b0d00384b02","54d652097e05890d006f153e","54dd1315ca1e5219007e9daa","54e21e2b22de1c230094b147","54e68e62a43fe13500db3879","54fa1d3fe7a0ba2f00306309","551c453a23a1ee190034d19a","551df586e52a0b23000c62b6","551f39be6886f8230055f02a","55a6720751457325000e4d97"],"is_deprecated":false,"is_hidden":false,"is_beta":true,"is_stable":true,"codename":"","version_clean":"0.1.0","version":"0.1"},"updates":[],"next":{"pages":[],"description":""},"createdAt":"2015-02-06T23:49:58.606Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":2,"body":"Packages are the building blocks of cloud9. Every bit of functionality of Cloud9 is implemented in a package. A package consists of one or more plugins. All plugins are first class citizens and therefore have access to all the APIs that the core Cloud9 plugins have access to. This means that you are able to extend, implement or re-implement any feature of Cloud9.\n\nA package can contain several resources to implement your feature(s). The default package template consists of:\n\n```\nc9.ide.default\n    images/\n    snippets/\n    modes/\n    package.json\n    plugin.html\n    plugin.js\n    README.md\n```\n\nNot every package will include all these files and directories. A package must at least contain a package.json and the implementation of a plugin in a .js file.\n\nWe have a tutorial on [creating your first package](doc:create-your-first-package).\n\nWe also have a guide on how to [develop a package](doc:package-development-workflow). That guide explains how to easily get started creating a new package and how to use Cloud9 to easily develop plugins.\n\n# package.json\n\nCloud9 packages contain a package.json in their top level directory similar to [npm packages](http://en.wikipedia.org/wiki/Npm_\\(software\\). This file contains metadata about the package such as it's version, which plugins are available and what config options the plugin(s) should be loaded with.\n\nIn addition to the regular [npm package.json fields](https://docs.npmjs.com/files/package.json), Cloud9 package.json files have their own additions:\n[block:html]\n{\n  \"html\": \"<style>\\n    td, th{ vertical-align: top; text-align: left }\\n    th{ white-space: nowrap } \\n</style>\\n\\n<table>\\n    <tr><th>plugins</th><td>an object with the path relative to the package root of the plugins to load as the key, and an object containing 0 or more properties that are passed as the configuration object to the plugin. (i.e. <code>{ plugins: { \\\"example\\\": { \\\"color: \\\"red\\\" } }</code>)</td></tr>\\n    <tr><th>categories</th><td>an array of all the categories this plugin can be found in (max 5)</td></tr>\\n    <tr><th>repository</th><td>Similar to the repository field in npm packages. For Cloud9 packages this field is required.</td></tr>\\n</table>\"\n}\n[/block]\n# The Plugin\n\nIf you want to extend or change Cloud9's behavior you'll add one or more plugins to your package. Each plugin should have a single purpose and can depend on any other plugin that is available. Your plugins can also depend on each other, though circular dependencies are not allowed. Each plugin you create should be specified in the package.json.\n\n## Dependencies\n\nPlugins provide an interfaces to the Cloud9 plugin ecosystem. These interfaces can be consumed by other plugins. There are many interfaces are available out of the box, provided by Cloud9's core plugins. Some of the core interfaces that cloud9 provides are `Plugin`, `fs`, `proc` and `net`. There are many others and I refer to the [documentation](https://docs.c9.io/api) for them all. In the examples below we'll use `commands`, `ui`, `menus`, `preferences` and `settings`.\n\nNote that all Cloud9 plugins are client side plugins. One of the consequences of this is that we use AMD style loading of the modules (using 'define('). Cloud9 uses [require.js](http://requirejs.org/) under the hood.\n\nThe next example shows the signature of a plugin that depends on `Plugin` and `fs`:\n\n```\ndefine(function(require, exports, module) {\n    main.consumes = [\"Plugin\", \"fs\"];\n    main.provides = [\"myplugin\"];\n    return main;\n\n    function main(options, imports, register) {\n        var Plugin = imports.Plugin;\n        var fs = imports.fs;\n        \n        var plugin = new Plugin(\"Your name\", main.consumes);\n        \n        register(null, {\n            \"myplugin\": plugin\n        });\n    }\n});\n```\n\nIn the example above the `options` argument to the main function will contain the properties that are specified in the package.json. In addition it will contain the `staticPrefix` property and the `apikey` property. The `staticPrefix` is an absolute url pointing to the root of the package folder, which can be used to retrieve resources from that directory. The `apikey` property is used to store persistent data in the database and is better described in the [using persistent data](doc:storing-persistent-data) tutorial.\n\n## Lifecycle\n\nThe lifecycle of a basic plugin has only two events. It starts with load and ends with unload. Other plugins such as [Editor](https://docs.c9.io/api/#!/api/Editor) have many more events. Each event in the lifecycle is triggered by an event on the plugin object.\n\nTo implement the lifecycle for a plugin, add the following code after creating an instance of the plugin:\n\n```\nvar x = 10;\n\nfunction load() {\n    x = 100;\n}\n\n/***** Lifecycle *****/\n\nplugin.on(\"load\", function() {\n    load();\n});\nplugin.on(\"unload\", function() {\n    x = 10;\n});\n```\n\nIn this example we introduce the x variable within the main scope. It is imperative to always reset any variables in the main scope in the unload event. Your plugin can be loaded and unloaded dynamically and this clean up is essential for that.\n\nNote that the load event is triggered immediately after loading the package, so at the start of Cloud9. Any heavy lifting, such as creating HTML elements should be deferred until the user needs them. For more information on this, see the [creating your first package](doc:create-your-first-package) tutorial.\n\n## Public API\n\nYour plugin can expose properties, methods and events to others that reference your plugin. When setting the public API you lock down your plugin from being tampered with by others. This is important for security reasons. The example below adds a property and two events and methods to the interface of a plugin:\n\n```\nvar plugin = new Plugin(\"Your name\", main.consumes);\nvar emit = plugin.getEmitter();\n\nvar showing;\n\nfunction show(){ showing = true; emit(\"show\"); }\nfunction hide(){ showing = false; emit(\"hide\"); }\n\n/***** Register and define API *****/\n\nplugin.freezePublicAPI({\n    get showing(){ return showing; },\n    \n    _events: [\n        \"show\",\n        \"hide\"\n    ],\n    \n    show: show,\n    hide: hide\n});\n```\n\n# Style Sheets\n\nStyle sheets can be placed anywhere in your package. Style sheets can be written as CSS or Less, but Less is recommended. Any style sheets that is loaded will be attached to the DOM by executing the following code:\n\n```\nui.insertCss(require(\"text!./style.css\"), options.staticPrefix, plugin);\n```\n\nNotice how in the example `./` is used to specify a path relative to the plugin file. By supplying the plugin as the last argument the css will be automatically removed when the plugin is unloaded. This pattern is used throughout the Cloud9 APIs. The staticPrefix contains the path to the package directory and is used to load images from your package in the css. Use :::at:::{image-path} in your css to load images and other resources.\n\n## Theming\n\n...\n\n# HTML\n\nIn the same way as css, you can insert HTML from an .html file into the DOM with a single command:\n\n```\nvar markup = require(\"text!./plugin.html\");\nui.insertHtml(document.body, markup, plugin);\n```\n\nThe last argument is again for cleanup, automatically removing the added HTML elements when the plugin is unloaded.\n\n# Readme\n\nThe README.md file is optional. It will allow others to read about and understand the function of your package. We recommend that you create and maintain a README.md file for your package.\n\n# Commands\n\nA common use of plugins is to add extra commands to the vocabulary of Cloud9. Commands consist of code to execute a name and key binding. Commands can be referenced by menu items and buttons as way to trigger the command. Commands are always found in the commands pane and they key bindings can be configured in the key bindings editor in the preferences.\n\nThe following example shows how to create a very basic command. \n\n```\ncommands.addCommand({\n    name: \"oneplusone\",\n    exec: function(){ \n        console.log(1 + 1);\n    },\n}, plugin);\n```\n\nThe command is called `oneplusone` and when [triggered](https://docs.c9.io/api/#!/api/commands-method-exec) the exec function is called, printing `2` in the browser's console. Here's a short code snippet that triggers this command:\n\n```\ncommands.exec(\"oneplusone\");\n```\n\nThe next example is more detailed and specifies a lot more details about the command. This specific example is used in the [format json tutorial]();\n\n```\ncommands.addCommand({\n    name: \"formatjson\",\n    group: \"Format\",\n    bindKey: { \n        mac: \"Shift-Command-J\", \n        win: \"Ctrl-Shift-J\" \n    },\n    exec: function(){ \n        formatJson() \n    },\n    isAvailable: function(editor) {\n        if (editor && editor.ace)\n            return !editor.ace.selection.isEmpty();\n        return false;\n    }\n}, plugin);\n```\n\nOf special interest are the bindKeys, which are the key combos that can be used to trigger this command. Users can change these defaults in the key bindings editor in the preference panel.\n\nThe isAvailable() function determines context for the command. In this case, the `formatjson` command is only available when an ace editor is focussed and there is code selected.\n\n# Menus\n\nThere are two different ways to add menus to Cloud9. The application menu has a different api from the context menu, for practical reasons.\n\n## Application Menu\n\nYou are free to place extra menu items anywhere in the menu structure of Cloud9. You can use an integer to specify the position of your menu items relative to others. The next example shows how to add a menu item to the File menu.\n\n```\nmenus.addItemByPath(\"File/My Menu Item\", new ui.item({\n    command: \"mycommand\"\n}), 300, plugin);\n```\n\nBased on the result of isAvailable() of the command, this menu will either show as enabled or disabled. \n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Positioning using an index\",\n  \"body\": \"Throughout Cloud9 you'll see APIs that allow you to sort your items using integers. By using an int to set the position it is possible to determine the approximate position of, for instance an item in a menu without having any knowledge about other plugins that are loaded. Usually all plugins are a-like and there are no special ranges. Simply choose a position that makes sense to you and use it. \\n\\nTo see what the position ints are simply load Cloud9 with `?menus=1` in the url.\"\n}\n[/block]\n\nAdding a divider works in a similar way:\n\n```\nmenus.addItemByPath(\"File/~\", new ui.divider(), 310, plugin);\n```\n\n## Context Menu\n\nCommands that are linked to specific parts of the interface should be triggered by a menu item in a context menu. The context menu api is slightly different and requires the `Menu`, `MenuItem` and `Divider` plugins in your list of consumed plugins.\n\nThis example shows a straightforward way to create a menu:\n\n```\nvar menu = new Menu({ items: [\n    new MenuItem({ command: \"open\", caption: \"Open...\" }),\n    new MenuItem({ command: \"save\", caption: \"Save\" }),\n    new MenuItem({ command: \"saveas\", caption: \"Save As...\" }),\n]}, plugin);\n\nmenu.show(10, 10);\n```\n\nIt's easy to manipulate menus when needed:\n\n```\nvar item = new MenuItem({ \n    caption : \"Alert\" \n    icon    : \"alert.png\" \n    onclick : function(){\n        alert(\"Alert!\");\n    }\n});\nmenu.append(item);\n\nitem.disabled = true;\nitem.caption = \"No alert\";\n```\n\nSubmenus are normal context menus that are referenced by a menu item:\n\n```\nvar listMenu = new Menu({}, plugin);\nvar item = new MenuItem({ caption: \"List...\", submenu: listMenu });\n\n```\n\n# Adding Configuration Settings\n\nThere are many reasons why plugins need to store persistent data across sessions. Often this can be data specific to a user, the state of the plugin and it's UI or things related to the project. \n\nThe `settings` plugin allows plugins to store settings in a tree-like structure, similar to other key-value stores like Redis or the windows registry. There are three root contexts in which data can be stored. The `user`, `project` and `state`.\n\nUser settings are loaded on each workspace that the user is logged into. Preference like the selected theme and keyboard shortcuts are stored in the `user` context. Settings that are specific to a project are stored in the `project` context. These settings are stored in the .c9/project.settings relative to the project directory. Settings like the tab distances are stored here. Anyone using that project will load these settings. Finally, the application state is stored in the `state` context. Settings like a panel's width or the selection state of the tree are stored here.\n\nAs a plugin developer, you choose where to store which setting. First specify the defaults:\n\n```\nsettings.on(\"read\", function(){\n    settings.setDefaults(\"user/my-plugin\", [\n        [\"someKey\", \"value\"],\n        [\"otherKey\", \"value\"]\n    ]);\n}, plugin);\n```\n\nIn this case we're adding some user settings. Set and fetch the values like this:\n\n```\nsettings.set(\"user/my-plugin/@someKey\", 100);\nsettings.getNumber(\"user/my-plugin/@someKey\");\n```\n\nOften you'll want to listen to changes to a key. For instance, when a user set's a setting via the preference pane. To respond to the change of a setting use the following code:\n\n```\nsettings.on(\"user/my-plugin/@someKey\", function(value){\n    console.log(\"Value changed to:\", value);\n}, plugin);\n```\n\nNote that the plugin is passed as the 3rd argument to make sure this event handler is cleaned when the plugin unloads.\n\n## Preferences\n\nWhen you want to enable a user to configure some of these settings, use the preferences API to add some UI elements to the preference pane.\n\nSimply describe the widget, the place of the widget in the navigation tree and the setting it is operating on.\n\n```\nprefs.add({\n    \"Example\" : {\n        position: 450,\n        \"My Plugin\" : {\n            position: 100,\n            \"First Setting\": {\n                type: \"checkbox\",\n                setting: \"user/my-plugin/@first\",\n                position: 100\n            }\n        }\n    }\n}, plugin);\n\n```\n\nSimilar to the menus, use the position index to specify how to sort the vertical position of your widget.\n\nThe next example show's how to create a dropdown:\n\n```\n\"Second Setting\": {\n    type: \"dropdown\",\n    setting: \"user/my-plugin/@second\",\n    width: \"185\",\n    position: 200,\n    items: [\n        { value: \"you\", caption: \"You\" },\n        { value: \"me\", caption: \"Me\" },\n        { value: \"all\", caption: \"All\" }\n    ]\n}\n```\n\n# Snippets\n\n...\n\n\n# Language Modes\n\n...\n\n\n# Bundle External Resources\n\nIt's easy to reference external resources from within your plugins. Here are the most common ones:\n\nLoading additional files:\n\n```\nvar lib = require(\"./some_library\");\n```\n\nOr loading files as text:\n\n```\nvar text = require(\"text!./somedata.csv\");\n```\n\nAll your CSS files are parsed as LESS files. To references images in your package simply reference the `@{image-path}` variable: \n\n```\nbackground: url(\"@{image-path}/bg.png\");\n```\n\nLastly, for code in the plugin itself, `options.staticPrefix` will give you the url to the root of the package.\n\n```\niframe.src = options.staticPrefix + \"/index.html\";\n```\n\n# Tests\n\nEach plugin **is required** to have tests. The tests for each plugin should be in a file with the same name as your plugin and have \\_test.js appended to it. For a file called plugin.js the test is called plugin_test.js. \n\n## Writing Tests\n\nUnder the hood, Jasmine executes your tests, so you can assume that any DSL available there is also available to your package.\n\nTests are executed on the client side and they are loaded the same way your plugin is loaded. This means that you can specify any plugin as a dependency for your tests. This includes your own plugin. See [plugin_test.js](...) as a template for your tests.\n\n## Running Tests\n\nStart Cloud9 in debug mode via `Tools/Developer/Start in Debug Mode`. Cloud9 will open in debug mode in a new window. You can now start via the menu `Tools/Developer/Run Test/plugin.js`. Where `plugin.js` is the name of your plugin.\n\n# Publishing\n\nTo allow others use your plugin, you need to make it available on any cdn. Make sure that you have the latest version of c9 cli installed from npm. Go to the directory of your plugin and run c9 build. This command will create c9build folder with all the code of your plugin optimized for loading.\nIf you are using github, you can push this folder to gh-pages branch of your repository, but any other cdn will work too. \n\nTest that your plugin works by copying the url of `c9build/package.<plugin.name>.js` file, and loading it using your init script:\n\n```\nservices.pluginManager.loadPackage([\n        \"https://cdn.url/to/package.<plugin.name>.js\", \n])\n```\n\nCreate a post on https://community.c9.io to let other users find about your plugin.\n\nSee also https://community.c9.io/t/distributing-and-installing-custom-cloud9-plugins/15168","excerpt":"","slug":"create-a-package","type":"basic","title":"Create a Package"}
Packages are the building blocks of cloud9. Every bit of functionality of Cloud9 is implemented in a package. A package consists of one or more plugins. All plugins are first class citizens and therefore have access to all the APIs that the core Cloud9 plugins have access to. This means that you are able to extend, implement or re-implement any feature of Cloud9. A package can contain several resources to implement your feature(s). The default package template consists of: ``` c9.ide.default images/ snippets/ modes/ package.json plugin.html plugin.js README.md ``` Not every package will include all these files and directories. A package must at least contain a package.json and the implementation of a plugin in a .js file. We have a tutorial on [creating your first package](doc:create-your-first-package). We also have a guide on how to [develop a package](doc:package-development-workflow). That guide explains how to easily get started creating a new package and how to use Cloud9 to easily develop plugins. # package.json Cloud9 packages contain a package.json in their top level directory similar to [npm packages](http://en.wikipedia.org/wiki/Npm_\(software\). This file contains metadata about the package such as it's version, which plugins are available and what config options the plugin(s) should be loaded with. In addition to the regular [npm package.json fields](https://docs.npmjs.com/files/package.json), Cloud9 package.json files have their own additions: [block:html] { "html": "<style>\n td, th{ vertical-align: top; text-align: left }\n th{ white-space: nowrap } \n</style>\n\n<table>\n <tr><th>plugins</th><td>an object with the path relative to the package root of the plugins to load as the key, and an object containing 0 or more properties that are passed as the configuration object to the plugin. (i.e. <code>{ plugins: { \"example\": { \"color: \"red\" } }</code>)</td></tr>\n <tr><th>categories</th><td>an array of all the categories this plugin can be found in (max 5)</td></tr>\n <tr><th>repository</th><td>Similar to the repository field in npm packages. For Cloud9 packages this field is required.</td></tr>\n</table>" } [/block] # The Plugin If you want to extend or change Cloud9's behavior you'll add one or more plugins to your package. Each plugin should have a single purpose and can depend on any other plugin that is available. Your plugins can also depend on each other, though circular dependencies are not allowed. Each plugin you create should be specified in the package.json. ## Dependencies Plugins provide an interfaces to the Cloud9 plugin ecosystem. These interfaces can be consumed by other plugins. There are many interfaces are available out of the box, provided by Cloud9's core plugins. Some of the core interfaces that cloud9 provides are `Plugin`, `fs`, `proc` and `net`. There are many others and I refer to the [documentation](https://docs.c9.io/api) for them all. In the examples below we'll use `commands`, `ui`, `menus`, `preferences` and `settings`. Note that all Cloud9 plugins are client side plugins. One of the consequences of this is that we use AMD style loading of the modules (using 'define('). Cloud9 uses [require.js](http://requirejs.org/) under the hood. The next example shows the signature of a plugin that depends on `Plugin` and `fs`: ``` define(function(require, exports, module) { main.consumes = ["Plugin", "fs"]; main.provides = ["myplugin"]; return main; function main(options, imports, register) { var Plugin = imports.Plugin; var fs = imports.fs; var plugin = new Plugin("Your name", main.consumes); register(null, { "myplugin": plugin }); } }); ``` In the example above the `options` argument to the main function will contain the properties that are specified in the package.json. In addition it will contain the `staticPrefix` property and the `apikey` property. The `staticPrefix` is an absolute url pointing to the root of the package folder, which can be used to retrieve resources from that directory. The `apikey` property is used to store persistent data in the database and is better described in the [using persistent data](doc:storing-persistent-data) tutorial. ## Lifecycle The lifecycle of a basic plugin has only two events. It starts with load and ends with unload. Other plugins such as [Editor](https://docs.c9.io/api/#!/api/Editor) have many more events. Each event in the lifecycle is triggered by an event on the plugin object. To implement the lifecycle for a plugin, add the following code after creating an instance of the plugin: ``` var x = 10; function load() { x = 100; } /***** Lifecycle *****/ plugin.on("load", function() { load(); }); plugin.on("unload", function() { x = 10; }); ``` In this example we introduce the x variable within the main scope. It is imperative to always reset any variables in the main scope in the unload event. Your plugin can be loaded and unloaded dynamically and this clean up is essential for that. Note that the load event is triggered immediately after loading the package, so at the start of Cloud9. Any heavy lifting, such as creating HTML elements should be deferred until the user needs them. For more information on this, see the [creating your first package](doc:create-your-first-package) tutorial. ## Public API Your plugin can expose properties, methods and events to others that reference your plugin. When setting the public API you lock down your plugin from being tampered with by others. This is important for security reasons. The example below adds a property and two events and methods to the interface of a plugin: ``` var plugin = new Plugin("Your name", main.consumes); var emit = plugin.getEmitter(); var showing; function show(){ showing = true; emit("show"); } function hide(){ showing = false; emit("hide"); } /***** Register and define API *****/ plugin.freezePublicAPI({ get showing(){ return showing; }, _events: [ "show", "hide" ], show: show, hide: hide }); ``` # Style Sheets Style sheets can be placed anywhere in your package. Style sheets can be written as CSS or Less, but Less is recommended. Any style sheets that is loaded will be attached to the DOM by executing the following code: ``` ui.insertCss(require("text!./style.css"), options.staticPrefix, plugin); ``` Notice how in the example `./` is used to specify a path relative to the plugin file. By supplying the plugin as the last argument the css will be automatically removed when the plugin is unloaded. This pattern is used throughout the Cloud9 APIs. The staticPrefix contains the path to the package directory and is used to load images from your package in the css. Use @{image-path} in your css to load images and other resources. ## Theming ... # HTML In the same way as css, you can insert HTML from an .html file into the DOM with a single command: ``` var markup = require("text!./plugin.html"); ui.insertHtml(document.body, markup, plugin); ``` The last argument is again for cleanup, automatically removing the added HTML elements when the plugin is unloaded. # Readme The README.md file is optional. It will allow others to read about and understand the function of your package. We recommend that you create and maintain a README.md file for your package. # Commands A common use of plugins is to add extra commands to the vocabulary of Cloud9. Commands consist of code to execute a name and key binding. Commands can be referenced by menu items and buttons as way to trigger the command. Commands are always found in the commands pane and they key bindings can be configured in the key bindings editor in the preferences. The following example shows how to create a very basic command. ``` commands.addCommand({ name: "oneplusone", exec: function(){ console.log(1 + 1); }, }, plugin); ``` The command is called `oneplusone` and when [triggered](https://docs.c9.io/api/#!/api/commands-method-exec) the exec function is called, printing `2` in the browser's console. Here's a short code snippet that triggers this command: ``` commands.exec("oneplusone"); ``` The next example is more detailed and specifies a lot more details about the command. This specific example is used in the [format json tutorial](); ``` commands.addCommand({ name: "formatjson", group: "Format", bindKey: { mac: "Shift-Command-J", win: "Ctrl-Shift-J" }, exec: function(){ formatJson() }, isAvailable: function(editor) { if (editor && editor.ace) return !editor.ace.selection.isEmpty(); return false; } }, plugin); ``` Of special interest are the bindKeys, which are the key combos that can be used to trigger this command. Users can change these defaults in the key bindings editor in the preference panel. The isAvailable() function determines context for the command. In this case, the `formatjson` command is only available when an ace editor is focussed and there is code selected. # Menus There are two different ways to add menus to Cloud9. The application menu has a different api from the context menu, for practical reasons. ## Application Menu You are free to place extra menu items anywhere in the menu structure of Cloud9. You can use an integer to specify the position of your menu items relative to others. The next example shows how to add a menu item to the File menu. ``` menus.addItemByPath("File/My Menu Item", new ui.item({ command: "mycommand" }), 300, plugin); ``` Based on the result of isAvailable() of the command, this menu will either show as enabled or disabled. [block:callout] { "type": "warning", "title": "Positioning using an index", "body": "Throughout Cloud9 you'll see APIs that allow you to sort your items using integers. By using an int to set the position it is possible to determine the approximate position of, for instance an item in a menu without having any knowledge about other plugins that are loaded. Usually all plugins are a-like and there are no special ranges. Simply choose a position that makes sense to you and use it. \n\nTo see what the position ints are simply load Cloud9 with `?menus=1` in the url." } [/block] Adding a divider works in a similar way: ``` menus.addItemByPath("File/~", new ui.divider(), 310, plugin); ``` ## Context Menu Commands that are linked to specific parts of the interface should be triggered by a menu item in a context menu. The context menu api is slightly different and requires the `Menu`, `MenuItem` and `Divider` plugins in your list of consumed plugins. This example shows a straightforward way to create a menu: ``` var menu = new Menu({ items: [ new MenuItem({ command: "open", caption: "Open..." }), new MenuItem({ command: "save", caption: "Save" }), new MenuItem({ command: "saveas", caption: "Save As..." }), ]}, plugin); menu.show(10, 10); ``` It's easy to manipulate menus when needed: ``` var item = new MenuItem({ caption : "Alert" icon : "alert.png" onclick : function(){ alert("Alert!"); } }); menu.append(item); item.disabled = true; item.caption = "No alert"; ``` Submenus are normal context menus that are referenced by a menu item: ``` var listMenu = new Menu({}, plugin); var item = new MenuItem({ caption: "List...", submenu: listMenu }); ``` # Adding Configuration Settings There are many reasons why plugins need to store persistent data across sessions. Often this can be data specific to a user, the state of the plugin and it's UI or things related to the project. The `settings` plugin allows plugins to store settings in a tree-like structure, similar to other key-value stores like Redis or the windows registry. There are three root contexts in which data can be stored. The `user`, `project` and `state`. User settings are loaded on each workspace that the user is logged into. Preference like the selected theme and keyboard shortcuts are stored in the `user` context. Settings that are specific to a project are stored in the `project` context. These settings are stored in the .c9/project.settings relative to the project directory. Settings like the tab distances are stored here. Anyone using that project will load these settings. Finally, the application state is stored in the `state` context. Settings like a panel's width or the selection state of the tree are stored here. As a plugin developer, you choose where to store which setting. First specify the defaults: ``` settings.on("read", function(){ settings.setDefaults("user/my-plugin", [ ["someKey", "value"], ["otherKey", "value"] ]); }, plugin); ``` In this case we're adding some user settings. Set and fetch the values like this: ``` settings.set("user/my-plugin/@someKey", 100); settings.getNumber("user/my-plugin/@someKey"); ``` Often you'll want to listen to changes to a key. For instance, when a user set's a setting via the preference pane. To respond to the change of a setting use the following code: ``` settings.on("user/my-plugin/@someKey", function(value){ console.log("Value changed to:", value); }, plugin); ``` Note that the plugin is passed as the 3rd argument to make sure this event handler is cleaned when the plugin unloads. ## Preferences When you want to enable a user to configure some of these settings, use the preferences API to add some UI elements to the preference pane. Simply describe the widget, the place of the widget in the navigation tree and the setting it is operating on. ``` prefs.add({ "Example" : { position: 450, "My Plugin" : { position: 100, "First Setting": { type: "checkbox", setting: "user/my-plugin/@first", position: 100 } } } }, plugin); ``` Similar to the menus, use the position index to specify how to sort the vertical position of your widget. The next example show's how to create a dropdown: ``` "Second Setting": { type: "dropdown", setting: "user/my-plugin/@second", width: "185", position: 200, items: [ { value: "you", caption: "You" }, { value: "me", caption: "Me" }, { value: "all", caption: "All" } ] } ``` # Snippets ... # Language Modes ... # Bundle External Resources It's easy to reference external resources from within your plugins. Here are the most common ones: Loading additional files: ``` var lib = require("./some_library"); ``` Or loading files as text: ``` var text = require("text!./somedata.csv"); ``` All your CSS files are parsed as LESS files. To references images in your package simply reference the `@{image-path}` variable: ``` background: url("@{image-path}/bg.png"); ``` Lastly, for code in the plugin itself, `options.staticPrefix` will give you the url to the root of the package. ``` iframe.src = options.staticPrefix + "/index.html"; ``` # Tests Each plugin **is required** to have tests. The tests for each plugin should be in a file with the same name as your plugin and have \_test.js appended to it. For a file called plugin.js the test is called plugin_test.js. ## Writing Tests Under the hood, Jasmine executes your tests, so you can assume that any DSL available there is also available to your package. Tests are executed on the client side and they are loaded the same way your plugin is loaded. This means that you can specify any plugin as a dependency for your tests. This includes your own plugin. See [plugin_test.js](...) as a template for your tests. ## Running Tests Start Cloud9 in debug mode via `Tools/Developer/Start in Debug Mode`. Cloud9 will open in debug mode in a new window. You can now start via the menu `Tools/Developer/Run Test/plugin.js`. Where `plugin.js` is the name of your plugin. # Publishing To allow others use your plugin, you need to make it available on any cdn. Make sure that you have the latest version of c9 cli installed from npm. Go to the directory of your plugin and run c9 build. This command will create c9build folder with all the code of your plugin optimized for loading. If you are using github, you can push this folder to gh-pages branch of your repository, but any other cdn will work too. Test that your plugin works by copying the url of `c9build/package.<plugin.name>.js` file, and loading it using your init script: ``` services.pluginManager.loadPackage([ "https://cdn.url/to/package.<plugin.name>.js", ]) ``` Create a post on https://community.c9.io to let other users find about your plugin. See also https://community.c9.io/t/distributing-and-installing-custom-cloud9-plugins/15168