c9-core/plugins/c9.ide.dialog/dialog.js

528 wiersze
20 KiB
JavaScript
Czysty Zwykły widok Historia

2015-02-10 19:41:24 +00:00
define(function(require, module, exports) {
main.consumes = ["Plugin", "ui", "commands", "focusManager"];
main.provides = ["Dialog"];
return main;
function main(options, imports, register) {
var Plugin = imports.Plugin;
var ui = imports.ui;
var commands = imports.commands;
var focusManager = imports.focusManager;
var EventEmitter = require("events").EventEmitter;
Dialog.queue = [];
Dialog.addToQueue = function(dialog, fn, force) {
var emitter = new EventEmitter();
emitter.visible = undefined;
function queueItem(){
fn(function(){
Dialog.queue.splice(Dialog.queue.indexOf(queueItem), 1);
emitter.emit("hide");
emitter.visible = 0;
if (Dialog.queue.length)
Dialog.queue[0]();
else {
// Timeout to prevent the editor
// from getting the key stroke
setTimeout(function(){
// Return last focussed element
focusManager.focus(Dialog.lastFocus);
delete Dialog.lastFocus;
});
}
});
if (emitter.emit("show") === false)
dialog.hide();
else
emitter.visible = 1;
}
if (force)
queueItem();
else {
Dialog.queue.push(queueItem);
// Save last focussed element
if (!Dialog.lastFocus)
Dialog.lastFocus = focusManager.activeElement;
// Timeout to make sure events can be set on the emitter.
if (Dialog.queue.length == 1)
setTimeout(Dialog.queue[0]);
}
return emitter;
};
function Dialog(developer, deps, options) {
var plugin = new Plugin(developer, deps.concat(main.consumes));
var emit = plugin.getEmitter();
var name = options.name;
var left = options.left;
var top = options.top;
var width = options.width || 512;
var title = options.title;
var heading = options.heading;
var body = options.body;
var custom = options.custom;
var modal = options.modal;
var zindex = options.zindex;
var allowClose = options.allowClose;
var elements = options.elements;
var resizable = options.resizable || false;
var widths = options.widths || {};
var count = 0;
var dialog, buttons, titles;
var loaded;
function load(){
if (loaded) return;
loaded = true;
}
var drawn = false;
function draw(htmlNode) {
if (drawn) return;
drawn = true;
// Create UI elements
dialog = new ui.modalwindow({
id: "window",
title: title,
icon: "",
center: "true",
modal: modal,
buttons: allowClose ? "close" : "",
width: width,
zindex: zindex || "",
skin: "bk-window2",
class: "relative" + (options.dark ? " dark" : ""),
childNodes: [
new ui.vbox({
id: "titles",
padding: 10,
edge: "15 20 25 20",
}),
new ui.hbox({
id: "buttons",
class: "dialog-buttons",
pack: "end",
padding: "8",
height: "46",
edge: "11 10 10"
})
]
});
apf.document.documentElement.appendChild(dialog);
plugin.addElement(dialog);
dialog.on("prop.visible", function(e) {
if (e.value) emit("show");
else emit("hide");
});
dialog.on("keydown", function(e) {
if (allowClose && e.keyCode == 27)
dialog.hide();
});
commands.addCommand({
name: plugin.name,
bindKey: { mac: "ESC", win: "ESC" },
group: "ignore",
isAvailable: function(){
return dialog.visible;
},
exec: function(){
dialog.dispatchEvent("keydown", { keyCode : 27 });
}
}, plugin);
titles = plugin.getElement("titles");
buttons = plugin.getElement("buttons");
// Create dynamic UI elements
elements.forEach(function(item) {
createItem(null, null, item);
});
emit.sticky("draw", {
aml: titles,
html: titles.$int
});
if (resizable)
plugin.resizable = resizable;
}
/***** Method *****/
function queue(implementation, force) {
if (!plugin.loaded)
return;
return Dialog.addToQueue(dialog, function(next) {
// Draw everything if needed
draw();
// Call the show implementation
implementation();
// Update UI
if (!custom) {
titles.$int.innerHTML = "<h3 style='margin:0 0 10px 0'>"
+ heading + "</h3><div class='alertMsg'>"
+ body + "</div>";
}
// allow selecting dialog message text
titles.textselect = !custom;
// When the dialog closes the next dialog can appear
plugin.once("hide", next);
// Show UI
dialog.show();
// Focus UI
focusManager.focus(dialog, true);
}, force);
}
function hide(){
dialog && dialog.hide();
}
// @todo this looks very similar to forms.js. Perhaps able to merge?
function update(items) {
items.forEach(function(item) {
if (!drawn) {
elements.every(function(el) {
if (el.id == item.id) {
for (var prop in item) {
el[prop] = item[prop];
}
return false;
}
return true;
});
return;
}
var el = plugin.getElement(item.id);
switch(el.type) {
case "dropdown":
var dropdown = el.lastChild;
var data = item.items.map(function(item) {
return "<item value='" + item.value
+ "'><![CDATA[" + item.caption + "]]></item>";
}).join("");
if (data)
dropdown.$model.load("<items>" + data + "</items>");
if (item.value)
dropdown.setAttribute("value", item.value);
break;
default:
if ("value" in item)
el.setAttribute('value', item.value);
if ("onclick" in item)
el.onclick = item.onclick;
if ("visible" in item)
el.setAttribute("visible", item.visible)
if ("zindex" in item)
el.setAttribute("zindex", item.zindex)
break;
}
})
}
// @todo this looks very similar to forms.js. Perhaps able to merge?
function createItem(heading, name, options) {
var position = options.position || count++;
var node;
switch(options.type) {
case "checkbox":
node = new ui.checkbox({
label: options.caption,
value: options.defaultValue || "",
values: options.values,
skin: "checkbox_black",
});
break;
case "dropdown":
var model = options.model || new ui.model();
var data = options.items && options.items.map(function(item) {
return "<item value='" + item.value + "'><![CDATA[" + item.caption + "]]></item>";
}).join("");
if (data) model.load("<items>" + data + "</items>");
node = new ui.dropdown({
model: model,
width: options.width || widths.dropdown,
skin: "black_dropdown",
value: options.defaultValue || "",
each: options.each || "[item]",
caption: options.caption || "[text()]",
eachvalue: options.eachvalue || "[@value]",
"empty-message" : options["empty-message"]
});
break;
case "textbox":
node = new ui.textbox({
skin: "searchbox",
"initial-message": options.message || "",
width: options.width || widths.textbox,
value: options.defaultValue || "",
realtime: options.realtime || 1
});
break;
case "button":
node = new ui.button({
skin: "btn-default-css3",
"class" : options.color ? "btn-" + options.color : "",
margin: options.margin,
caption: options.caption || "",
submenu: options.submenu && options.submenu.aml
|| options.submenu || "",
width: options.width || widths.button,
"default" : options["default"] ? "1" : ""
});
break;
case "label":
node = new ui.label({
caption: options.caption,
style: options.style,
width: options.width || ""
});
break;
case "image":
node = new ui.img({
skin: "simpleimg",
src: options.src,
margin: options.margin,
width: options.width,
height: options.height
});
break;
case "divider":
node = new ui.divider({
skin: "c9-divider-hor",
margin: "5 0 5 0"
});
break;
case "filler":
node = new ui.filler();
break;
case "custom":
node = options.node;
break;
default:
throw new Error("Unknown form element type: "
+ options.type);
}
if (options.id || options.name)
node.setAttribute("id", options.id || options.name);
if (options.visible !== undefined)
node.setAttribute("visible", options.visible);
if (options.onclick)
node.onclick = options.onclick;
if (options.hotkey) {
dialog.on("keydown", function(e) {
if (options.hotkey == "ESC" && e.keyCode == 27
|| String.fromCharCode(e.keyCode) == options.hotkey)
node.dispatchEvent("click");
});
}
ui.insertByIndex(buttons, node, position, plugin);
plugin.addElement(node);
return node;
}
/***** Register and define API *****/
plugin.freezePublicAPI.baseclass();
/**
* Dialog Builder for Cloud9. Use this class to create dialogs.
*
* Example:
*
* var dialog = new Dialog("Developer Name", main.consumes, {
* allowClose : true,
* modal : false,
* elements : [
* { type: "checkbox", caption: "Apply to all open files" },
* { type: "filler" },
* { type: "button", id: "keepmine", color: "blue", caption: "Keep Mine" },
* { type: "button", id: "useremote", color: "blue", caption: "User Remote" },
* { type: "button", id: "mergeboth", color: "green", caption: "Merge Both", "default": true }
* ]
* });
*/
/**
* @constructor
* Creates a new Dialog instance based on an abstract definition of
* the dialog elements.
*
* @param {Object} [options] The definition of the dialog.
*/
plugin.freezePublicAPI({
/**
* The APF element that is the parent to all form elements.
* @property {AMLElement} aml
* @private
* @readonly
*/
get aml(){ return dialog; },
/**
* @property {Number} width
*/
get width(){ return width; },
set width(v) {
width = v;
dialog && dialog.setAttribute("width", width);
},
/**
* @property {Number} left
*/
get left(){ return left; },
set left(v) {
left = v;
dialog && dialog.setAttribute("left", left);
},
/**
* @property {Number} top
*/
get top(){ return top; },
set top(v) {
top = v;
dialog && dialog.setAttribute("top", top);
},
/**
* @property {Number} title
*/
get title(){ return title; },
set title(v) {
title = v;
dialog && dialog.setAttribute("title", title);
},
/**
* @property {Boolean} resizable
*/
get resizable(){ return resizable; },
set resizable(v) {
if (v === resizable) return;
resizable = v;
if (!dialog) return;
dialog.setAttribute("height", v ? dialog.getHeight() : "");
dialog.setAttribute("width", v ? dialog.getWidth() : "");
dialog.setAttribute("class", v ? "" : "relative");
titles.setAttribute("anchors", v ? "0 0 46 0" : "");
buttons.setAttribute("bottom", v ? "0" : "");
buttons.setAttribute("left", v ? "0" : "");
buttons.setAttribute("right", v ? "0" : "");
dialog.setAttribute("resizable", v);
},
/**
* @property {Number} heading
*/
get heading(){ return heading; },
set heading(v) {
heading = v;
},
/**
* @property {Number} body
*/
get body(){ return body; },
set body(v) {
body = v;
},
/**
* @property {Boolean} modal
* @readonly
*/
get modal(){ return modal; },
/**
* @property {Boolean} allowClose
*/
get allowClose(){ return allowClose; },
set allowClose(v) {
allowClose = v;
dialog && dialog.setAttribute("buttons", v ? "close" : "");
},
_events: [
/**
* Fires when the form is drawn.
* @event draw
*/
"draw",
/**
* Fires when the form becomes visible. This happens when
* it's attached to an HTML element using the {@link #attachTo}
* method, or by calling the {@link #method-show} method.
* @event show
*/
"show",
/**
* Fires when the form becomes hidden. This happens when
* it's detached from an HTML element using the {@link #detach}
* method, or by calling the {@link #method-hide} method.
* @event hide
*/
"hide"
],
/**
* Updates form elements with new values. This method currently
* only supports updating the items of a dropdown element.
*
* Example:
*
* dialog.update([
* { id: "keepmine", onclick: function(){} },
* { id: "useremote", onclick: function(){} },
* { id: "mergeboth", onclick: function(){} }
* ]);
*
* @param {Array} items
*/
update: update,
/**
* Show the dialog when no other dialogs are there.
* @param {Function} callback
* @fires show
*/
queue: queue,
/**
* Hide the dialog.
* @fires hide
*/
hide: hide
});
plugin.load(name, "dialog");
return plugin;
}
register("", {
Dialog: Dialog
});
}
});