BootstrapFrontend.setup({
styleChanger: {if $__wcf->getStyleHandler()->countStyles() > 1}true{else}false{/if}
});
+
+ require(['WoltLab/WCF/Controller/Popover'], function(ControllerPopover) {
+ ControllerPopover.init({
+ attributeName: 'data-user-id',
+ className: 'userLink',
+ identifier: 'com.woltlab.wcf.user',
+ loadCallback: function(objectId, popover) {
+ new WCF.Action.Proxy({
+ autoSend: true,
+ data: {
+ actionName: 'getUserProfile',
+ className: 'wcf\\data\\user\\UserProfileAction',
+ objectIDs: [ objectId ]
+ },
+ success: (function(data) {
+ popover.setContent('com.woltlab.wcf.user', objectId, data.returnValues.template);
+ }).bind(this),
+ failure: (function(data) {
+ // TODO
+ }).bind(this)
+ });
+ }
+ });
+ });
});
</script>
WCF.System.PageNavigation.init('.pageNavigation');
WCF.Date.Picker.init();
- new WCF.User.ProfilePreview();
+ //new WCF.User.ProfilePreview();
new WCF.Notice.Dismiss();
WCF.User.Profile.ActivityPointList.init();
});
//]]>
</script>
-<!--[IF IE 9]>
-<script data-relocate="true">
- $(function() {
- function fixButtonTypeIE9() {
- $('button').each(function(index, button) {
- var $button = $(button);
- if (!$button.attr('type')) {
- $button.attr('type', 'button');
- }
- });
- }
-
- WCF.DOMNodeInsertedHandler.addCallback('WCF.FixButtonTypeIE9', fixButtonTypeIE9);
- fixButtonTypeIE9();
- });
-</script>
-<![ENDIF]-->
{include file='imageViewer'}
*/
forEach: function(identifier, callback) {
var callbacks = this._dictionary.get(identifier);
- if (callbacks !== null) {
+ if (callbacks !== undefined) {
callbacks.forEach(callback);
}
}
--- /dev/null
+define(['Dictionary', 'DOM/Util', 'UI/Alignment'], function(Dictionary, DOMUtil, UIAlignment) {
+ "use strict";
+
+ var _activeId = 0;
+ var _activeIdentifier = '';
+ var _cache = null;
+ var _handlers = null;
+ var _suspended = false;
+ var _timeoutEnter = null;
+ var _timeoutLeave = null;
+
+ var _popover = null;
+ var _popoverContent = null;
+ var _popoverLoading = null;
+
+ /** @const */ var STATE_NONE = 0;
+ /** @const */ var STATE_LOADING = 1;
+ /** @const */ var STATE_READY = 2;
+
+ /**
+ * @constructor
+ */
+ function ControllerPopover() {};
+ ControllerPopover.prototype = {
+ _setup: function() {
+ if (_popover !== null) {
+ return;
+ }
+
+ _cache = new Dictionary();
+ _handlers = new Dictionary();
+
+ _popover = document.createElement('div');
+ _popover.classList.add('popover');
+
+ var pointer = document.createElement('span');
+ pointer.classList.add('elementPointer');
+ pointer.appendChild(document.createElement('span'));
+ _popover.appendChild(pointer);
+
+ _popoverLoading = document.createElement('span');
+ _popoverLoading.className = 'icon icon48 fa-spinner';
+ _popover.appendChild(_popoverLoading);
+
+ _popoverContent = document.createElement('div');
+ _popoverContent.classList.add('popoverContent');
+ _popover.appendChild(_popoverContent);
+
+ document.body.appendChild(_popover);
+
+ window.addEventListener('beforeunload', (function() {
+ _suspended = true;
+ this._hide(true);
+ }).bind(this));
+
+ WCF.DOMNodeInsertedHandler.addCallback('WoltLab/WCF/Controller/Popover', this._init.bind(this));
+ },
+
+ init: function(options) {
+ if ($.browser.mobile) {
+ return;
+ }
+
+ options.attributeName = options.attributeName || 'data-object-id';
+
+ this._setup();
+
+ if (_handlers.has(options.identifier)) {
+ return;
+ }
+
+ _cache.set(options.identifier, new Dictionary());
+ _handlers.set(options.identifier, {
+ attributeName: options.attributeName,
+ elements: document.getElementsByClassName(options.className),
+ loadCallback: options.loadCallback
+ });
+
+ this._init(options.identifier)
+ },
+
+ setContent: function(identifier, objectId, content) {
+ content = (typeof content === 'string') ? content.trim() : '';
+ if (content.length === 0) {
+ throw new Error("Expected a non-empty HTML string for '" + objectId + "' (identifier: '" + identifier + "').");
+ }
+
+ var objects = _cache.get(identifier);
+ if (objects === undefined) {
+ throw new Error("Expected a valid identifier, '" + identifier + "' is invalid.");
+ }
+
+ var obj = objects.get(objectId);
+ if (obj === undefined) {
+ throw new Error("Expected a valid object id, '" + objectId + "' is invalid (identifier: '" + identifier + "').");
+ }
+
+ obj.element = DOMUtil.createFragmentFromHtml(content);
+ obj.state = STATE_READY;
+ console.debug(obj);
+ this._show(identifier, objectId);
+ },
+
+ _init: function(identifier) {
+ if (typeof identifier === 'string' && identifier.length) {
+ this._initElements(identifier, _handlers.get(identifier));
+ }
+ else {
+ _handlers.forEach((function(options, identifier) {
+ this._initElements(identifier, options);
+ }).bind(this));
+ }
+ },
+
+ _initElements: function(identifier, options) {
+ var cachedElements = _cache.get(identifier);
+ console.debug(identifier);
+ console.debug(options);
+ for (var i = 0, length = options.elements.length; i < length; i++) {
+ var element = options.elements[i];
+ var objectId = ~~element.getAttribute(options.attributeName);
+
+ if (objectId === 0 || cachedElements.has(objectId)) {
+ continue;
+ }
+
+ element.addEventListener('mouseenter', (function() { this._mouseEnter(identifier, objectId); }).bind(this));
+ element.addEventListener('mouseleave', (function() { this._mouseLeave(identifier, objectId); }).bind(this));
+
+ if (element.nodeName === 'A' && element.getAttribute('href')) {
+ element.addEventListener('click', (function() {
+ this._hide(true);
+ }).bind(this))
+ }
+
+ cachedElements.set(objectId, {
+ element: element,
+ state: STATE_NONE
+ });
+ }
+ },
+
+ _mouseEnter: function(identifier, objectId) {
+ if (this._timeoutEnter !== null) {
+ window.clearTimeout(this._timeoutEnter);
+ }
+
+ this._hoverIdentifier = identifier;
+ this._hoverId = objectId;
+ window.setTimeout((function() {
+ if (this._hoverId === objectId) {
+ this._show(identifier, objectId);
+ }
+ }).bind(this));
+ },
+
+ _mouseLeave: function(identifier, objectId) {
+
+ },
+
+ _show: function(identifier, objectId) {
+ if (this._intervalOut !== null) {
+ window.clearTimeout(this._intervalOut);
+ }
+
+ if (_popover.classList.contains('active')) {
+ this._hide(true);
+ }
+
+ if (_activeId && _activeId !== objectId) {
+ var cachedContent = _cache.get(_activeElementId);
+ while (_popoverContent.childNodes.length) {
+ cachedContent.appendChild(_popoverContent.childNodes[0]);
+ }
+ }
+
+ var content = _cache.get(identifier).get(objectId);
+ if (content.state === STATE_READY) {
+ _popoverContent.classList.remove('loading');
+ _popoverContent.appendChild(content.element);
+ }
+ else if (content.state === STATE_NONE) {
+ _popoverContent.classList.add('loading');
+ }
+
+ _activeId = objectId;
+ _activeIdentifier = identifier;
+
+ if (content.state === STATE_NONE) {
+ content.state = STATE_LOADING;
+
+ this._load(identifier, objectId);
+ }
+ },
+
+ _hide: function(disableAnimation) {
+ _popover.classList.remove('active');
+
+ if (disableAnimation) {
+ _popover.classList.add('disableAnimation');
+ }
+
+ _activeIdentifier = '';
+ _activeId = null;
+ },
+
+ _load: function(identifier, objectId) {
+ _handlers.get(identifier).loadCallback(objectId, this);
+ },
+
+ _rebuild: function(elementId) {
+ if (elementId !== _activeElementId) {
+ return;
+ }
+
+ _popover.classList.add('active');
+ _popoverContent.appendChild(_cache.get(elementId));
+ _popoverContent.classList.remove('loading');
+
+ UIAlignment.set(_popover, document.getElementById(elementId), {
+ pointer: true
+ });
+ }
+ };
+
+ return new ControllerPopover();
+});
*/
function DOMUtil() {};
DOMUtil.prototype = {
+ /**
+ * Returns a DocumentFragment containing the provided HTML string as DOM nodes.
+ *
+ * @param {string} html HTML string
+ * @return {DocumentFragment} fragment containing DOM nodes
+ */
+ createFragmentFromHtml: function(html) {
+ var tmp = document.createElement('div');
+ tmp.innerHTML = html;
+
+ var fragment = document.createDocumentFragment();
+ while (tmp.childNodes.length) {
+ fragment.appendChild(tmp.childNodes[0]);
+ }
+
+ return fragment;
+ },
+
/**
* Returns a unique element id.
*
* @param {*} value value
*/
set: function(key, value) {
+ if (typeof key === 'number') key = key.toString();
+
if (typeof key !== "string") {
- throw new TypeError("Only strings can be used as keys, rejected '" + + "' (" + typeof key + ").");
+ throw new TypeError("Only strings can be used as keys, rejected '" + key + "' (" + typeof key + ").");
}
if (_hasMap) this._dictionary.set(key, value);
* @param {string} key key
*/
remove: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
if (_hasMap) this._dictionary.remove(key);
else this._dictionary[key] = undefined;
},
* @return {boolean} true if key exists and value is not undefined
*/
has: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
if (_hasMap) return this._dictionary.has(key);
else {
return (this._dictionary.hasOwnProperty(key) && typeof this._dictionary[key] !== "undefined");
},
/**
- * Retrieves a value by key, returns null if not found or undefined.
+ * Retrieves a value by key, returns undefined if there is no match.
*
* @param {string} key key
* @return {*}
*/
get: function(key) {
+ if (typeof key === 'number') key = key.toString();
+
if (this.has(key)) {
if (_hasMap) return this._dictionary.get(key);
else return this._dictionary[key];
}
- return null;
+ return undefined;
},
/**
}
var actions = _listeners.get(identifier);
- if (actions === null) {
+ if (actions === undefined) {
actions = new Dictionary();
_listeners.set(identifier, actions);
}
var callbacks = actions.get(action);
- if (callbacks === null) {
+ if (callbacks === undefined) {
callbacks = new Dictionary();
actions.set(action, callbacks);
}
data = data || {};
var actions = _listeners.get(identifier);
- if (actions !== null) {
+ if (actions !== undefined) {
var callbacks = actions.get(action);
- if (callbacks !== null) {
+ if (callbacks !== undefined) {
callbacks.forEach(function(callback) {
callback(data);
});
*/
remove: function(identifier, action, uuid) {
var actions = _listeners.get(identifier);
- if (actions === null) {
+ if (actions === undefined) {
return;
}
var callbacks = actions.get(action);
- if (callbacks === null) {
+ if (callbacks === undefined) {
return;
}
* @param {string=} action action name
*/
removeAll: function(identifier, action) {
+ if (typeof action !== 'string') action = undefined;
+
var actions = _listeners.get(identifier);
- if (actions === null) {
+ if (actions === undefined) {
return;
}
}
}
else if (align === 'right') {
- console.debug(windowWidth + " | " + refOffsets.left + " | " + refDimensions.width);
right = windowWidth - (refOffsets.left + refDimensions.width);
if (right < 0) {
result = false;
*/
setTitle: function(id, title) {
var data = _dialogs.get(id);
- if (typeof data === 'undefined') {
+ if (data === undefined) {
throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
}
*/
_updateDialog: function(id, html) {
var data = _dialogs.get(id);
- if (typeof data === 'undefined') {
+ if (data === undefined) {
throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
}
*/
rebuild: function(id) {
var data = _dialogs.get(id);
- if (typeof data === 'undefined') {
+ if (data === undefined) {
throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
}
*/
close: function(id) {
var data = _dialogs.get(id);
- if (typeof data === 'undefined') {
+ if (data === undefined) {
throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
}
*/
setAlignmentById: function(containerId) {
var dropdown = _dropdowns.get(containerId);
- if (dropdown === null) {
+ if (dropdown === undefined) {
throw new Error("Unknown dropdown identifier '" + containerId + "'.");
}
*/
close: function(containerId) {
var dropdown = _dropdowns.get(containerId);
- if (dropdown !== null) {
+ if (dropdown !== undefined) {
dropdown.classList.remove('dropdownOpen');
_menus.get(containerId).classList.remove('dropdownOpen');
}
// check if 'isOverlayDropdownButton' is set which indicates if
// the dropdown toggle is in an overlay
var dropdown = _dropdowns.get(targetId);
- if (dropdown !== null && dropdown.getAttribute('data-is-overlay-dropdown-button') === null) {
+ if (dropdown !== undefined && dropdown.getAttribute('data-is-overlay-dropdown-button') === null) {
var dialogContent = DOMTraverse.parentByClass(dropdown, 'dialogContent');
dropdown.setAttribute('data-is-overlay-dropdown-button', (dialogContent !== null));
*/
rebuild: function(containerId) {
var container = this._containers.get(containerId);
- if (container === null) {
+ if (container === undefined) {
throw "Expected a valid element id, '" + containerId + "' is unknown.";
}
var items = DOMTraverse.childrenByTag(list, 'LI');
var dropdown = this._dropdowns.get(containerId);
var dropdownWidth = 0;
- if (dropdown !== null) {
+ if (dropdown !== undefined) {
// show all items for calculation
for (var i = 0, length = items.length; i < length; i++) {
var item = items[i];
dropdownMenu.innerHTML = '';
dropdownMenu.appendChild(fragment);
}
- else if (dropdown !== null && dropdown.parentNode !== null) {
+ else if (dropdown !== undefined && dropdown.parentNode !== null) {
dropdown.parentNode.removeChild(dropdown);
}
}
* Returns a SimpleTabMenu instance for given container id.
*
* @param {string} containerId tab menu container id
- * @return {SimpleTabMenu} tab menu object
+ * @return {(SimpleTabMenu|undefined)} tab menu object
*/
getTabMenu: function(containerId) {
return _tabMenus.get(containerId);
}
var container = this._containers.get(name);
- if (container === null) {
+ if (container === undefined) {
throw new Error("Expected content element for li[data-name='" + name + "'] (tab menu id: '" + this._containerId + "').");
}
else if (container.parentNode !== this._container) {
if (preselect !== false) {
if (preselect !== true) {
var tab = this._tabs.get(preselect);
- if (tab !== null) {
+ if (tab !== undefined) {
this.select(null, tab, true);
}
}
}
}
- .pointer {
- border-color: @wcfTooltipBackgroundColor transparent;
- border-style: solid;
- border-width: 0 5px 5px;
- left: 50%;
- position: absolute;
- top: -5px;
- }
-
.boxShadow(0, 3px, rgba(0, 0, 0, .3), 7px);
- &.inverse > .pointer {
- border-width: 5px 5px 0;
- }
-
&.active {
opacity: 1;
visibility: visible;
}
}
-/* popover */
-.popover {
- background-color: rgba(0, 0, 0, .4);
- border-radius: 6px;
- padding: @wcfGapSmall;
- position: absolute;
- vertical-align: middle;
- width: 400px !important;
- z-index: 500;
-
- .boxShadow(0, 1px, rgba(0, 0, 0, .3), 7px);
-
- > .icon-spinner {
- color: white;
- left: 50%;
- margin-left: -21px;
- margin-top: -21px;
- position: absolute;
- top: 50%;
-
- .textShadow(black);
- }
-
- > .popoverContent {
- background-color: @wcfContainerBackgroundColor;
- border-radius: 6px;
- color: @wcfColor;
- max-height: 300px;
- min-height: 16px;
- opacity: 0;
- overflow: hidden;
- padding: @wcfGapSmall @wcfGapMedium;
- }
-
- &::after {
- border: 10px solid transparent;
- content: "";
- display: inline-block;
- position: absolute;
- z-index: 100;
- }
-
- &.top::after {
- border-bottom-width: 0;
- border-top-color: rgba(0, 0, 0, .3);
- bottom: -10px;
- }
-
- &.bottom::after {
- border-bottom-color: rgba(0, 0, 0, .3);
- border-top-width: 0;
- top: -10px;
- }
-
- &.right::after {
- left: 10px;
- }
-
- &.left::after {
- right: 10px;
- }
-}
-
-
/* ### badges ### */
/* default values */
.badge {
--- /dev/null
+.popover {
+ background-color: rgba(0, 0, 0, .4);
+ border: 3px solid transparent;
+ border-radius: 3px;
+ position: absolute;
+ vertical-align: middle;
+ width: 400px !important;
+ z-index: 500;
+
+ .boxShadow(0, 1px, rgba(0, 0, 0, .3), 7px);
+
+ > .fa-spinner {
+ color: white;
+ left: 50%;
+ margin-left: -21px;
+ margin-top: -21px;
+ position: absolute;
+ top: 50%;
+
+ .textShadow(black);
+ }
+
+ > .popoverContent {
+ background-color: @wcfContainerBackgroundColor;
+ border-radius: 3px;
+ color: @wcfColor;
+ max-height: 300px;
+ min-height: 16px;
+ opacity: 0;
+ overflow: hidden;
+ padding: @wcfGapSmall @wcfGapMedium;
+ }
+
+ > .elementPointer {
+ border-color: rgba(0, 0, 0, .4) transparent;
+ border-style: solid;
+ border-width: 0 5px 5px;
+ top: -3px;
+
+ &.flipVertical {
+ border-width: 5px 5px 0;
+ bottom: -3px;
+ }
+ }
+
+ &::after {
+ border: 10px solid transparent;
+ content: "";
+ display: inline-block;
+ position: absolute;
+ z-index: 100;
+ }
+
+ &.top::after {
+ border-bottom-width: 0;
+ border-top-color: rgba(0, 0, 0, .3);
+ bottom: -10px;
+ }
+
+ &.bottom::after {
+ border-bottom-color: rgba(0, 0, 0, .3);
+ border-top-width: 0;
+ top: -10px;
+ }
+
+ &.right::after {
+ left: 10px;
+ }
+
+ &.left::after {
+ right: 10px;
+ }
+}