c9-core/plugins/c9.ide.preview/previewer.js

586 wiersze
24 KiB
JavaScript

define(function(require, module, exports) {
main.consumes = [
"Plugin", "preview", "MenuItem", "Menu", "Divider", "tabManager",
"error_handler"
];
main.provides = ["Previewer"];
return main;
function main(options, imports, register) {
var Menu = imports.Menu;
var Plugin = imports.Plugin;
var preview = imports.preview;
var errorHandler = imports.error_handler;
var MenuItem = imports.MenuItem;
var Divider = imports.Divider;
var tabs = imports.tabManager;
function Previewer(developer, deps, options) {
var plugin = new Plugin(developer, deps);
var emit = plugin.getEmitter();
emit.setMaxListeners(1000);
var caption = options.caption;
var onclick = options.onclick;
var submenu = options.submenu;
var divider = options.divider;
var selector = options.selector || function() { return false; };
var index = options.index || 100;
var menu, item, div;
var currentSession, currentDocument;
plugin.on("load", function() {
preview.register(plugin, selector);
var rootMenu = preview.previewMenu;
item = rootMenu.append(new MenuItem({
caption: caption,
position: index
}));
if (onclick || !submenu)
item.on("click", onclick || function() {
var editor = tabs.focussedTab.editor;
editor.setPreviewer(plugin.name);
});
if (submenu) {
item.submenu = menu = submenu instanceof Menu
? submenu : new Menu({}, plugin);
}
if (divider)
div = rootMenu.append(new Divider({ position: index + 10 }));
tabs.on("focusSync", function(e) {
if (e.tab.editorType == "preview") {
var session = e.tab.document.getSession();
if (session.previewer == plugin) {
if (currentSession != session) {
activateDocument(e.tab.document);
}
}
}
}, plugin);
});
/***** Methods *****/
function loadDocument(doc, editor, state) {
if (!doc.meta.$previewInited) {
doc.addOther(function() { navigate({ doc: doc }, true); });
doc.meta.$previewInited = true;
}
if (state)
setState(doc, state);
var session = doc.getSession();
session.changeListener = function() {
if (!session.previewTab || !session.previewTab.loaded)
return;
update({
doc: session.previewTab.document,
saved: session.previewTab
.document.undoManager.isAtBookmark()
});
};
session.renameListener = function(e) {
if (!session.previewTab || !session.previewTab.loaded)
return;
navigate({ url: e.path, doc: session.previewTab.document });
};
tabs.on("open", function(e) {
if (e.tab.path == session.path) {
updatePreviewTab(session, null, e.tab);
session.changeListener();
}
}, session);
emit("sessionStart", {
doc: doc,
tab: doc.tab,
session: session,
editor: editor,
state: state
});
}
function unloadDocument(doc, options) {
if (!options) options = {};
options.doc = doc;
options.session = doc.getSession();
options.tab = doc.tab;
emit("sessionEnd", options);
}
function activateDocument(doc) {
currentDocument = doc;
currentSession = doc.getSession();
emit("sessionActivate", {
doc: currentDocument,
tab: currentDocument.tab,
session: currentSession
});
}
function deactivateDocument(doc) {
currentDocument = null;
currentSession = null;
emit("sessionDeactivate", {
doc: doc,
tab: doc.tab,
session: doc.getSession()
});
}
function update(e) {
if (!currentDocument) return;
var session = currentDocument.getSession();
if (session.previewTab && e.doc == session.previewTab.document) {
e.previewDocument = e.doc;
e.session = session;
emit("update", e);
}
}
function reload() {
emit("reload", { session: currentSession });
}
function popout() {
emit("popout", { session: currentSession });
}
function updatePreviewTab(session, remove, tab) {
if (!remove) {
if (!tab)
tab = tabs.findTab(session.path);
if (tab === session.previewTab)
return;
}
if (session.previewTab) {
var doc = session.previewTab.document;
// Remove previous change listener
if (session.changeListener)
doc.undoManager.off("change", session.changeListener);
// Remove previous path listener
if (session.renameListener)
doc.tab.off("path.set", session.renameListener);
}
if (remove) return;
// Find new tab
session.previewTab = tab;
// Set new change listener
if (session.previewTab) {
doc = session.previewTab.document;
// Listen to value changes
doc.undoManager.on("change", session.changeListener);
// Listen to path changes
doc.tab.on("setPath", session.renameListener);
}
return session.previewTab;
}
function navigate(e, remove) {
var session = e && e.doc ? e.doc.getSession() : currentSession;
if (!session) {
// todo remove this after a while
var err = new Error("navigate called without session");
errorHandler.reportError(err, { doc: !!(e && e.doc), remove: remove }, ["collab"]);
return;
}
if (remove) // For cleanup
return updatePreviewTab(session, true);
e.tab = updatePreviewTab(session);
e.session = session;
if (session == currentSession)
emit("navigate", e);
session.path = e.url;
}
function getState(doc, state) {
emit("getState", {
doc: doc,
session: doc.getSession(),
state: state
});
return state;
}
function setState(doc, state) {
emit("setState", {
doc: doc,
session: doc.getSession(),
state: state || {}
});
}
function focus(regain, lost) {
emit("focus", {
regain: regain || false,
lost: lost || false
});
}
function blur() {
emit("blur");
}
/***** Register and define API *****/
plugin.freezePublicAPI.baseclass();
/**
* Previewer base class for the {@link Preview preview pane}.
*
* A previewer registers for certain type of content, based on a
* filename or path. The content can be displayed through any means
* possible in the browser. Many previewers create an iframe in which
* they render content. Others just update a div with some generated
* result.
*
* Creating a previewer is very much like creating an editor. There
* is a set of events that can be hooked to implement behavior.
*
* The event flow of a previewer plugin is as follows:
*
* * {@link #event-documentLoad} - *A source file is previewed*
* * {@link #event-documentActivate} - *A source file is now active in the previewer*
* * {@link #event-update} - *The contents of the source file is updated*
* * {@link #event-documentDeactivate} - *Another document is loaded as the active document in the previewer*
* * {@link #event-documentUnload} - *The tab for this preview is closed*
*
* This is in addition to the event flow of the {@link Plugin} base class.
*
* #### User Actions:
*
* * {@link #event-reload} - *Refresh the contents*
* * {@link #event-navigate} - *Load a different file to preview in the same session*
* * {@link #event-focus} - *The previewer got focus*
* * {@link #event-blur} - *The previewer lost focus*
*
* Implementing your own debug panel takes a new Previewer() object
* rather than a new Plugin() object. Here's a short example:
*
* var plugin = new Previewer("(Company) Name", main.consumes, {
* caption : "HTML",
* index : 10,
* divider : true,
* selector : function(path) {
* return path.match(/(?:\.html|\.htm|\.xhtml)$|^https?\:\/\//);
* }
* });
*
* plugin.on("documentLoad", function(e) {
* var session = e.doc.getSession();
* if (!session.iframe)
* session.iframe = createIframe();
* e.editor.container.appendChild(session.iframe);
* });
*
* plugin.on("documentActivate", function(e) {
* var session = e.doc.getSession();
* session.iframe.style.display = "block";
* });
*
* plugin.on("documentDeactivate", function(e) {
* var session = e.doc.getSession();
* session.iframe.style.display = "none";
* });
*
* plugin.on("update", function(e) {
* var value = e.previewDocument.value;
* var session = e.doc.getSession();
* updateContent(session.iframe, value);
* });
*
* // etc...
*
* plugin.freezePublicAPI({
* });
*
* @class Previewer
* @extends Plugin
*/
/**
* @constructor
* Creates a new Previewer instance.
* @param {String} developer The name of the developer of the plugin
* @param {String[]} deps A list of dependencies for this
* plugin. In most cases it's a reference to `main.consumes`.
* @param {Object} options The options for the previewer.
* @param {String} options.caption The caption of the menu item.
* @param {Boolean/Menu} [options.submenu] Specifies whether to create a submenu. If a menu is specified, it will be used as submenu instead.
* @param {Divider} [options.divider] Specifies whether to create a divider below the menu item.
* @param {Number} [options.index] The position of the menu item in the menu
* @param {Function} [options.selector] Return true if your previewer can handle the content.
* @param {String} [options.path] The path of the file that is to be previewed.
* @param {Function} [options.onclick] A function that is called when the user clicks on the menu item.
*/
plugin.freezePublicAPI({
/**
* @property {Menu} menu The sub menu for the previewer menu item (if any).
* @readonly
*/
get menu() { return menu; },
/**
* @property {MenuItem} item The menu item for the preview menu.
* @readonly
*/
get item() { return item; },
/**
* @property {Divider} divider The divider for the previewer menu (if any).
* @readonly
*/
get divider() { return div; },
/**
* @property {Document} activeDocument The document that is currently
* active (visible) in this previewer.
* @readonly
*/
get activeDocument() { return currentDocument; },
/**
* @property {Session} activeSession The session that belongs to the active
* document. This can also be retrieved using
* `previewer.activeDocument.getSession()`.
* @readonly
*/
get activeSession() { return currentSession; },
_events: [
/**
* Fires when a preview session is started for this previewer.
* This event is also fired when the session is attached to another
* instance of the same previewer (in a split view situation). Often you
* want to keep the session information partially in tact when this
* happens.
*
* *N.B.: The document to preview
* is accessible via the session:
* `e.session.previewTab.document`.*
*
* @event sessionStart
* @param {Object} e
* @param {Document} e.doc the document that is loaded into the previewer
* @param {Tab} e.tab the tab that the previewer is a part of
* @param {Session} e.session the preview session
* @param {Object} e.state state that was saved in the document
* @param {Editor} e.editor the instance of the {@link Preview} editor
*/
"sessionStart",
/**
* Fires when a preview session becomes active.
* This event is also fired every time a tab becomes the active tab of
* a pane. Use it to show / hide whatever is necessary.
*
* *N.B.: The document to preview
* is accessible via the session:
* `e.session.previewTab.document`.*
*
* @event sessionActivate
* @param {Object} e
* @param {Document} e.doc the document that is activate
* @param {Tab} e.tab the tab that the previewer is a part of
* @param {Session} e.session the preview session
*/
"sessionActivate",
/**
* Fires when a preview session is no longer active
* This event is also fired every time a tab stops being the active tab of
* a pane. Use it to show / hide whatever is necessary.
*
* *N.B.: The document to preview
* is accessible via the session:
* `e.session.previewTab.document`.*
*
* @event sessionDeactivate
* @param {Object} e
* @param {Document} e.doc the document that is activate
* @param {Tab} e.tab the tab that the previewer is a part of
* @param {Session} e.session the preview session
*/
"sessionDeactivate",
/**
* Fires when a session ends for this previewer.
* This event is also fired when the session is attached to another
* instance of the previewer (in a split view situation).
*
* *N.B.: The document to preview
* is accessible via the session:
* `e.session.previewTab.document`.*
*
* @event sessionEnd
* @param {Object} e
* @param {Document} e.doc the document that was loaded into the previewer
* @param {Tab} e.tab the tab that the previewer is a part of
* @param {Session} e.session the preview session
*/
"sessionEnd",
/**
* Fires when the state of the previewer is retrieved
* @event getState
* @param {Object} e
* @param {Document} e.doc the document for which the state is retrieved
* @param {Object} e.state the state to add values to {See #getState}
*/
"getState",
/**
* Fires when the state of the previewer is set
* @event setState
* @param {Object} e
* @param {Document} e.doc the document for which the state is set
* @param {Object} e.state the state that is being set
*/
"setState",
/**
* Fires when the previewer gets the focus. See also
* {@link tabManager#focusTab}, {@link tabManager#focussedTab}
* @event focus
* @param {Object} e
* @param {Boolean} e.regain whether the focus is regained.
* This means that the previewer had lost the focus
* previously (the focus event with e.lost set to true
* was called.) and now the focus has been given back to
* the tabs.
* @param {Boolean} e.lost whether the focus is lost,
* while the previewer remains the focussed previewer. This
* happens when an element outside of the previewers
* (for instance the tree or a menu) gets the focus.
*/
"focus",
/**
* Fires when the previewer looses focus.
* @event blur
*/
"blur",
/**
* Fires when the document that is being previewed is updated.
* @event update
* @param {Object} e
* @param {Document} e.doc The document of the previewer.
* @param {Document} e.previewDocument The document of the file that is being previewed, if any.
* @param {Boolean} e.saved Whether the content has been saved to disk.
*/
"update",
/**
* Fires when the user reloads the contents.
* @event reload
*/
"reload",
/**
* Fires when the user would like the plugin to popout in it's own window.
* @event popout
*/
"popout",
/**
* Fires when the user requests a different location to be previewed.
* @event navigate
* @param {Object} e
* @param {Document} e.doc The document of the previewer.
* @param {String} e.url The url or path that the user entered in the location box.
*/
"navigate"
],
/**
* Unloads the document from this editor.
* @private
*/
unloadDocument: unloadDocument,
/**
* Loads the document in this editor to be displayed.
* @param {Document} doc the document to display
*/
loadDocument: loadDocument,
/**
* Sets the focus to this editor
*/
focus: focus,
/**
* Removes the focus from this editor
*/
blur: blur,
/**
* Sets the document as the active document.
* @param {Document} doc the document to activate
*/
activateDocument: activateDocument,
/**
* Clears the document as the active document.
* @param {Document} doc the document to deactivate
*/
deactivateDocument: deactivateDocument,
/**
* Reload the preview of the active document.
*/
reload: reload,
/**
* Pop the preview out into it's own window.
*/
popout: popout,
/**
* @ignore
*/
navigate: navigate,
/**
* Retrieves the state of a previewer
* @param {Document} doc the document for which to return the state
* @return {Object}
*/
getState: getState,
/**
* Sets the state of this previewer
* @param {Document} doc the document for which to set the state
* @param {Object} state the state of the document for this editor
*/
setState: setState
});
return plugin;
}
/***** Register and define API *****/
register(null, {
Previewer: Previewer
});
}
});