infusion
Version:
Infusion is an application framework for developing flexible stuff with JavaScript
220 lines (200 loc) • 7.49 kB
JavaScript
/*
Copyright The Infusion copyright holders
See the AUTHORS.md file at the top-level directory of this distribution and at
https://github.com/fluid-project/infusion/raw/main/AUTHORS.md.
Licensed under the Educational Community License (ECL), Version 2.0 or the New
BSD license. You may not use this file except in compliance with one these
Licenses.
You may obtain a copy of the ECL 2.0 License and BSD License at
https://github.com/fluid-project/infusion/raw/main/Infusion-LICENSE.txt
*/
"use strict";
fluid.registerNamespace("fluid.tooltip");
fluid.tooltip.computeContentFunc = function (that) {
that.contentFunc = that.options.contentFunc ? that.options.contentFunc : that.modelToContentFunc();
};
fluid.tooltip.updateContentImpl = function (that) {
that.computeContentFunc();
if (that.initialised) {
that.container.tooltip("option", "content", that.contentFunc);
}
};
fluid.tooltip.idSearchFunc = function (idToContentFunc) {
return function (/* callback*/) {
var target = this;
if ($.contains( target.ownerDocument, target )) { // prevent widget from trying to open tooltip for element no longer in document (FLUID-5394)
var idToContent = idToContentFunc();
var ancestor = fluid.findAncestor(target, function (element) {
return idToContent[element.id];
});
return ancestor ? idToContent[ancestor.id] : null;
} else {
return null;
}
};
};
fluid.tooltip.modelToContentFunc = function (that) {
var model = that.model;
if (model.idToContent) {
return fluid.tooltip.idSearchFunc(function () {
return that.model.idToContent;
});
} else if (model.content) {
return function () {
return model.content;
};
}
};
// Resolve FLUID-5673 by resolving the event target upwards to the nearest match for "items" - this will
// reproduce the natural effect operated by event bubbling in conjunction with the widget
fluid.tooltip.resolveTooltipTarget = function (items, event) {
var originalTarget = fluid.resolveEventTarget(event);
var tooltipTarget = $(originalTarget).closest(items);
return tooltipTarget[0];
};
// Note that fluid.resolveEventTarget is required
// because of strange dispatching within tooltip widget's "_open" method
// -> this._trigger( "open", event, { tooltip: tooltip };
// the target of the outer event will be incorrect
fluid.tooltip.makeOpenHandler = function (that) {
return function (event, tooltip) {
fluid.tooltip.closeAll(that);
var originalTarget = fluid.tooltip.resolveTooltipTarget(that.options.items, event);
var key = fluid.allocateSimpleId(originalTarget);
that.openIdMap[key] = true;
if (that.initialised) {
that.events.afterOpen.fire(that, originalTarget, tooltip.tooltip, event);
}
};
};
fluid.tooltip.makeCloseHandler = function (that) {
return function (event, tooltip) {
if (that.initialised) { // underlying jQuery UI component will fire various spurious close events after it has been destroyed
var originalTarget = fluid.tooltip.resolveTooltipTarget(that.options.items, event);
delete that.openIdMap[originalTarget.id];
that.events.afterClose.fire(that, originalTarget, tooltip.tooltip, event);
}
};
};
fluid.tooltip.closeAll = function (that) {
var dokkument = fluid.getDocument(that.container);
fluid.each(that.openIdMap, function (value, key) {
var target = fluid.byId(key, dokkument);
// "white-box" behaviour - fabricating this fake event shell triggers the standard "close" sequence including notifying
// our own handler. This will be very fragile to changes in jQuery UI and the underlying widget code
that.container.tooltip("close", {
type: "close",
currentTarget: target,
target: target
});
});
fluid.clear(that.openIdMap);
};
fluid.tooltip.setup = function (that) {
fluid.tooltip.updateContentImpl(that);
var directOptions = {
content: that.contentFunc,
open: fluid.tooltip.makeOpenHandler(that),
close: fluid.tooltip.makeCloseHandler(that)
};
var fullOptions = $.extend(true, directOptions, that.options.widgetOptions);
that.container.tooltip(fullOptions);
that.initialised = true;
};
fluid.tooltip.doDestroy = function (that) {
if (that.initialised) {
fluid.tooltip.closeAll(that, true);
var dokkument = fluid.getDocument(that.container),
container = that.container[0];
// jQuery UI framework will throw a fit if we have instantiated a widget on a DOM element and then
// removed it from the DOM. This apparently can't be detected via the jQuery UI API itself.
if ($.contains(dokkument, container) || dokkument === container) {
that.container.tooltip("destroy");
}
that.initialised = false; // TODO: proper framework facility for this coming with FLUID-4890
}
};
fluid.defaults("fluid.tooltip", {
gradeNames: ["fluid.viewComponent"],
widgetOptions: {
tooltipClass: "{that}.options.styles.tooltip",
classes: {
"ui-tooltip": "{that}.options.styles.tooltip",
"ui-tooltip-content": "{that}.options.styles.tooltipContent"
},
position: "{that}.options.position",
items: "{that}.options.items",
show: {
duration: "{that}.options.duration",
delay: "{that}.options.delay"
},
hide: {
duration: "{that}.options.duration",
delay: "{that}.options.delay"
}
},
invokers: {
/**
* Manually displays the tooltip
*/
open: {
"this": "{that}.container",
method: "tooltip",
args: "open"
},
/**
* Manually hides the tooltip
*/
close: {
funcName: "fluid.tooltip.closeAll",
args: "{that}"
},
updateContent: {
changePath: "content",
value: "{arguments}.0"
},
computeContentFunc: {
funcName: "fluid.tooltip.computeContentFunc",
args: ["{that}"]
},
modelToContentFunc: {
funcName: "fluid.tooltip.modelToContentFunc",
args: "{that}"
}
},
model: {
// backward compatibility for pre-1.5 users of Tooltip
content: "{that}.options.content"
// content: String,
// idToContent: Object {String -> String}
},
members: {
openIdMap: {}
},
styles: {
tooltip: ""
},
events: {
afterOpen: null, // arguments: that, event.target, tooltip, event
afterClose: null // arguments: that, event.target, tooltip, event
},
listeners: {
"onCreate.setup": "fluid.tooltip.setup",
"onDestroy.doDestroy": "fluid.tooltip.doDestroy"
},
modelListeners: {
// TODO: We could consider a more fine-grained scheme for this,
// listening to content and idToContent separately
"": {
funcName: "fluid.tooltip.updateContentImpl",
excludeSource: "init",
args: "{that}"
}
},
position: {
my: "left top",
at: "left bottom"
},
items: "*",
delay: 300
});