/*======================================================================
* Exhibit UI Utilities
*======================================================================
*/
Exhibit.UI = new Object();
/*----------------------------------------------------------------------
* Component instantiation functions
*----------------------------------------------------------------------
*/
Exhibit.UI.componentMap = {};
Exhibit.UI.registerComponent = function(name, comp) {
var msg = "Cannot register component " + name + " -- ";
if (name in Exhibit.UI.componentMap) {
SimileAjax.Debug.warn(msg + 'another component has taken that name');
} else if (!comp) {
SimileAjax.Debug.warn(msg + 'no component object provided')
} else if (!comp.create) {
SimileAjax.Debug.warn(msg + "component has no create function");
} else if (!comp.createFromDOM) {
SimileAjax.Debug.warn(msg + "component has no createFromDOM function");
} else {
Exhibit.UI.componentMap[name] = comp;
}
};
Exhibit.UI.create = function(configuration, elmt, uiContext) {
if ("role" in configuration) {
var role = configuration.role;
if (role != null && role.startsWith("exhibit-")) {
role = role.substr("exhibit-".length);
}
if (role in Exhibit.UI.componentMap) {
var createFunc = Exhibit.UI.componentMap[role].create;
return createFunc(configuration, elmt, uiContext);
}
switch (role) {
case "lens":
case "edit-lens":
Exhibit.UIContext.registerLens(configuration, uiContext.getLensRegistry());
return null;
case "view":
return Exhibit.UI.createView(configuration, elmt, uiContext);
case "facet":
return Exhibit.UI.createFacet(configuration, elmt, uiContext);
case "coordinator":
return Exhibit.UI.createCoordinator(configuration, uiContext);
case "coder":
return Exhibit.UI.createCoder(configuration, uiContext);
case "viewPanel":
return Exhibit.ViewPanel.create(configuration, elmt, uiContext);
case "logo":
return Exhibit.Logo.create(configuration, elmt, uiContext);
case "hiddenContent":
elmt.style.display = "none";
return null;
}
}
return null;
};
Exhibit.UI.createFromDOM = function(elmt, uiContext) {
var role = Exhibit.getRoleAttribute(elmt);
if (role in Exhibit.UI.componentMap) {
var createFromDOMFunc = Exhibit.UI.componentMap[role].createFromDOM;
return createFromDOMFunc(elmt, uiContext);
}
switch (role) {
case "lens":
case "edit-lens":
Exhibit.UIContext.registerLensFromDOM(elmt, uiContext.getLensRegistry());
return null;
case "view":
return Exhibit.UI.createViewFromDOM(elmt, null, uiContext);
case "facet":
return Exhibit.UI.createFacetFromDOM(elmt, null, uiContext);
case "coordinator":
return Exhibit.UI.createCoordinatorFromDOM(elmt, uiContext);
case "coder":
return Exhibit.UI.createCoderFromDOM(elmt, uiContext);
case "viewPanel":
return Exhibit.ViewPanel.createFromDOM(elmt, uiContext);
case "logo":
return Exhibit.Logo.createFromDOM(elmt, uiContext);
case "hiddenContent":
elmt.style.display = "none";
return null;
}
return null;
};
Exhibit.UI.generateCreationMethods = function(constructor) {
constructor.create = function(configuration, elmt, uiContext) {
var newContext = Exhibit.UIContext.create(configuration, uiContext);
var settings = {};
Exhibit.SettingsUtilities.collectSettings(
configuration,
constructor._settingSpecs || {},
settings);
return new constructor(elmt, newContext, settings);
};
constructor.createFromDOM = function(elmt, uiContext) {
var newContext = Exhibit.UIContext.createFromDOM(elmt, uiContext);
var settings = {};
Exhibit.SettingsUtilities.collectSettingsFromDOM(
elmt,
constructor._settingSpecs || {},
settings);
return new constructor(elmt, newContext, settings);
};
};
Exhibit.UI.createView = function(configuration, elmt, uiContext) {
var viewClass = "viewClass" in configuration ? configuration.viewClass : Exhibit.TileView;
if (typeof viewClass == "string") {
viewClass = Exhibit.UI.viewClassNameToViewClass(viewClass);
}
return viewClass.create(configuration, elmt, uiContext);
};
Exhibit.UI.createViewFromDOM = function(elmt, container, uiContext) {
var viewClass = Exhibit.UI.viewClassNameToViewClass(Exhibit.getAttribute(elmt, "viewClass"));
return viewClass.createFromDOM(elmt, container, uiContext);
};
Exhibit.UI.viewClassNameToViewClass = function(name) {
if (name != null && name.length > 0) {
try {
return Exhibit.UI._stringToObject(name, "View");
} catch (e) {
SimileAjax.Debug.warn("Unknown viewClass " + name);
}
}
return Exhibit.TileView;
};
Exhibit.UI.createFacet = function(configuration, elmt, uiContext) {
var facetClass = "facetClass" in configuration ? configuration.facetClass : Exhibit.ListFacet;
if (typeof facetClass == "string") {
facetClass = Exhibit.UI.facetClassNameToFacetClass(facetClass);
}
return facetClass.create(configuration, elmt, uiContext);
};
Exhibit.UI.createFacetFromDOM = function(elmt, container, uiContext) {
var facetClass = Exhibit.UI.facetClassNameToFacetClass(Exhibit.getAttribute(elmt, "facetClass"));
return facetClass.createFromDOM(elmt, container, uiContext);
};
Exhibit.UI.facetClassNameToFacetClass = function(name) {
if (name != null && name.length > 0) {
try {
return Exhibit.UI._stringToObject(name, "Facet");
} catch (e) {
SimileAjax.Debug.warn("Unknown facetClass " + name);
}
}
return Exhibit.ListFacet;
};
Exhibit.UI.createCoder = function(configuration, uiContext) {
var coderClass = "coderClass" in configuration ? configuration.coderClass : Exhibit.ColorCoder;
if (typeof coderClass == "string") {
coderClass = Exhibit.UI.coderClassNameToCoderClass(coderClass);
}
return coderClass.create(configuration, uiContext);
};
Exhibit.UI.createCoderFromDOM = function(elmt, uiContext) {
var coderClass = Exhibit.UI.coderClassNameToCoderClass(Exhibit.getAttribute(elmt, "coderClass"));
return coderClass.createFromDOM(elmt, uiContext);
};
Exhibit.UI.coderClassNameToCoderClass = function(name) {
if (name != null && name.length > 0) {
try {
return Exhibit.UI._stringToObject(name, "Coder");
} catch (e) {
SimileAjax.Debug.warn("Unknown coderClass " + name);
}
}
return Exhibit.ColorCoder;
};
Exhibit.UI.createCoordinator = function(configuration, uiContext) {
return Exhibit.Coordinator.create(configuration, uiContext);
};
Exhibit.UI.createCoordinatorFromDOM = function(elmt, uiContext) {
return Exhibit.Coordinator.createFromDOM(elmt, uiContext);
};
Exhibit.UI._stringToObject = function(name, suffix) {
if (!name.startsWith("Exhibit.")) {
if (!name.endsWith(suffix)) {
try {
return eval("Exhibit." + name + suffix);
} catch (e) {
// ignore
}
}
try {
return eval("Exhibit." + name);
} catch (e) {
// ignore
}
}
if (!name.endsWith(suffix)) {
try {
return eval(name + suffix);
} catch (e) {
// ignore
}
}
try {
return eval(name);
} catch (e) {
// ignore
}
throw new Error("Unknown class " + name);
};
/*----------------------------------------------------------------------
* Help and Debugging
*----------------------------------------------------------------------
*/
Exhibit.UI.docRoot = "http://simile.mit.edu/wiki/";
Exhibit.UI.validator = "http://simile.mit.edu/babel/validator";
Exhibit.UI.showHelp = function(message, url, target) {
target = (target) ? target : "_blank";
if (url != null) {
if (window.confirm(message + "\n\n" + Exhibit.l10n.showDocumentationMessage)) {
window.open(url, target);
}
} else {
window.alert(message);
}
};
Exhibit.UI.showJavascriptExpressionValidation = function(message, expression) {
var target = "_blank";
if (window.confirm(message + "\n\n" + Exhibit.l10n.showJavascriptValidationMessage)) {
window.open(Exhibit.UI.validator + "?expresson=" + encodeURIComponent(expression), target);
}
};
Exhibit.UI.showJsonFileValidation = function(message, url) {
var target = "_blank";
if (url.indexOf("file:") == 0) {
if (window.confirm(message + "\n\n" + Exhibit.l10n.showJsonValidationFormMessage)) {
window.open(Exhibit.UI.validator, target);
}
} else {
if (window.confirm(message + "\n\n" + Exhibit.l10n.showJsonValidationMessage)) {
window.open(Exhibit.UI.validator + "?url=" + url, target);
}
}
};
/*----------------------------------------------------------------------
* Status Indication and Feedback
*----------------------------------------------------------------------
*/
Exhibit.UI._busyIndicator = null;
Exhibit.UI._busyIndicatorCount = 0;
Exhibit.UI.showBusyIndicator = function() {
Exhibit.UI._busyIndicatorCount++;
if (Exhibit.UI._busyIndicatorCount > 1) {
return;
}
if (Exhibit.UI._busyIndicator == null) {
Exhibit.UI._busyIndicator = Exhibit.UI.createBusyIndicator();
}
var scrollTop = ("scrollTop" in document.body) ?
document.body.scrollTop :
document.body.parentNode.scrollTop;
var height = ("innerHeight" in window) ?
window.innerHeight :
("clientHeight" in document.body ?
document.body.clientHeight :
document.body.parentNode.clientHeight);
var top = Math.floor(scrollTop + height / 3);
Exhibit.UI._busyIndicator.style.top = top + "px";
document.body.appendChild(Exhibit.UI._busyIndicator);
};
Exhibit.UI.hideBusyIndicator = function() {
Exhibit.UI._busyIndicatorCount--;
if (Exhibit.UI._busyIndicatorCount > 0) {
return;
}
try {
document.body.removeChild(Exhibit.UI._busyIndicator);
} catch(e) {
// silent
}
};
/*----------------------------------------------------------------------
* Common UI Generation
*----------------------------------------------------------------------
*/
Exhibit.UI.protectUI = function(elmt) {
SimileAjax.DOM.appendClassName(elmt, "exhibit-ui-protection");
};
Exhibit.UI.makeActionLink = function(text, handler, layer) {
var a = document.createElement("a");
a.href = "javascript:";
a.className = "exhibit-action";
a.innerHTML = text;
var handler2 = function(elmt, evt, target) {
if ("true" != elmt.getAttribute("disabled")) {
handler(elmt, evt, target);
}
}
SimileAjax.WindowManager.registerEvent(a, "click", handler2, layer);
return a;
};
Exhibit.UI.enableActionLink = function(a, enabled) {
a.setAttribute("disabled", enabled ? "false" : "true");
a.className = enabled ? "exhibit-action" : "exhibit-action-disabled";
};
Exhibit.UI.makeItemSpan = function(itemID, label, uiContext, layer) {
if (label == null) {
label = database.getObject(itemID, "label");
if (label == null) {
label = itemID;
}
}
var a = SimileAjax.DOM.createElementFromString(
"" + label + "");
var handler = function(elmt, evt, target) {
Exhibit.UI.showItemInPopup(itemID, elmt, uiContext);
}
SimileAjax.WindowManager.registerEvent(a, "click", handler, layer);
return a;
};
Exhibit.UI.makeValueSpan = function(label, valueType, layer) {
var span = document.createElement("span");
span.className = "exhibit-value";
if (valueType == "url") {
var url = label;
if (Exhibit.params.safe && url.trim().startsWith("javascript:")) {
span.appendChild(document.createTextNode(url));
} else {
span.innerHTML =
"" +
(label.length > 50 ?
label.substr(0, 20) + " ... " + label.substr(label.length - 20) :
label) +
"";
}
} else {
if (Exhibit.params.safe) {
label = Exhibit.Formatter.encodeAngleBrackets(label);
}
span.innerHTML = label;
}
return span;
};
Exhibit.UI.calculatePopupPosition = function(elmt) {
var coords = SimileAjax.DOM.getPageCoordinates(elmt);
return {
x: coords.left + Math.round(elmt.offsetWidth / 2),
y: coords.top + Math.round(elmt.offsetHeight / 2)
};
}
Exhibit.UI.showItemInPopup = function(itemID, elmt, uiContext, opts) {
SimileAjax.WindowManager.popAllLayers();
opts = opts || {};
opts.coords = opts.coords || Exhibit.UI.calculatePopupPosition(elmt);
var itemLensDiv = document.createElement("div");
var lensOpts = {
inPopup: true,
coords: opts.coords
};
if (opts.lensType == 'normal') {
lensOpts.lensTemplate = uiContext.getLensRegistry().getNormalLens(itemID, uiContext);
} else if (opts.lensType == 'edit') {
lensOpts.lensTemplate = uiContext.getLensRegistry().getEditLens(itemID, uiContext);
} else if (opts.lensType) {
SimileAjax.Debug.warn('Unknown Exhibit.UI.showItemInPopup opts.lensType: ' + opts.lensType);
}
uiContext.getLensRegistry().createLens(itemID, itemLensDiv, uiContext, lensOpts);
SimileAjax.Graphics.createBubbleForContentAndPoint(
itemLensDiv,
opts.coords.x,
opts.coords.y,
uiContext.getSetting("bubbleWidth")
);
};
Exhibit.UI.createButton = function(name, handler, className) {
var button = document.createElement("button");
button.className = (className || "exhibit-button") + " screen";
button.innerHTML = name;
if (handler) {
SimileAjax.WindowManager.registerEvent(button, "click", handler);
}
return button;
};
Exhibit.UI.createPopupMenuDom = function(element) {
var div = document.createElement("div");
div.className = "exhibit-menu-popup exhibit-ui-protection";
var dom = {
elmt: div,
close: function() {
document.body.removeChild(this.elmt);
},
open: function() {
var self = this;
this.layer = SimileAjax.WindowManager.pushLayer(function() { self.close(); }, true, div);
var docWidth = document.body.offsetWidth;
var docHeight = document.body.offsetHeight;
var coords = SimileAjax.DOM.getPageCoordinates(element);
div.style.top = (coords.top + element.scrollHeight) + "px";
div.style.right = (docWidth - (coords.left + element.scrollWidth)) + "px";
document.body.appendChild(this.elmt);
},
appendMenuItem: function(label, icon, onClick) {
var self = this;
var a = document.createElement("a");
a.className = "exhibit-menu-item";
a.href = "javascript:";
SimileAjax.WindowManager.registerEvent(a, "click", function(elmt, evt, target) {
onClick(elmt, evt, target);
SimileAjax.WindowManager.popLayer(self.layer);
SimileAjax.DOM.cancelEvent(evt);
return false;
});
var div = document.createElement("div");
a.appendChild(div);
div.appendChild(SimileAjax.Graphics.createTranslucentImage(
icon != null ? icon : (Exhibit.urlPrefix + "images/blank-16x16.png")));
div.appendChild(document.createTextNode(label));
this.elmt.appendChild(a);
},
appendSeparator: function() {
var hr = document.createElement("hr");
this.elmt.appendChild(hr);
}
};
return dom;
};
Exhibit.UI.createBusyIndicator = function() {
var urlPrefix = Exhibit.urlPrefix + "images/";
var containerDiv = document.createElement("div");
if (SimileAjax.Graphics.pngIsTranslucent) {
var topDiv = document.createElement("div");
topDiv.style.height = "33px";
topDiv.style.background = "url(" + urlPrefix + "message-bubble/message-top-left.png) top left no-repeat";
topDiv.style.paddingLeft = "44px";
containerDiv.appendChild(topDiv);
var topRightDiv = document.createElement("div");
topRightDiv.style.height = "33px";
topRightDiv.style.background = "url(" + urlPrefix + "message-bubble/message-top-right.png) top right no-repeat";
topDiv.appendChild(topRightDiv);
var middleDiv = document.createElement("div");
middleDiv.style.background = "url(" + urlPrefix + "message-bubble/message-left.png) top left repeat-y";
middleDiv.style.paddingLeft = "44px";
containerDiv.appendChild(middleDiv);
var middleRightDiv = document.createElement("div");
middleRightDiv.style.background = "url(" + urlPrefix + "message-bubble/message-right.png) top right repeat-y";
middleRightDiv.style.paddingRight = "44px";
middleDiv.appendChild(middleRightDiv);
var contentDiv = document.createElement("div");
middleRightDiv.appendChild(contentDiv);
var bottomDiv = document.createElement("div");
bottomDiv.style.height = "55px";
bottomDiv.style.background = "url(" + urlPrefix + "message-bubble/message-bottom-left.png) bottom left no-repeat";
bottomDiv.style.paddingLeft = "44px";
containerDiv.appendChild(bottomDiv);
var bottomRightDiv = document.createElement("div");
bottomRightDiv.style.height = "55px";
bottomRightDiv.style.background = "url(" + urlPrefix + "message-bubble/message-bottom-right.png) bottom right no-repeat";
bottomDiv.appendChild(bottomRightDiv);
} else {
containerDiv.style.border = "2px solid #7777AA";
containerDiv.style.padding = "20px";
containerDiv.style.background = "white";
SimileAjax.Graphics.setOpacity(containerDiv, 90);
var contentDiv = document.createElement("div");
containerDiv.appendChild(contentDiv);
}
containerDiv.className = "exhibit-busyIndicator";
contentDiv.className = "exhibit-busyIndicator-content";
var img = document.createElement("img");
img.src = urlPrefix + "progress-running.gif";
contentDiv.appendChild(img);
contentDiv.appendChild(document.createTextNode(" " + Exhibit.l10n.busyIndicatorMessage));
return containerDiv;
};
Exhibit.UI.createFocusDialogBox = function(itemID, exhibit, configuration) {
var template = {
tag: "div",
className: "exhibit-focusDialog exhibit-ui-protection",
children: [
{ tag: "div",
className: "exhibit-focusDialog-viewContainer",
field: "viewContainer"
},
{ tag: "div",
className: "exhibit-focusDialog-controls",
children: [
{ tag: "button",
field: "closeButton",
children: [ Exhibit.l10n.focusDialogBoxCloseButtonLabel ]
}
]
}
]
};
var dom = SimileAjax.DOM.createDOMFromTemplate(template);
dom.close = function() {
document.body.removeChild(dom.elmt);
};
dom.open = function() {
dom.layer = SimileAjax.WindowManager.pushLayer(function() { dom.close(); }, false);
var lens = new Exhibit.Lens(itemID, dom.viewContainer, exhibit, configuration);
dom.elmt.style.top = (document.body.scrollTop + 100) + "px";
document.body.appendChild(dom.elmt);
SimileAjax.WindowManager.registerEvent(
dom.closeButton,
"click",
function(elmt, evt, target) {
SimileAjax.WindowManager.popLayer(dom.layer);
SimileAjax.DOM.cancelEvent(evt);
return false;
},
dom.layer
);
};
return dom;
};
Exhibit.UI.createTranslucentImage = function(relativeUrl, verticalAlign) {
return SimileAjax.Graphics.createTranslucentImage(Exhibit.urlPrefix + relativeUrl, verticalAlign);
};
Exhibit.UI.createTranslucentImageHTML = function(relativeUrl, verticalAlign) {
return SimileAjax.Graphics.createTranslucentImageHTML(Exhibit.urlPrefix + relativeUrl, verticalAlign);
};
// jQuery can't search for attributes with colons in them
Exhibit.UI.findAttribute = function(attr, value, parent) {
var parent = SimileAjax.jQuery(parent || document.body);
var f = function( ) {
var v = this.getAttribute(attr);
if (value === undefined) {
return !!v;
} else if (value instanceof Array) {
return value.indexOf(v) != -1;
} else {
return value.toString() == v;
}
}
return parent.find('*').add(parent).filter(f);
}