{"_id":"54deb1133a91410d001b1791","parentDoc":null,"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"},"category":{"_id":"54e68e62a43fe13500db3879","__v":0,"project":"54d53c7b23010a0d001aca0c","pages":[],"version":"54d5635532d98b0d00384afb","sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-02-20T01:31:13.999Z","from_sync":false,"order":5,"slug":"editor-plugins","title":"Editor Plugins"},"__v":115,"project":"54d53c7b23010a0d001aca0c","user":"54cfa8e1a8a4fd0d00b7fd1d","updates":["55e41169ec33470d004af790"],"next":{"pages":[],"description":""},"createdAt":"2015-02-14T02:21:07.723Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"auth":"required","params":[],"url":""},"isReference":false,"order":2,"body":"A document object represents the contents of a file. It can hold any type of data (so not just strings). The document object is automatically created when opening a tab and is from then on paired with that tab. Most of the API available on the document is used by editor implementations. This article is geared primarily towards working with documents in custom editors.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Creating documents\",\n  \"body\": \"We recommend using the Document class to create documents when you need an abstract representation of file contents.  In most cases you won't need to create documents as this is done automatically when a file is opened via the `tabManager.open()` method. However there is no reason you couldn't create documents and use them for other purposes.\"\n}\n[/block]\n# Object structure\n\nBesides tabs and panes there are several other objects involved in displaying content and making it editable. The following hierarchy describes the relationships between those objects.\n\n- [Pane](https://docs.c9.io/api/#!/api/Pane) - Represent a single pane, housing multiple tabs\n  - [Editor](https://docs.c9.io/api/#!/api/Editor) - The editor responsible for displaying the document in the tab\n  - [Tab](https://docs.c9.io/api/#!/api/Tab) - A single tab (button) in a pane\n    - [Document](https://docs.c9.io/api/#!/api/Document) - The representation of a file in the tab\n      - [Session](https://docs.c9.io/api/#!/api/Session) - The session information of the editor\n      - [UndoManager](https://docs.c9.io/api/#!/api/UndoManager) - The object that manages the undo stack for this document\n\nIt is important to note that the `Editor` plugin(s) are related to the pane, not the tab. There's only one instance of a certain editor for each pane. The editor is responsible for displaying the content of a document based on the tab that is active. There can be a maximum of one tab active per pane.\n\n# Opening Files\n\n*For more information on this topic see the [Tabs guide](http://cloud9-sdk.readme.io/v0.1/docs/tabs#opening-tabs)*\n\nThe easiest way to get a references to a document is by opening a file.\n```javascript\ntabManager.openFile(\"/file.js\", function(err, tab){\n    if (err) return console.error(err);\n    \n    var doc = tab.document;\n});\n```\n\n# File Contents\n\n## Retrieving the Contents\n\nThe callback in the example above is called when the contents of the file is loaded. You can then easily fetch the contents of the file like this.\n```javascript\nvar value = doc.value;\n```\n\nIt is important to be aware that requesting the value from the document will trigger the current editor of the document to serialize it's contents. This is often an expensive operation and doesn't scale very well. To prevent this from happening you can access the `recentValue` property which is set when the document is loaded and on each save. A drawback is that it will not have the state in between saves.\n\nIn the following example we fetch a reference to the document prior to the contents being loaded. We then hook the ready event to wait for the contents to become available.\n```javascript\nvar tab = tabManager.openFile(\"file.js\");\nvar doc = tab.document;\n\n// The ready property will be false at this point\nconsole.log(doc.ready);\n\ndoc.on(\"ready\", function(){\n    console.log(\"The loaded value is\", doc.recentValue);\n});\n```\n\n## Setting the Contents\n\nThe most straight forward way to set the contents of a document is to set the `value` property of a document.\n\n```javascript\ndoc.value = \"example\";\n``` \n\nThis will notify the editor that the value has been set to \"example\". It is important to note that the [undo manager](doc:the-undomanager) will not be affected by this. It is therefore recommended to only set this property at the very start of the document. After that use the `setBookmarkedValue()` method to give the document a new value. This will add a new state to the [undo manager](doc:the-undomanager) and will bookmark it which will set the `changed` property on the document to `false`.\n\n```javascript\ndoc.setBookmarkedValue(\"example\");\n```\n\nSetting the value this way has the benefit of preserving the state of your document. For instance for the `ace` plugin this means that folds, selection and the scroll state are kept when updating the value.\n\n##  Listening to Events\n\nWhen you are implementing a [custom editor plugin](doc:creating-an-editor-plugin) you get a document passed in the `documentLoad` event. To process changes to that document listen to the `setValue` and `getValue` events.\n\nThe editor is responsible for calculating the current value of the document. Whenever the value property is accessed, calculate the serialized state and return it.\n```javascript\nvar session = doc.getSession();\ndoc.on(\"getValue\", function(e) {\n    return serializeValue(session);\n}, session);\n```\n\nThe editor is also responsible for displaying the current contents of the document. The `setValue` event is called every time the value of a document is set. (So also when `setBookmarkedValue()` is called).\n```javascript\nvar session = doc.getSession();\ndoc.on(\"setValue\", function(e) {\n    loadValueIntoEditor(e.value);\n}, session);\n```\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Using the session object\",\n  \"body\": \"Pass the `session` object when setting the event handlers to make sure these event handlers are removed when the session terminates. For more information on sessions see below.\"\n}\n[/block]\n# Changed\n\nTo determine whether a document has unsaved changes check the `changed` property. It is set to `true` when the document has unsaved changed and to `false` otherwise. There are several ways to influence this property:\n\n- The undo manager arrived at a bookmark via `doc.undoManager.undo()` or `redo()`\n- Set the current state of the undo manager as bookmarked `doc.undoManager.bookmark()`\n- Set the value of the doc via `doc.setBookmarkedValue()`\n- Change the contents of the document via `doc.undoManager.add(undoItem)`\n\nListen to the `changed` event to get notified when this state changes. Note that unlike the `change` event of the undo manager which is called for each change, this `changed` event is only called when the state changes from true to false or vice versa.\n\n```javascript\ndoc.on(\"changed\", function(e){\n    doc.tab.title = doc.tab.path + (e.changed ? \"*\" : \"\");\n});\n```\n\n# Progress\n\nLoading and saving content of a document can take time. To keep track of the progress the document emits a `progress` event that allows an editor or other plugin to display a UI.\n\n```javascript\ndoc.on(\"progress\", function(e){\n    if (e.complete)\n        console.log(\"Downloaded \", e.total, \"bytes\");\n    else\n        console.log(e.loaded, \"bytes downloaded of\", e.total);\n});\n```\n\nIn [some cases](http://cloud9-sdk.readme.io/v0.1/docs/tabs#loading-content-from-an-alternative-source) your plugin might be in control of loading or storing the content of a document. For those cases you want to set the progress on the document in your plugin.\n\n```javascript\ndoc.progress({\n    loaded: loadedBytes,\n    total: totalBytes,\n    complete: loadedBytes == totalBytes,\n    upload: false // Set this to true when storing the contents of the document\n});\n```\n\n# Serializing and Deserializing State\n\nAll objects of the object structure described in the top of this article have the ability to serialize and deserialize their state into a JSON serializable `state` object. By default the state of a document includes the state of it's properties like `value`, `changed`, `title` and `caption` as well as some of the objects like `meta` and `undoManager` in serializable form. Each editor stores their state on a property that has the same name as the editor's `type` property.\n\n```javascript\n// Retrieve the state\nvar state = doc.getState();\n\n// Set the state\ndoc.setState(state);\n\n// Editor state, for example when this doc is loaded in the ace editor\nvar scrollTop = state.ace.scrolltop;\n```\n\nState retrieving and setting is done automatically for documents that are connected to a tab. This state is stored in the state settings and automatically retrieved when the IDE loads.\n\nIf you want to listen in or add to the state of a document you can set these events.\n```javascript\ndoc.on(\"getState\", function(e){\n    e.state.myExtraProperty = 100;\n});\n\ndoc.on(\"setState\", function(e){\n    var prop = e.state.myExtraProperty;\n});\n```\n\nWhen you get a reference to the document after it's created, for instance via the `open` event of the `tabManager` the state is already set. Read from the `lastState` property to get the last state set.\n\n```javascript\nvar state = doc.lastState;\n```\n\n# Editor\n\nFetch a reference to the editor that currently renders the contents of the document like this.\n```javascript\nvar currentEditor = doc.editor;\n```\nNote that this value will be null if the editor has not yet loaded this document. To fetch the `type` of the editor that will be created when the tab that this document belongs to is activated, retrieve `doc.tab.editorType`.\n\n# Title and Tooltip\n\nDocuments can have a title and a tooltip text that is displayed as the title and tooltip of the tab they are loaded in. For documents that load data from files the `title` is set to the filename and the `tooltip` is set to the full path. It is the editor plugin that decides what the values of these properties are.\n\n# Sessions\n\nA session object allows editors to store information specific for that editor-document relationship. Because a plugin API is [immutable](http://cloud9-sdk.readme.io/v0.1/docs/plugin#immutable-api) it is not possible to store this information on the document itself. In addition, the session object facilitates easy clean up of any events and other state recorded (more in this below).\n\nSession objects are created for each editor type that a document is loaded on. A document can be loaded in different editors through it's life time by one of these (user) actions:\n\n- A user moves a tab from one pane to another (each pane has max one instance of a certain editor type)\n- A user switches the editor (type) via `View/Editors` menu\n- Some plugin switches the editor via the `switchEditor()` method on a tab.\n\nWhenever a document switches editor, the session object is unloaded in order to allow the session to be loaded into the new editor. This means it will clean up any event that was set in the context of the session and it will call the `unload` event.\n\nFor instance, the terminal editor plugin stores the connection id on the `session` object. It also sets numerous events that are unset when a session is unloaded.\n\n## The getSession() Method\n\nThe `doc.getSession()` method returns a session object that is unique for the editor *type* that has the document *currently loaded*. If the session object doesn't exist yet it will create it. A consequence of this is that instances of the same editor have access to the same session object and can use that to transfer information between them.\n\n## Loading a Document in an Editor\n[block:callout]\n{\n  \"type\": \"danger\",\n  \"title\": \"Editor Plugin\",\n  \"body\": \"In the following examples, the `editor` variable is an implementation of [a custom editor](doc:creating-an-editor-plugin).\"\n}\n[/block]\nThe following example uses a session object in a custom editor plugin that uses an iframe to render the content of a document.\n```javascript\neditor.on(\"documentLoad\", function(e){\n    var doc = e.doc;\n    var session = doc.getSession();\n    \n    session.iframe = document.createElement(\"iframe\");\n\n    doc.on(\"setValue\", function get(e) {\n        var url = e.value;\n        session.iframe.src = url;\n    }, session);\n});\n```\n\nBy passing the session object as the 3rd argument of the `on()` method it will be cleaned up when the session unloads. \n\n## Unloading a Document from an Editor\n\nYou have to be aware of the unload flows of both the document and the session to make a decision on where to clean up references.\n\nA document is unloaded when a user closes a tab. Period. Use the unload flow of a document to clean up anything that is specific to the document. In most cases you won't need to hook the unload of the document.\n\nThe session is unloaded when a document is unloaded from an editor (Remember that the session represents the relationship between the document and the editor). This happens when a user closes a tab. **This also happens when a user moves a tab  between panes**.\n\nIn your custom editor plugin you want to make sure that not *everything* is unloaded when the user moves a document between panes. In many cases only part of the init flow of a session needs to be repeated to set the right references. Here's an example:\n\n```javascript\nvar id = 100; // Specific to this editor\n\neditor.on(\"documentLoad\", function(e){\n    var doc = e.doc;\n    var session = doc.getSession();\n    \n    // This event handler is specific to the editor instance\n    doc.on(\"setValue\", function get(e) {\n        session.iframe.src = e.value + \"?id=\" + id;\n    }, session);\n    \n    // Any code after this is only executed the first time or \n    // when the session comes from a different editor.\n    if (session.inited) return;\n    session.inited = true;\n\n    // The iframe can be carried over to other editors of the same type\n    session.iframe = document.createElement(\"iframe\");\n});\n```\n\nNow when the document moves between panes the event handler is cleared and set again, while the iframe remains. You can append the iframe to the container of your editor in the `documentActivate` event which is called when the tab becomes active.\n\n```javascript\neditor.on(\"documentActivate\", function(e){\n    var doc = e.doc;\n    var session = doc.getSession();\n    \n    // Remove a previous iframe, if any\n    if (container.firstChild)\n        container.removeChild(container.firstChild); \n    \n    // Set the iframe to be displayed\n    container.appendChild(session.iframe);\n});\n```\n\nA consequence of this is that we need to clear the state for when the document is loaded in a different editor or when the tab is closed. This is where the `documentUnload` event comes in.\n\n```javascript\neditor.on(\"documentUnload\", function(e){\n    var doc = e.doc;\n    var session = doc.getSession();\n    \n    // Do nothing when switching to an editor of the same type\n    if (e.toEditor.type == e.fromEditor.type)\n        return; \n    \n    session.iframe.parentNode.removeChild(session.iframe);\n    delete session.iframe;\n    delete session.inited;\n});\n```\n\nNote that if you do want to keep the iframe around, in case the user switches back to your editor, then clean those items up in the `doc.on(\"unload\"` handler.\n\n# The meta object\n\nThe document plugin offers a meta object that is used by plugins to set extra state on the document.\n[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"The meta object\",\n  \"body\": \"The `meta` object, available on most plugins (e.g. document, tab, menu) allows you to set properties that are not directly part of the API for that plugin, but which properties can be used by other plugins. For instance the save plugin will check for `meta.ignoreSave` on a document and will not warn when a document with changes is closed when that property is set. \\n\\nAny plugin can extend the `meta` object of a plugin with custom properties. Note that properties starting with `$` are not saved in the object state, while all other properties are and are thus preserved between reloads of the IDE.\"\n}\n[/block]\nThe following table is a subset of the flags that Cloud9 plugins set on the `document.meta` object.\n[block:html]\n{\n  \"html\": \"<table><tr><th>Property</th><th>Plugin</th><th>Description</th></tr>\\n  <tr><td>newfile     </td><td>tabManager      </td><td>Indicates that tabManager shouldn't load the contents. Will be removed by the save plugin after the first save.</td></tr>\\n<tr><td>cloned      </td><td>tabManager      </td><td>Indicates that this tab is a cloned tab (As in the 'Duplicate View' behavior)</td></tr>\\n<tr><td>timestamp   </td><td>metadata        </td><td>The last datetime the document state was synced with the filesystem</td></tr>\\n<tr><td>$ignoreSave </td><td>save            </td><td>The save plugin won't complain when closing a tab with unsaved changes when this is set.</td></tr>\\n<tr><td>ignoreSave  </td><td>save            </td><td>The save plugin won't complain when closing a tab with unsaved changes when this is set. Preserved across reloads.</td></tr>\\n<tr><td>$saving     </td><td>save            </td><td>Indicates the document is being saved</td></tr>\\n<tr><td>$savedValue </td><td>watcher         </td><td>The last known saved value.</td></tr>\\n<tr><td>nofs        </td><td>*               </td><td>Indicates this file does not exist on the filesystem. The save plugin won't try to save this document. The tabManager won't try to load it.</td></tr>\\n<tr><td>error       </td><td>autosave        </td><td>Indicates this document is in some form of error state. Autosave won't try to save this document.</td></tr>\\n<tr><td>$slowSave   </td><td>autosave        </td><td>Indicates this document took a long time to save. Autosave will save this document less often.</td></tr>\\n</table>\\n\"\n}\n[/block]","excerpt":"","slug":"documents","type":"basic","title":"Documents"}
A document object represents the contents of a file. It can hold any type of data (so not just strings). The document object is automatically created when opening a tab and is from then on paired with that tab. Most of the API available on the document is used by editor implementations. This article is geared primarily towards working with documents in custom editors. [block:callout] { "type": "warning", "title": "Creating documents", "body": "We recommend using the Document class to create documents when you need an abstract representation of file contents. In most cases you won't need to create documents as this is done automatically when a file is opened via the `tabManager.open()` method. However there is no reason you couldn't create documents and use them for other purposes." } [/block] # Object structure Besides tabs and panes there are several other objects involved in displaying content and making it editable. The following hierarchy describes the relationships between those objects. - [Pane](https://docs.c9.io/api/#!/api/Pane) - Represent a single pane, housing multiple tabs - [Editor](https://docs.c9.io/api/#!/api/Editor) - The editor responsible for displaying the document in the tab - [Tab](https://docs.c9.io/api/#!/api/Tab) - A single tab (button) in a pane - [Document](https://docs.c9.io/api/#!/api/Document) - The representation of a file in the tab - [Session](https://docs.c9.io/api/#!/api/Session) - The session information of the editor - [UndoManager](https://docs.c9.io/api/#!/api/UndoManager) - The object that manages the undo stack for this document It is important to note that the `Editor` plugin(s) are related to the pane, not the tab. There's only one instance of a certain editor for each pane. The editor is responsible for displaying the content of a document based on the tab that is active. There can be a maximum of one tab active per pane. # Opening Files *For more information on this topic see the [Tabs guide](http://cloud9-sdk.readme.io/v0.1/docs/tabs#opening-tabs)* The easiest way to get a references to a document is by opening a file. ```javascript tabManager.openFile("/file.js", function(err, tab){ if (err) return console.error(err); var doc = tab.document; }); ``` # File Contents ## Retrieving the Contents The callback in the example above is called when the contents of the file is loaded. You can then easily fetch the contents of the file like this. ```javascript var value = doc.value; ``` It is important to be aware that requesting the value from the document will trigger the current editor of the document to serialize it's contents. This is often an expensive operation and doesn't scale very well. To prevent this from happening you can access the `recentValue` property which is set when the document is loaded and on each save. A drawback is that it will not have the state in between saves. In the following example we fetch a reference to the document prior to the contents being loaded. We then hook the ready event to wait for the contents to become available. ```javascript var tab = tabManager.openFile("file.js"); var doc = tab.document; // The ready property will be false at this point console.log(doc.ready); doc.on("ready", function(){ console.log("The loaded value is", doc.recentValue); }); ``` ## Setting the Contents The most straight forward way to set the contents of a document is to set the `value` property of a document. ```javascript doc.value = "example"; ``` This will notify the editor that the value has been set to "example". It is important to note that the [undo manager](doc:the-undomanager) will not be affected by this. It is therefore recommended to only set this property at the very start of the document. After that use the `setBookmarkedValue()` method to give the document a new value. This will add a new state to the [undo manager](doc:the-undomanager) and will bookmark it which will set the `changed` property on the document to `false`. ```javascript doc.setBookmarkedValue("example"); ``` Setting the value this way has the benefit of preserving the state of your document. For instance for the `ace` plugin this means that folds, selection and the scroll state are kept when updating the value. ## Listening to Events When you are implementing a [custom editor plugin](doc:creating-an-editor-plugin) you get a document passed in the `documentLoad` event. To process changes to that document listen to the `setValue` and `getValue` events. The editor is responsible for calculating the current value of the document. Whenever the value property is accessed, calculate the serialized state and return it. ```javascript var session = doc.getSession(); doc.on("getValue", function(e) { return serializeValue(session); }, session); ``` The editor is also responsible for displaying the current contents of the document. The `setValue` event is called every time the value of a document is set. (So also when `setBookmarkedValue()` is called). ```javascript var session = doc.getSession(); doc.on("setValue", function(e) { loadValueIntoEditor(e.value); }, session); ``` [block:callout] { "type": "warning", "title": "Using the session object", "body": "Pass the `session` object when setting the event handlers to make sure these event handlers are removed when the session terminates. For more information on sessions see below." } [/block] # Changed To determine whether a document has unsaved changes check the `changed` property. It is set to `true` when the document has unsaved changed and to `false` otherwise. There are several ways to influence this property: - The undo manager arrived at a bookmark via `doc.undoManager.undo()` or `redo()` - Set the current state of the undo manager as bookmarked `doc.undoManager.bookmark()` - Set the value of the doc via `doc.setBookmarkedValue()` - Change the contents of the document via `doc.undoManager.add(undoItem)` Listen to the `changed` event to get notified when this state changes. Note that unlike the `change` event of the undo manager which is called for each change, this `changed` event is only called when the state changes from true to false or vice versa. ```javascript doc.on("changed", function(e){ doc.tab.title = doc.tab.path + (e.changed ? "*" : ""); }); ``` # Progress Loading and saving content of a document can take time. To keep track of the progress the document emits a `progress` event that allows an editor or other plugin to display a UI. ```javascript doc.on("progress", function(e){ if (e.complete) console.log("Downloaded ", e.total, "bytes"); else console.log(e.loaded, "bytes downloaded of", e.total); }); ``` In [some cases](http://cloud9-sdk.readme.io/v0.1/docs/tabs#loading-content-from-an-alternative-source) your plugin might be in control of loading or storing the content of a document. For those cases you want to set the progress on the document in your plugin. ```javascript doc.progress({ loaded: loadedBytes, total: totalBytes, complete: loadedBytes == totalBytes, upload: false // Set this to true when storing the contents of the document }); ``` # Serializing and Deserializing State All objects of the object structure described in the top of this article have the ability to serialize and deserialize their state into a JSON serializable `state` object. By default the state of a document includes the state of it's properties like `value`, `changed`, `title` and `caption` as well as some of the objects like `meta` and `undoManager` in serializable form. Each editor stores their state on a property that has the same name as the editor's `type` property. ```javascript // Retrieve the state var state = doc.getState(); // Set the state doc.setState(state); // Editor state, for example when this doc is loaded in the ace editor var scrollTop = state.ace.scrolltop; ``` State retrieving and setting is done automatically for documents that are connected to a tab. This state is stored in the state settings and automatically retrieved when the IDE loads. If you want to listen in or add to the state of a document you can set these events. ```javascript doc.on("getState", function(e){ e.state.myExtraProperty = 100; }); doc.on("setState", function(e){ var prop = e.state.myExtraProperty; }); ``` When you get a reference to the document after it's created, for instance via the `open` event of the `tabManager` the state is already set. Read from the `lastState` property to get the last state set. ```javascript var state = doc.lastState; ``` # Editor Fetch a reference to the editor that currently renders the contents of the document like this. ```javascript var currentEditor = doc.editor; ``` Note that this value will be null if the editor has not yet loaded this document. To fetch the `type` of the editor that will be created when the tab that this document belongs to is activated, retrieve `doc.tab.editorType`. # Title and Tooltip Documents can have a title and a tooltip text that is displayed as the title and tooltip of the tab they are loaded in. For documents that load data from files the `title` is set to the filename and the `tooltip` is set to the full path. It is the editor plugin that decides what the values of these properties are. # Sessions A session object allows editors to store information specific for that editor-document relationship. Because a plugin API is [immutable](http://cloud9-sdk.readme.io/v0.1/docs/plugin#immutable-api) it is not possible to store this information on the document itself. In addition, the session object facilitates easy clean up of any events and other state recorded (more in this below). Session objects are created for each editor type that a document is loaded on. A document can be loaded in different editors through it's life time by one of these (user) actions: - A user moves a tab from one pane to another (each pane has max one instance of a certain editor type) - A user switches the editor (type) via `View/Editors` menu - Some plugin switches the editor via the `switchEditor()` method on a tab. Whenever a document switches editor, the session object is unloaded in order to allow the session to be loaded into the new editor. This means it will clean up any event that was set in the context of the session and it will call the `unload` event. For instance, the terminal editor plugin stores the connection id on the `session` object. It also sets numerous events that are unset when a session is unloaded. ## The getSession() Method The `doc.getSession()` method returns a session object that is unique for the editor *type* that has the document *currently loaded*. If the session object doesn't exist yet it will create it. A consequence of this is that instances of the same editor have access to the same session object and can use that to transfer information between them. ## Loading a Document in an Editor [block:callout] { "type": "danger", "title": "Editor Plugin", "body": "In the following examples, the `editor` variable is an implementation of [a custom editor](doc:creating-an-editor-plugin)." } [/block] The following example uses a session object in a custom editor plugin that uses an iframe to render the content of a document. ```javascript editor.on("documentLoad", function(e){ var doc = e.doc; var session = doc.getSession(); session.iframe = document.createElement("iframe"); doc.on("setValue", function get(e) { var url = e.value; session.iframe.src = url; }, session); }); ``` By passing the session object as the 3rd argument of the `on()` method it will be cleaned up when the session unloads. ## Unloading a Document from an Editor You have to be aware of the unload flows of both the document and the session to make a decision on where to clean up references. A document is unloaded when a user closes a tab. Period. Use the unload flow of a document to clean up anything that is specific to the document. In most cases you won't need to hook the unload of the document. The session is unloaded when a document is unloaded from an editor (Remember that the session represents the relationship between the document and the editor). This happens when a user closes a tab. **This also happens when a user moves a tab between panes**. In your custom editor plugin you want to make sure that not *everything* is unloaded when the user moves a document between panes. In many cases only part of the init flow of a session needs to be repeated to set the right references. Here's an example: ```javascript var id = 100; // Specific to this editor editor.on("documentLoad", function(e){ var doc = e.doc; var session = doc.getSession(); // This event handler is specific to the editor instance doc.on("setValue", function get(e) { session.iframe.src = e.value + "?id=" + id; }, session); // Any code after this is only executed the first time or // when the session comes from a different editor. if (session.inited) return; session.inited = true; // The iframe can be carried over to other editors of the same type session.iframe = document.createElement("iframe"); }); ``` Now when the document moves between panes the event handler is cleared and set again, while the iframe remains. You can append the iframe to the container of your editor in the `documentActivate` event which is called when the tab becomes active. ```javascript editor.on("documentActivate", function(e){ var doc = e.doc; var session = doc.getSession(); // Remove a previous iframe, if any if (container.firstChild) container.removeChild(container.firstChild); // Set the iframe to be displayed container.appendChild(session.iframe); }); ``` A consequence of this is that we need to clear the state for when the document is loaded in a different editor or when the tab is closed. This is where the `documentUnload` event comes in. ```javascript editor.on("documentUnload", function(e){ var doc = e.doc; var session = doc.getSession(); // Do nothing when switching to an editor of the same type if (e.toEditor.type == e.fromEditor.type) return; session.iframe.parentNode.removeChild(session.iframe); delete session.iframe; delete session.inited; }); ``` Note that if you do want to keep the iframe around, in case the user switches back to your editor, then clean those items up in the `doc.on("unload"` handler. # The meta object The document plugin offers a meta object that is used by plugins to set extra state on the document. [block:callout] { "type": "warning", "title": "The meta object", "body": "The `meta` object, available on most plugins (e.g. document, tab, menu) allows you to set properties that are not directly part of the API for that plugin, but which properties can be used by other plugins. For instance the save plugin will check for `meta.ignoreSave` on a document and will not warn when a document with changes is closed when that property is set. \n\nAny plugin can extend the `meta` object of a plugin with custom properties. Note that properties starting with `$` are not saved in the object state, while all other properties are and are thus preserved between reloads of the IDE." } [/block] The following table is a subset of the flags that Cloud9 plugins set on the `document.meta` object. [block:html] { "html": "<table><tr><th>Property</th><th>Plugin</th><th>Description</th></tr>\n <tr><td>newfile </td><td>tabManager </td><td>Indicates that tabManager shouldn't load the contents. Will be removed by the save plugin after the first save.</td></tr>\n<tr><td>cloned </td><td>tabManager </td><td>Indicates that this tab is a cloned tab (As in the 'Duplicate View' behavior)</td></tr>\n<tr><td>timestamp </td><td>metadata </td><td>The last datetime the document state was synced with the filesystem</td></tr>\n<tr><td>$ignoreSave </td><td>save </td><td>The save plugin won't complain when closing a tab with unsaved changes when this is set.</td></tr>\n<tr><td>ignoreSave </td><td>save </td><td>The save plugin won't complain when closing a tab with unsaved changes when this is set. Preserved across reloads.</td></tr>\n<tr><td>$saving </td><td>save </td><td>Indicates the document is being saved</td></tr>\n<tr><td>$savedValue </td><td>watcher </td><td>The last known saved value.</td></tr>\n<tr><td>nofs </td><td>* </td><td>Indicates this file does not exist on the filesystem. The save plugin won't try to save this document. The tabManager won't try to load it.</td></tr>\n<tr><td>error </td><td>autosave </td><td>Indicates this document is in some form of error state. Autosave won't try to save this document.</td></tr>\n<tr><td>$slowSave </td><td>autosave </td><td>Indicates this document took a long time to save. Autosave will save this document less often.</td></tr>\n</table>\n" } [/block]