move ui code for pluginManager into separate plugin

pull/125/merge
nightwing 2017-05-01 18:46:15 +04:00
rodzic 82a2d8a260
commit 5acd862c43
3 zmienionych plików z 988 dodań i 861 usunięć

Wyświetl plik

@ -88,6 +88,7 @@ module.exports = function(options) {
staticPrefix: staticPrefix + "/plugins/c9.ide.plugins",
devel: devel
},
"plugins/c9.ide.plugins/gui",
// {
// packagePath: "plugins/c9.ide.plugins/test",
// staticPrefix: staticPrefix + "/plugins/c9.ide.plugins"

Wyświetl plik

@ -0,0 +1,923 @@
define(function(require, exports, module) {
main.consumes = [
"PreferencePanel", "settings", "ui", "util", "ext", "c9", "Plugin",
"layout", "proc", "menus", "commands", "pluginManager",
"dialog.error", "dialog.info", "tree.favorites", "fs", "tree", "vfs",
"preferences.experimental", "apf", "hub", "dialog.notification"
];
main.provides = ["pluginManagerUi"];
return main;
function main(options, imports, register) {
var PreferencePanel = imports.PreferencePanel;
var settings = imports.settings;
var layout = imports.layout;
var commands = imports.commands;
var menus = imports.menus;
var ui = imports.ui;
var c9 = imports.c9;
var fs = imports.fs;
var ext = imports.ext;
var util = imports.util;
var apf = imports.apf;
var showError = imports["dialog.error"].show;
var showInfo = imports["dialog.info"].show;
var notify = imports["dialog.notification"].show;
var experimental = imports["preferences.experimental"];
var pluginManager = imports.pluginManager;
var architectApp = imports.hub.app;
var search = require("../c9.ide.navigate/search");
var Tree = require("ace_tree/tree");
var TreeData = require("./managerdp");
var qs = require("querystring");
var escapeHTML = require("ace/lib/lang").escapeHTML;
var CORE = {};
var TEMPLATES = {
"plugin.simple": "Empty Plugin",
"plugin.default": "Full Plugin",
"plugin.installer": "Installer Plugin",
"plugin.bundle": "Cloud9 Bundle"
};
/***** Initialization *****/
var DEBUG = c9.location.indexOf("debug=2") > -1;
var ENABLED = DEBUG || experimental.addExperiment(
"plugin-manager",
options.devel,
"SDK/Plugin Manager"
);
var plugin = new PreferencePanel("Ajax.org", main.consumes, {
caption: "Plugin Explorer",
className: "plugins",
form: false,
noscroll: true,
index: 200,
visible: ENABLED,
});
var emit = plugin.getEmitter();
var model;
var datagrid;
var filterbox;
var btnInstall;
var btnUninstall;
var btnServices;
var btnReadme;
var btnReloadLast;
var localPlugins;
var loaded = false;
function load() {
if (loaded) return false;
loaded = true;
menus.addItemByPath("Tools/~", new ui.divider(), 100000, plugin);
menus.addItemByPath("Tools/Developer", null, 100100, plugin);
if (!DEBUG) {
menus.addItemByPath("Tools/Developer/Start in Debug Mode", new ui.item({
onclick: function() {
var url = location.href + (location.href.indexOf("?") > -1
? "&debug=2"
: "?debug=2");
util.openNewWindow(url);
}
}), 900, plugin);
}
if (!ENABLED) {
return;
}
commands.addCommand({
name: "openPluginManager",
group: "Plugins",
exec: function() {
commands.exec("openpreferences", null, { panel: plugin });
}
}, plugin);
menus.addItemByPath("Tools/Developer/Open Plugin Explorer", new ui.item({
command: "openPluginManager"
}), 1100, plugin);
if (DEBUG) {
notify("<div class='c9-readonly'>You are in <span style='color:rgb(245, 234, 15)'>Debug</span> Mode. "
+ "<button id='.pm_btn1'>Open Plugin Explorer</button>"
+ "<button id='.pm_btn2' style='float:right'>Reload last Plugin</button>",
false);
document.getElementById(".pm_btn1").onclick = function() { commands.exec("openPluginManager") };
btnReloadLast = document.getElementById(".pm_btn2");
btnReloadLast.onclick = function() { commands.exec("reloadLastPlugin") };
updateReloadLastButton();
pluginManager.readAvailablePlugins(function(err, available) {
if (err) return console.error(err);
localPlugins = available;
reloadModel();
if (sessionStorage.localPackages) {
pluginManager.loadPackage(
util.safeParseJson(sessionStorage.localPackages) || []
);
}
var updateSessionStorage = function() {
var packages = pluginManager.packages;
sessionStorage.localPackages = JSON.stringify(Object.keys(packages).map(function(x) {
if (packages[x] && packages[x].fromVfs && packages[x].enabled)
return packages[x].filePath;
}).filter(Boolean));
};
pluginManager.on("enablePackage", updateSessionStorage);
pluginManager.on("disablePackage", updateSessionStorage);
});
commands.addCommand({
name: "reloadLastPlugin",
bindKey: { mac: "F4", win: "F4" },
hint: "reload plugin last reloaded in plugin manager",
exec: function() {
var names = getLastReloaded();
if (!names)
return commands.exec("openPluginManager", null, { panel: plugin });
reload(names);
}
}, plugin);
menus.addItemByPath("Tools/Developer/Reload Last Plugin", new ui.item({
command: "reloadLastPlugin",
isAvailable: getLastReloaded
}), 1300, plugin);
}
menus.addItemByPath("File/New Plugin", null, 210, plugin);
Object.keys(TEMPLATES).forEach(function(name) {
menus.addItemByPath("File/New Plugin/" + TEMPLATES[name], new ui.item({
onclick: function() {
pluginManager.createNewPlugin(name);
}
}), 210, plugin);
});
}
var drawn;
function draw(e) {
if (drawn) return;
drawn = true;
function scheduleReloadModel() {
if (scheduleReloadModel.timer) return;
scheduleReloadModel.timer = setTimeout(function() {
scheduleReloadModel.timer = null;
reloadModel();
});
}
ext.on("register", scheduleReloadModel);
pluginManager.on("change", scheduleReloadModel);
pluginManager.on("enablePackage", scheduleReloadModel);
pluginManager.on("disablePackage", scheduleReloadModel);
ui.insertCss(require("text!./style.css"), plugin);
model = new TreeData();
model.emptyMessage = "No plugins found";
model.columns = [{
caption: "Name",
value: "name",
width: "250",
type: "tree"
}, {
caption: "Startup Time",
// value: "time",
width: "100",
getText: function(p) {
if (p.time !== undefined)
return (p.time || 0) + "ms";
var total = 0;
function recur(p) {
if (p.time != undefined)
return total += p.time;
if (p.items)
p.items.forEach(recur);
}
recur(p);
return (p.time = total) + "ms";
}
}, {
caption: "Enabled",
value: "enabled",
width: "100"
}];
model.columns = null;
layout.on("eachTheme", function(e) {
var height = parseInt(ui.getStyleRule(".bar-preferences .blackdg .tree-row", "height"), 10) || 24;
model.rowHeightInner = height;
model.rowHeight = height;
if (e.changed) datagrid.resize(true);
});
architectApp.on("ready-additional", function() {
reloadModel();
});
reloadModel();
var hbox = new ui.hbox({
htmlNode: e.html,
padding: 5,
edge: "10 10 0 10",
childNodes: [
filterbox = new apf.codebox({
realtime: true,
skin: "codebox",
"class": "dark",
clearbutton: true,
focusselect: true,
height: 27,
width: 250,
singleline: true,
"initial-message": "Search installed plugins"
}),
btnReadme = new ui.button({
skin: "c9-toolbarbutton-glossy",
caption: "Readme",
class: "serviceButton",
onclick: function() {
mode = "readme";
scheduleRedraw();
}
}),
btnServices = new ui.button({
skin: "c9-toolbarbutton-glossy",
caption: "services",
class: "serviceButton",
onclick: function() {
mode = "services";
scheduleRedraw();
}
}),
new ui.filler({}),
btnInstall = new ui.button({
skin: "c9-toolbarbutton-glossy",
caption: "Enable",
class: "btn-red",
onclick: function() {
reloadGridSelection(true);
}
}),
btnUninstall = new ui.button({
skin: "c9-toolbarbutton-glossy",
caption: "Disable",
class: "btn-red",
onclick: function() {
reloadGridSelection(false);
}
}),
new ui.button({
skin: "c9-toolbarbutton-glossy",
caption: "Reload",
onclick: function() {
reloadGridSelection();
}
})
]
});
var treeBar;
var descriptionBar;
var hboxInner = new ui.hsplitbox({
anchor: "0 0 0 0",
htmlNode: e.html,
class: "bar-preferences",
splitter: true,
childNodes: [
treeBar = new ui.bar({ width: 250 }),
descriptionBar = new ui.bar({ textselect: true }),
]
});
var div = treeBar.$ext.appendChild(document.createElement("div"));
div.style.position = "absolute";
div.style.left = "0px";
div.style.right = "0px";
div.style.bottom = "0px";
div.style.top = "0px";
hboxInner.$ext.style.position = "absolute";
hboxInner.$ext.style.left = "10px";
hboxInner.$ext.style.right = "10px";
hboxInner.$ext.style.bottom = "10px";
hboxInner.$ext.style.top = "50px";
datagrid = new Tree(div);
datagrid.setTheme({ cssClass: "blackdg" });
datagrid.setDataProvider(model);
layout.on("resize", function() { datagrid.resize() }, plugin);
function setTheme(e) {
filterbox.setAttribute("class",
e.theme.indexOf("dark") > -1 ? "dark" : "");
}
layout.on("themeChange", setTheme);
setTheme({ theme: settings.get("user/general/@skin") });
filterbox.ace.commands.addCommands([
{
bindKey: "Enter",
exec: function() { }
}, {
bindKey: "Esc",
exec: function(ace) { ace.setValue(""); }
}
]);
filterbox.ace.on("input", function(e) {
applyFilter();
});
// when tab is restored datagrids size might be wrong
// todo: remove this when apf bug is fixed
datagrid.once("mousemove", function() {
datagrid.resize(true);
});
datagrid.on("changeSelection", scheduleRedraw);
model.on("change", scheduleRedraw);
plugin.on("reloadModel", scheduleRedraw);
model.getCheckboxHTML = function(node) {
var enabled = node.enabled;
if (enabled == null || node.isGroup) return "";
return "<span class='checkbox "
+ (node.loading ? "loading" : "")
+ (enabled == -1
? "half-checked "
: (enabled ? "checked " : ""))
+ "'></span>";
};
var mode = "services";
var readmeCache = {};
function renderDetails() {
var items = datagrid.selection.getSelectedNodes();
var hasEnabled = 0;
var hasDisabled = 0;
items.forEach(function(x) {
if (x.enabled != 0)
hasEnabled = true;
if (x.enabled != 1)
hasDisabled = true;
});
btnUninstall.setProperty("visible", hasEnabled);
btnInstall.setProperty("visible", hasDisabled);
if (mode == "services") {
renderServiceDetails(items);
btnServices.$ext.classList.add("serviceButtonActive");
btnReadme.$ext.classList.remove("serviceButtonActive");
}
else if (mode == "readme") {
renderReadmeDetails(items);
btnReadme.$ext.classList.add("serviceButtonActive");
btnServices.$ext.classList.remove("serviceButtonActive");
}
else {
descriptionBar.$ext.innerHTML = "";
btnReadme.$ext.classList.remove("serviceButtonActive");
btnServices.$ext.classList.remove("serviceButtonActive");
}
if (items.length == 1 && items[0].__error) {
var errorContainer = document.createElement("div");
errorContainer.style.cssText = "padding: 5px; white-space: pre-wrap";
errorContainer.textContent = items[0].__error.message;
descriptionBar.$ext.insertBefore(errorContainer, descriptionBar.$ext.firstChild);
}
}
function renderReadmeDetails(items) {
if (items.length > 1) {
descriptionBar.$ext.textContent = "Multipe items selected.";
return;
}
var item = items[0];
while (item && !item.packageConfig) {
item = item.parent;
}
if (!item) {
descriptionBar.$ext.textContent = "Readme is not available.";
return;
}
if (readmeCache[item.name]) {
var div = document.createElement("div");
div.textContent = readmeCache[item.name];
div.style.cssText = "padding: 5px; white-space: pre-wrap";
descriptionBar.$ext.textContent = "";
descriptionBar.$ext.appendChild(div);
return;
}
descriptionBar.$ext.textContent = "Loading...";
if (item.packageConfig.filePath) {
var parts = item.packageConfig.filePath.split("/");
parts.pop();
parts.push("README.md");
var path = parts.join("/");
fs.readFile(path, function(e, v) {
if (e)
return readmeCache[item.name] = "Readme is not available.";
readmeCache[item.name] = v;
renderDetails();
});
}
}
function renderServiceDetails(items) {
function addUnique(item, array) {
if (array.indexOf(item) == -1) array.push(item);
}
var provided = [];
items.forEach(function addProvides(x) {
if (x.map) {
Object.keys(x.map).forEach(function(n) {
addProvides(x.map[n]);
});
}
if (x.provides) {
x.provides.forEach(function(p) {
addUnique(p, provided);
});
}
});
var consumedMap = Object.create(null);
provided.forEach(function(service) {
consumedMap[service] = 0;
});
pluginManager.addAllProviders(consumedMap);
var consumedList = Object.keys(consumedMap);
var consumedGroups = splitToGroups(consumedList, consumedMap);
var dependents = Object.create(null);
provided.forEach(function(service) {
dependents[service] = 0;
});
pluginManager.addAllDependents(dependents);
var depList = Object.keys(dependents);
var depGroups = splitToGroups(depList, dependents);
function splitToGroups(list, map) {
var groups = [];
list.forEach(function(n) {
var level = Math.abs(map[n]);
if (!groups[level])
groups[level] = [];
groups[level].push(n);
});
groups = groups.filter(Boolean).slice(1);
return groups;
}
function formatServices(list, style) {
return "[" + list.map(function(x) {
return '<span class="serviceButton">' + escapeHTML(x) + '</span>';
}).join(", ") + "]";
}
descriptionBar.$ext.style.overflow = "auto";
var serviceDesc = '<h1>Provided services [' + provided.length + ']</h1>\
<p type="provided">' + (provided.length ? formatServices(provided) : "") + '</p>\
<h1>Consumed services [' + (consumedList.length - provided.length) + ']</h1>\
<p type="consumed">' + consumedGroups.map(formatServices).join("<br/>") + '</p>\
<h1>Dependent services [' + (depList.length - provided.length) + ']</h1>\
<p type="dependent">' + depGroups.map(formatServices).join("<br/>") + '</p>\
<br/>';
descriptionBar.$ext.innerHTML = '<div class="basic intro" \
style="padding:12px 0 12px 12px;white-space:pre-line">\
<p>' + (items.length == 1
? '<span class="serviceButton">' + escapeHTML(items[0].path) + '</span>'
: (items.length || "no") + " plugins selected") + '</p>\
<hr></hr>'
+ (items.length ? serviceDesc : "")
+ '</div>';
}
descriptionBar.$ext.addEventListener("click", function(e) {
if (e.button) return;
var el = e.target;
var isButton = el.classList.contains("serviceButton");
var text = el.textContent;
var serviceToPlugin = architectApp.serviceToPlugin;
if (e.detail == 1 && isButton && text) {
var path = serviceToPlugin[text] ? serviceToPlugin[text].packagePath : text;
var node = function search(node) {
if (node.path == path)
return node;
if (node.items) {
for (var i = 0; i < node.items.length; i++) {
var result = search(node.items[i]);
if (result)
return result;
}
}
}(model.cachedRoot);
if (node)
datagrid.reveal(node);
}
});
var hoverDetails = { hovered: "", highlighted: "" };
descriptionBar.$ext.addEventListener("mousemove", function(e) {
if (e.button) return;
var el = e.target;
var isButton = el.classList.contains("serviceButton");
var text = el.textContent;
hoverDetails.parent = el.parentNode;
hoverDetails.hovered = isButton ? text : "";
hoverDetails.type = isButton ? el.parentNode.getAttribute("type") : "";
if (hoverDetails.hovered != hoverDetails.highlighted)
requestAnimationFrame(updateHighlights);
});
function updateHighlights() {
if (hoverDetails.hovered == hoverDetails.highlighted) return;
var serviceToPlugin = architectApp.serviceToPlugin;
if (!serviceToPlugin) return;
var nodes = descriptionBar.$ext.querySelectorAll(".serviceButton");
var deps = Object.create(null);
deps[hoverDetails.hovered] = 0;
pluginManager.addAllDependents(deps);
pluginManager.addAllProviders(deps);
for (var i = 0; i < nodes.length; i++) {
var node = nodes[i];
var val = node.textContent;
var highlight1 = false;
var highlight2 = false;
if (hoverDetails.type == "dependent") {
if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") {
highlight1 = deps[val] < 0;
highlight2 = deps[val] > 0;
}
} else if (hoverDetails.type == "consumed") {
if (hoverDetails.parent == node.parentNode || node.parentNode.getAttribute("type") == "provided") {
highlight1 = deps[val] > 0;
highlight2 = deps[val] < 0;
}
}
nodes[i].style.borderBottom = highlight1 ? "1px solid" : "";
if (!highlight1) {
nodes[i].style.borderBottom = highlight2 ? "1px dashed rgba(125, 125, 125, 0.9)" : "";
}
}
hoverDetails.highlighted = hoverDetails.hovered;
}
function scheduleRedraw() {
window.requestAnimationFrame(renderDetails);
}
var mnuCtxTree = new ui.menu({
id: "mnuChat",
}, plugin);
menus.decorate(mnuCtxTree);
plugin.addElement(mnuCtxTree);
menus.addItemByPath("context/pluginManager/", mnuCtxTree, 0, plugin);
menus.addItemByPath("context/pluginManager/Reveal in File Tree", new ui.item({
isAvailable: function() {
var selected = datagrid.selection.getCursor();
return selected && selected.packageConfig && selected.packageConfig.filePath;
},
onclick: function() {
var selected = datagrid.selection.getCursor();
var tabbehavior = architectApp.services.tabbehavior;
var filePath = selected.packageConfig && selected.packageConfig.filePath;
if (filePath) {
tabbehavior.revealtab({ path: filePath });
}
},
}), plugin);
menus.addItemByPath("context/pluginManager/Disable", new ui.item({
isAvailable: function() {
var selected = datagrid.selection.getCursor();
return selected && selected.enabled != 0;
},
onclick: function() { reloadGridSelection(false); },
}), plugin);
menus.addItemByPath("context/pluginManager/Enable", new ui.item({
isAvailable: function() {
var selected = datagrid.selection.getCursor();
return selected && selected.enabled != 1;
},
onclick: function() { reloadGridSelection(true); },
}), plugin);
menus.addItemByPath("context/pluginManager/Reload", new ui.item({
onclick: function() { reloadGridSelection(); },
}), plugin);
treeBar.setAttribute("contextmenu", mnuCtxTree);
}
/***** Methods *****/
function reloadModel() {
if (!model) return;
var packages = pluginManager.packages;
if (!CORE.pluginManager) {
CORE.pluginManager = 1;
pluginManager.addAllProviders(CORE);
Object.keys(CORE).forEach(function(n) {
if (architectApp.serviceToPlugin[n])
CORE[architectApp.serviceToPlugin[n].packagePath] = 1;
});
}
var GROUPS = {
// "changed": "Recently Changed",
"remote": "Remote Plugins",
"vfs": "Locally Installed Plugins",
"pre": "Pre-installed Plugins",
"core": "Core Plugins",
};
var groups = model.groups || Object.create(null);
if (!model.groups) {
var root = [];
model.cachedRoot = { items: root };
model.groups = groups;
Object.keys(GROUPS).forEach(function(name) {
root.push(groups[name] = {
map: Object.create(null),
isOpen: name != "runtime",
className: "group",
isGroup: true,
isType: name,
noSelect: true,
name: GROUPS[name]
});
});
}
function addPlugin(plugin, node) {
if (!plugin.provides || !plugin.consumes)
return;
if (plugin.packagePath) {
var parts = plugin.packagePath.split("/");
var path = parts.shift();
parts.forEach(function(p, i) {
path = path + "/" + p;
if (!node.map)
node.map = Object.create(null);
if (!node.map[p]) {
node.map[p] = {
path: path,
parent: node,
};
}
node.map[p].name = p;
if (i == parts.length - 1) {
var enabled = 1;
node.map[p].provides = plugin.provides;
plugin.provides.forEach(function(x) {
var service = architectApp.services[x];
if (!service || (!service.loaded && service.unload)) {
enabled = 0;
}
});
node.map[p].enabled = enabled;
}
node = node.map[p];
node.className = plugin.__error ? "load-error" : "";
node.__error = plugin.__error;
});
}
}
function addPackage(name) {
var pkg = packages[name];
var parent = pkg.filePath ? groups.vfs : groups.remote;
var node = parent.map[name] = parent.map[name] || {
path: "plugins/" + name,
name: name,
enabled: 0,
parent: parent,
};
node.packageConfig = pkg;
node.className = pkg.__error ? "load-error" : "";
node.__error = pkg.__error;
node.loading = pkg.loading;
}
architectApp.config.forEach(function(plugin) {
var node = CORE[plugin.packagePath] ? groups.core : groups.pre;
addPlugin(plugin, node);
});
if (localPlugins) {
localPlugins.forEach(function(name) {
if (!packages[name]) {
packages[name] = {
name: name,
filePath: "~/.c9/plugins/" + name + "/package.json",
};
}
});
}
Object.keys(packages).forEach(function(n) {
var pkg = packages[n];
var node = pkg.filePath ? groups.vfs : groups.remote;
addPackage(n);
if (pkg.c9 && pkg.c9.plugins) {
pkg.c9.plugins.forEach(function(plugin) {
addPlugin(plugin, node);
});
}
});
function flatten(node, index) {
if (node.map) {
node.items = node.children = Object.keys(node.map).map(function(x) {
return node.map[x];
});
}
if (node.items) {
node.items.forEach(flatten);
if (node.items.length == 1 && !node.isGroup) {
var other = node.items[0];
if (node.parent && node.parent.items[index] == node) {
node.parent.items[index] = other;
other.name = node.name + "/" + other.name;
other.packageConfig = node.packageConfig;
other.filePath = node.filePath;
other.url = node.url;
other.loading = node.loading;
}
}
if (!node.isGroup) {
node.enabled = null;
node.items.some(function(i) {
if (node.enabled == null) {
node.enabled = i.enabled;
}
if (i.enabled != node.enabled) {
node.enabled = -1;
return true;
}
});
}
}
}
flatten(model.cachedRoot);
applyFilter();
emit("reloadModel");
}
function applyFilter() {
model.keyword = filterbox && filterbox.getValue();
if (!model.keyword) {
model.reKeyword = null;
model.setRoot(model.cachedRoot);
// model.isOpen = function(node) { return node.isOpen; }
}
else {
model.reKeyword = new RegExp("("
+ util.escapeRegExp(model.keyword) + ")", 'i');
var root = search.treeSearch(model.cachedRoot.items, model.keyword, true);
model.setRoot(root);
// model.isOpen = function(node) { return true; };
}
}
function reload(names) {
var nodes = names.split(/\s*,\s*/).map(function(name) {
if (pluginManager.packages[name])
return { packageConfig: pluginManager.packages[name] };
return { path: name };
});
reloadGridSelection(null, nodes);
}
function reloadGridSelection(mode, nodes) {
if (!nodes)
nodes = datagrid.selection.getSelectedNodes();
var reloadLast = pluginManager.reload(nodes, mode);
if (reloadLast.length && mode == null) {
var href = document.location.href.replace(/[?&]reload=[^&]+/, "");
href += (href.match(/\?/) ? "&" : "?") + "reload=" + reloadLast.join(",");
window.history.replaceState(window.history.state, null, href);
showReloadTip();
updateReloadLastButton();
}
}
function updateReloadLastButton() {
var last = getLastReloaded();
if (last) {
btnReloadLast.visible = true;
btnReloadLast.textContent = "Reload " + last;
} else {
btnReloadLast.visible = false;
}
}
function showReloadTip(name) {
if (options.devel) {
var key = commands.getHotkey("reloadLastPlugin");
if (commands.platform == "mac")
key = apf.hotkeys.toMacNotation(key);
if (!getLastReloaded()) {
showInfo("Reloaded " + name + ". Press " + key + " to reload again.", 3000);
return;
}
}
showInfo("Reloaded " + name + ".", 1000);
}
function getLastReloaded() {
return qs.parse(document.location.search.substr(1)).reload;
}
/***** Lifecycle *****/
plugin.on("load", function() {
load();
});
plugin.on("draw", function(e) {
draw(e);
});
plugin.on("activate", function(e) {
datagrid && datagrid.resize();
});
plugin.on("resize", function(e) {
datagrid && datagrid.resize();
});
plugin.on("enable", function() {
});
plugin.on("disable", function() {
});
plugin.on("unload", function() {
loaded = false;
drawn = false;
btnServices = null;
btnReadme = null;
btnReloadLast = null;
model = null;
datagrid = null;
filterbox = null;
btnInstall = null;
btnUninstall = null;
localPlugins = null;
});
/***** Register and define API *****/
/**
*
**/
plugin.freezePublicAPI({
/*
* @ignore
*/
get datagrid() { return datagrid; },
});
register(null, {
"pluginManagerUi": plugin,
});
}
});