//<![CDATA[
$(function() {
new WCF.Effect.SmoothScroll();
- new WCF.Effect.BalloonTooltip();
new WCF.Sitemap();
{if $__wcf->getStyleHandler()->countStyles() > 1}new WCF.Style.Chooser();{/if}
WCF.System.PageNavigation.init('.pageNavigation');
* Quick login box
*/
WCF.User.QuickLogin = {
- /**
- * dialog overlay
- * @var jQuery
- */
- _dialog: null,
-
- /**
- * login message container
- * @var jQuery
- */
- _loginMessage: null,
-
/**
* Initializes the quick login box
*/
init: function() {
- $('.loginLink').click($.proxy(this._render, this));
-
- // prepend protocol and hostname
- $('#loginForm input[name=url]').val(function(index, value) {
- return window.location.protocol + '//' + window.location.host + value;
- });
- },
-
- /**
- * Displays the quick login box with a info message
- *
- * @param string message
- */
- show: function(message) {
- if (message) {
- if (this._loginMessage === null) {
- this._loginMessage = $('<p class="info" />').hide().prependTo($('#loginForm > form'));
+ require(['UI/Dialog'], function(UIDialog) {
+ var loginForm = document.getElementById('loginForm');
+
+ var links = document.getElementsByClassName('loginLink');
+ for (var i = 0, length = links.length; i < length; i++) {
+ links[i].addEventListener('click', function(event) {
+ event.preventDefault();
+
+ loginForm.style.removeProperty('display');
+
+ UIDialog.open('loginForm', null, {
+ title: WCF.Language.get('wcf.user.login')
+ });
+ });
}
- this._loginMessage.show().text(message);
- }
- else if (this._loginMessage !== null) {
- this._loginMessage.hide();
- }
-
- this._render();
- },
-
- /**
- * Renders the dialog
- *
- * @param jQuery.Event event
- */
- _render: function(event) {
- if (event !== undefined) {
- event.preventDefault();
- }
-
- if (this._dialog === null) {
- this._dialog = $('#loginForm').wcfDialog({
- title: WCF.Language.get('wcf.user.login')
- });
- this._dialog.find('#username').focus();
- }
- else {
- this._dialog.wcfDialog('open');
- }
+ var input = loginForm.querySelector('#loginForm input[name=url]');
+ if (input !== null) {
+ input.setAttribute('value', window.location.protocol + '//' + window.location.host + input.getAttribute('value'));
+ }
+ });
}
};
}
});
-/**
- * Creates the balloon tool-tip.
- */
-WCF.Effect.BalloonTooltip = Class.extend({
- /**
- * initialization state
- * @var boolean
- */
- _didInit: false,
-
- /**
- * tooltip element
- * @var jQuery
- */
- _tooltip: null,
-
- /**
- * cache viewport dimensions
- * @var object
- */
- _viewportDimensions: { },
-
- /**
- * Initializes tooltips.
- */
- init: function() {
- if (jQuery.browser.mobile) return;
-
- if (!this._didInit) {
- // create empty div
- this._tooltip = $('<div id="balloonTooltip" class="balloonTooltip"><span id="balloonTooltipText"></span><span class="pointer"><span></span></span></div>').appendTo($('body')).hide();
-
- // get viewport dimensions
- this._updateViewportDimensions();
-
- // update viewport dimensions on resize
- $(window).resize($.proxy(this._updateViewportDimensions, this));
-
- // observe DOM changes
- WCF.DOMNodeInsertedHandler.addCallback('WCF.Effect.BalloonTooltip', $.proxy(this.init, this));
-
- this._didInit = true;
- }
-
- // init elements
- $('.jsTooltip').each($.proxy(this._initTooltip, this));
- },
-
- /**
- * Updates cached viewport dimensions.
- */
- _updateViewportDimensions: function() {
- this._viewportDimensions = $(document).getDimensions();
- },
-
- /**
- * Initializes a tooltip element.
- *
- * @param integer index
- * @param object element
- */
- _initTooltip: function(index, element) {
- var $element = $(element);
-
- if ($element.hasClass('jsTooltip')) {
- $element.removeClass('jsTooltip');
- var $title = $element.attr('title');
-
- // ignore empty elements
- if ($title !== '') {
- $element.data('tooltip', $title);
- $element.removeAttr('title');
-
- $element.hover(
- $.proxy(this._mouseEnterHandler, this),
- $.proxy(this._mouseLeaveHandler, this)
- );
- $element.click($.proxy(this._mouseLeaveHandler, this));
- }
- }
- },
-
- /**
- * Shows tooltip on hover.
- *
- * @param object event
- */
- _mouseEnterHandler: function(event) {
- var $top, $left;
- var $element = $(event.currentTarget);
-
- var $title = $element.attr('title');
- if ($title && $title !== '') {
- $element.data('tooltip', $title);
- $element.removeAttr('title');
- }
-
- // reset tooltip position
- this._tooltip.css({
- top: "0px",
- left: "0px"
- });
-
- // empty tooltip, skip
- if (!$element.data('tooltip')) {
- this._tooltip.hide();
- return;
- }
-
- // update text
- this._tooltip.children('span:eq(0)').text($element.data('tooltip'));
-
- // get arrow
- var $arrow = this._tooltip.find('.pointer');
-
- // get arrow width
- this._tooltip.show();
- var $arrowWidth = $arrow.outerWidth();
- this._tooltip.hide();
-
- // calculate position
- var $elementOffsets = $element.getOffsets('offset');
- var $elementDimensions = $element.getDimensions('outer');
- var $tooltipDimensions = this._tooltip.getDimensions('outer');
- var $tooltipDimensionsInner = this._tooltip.getDimensions('inner');
-
- var $elementCenter = $elementOffsets.left + Math.ceil($elementDimensions.width / 2);
- var $tooltipHalfWidth = Math.ceil($tooltipDimensions.width / 2);
-
- // determine alignment
- var $alignment = 'center';
- if (($elementCenter - $tooltipHalfWidth) < 5) {
- $alignment = 'left';
- }
- else if ((this._viewportDimensions.width - 5) < ($elementCenter + $tooltipHalfWidth)) {
- $alignment = 'right';
- }
-
- // calculate top offset
- if ($elementOffsets.top + $elementDimensions.height + $tooltipDimensions.height - $(document).scrollTop() < $(window).height()) {
- $top = $elementOffsets.top + $elementDimensions.height + 7;
- this._tooltip.removeClass('inverse');
- $arrow.css('top', -5);
- }
- else {
- $top = $elementOffsets.top - $tooltipDimensions.height - 7;
- this._tooltip.addClass('inverse');
- $arrow.css('top', $tooltipDimensions.height);
- }
-
- var $property = (WCF.Language.get('wcf.global.pageDirection') == 'rtl' ? 'right' : 'left');
-
- // calculate left offset
- switch ($alignment) {
- case 'center':
- $left = Math.round($elementOffsets.left - $tooltipHalfWidth + ($elementDimensions.width / 2));
-
- $arrow.css($property, ($tooltipDimensionsInner.width / 2 - $arrowWidth / 2) + 'px');
- break;
-
- case 'left':
- $left = $elementOffsets.left;
-
- if ($property === 'right') {
- $arrow.css($property, ($tooltipDimensionsInner.width - $arrowWidth - 5) + 'px');
- }
- else {
- $arrow.css($property, '5px');
- }
- break;
-
- case 'right':
- $left = $elementOffsets.left + $elementDimensions.width - $tooltipDimensions.width;
-
- if ($property === 'right') {
- $arrow.css($property, '5px');
- }
- else {
- $arrow.css($property, ($tooltipDimensionsInner.width - $arrowWidth - 5) + 'px');
- }
- break;
- }
-
- // move tooltip
- this._tooltip.css({
- top: $top + "px",
- left: $left + "px"
- });
-
- // show tooltip
- this._tooltip.wcfFadeIn();
- },
-
- /**
- * Hides tooltip once cursor left the element.
- *
- * @param object event
- */
- _mouseLeaveHandler: function(event) {
- this._tooltip.stop().hide().css({
- opacity: 1
- });
- }
-});
-
/**
* Handles clicks outside an overlay, hitting body-tag through bubbling.
*
_after: function(dropdownMenu) { }
});
-/**
- * WCF implementation for dialogs, based upon ideas by jQuery UI.
- */
-$.widget('ui.wcfDialog', {
- /**
- * close button
- * @var jQuery
- */
- _closeButton: null,
-
- /**
- * dialog container
- * @var jQuery
- */
- _container: null,
-
- /**
- * dialog content
- * @var jQuery
- */
- _content: null,
-
- /**
- * modal overlay
- * @var jQuery
- */
- _overlay: null,
-
- /**
- * plain html for title
- * @var string
- */
- _title: null,
-
- /**
- * title bar
- * @var jQuery
- */
- _titlebar: null,
-
- /**
- * dialog visibility state
- * @var boolean
- */
- _isOpen: false,
-
- /**
- * option list
- * @var object
- */
- options: {
- // dialog
- autoOpen: true,
- closable: true,
- closeButtonLabel: null,
- closeConfirmMessage: null,
- closeViaModal: true,
- hideTitle: false,
- modal: true,
- title: '',
- zIndex: 400,
-
- // event callbacks
- onClose: null,
- onShow: null
- },
-
- /**
- * @see $.widget._createWidget()
- */
- _createWidget: function(options, element) {
- // ignore script tags
- if ($(element).getTagName() === 'script') {
- console.debug("[ui.wcfDialog] Ignored script tag");
- this.element = false;
- return null;
- }
-
- $.Widget.prototype._createWidget.apply(this, arguments);
- },
-
- /**
- * Initializes a new dialog.
- */
- _init: function() {
- if (this.options.autoOpen) {
- this.open();
- }
-
- // act on resize
- $(window).resize($.proxy(this._resize, this));
- },
-
- /**
- * Creates a new dialog instance.
- */
- _create: function() {
- if (this.options.closeButtonLabel === null) {
- this.options.closeButtonLabel = WCF.Language.get('wcf.global.button.close');
- }
-
- // create dialog container
- this._container = $('<div class="dialogContainer" />').hide().css({ zIndex: this.options.zIndex }).appendTo(document.body);
- this._titlebar = $('<header class="dialogTitlebar" />').hide().appendTo(this._container);
- this._title = $('<span class="dialogTitle" />').hide().appendTo(this._titlebar);
- this._closeButton = $('<a class="dialogCloseButton jsTooltip" title="' + this.options.closeButtonLabel + '"><span /></a>').click($.proxy(this.close, this)).hide().appendTo(this._titlebar);
- this._content = $('<div class="dialogContent" />').appendTo(this._container);
-
- this._setOption('title', this.options.title);
- this._setOption('closable', this.options.closable);
-
- // move target element into content
- var $content = this.element.detach();
- this._content.html($content);
-
- // create modal view
- if (this.options.modal) {
- this._overlay = $('#jsWcfDialogOverlay');
- if (!this._overlay.length) {
- this._overlay = $('<div id="jsWcfDialogOverlay" class="dialogOverlay" />').css({ height: '100%', zIndex: 399 }).hide().appendTo(document.body);
- }
-
- if (this.options.closable && this.options.closeViaModal) {
- this._overlay.click($.proxy(this.close, this));
-
- $(document).keyup($.proxy(function(event) {
- if (event.keyCode && event.keyCode === $.ui.keyCode.ESCAPE) {
- this.close();
- event.preventDefault();
- }
- }, this));
- }
- }
-
- WCF.DOMNodeInsertedHandler.execute();
- },
-
- /**
- * Sets the given option to the given value.
- * See the jQuery UI widget documentation for more.
- */
- _setOption: function(key, value) {
- this.options[key] = value;
-
- if (key == 'hideTitle' || key == 'title') {
- if (!this.options.hideTitle && this.options.title != '') {
- this._title.html(this.options.title).show();
- } else {
- this._title.html('');
- }
- } else if (key == 'closable' || key == 'closeButtonLabel') {
- if (this.options.closable) {
- this._closeButton.attr('title', this.options.closeButtonLabel).show().find('span').html(this.options.closeButtonLabel);
-
- WCF.DOMNodeInsertedHandler.execute();
- } else {
- this._closeButton.hide();
- }
- }
-
- if ((!this.options.hideTitle && this.options.title != '') || this.options.closable) {
- this._titlebar.show();
- } else {
- this._titlebar.hide();
- }
-
- return this;
- },
-
- /**
- * Opens this dialog.
- */
- open: function() {
- // ignore script tags
- if (this.element === false) {
- return;
- }
-
- if (this.isOpen()) {
- return;
- }
+jQuery.fn.extend({
+ wcfDialog: function(method) {
+ var args = arguments;
- if (this._overlay !== null) {
- WCF.activeDialogs++;
+ require(['DOM/Util', 'UI/Dialog'], (function(DOMUtil, UIDialog) {
+ var id = DOMUtil.identify(this[0]);
- if (WCF.activeDialogs === 1) {
- this._overlay.show();
+ if (method === 'close') {
+ UIDialog.close(id);
}
- }
-
- this.render();
- this._isOpen = true;
-
- this._content.find('.jsDialogAutoFocus:visible:first').focus();
- },
-
- /**
- * Returns true if dialog is visible.
- *
- * @return boolean
- */
- isOpen: function() {
- return this._isOpen;
- },
-
- /**
- * Closes this dialog.
- *
- * This function can be manually called, even if the dialog is set as not
- * closable by the user.
- *
- * @param object event
- */
- close: function(event) {
- if (!this.isOpen()) {
- return;
- }
-
- if (this.options.closeConfirmMessage) {
- WCF.System.Confirmation.show(this.options.closeConfirmMessage, $.proxy(function(action) {
- if (action === 'confirm') {
- this._close();
- }
- }, this));
- }
- else {
- this._close();
- }
-
- if (event !== undefined) {
- event.preventDefault();
- }
- },
-
- /**
- * Handles dialog closing, should never be called directly.
- *
- * @see $.ui.wcfDialog.close()
- */
- _close: function() {
- this._isOpen = false;
- this._container.wcfFadeOut();
-
- if (this._container.data('wcfDialogScrollOffset')) {
- window.scrollTo(0, this._container.data('wcfDialogScrollOffset'));
- }
-
- if (this._overlay !== null) {
- WCF.activeDialogs--;
-
- if (WCF.activeDialogs === 0) {
- this._overlay.hide();
+ else if (method === 'render') {
+ UIDialog.rebuild(id);
}
- }
-
- if (this.options.onClose !== null) {
- this.options.onClose();
- }
- },
-
- /**
- * Renders dialog on resize if visible.
- */
- _resize: function() {
- if (this.isOpen()) {
- this.render();
- }
- },
-
- /**
- * Renders this dialog, should be called whenever content is updated.
- */
- render: function() {
- // check if this if dialog was previously hidden and container is fixed
- // at 0px (mobile optimization), in this case scroll to top
- if (!this._container.is(':visible') && this._container.css('top') === '0px') {
- // save scrolling
- this._container.data('wcfDialogScrollOffset', $(window).scrollTop());
-
- window.scrollTo(0, 0);
- }
-
- // force dialog and it's contents to be visible
- this._container.show();
- this._content.children().show();
-
- // remove fixed content dimensions for calculation
- this._content.css({
- height: 'auto',
- width: 'auto'
- });
-
- // terminate concurrent rendering processes
- this._container.stop();
- this._content.stop();
-
- // set dialog to be fully opaque, prevents weird bugs in WebKit
- this._container.show().css('opacity', 1.0);
-
- // handle positioning of form submit controls
- var $heightDifference = 0;
- if (this._content.find('.formSubmit').length) {
- $heightDifference = this._content.find('.formSubmit').outerHeight();
-
- this._content.addClass('dialogForm').css({ marginBottom: $heightDifference + 'px' });
- }
- else {
- this._content.removeClass('dialogForm').css({ marginBottom: '0px' });
- }
-
- // force 800px or 90% width
- var $windowDimensions = $(window).getDimensions();
- if ($windowDimensions.width * 0.9 > 800) {
- this._container.css('maxWidth', '800px');
- }
-
- // calculate dimensions
- var $containerDimensions = this._container.getDimensions('outer');
- var $contentDimensions = this._content.getDimensions();
-
- // calculate maximum content height
- var $heightDifference = $containerDimensions.height - $contentDimensions.height;
- var $maximumHeight = $windowDimensions.height - $heightDifference - 120;
- this._content.css({ maxHeight: $maximumHeight + 'px' });
-
- this._determineOverflow();
-
- // calculate new dimensions
- $containerDimensions = this._container.getDimensions('outer');
-
- // move container
- var $leftOffset = Math.round(($windowDimensions.width - $containerDimensions.width) / 2);
- var $topOffset = Math.round(($windowDimensions.height - $containerDimensions.height) / 2);
-
- // place container at 20% height if possible
- var $desiredTopOffset = Math.round(($windowDimensions.height / 100) * 20);
- if ($desiredTopOffset < $topOffset) {
- $topOffset = $desiredTopOffset;
- }
-
- // apply offset
- this._container.css({
- left: $leftOffset + 'px',
- top: $topOffset + 'px'
- });
-
- // remove static dimensions
- this._content.css({
- height: 'auto',
- width: 'auto'
- });
-
- if (!this.isOpen()) {
- // hide container again
- this._container.hide();
-
- // fade in container
- this._container.wcfFadeIn($.proxy(function() {
- if (this.options.onShow !== null) {
- this.options.onShow();
+ else if (method === 'option') {
+ if (args.length === 3 && args[1] === 'title' && typeof args[2] === 'string') {
+ UIDialog.setTitle(id, args[2]);
}
- }, this));
- }
- },
-
- /**
- * Determines content overflow based upon static dimensions.
- */
- _determineOverflow: function() {
- var $max = $(window).getDimensions();
- var $maxHeight = this._content.css('maxHeight');
- this._content.css('maxHeight', 'none');
- var $dialog = this._container.getDimensions('outer');
-
- var $overflow = 'visible';
- if (($max.height * 0.8 < $dialog.height) || ($max.width * 0.8 < $dialog.width)) {
- $overflow = 'auto';
- }
-
- this._content.css('overflow', $overflow);
- this._content.css('maxHeight', $maxHeight);
-
- if ($overflow === 'visible') {
- // content may already overflow, even though the overall height is still below the threshold
- var $contentHeight = 0;
- this._content.children().each(function(index, child) {
- $contentHeight += $(child).outerHeight();
- });
-
- if (this._content.height() < $contentHeight) {
- $overflow = 'auto';
- this._content.css('overflow', 'auto');
- }
- }
-
- // Firefox ignores padding-bottom for elements within an overflowing container
- if ($.browser.mozilla && !$.browser.mobile) {
- if ($overflow === 'auto') {
- this._content.children('div').css('margin-bottom', this._content.css('padding-bottom'));
}
else {
- this._content.children('div').css('margin-bottom', false);
+ UIDialog.open(id, null, (args.length === 1 && typeof args[0] === 'object') ? args[0] : {});
}
- }
- },
-
- /**
- * Returns calculated content dimensions.
- *
- * @param integer maximumHeight
- * @return object
- */
- _getContentDimensions: function(maximumHeight) {
- var $contentDimensions = this._content.getDimensions();
-
- // set height to maximum height if exceeded
- if (maximumHeight && $contentDimensions.height > maximumHeight) {
- $contentDimensions.height = maximumHeight;
- }
-
- return $contentDimensions;
+ }).bind(this));
}
});
* @module WoltLab/WCF/Bootstrap
*/
define(
- [ 'jquery', 'favico', 'enquire', 'WoltLab/WCF/Date/Time/Relative', 'UI/SimpleDropdown', 'WoltLab/WCF/UI/Mobile', 'WoltLab/WCF/UI/TabMenu', 'WoltLab/WCF/UI/FlexibleMenu'],
- function($, favico, enquire, relativeTime, simpleDropdown, uiMobile, TabMenu, UIFlexibleMenu)
+ [ 'jquery', 'favico', 'enquire', 'WoltLab/WCF/Date/Time/Relative', 'UI/SimpleDropdown', 'WoltLab/WCF/UI/Mobile', 'WoltLab/WCF/UI/TabMenu', 'WoltLab/WCF/UI/FlexibleMenu', 'UI/Dialog', 'WoltLab/WCF/UI/Tooltip'],
+ function($, favico, enquire, relativeTime, simpleDropdown, UIMobile, UITabMenu, UIFlexibleMenu, UIDialog, UITooltip)
{
"use strict";
setup: function() {
relativeTime.setup();
simpleDropdown.setup();
- uiMobile.setup();
- TabMenu.setup();
+ UIMobile.setup();
+ UITabMenu.setup();
UIFlexibleMenu.setup();
+ UIDialog.setup();
+ UITooltip.setup();
$.holdReady(false);
}
};
},
+ /**
+ * Prepends an element to a parent element.
+ *
+ * @param {Element} el element to prepend
+ * @param {Element} parentEl future containing element
+ */
+ prepend: function(el, parentEl) {
+ if (parentEl.childElementCount === 0) {
+ parentEl.appendChild(el);
+ }
+ else {
+ parentEl.insertBefore(el, parentEl.children[0]);
+ }
+ },
+
/**
* Applies a list of CSS properties to an element.
*
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @module WoltLab/WCF/UI/Alignment
*/
-define(['Core', 'DOM/Util'], function(Core, DOMUtil) {
+define(['Core', 'DOM/Traverse', 'DOM/Util'], function(Core, DOMTraverse, DOMUtil) {
"use strict";
/**
// align the pointer element, expects .pointer as a direct child of given element
pointer: false,
+ // offset from/left side, ignored for center alignment
+ pointerOffset: 4,
+
// use static pointer positions, expects two items: class to move it to the bottom and the second to move it to the right
pointerClassNames: [],
// alternate element used to calculate dimensions
refDimensionsElement: null,
- // preferred alignment, possible values: left/right and top/bottom
+ // preferred alignment, possible values: left/right/center and top/bottom
horizontal: 'left',
vertical: 'bottom',
allowFlip: 'both'
}, options);
- if (!Array.isArray(options.pointerClassNames) || options.pointerClassNames.length !== 2) options.pointerClassNames = [];
- if (options.horizontal !== 'right') options.horizontal = 'left';
+ if (!Array.isArray(options.pointerClassNames) || options.pointerClassNames.length !== (options.pointer ? 1 : 2)) options.pointerClassNames = [];
+ if (['left', 'right', 'center'].indexOf(options.horizontal) === -1) options.horizontal = 'left';
if (options.vertical !== 'bottom') options.horizontal = 'top';
if (['both', 'horizontal', 'vertical', 'none'].indexOf(options.allowFlip) === -1) options.allowFlip = 'both';
// place element in the upper left corner to prevent calculation issues due to possible scrollbars
DOMUtil.setStyles(el, {
- bottom: 'auto',
- left: '0px',
- right: 'auto',
- top: '0px'
+ bottom: 'auto !important',
+ left: '0 !important',
+ right: 'auto !important',
+ top: '0 !important'
});
var elDimensions = DOMUtil.outerDimensions(el);
var refDimensions = DOMUtil.outerDimensions((options.refDimensionsElement instanceof Element ? options.refDimensionsElement : ref));
var refOffsets = DOMUtil.offset(ref);
var windowHeight = window.innerHeight;
- var windowWidth = window.innerWidth;
+ var windowWidth = document.body.clientWidth;
+
+ var horizontal = { result: null };
+ var alignCenter = false;
+ if (options.horizontal === 'center') {
+ alignCenter = true;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+
+ if (!horizontal.result) {
+ if (options.allowFlip === 'both' || options.allowFlip === 'horizontal') {
+ options.horizontal = 'left';
+ }
+ else {
+ horizontal.result = true;
+ }
+ }
+ }
// in rtl languages we simply swap the value for 'horizontal'
if (WCF.Language.get('wcf.global.pageDirection') === 'rtl') {
options.horizontal = (options.horizontal === 'left') ? 'right' : 'left';
}
- var horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
- if (!horizontal.result && (options.allowFlip === 'both' || options.allowFlip === 'horizontal')) {
- var horizontalFlipped = this._tryAlignmentHorizontal((options.horizontal === 'left' ? 'right' : 'left'), elDimensions, refDimensions, refOffsets, windowWidth);
- // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
- if (horizontalFlipped.result) {
- horizontal = horizontalFlipped;
+ if (!horizontal.result) {
+ var horizontalCenter = horizontal;
+ horizontal = this._tryAlignmentHorizontal(options.horizontal, elDimensions, refDimensions, refOffsets, windowWidth);
+ if (!horizontal.result && (options.allowFlip === 'both' || options.allowFlip === 'horizontal')) {
+ var horizontalFlipped = this._tryAlignmentHorizontal((options.horizontal === 'left' ? 'right' : 'left'), elDimensions, refDimensions, refOffsets, windowWidth);
+ // only use these results if it fits into the boundaries, otherwise both directions exceed and we honor the demanded direction
+ if (horizontalFlipped.result) {
+ horizontal = horizontalFlipped;
+ }
+ else if (alignCenter) {
+ horizontal = horizontalCenter;
+ }
}
}
// set pointer position
if (options.pointer) {
- //var pointer = null;
- // TODO: implement pointer support, e.g. for interactive dropdowns
- console.debug("TODO");
+ var pointer = DOMTraverse.childrenByClass(el, 'elementPointer');
+ pointer = pointer[0] || null;
+ if (pointer === null) {
+ throw new Error("Expected the .elementPointer element to be a direct children.");
+ }
+
+ if (horizontal.align === 'center') {
+ pointer.classList.add('center');
+
+ pointer.classList.remove('left');
+ pointer.classList.remove('right');
+ }
+ else {
+ pointer.classList.add(horizontal.align);
+
+ pointer.classList.remove('center');
+ pointer.classList.remove(horizontal.align === 'left' ? 'right' : 'left');
+ }
+
+ if (vertical.align === 'top') {
+ pointer.classList.add('flipVertical');
+ }
+ else {
+ pointer.classList.remove('flipVertical');
+ }
}
else if (options.pointerClassNames.length === 2) {
var pointerRight = 0;
result = false;
}
}
+ else if (align === 'right') {
+ console.debug(windowWidth + " | " + refOffsets.left + " | " + refDimensions.width);
+ right = windowWidth - (refOffsets.left + refDimensions.width);
+ if (right < 0) {
+ result = false;
+ }
+ }
else {
- right = refOffsets.left + refDimensions.width;
- if (right - elDimensions.width < 0) {
+ left = refOffsets.left + (refDimensions.width / 2) - (elDimensions.width / 2);
+ left = ~~left;
+
+ if (left < 0 || left + elDimensions.width > windowWidth) {
result = false;
}
}
return {
+ align: align,
left: left,
right: right,
result: result
var result = true;
if (align === 'top') {
- bottom = refOffsets.top + verticalOffset;
- if (bottom - elDimensions.height < 0) {
+ var bodyHeight = document.body.clientHeight;
+ bottom = (bodyHeight - refOffsets.top) + verticalOffset;
+ if (bottom + elDimensions.height > document.body.clientHeight) {
result = false;
}
}
}
return {
+ align: align,
bottom: bottom,
top: top,
result: result
--- /dev/null
+/**
+ * Modal dialog handler.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLab/WCF/UI/Dialog
+ */
+define(['jquery', 'Core', 'Dictionary', 'DOM/Util'], function($, Core, Dictionary, DOMUtil) {
+ "use strict";
+
+ var _activeDialog = null;
+ var _container = null;
+ var _dialogs = null;
+ var _keyupListener = null;
+
+ /**
+ * @constructor
+ */
+ function UIDialog() {};
+ UIDialog.prototype = {
+ /**
+ * Sets up global container and internal variables.
+ */
+ setup: function() {
+ _container = document.createElement('div');
+ _container.classList.add('dialogOverlay');
+ _container.setAttribute('aria-hidden', 'true');
+ _container.addEventListener('click', this._closeOnBackdrop.bind(this));
+
+ document.body.appendChild(_container);
+
+ _dialogs = new Dictionary();
+
+ _keyupListener = (function(event) {
+ if (event.keyCode === 27) {
+ if (event.target.nodeName !== 'INPUT' && event.target.nodeName !== 'TEXTAREA') {
+ this.close(_activeDialog);
+
+ return false;
+ }
+ }
+
+ return true;
+ }).bind(this);
+ },
+
+ /**
+ * Opens an dialog, if the dialog is already open the content container
+ * will be replaced by the HTML string contained in the parameter html.
+ *
+ * If id is an existing element id, html will be ignored and the referenced
+ * element will be appended to the content element instead.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?string} html content html
+ * @param {object<string, *>} options list of options, is completely ignored if the dialog already exists
+ */
+ open: function(id, html, options) {
+ if (_dialogs.has(id)) {
+ this._updateDialog(id, html);
+ }
+ else {
+ options = Core.extend({
+ backdropCloseOnClick: true,
+ closable: true,
+ closeButtonLabel: WCF.Language.get('wcf.global.button.close'),
+ closeConfirmMessage: '',
+ disposeOnClose: false,
+ title: '',
+
+ // callbacks
+ onBeforeClose: null,
+ onClose: null,
+ onShow: null
+ }, options);
+
+ if (!options.closable) options.backdropCloseOnClick = false;
+ if (options.closeConfirmMessage) {
+ options.onBeforeClose = (function(id) {
+ WCF.System.Confirmation.show(options.closeConfirmMessage, (function(action) {
+ if (action === 'confirm') {
+ this.close(id);
+ }
+ }).bind(this));
+ }).bind(this);
+ }
+
+ this._createDialog(id, html, options);
+ }
+ },
+
+ /**
+ * Sets the dialog title.
+ *
+ * @param {string} id element id
+ * @param {string} title dialog title
+ */
+ setTitle: function(id, title) {
+ var data = _dialogs.get(id);
+ if (typeof data === 'undefined') {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ var header = DOMTraverse.childrenByTag(data.dialog, 'HEADER');
+ DOMTraverse.childrenByTag(header[0], 'SPAN').textContent = title;
+ },
+
+ /**
+ * Creates the DOM for a new dialog and opens it.
+ *
+ * @param {string} id element id, if exists the html parameter is ignored in favor of the existing element
+ * @param {?string} html content html
+ * @param {object<string, *>} options list of options
+ */
+ _createDialog: function(id, html, options) {
+ var element = null;
+ if (html === null) {
+ element = document.getElementById(id);
+ if (element === null) {
+ throw new Error("Expected either a HTML string or an existing element id.");
+ }
+ }
+
+ var dialog = document.createElement('div');
+ dialog.classList.add('dialogContainer');
+ dialog.setAttribute('aria-hidden', 'true');
+ dialog.setAttribute('role', 'dialog')
+ dialog.setAttribute('data-id', id);
+
+ if (options.disposeOnClose) {
+ dialog.setAttribute('data-dispose-on-close', true);
+ }
+
+ var header = document.createElement('header');
+ dialog.appendChild(header);
+
+ if (options.title) {
+ var titleId = DOMUtil.getUniqueId();
+ dialog.setAttribute('aria-labelledby', titleId);
+
+ var title = document.createElement('span');
+ title.classList.add('dialogTitle');
+ title.textContent = options.title;
+ title.setAttribute('id', titleId);
+ header.appendChild(title);
+ }
+
+ if (options.closable) {
+ var closeButton = document.createElement('a');
+ closeButton.className = 'dialogCloseButton jsTooltip';
+ closeButton.setAttribute('title', options.closeButtonLabel);
+ closeButton.setAttribute('aria-label', options.closeButtonLabel);
+ closeButton.addEventListener('click', this._close.bind(this));
+ header.appendChild(closeButton);
+
+ var span = document.createElement('span');
+ span.textContent = options.closeButtonLabel;
+ closeButton.appendChild(span);
+ }
+
+ var contentContainer = document.createElement('div');
+ contentContainer.classList.add('dialogContent');
+ dialog.appendChild(contentContainer);
+
+ var content;
+ if (element === null) {
+ content = document.createElement('div');
+ content.setAttribute('id', id);
+ content.innerHTML = html;
+ }
+ else {
+ content = element;
+ }
+
+ contentContainer.appendChild(element);
+
+ _dialogs.set(id, {
+ backdropCloseOnClick: options.backdropCloseOnClick,
+ content: content,
+ dialog: dialog,
+ header: header,
+ onBeforeClose: options.onBeforeClose,
+ onClose: options.onClose,
+ onShow: options.onShow
+ });
+
+ if (_container.getAttribute('aria-hidden') === 'true') {
+ window.addEventListener('keyup', _keyupListener);
+ }
+
+ DOMUtil.prepend(dialog, _container);
+ _container.setAttribute('aria-hidden', 'false');
+ _container.setAttribute('data-close-on-click', (options.backdropCloseOnClick ? 'true' : 'false'));
+ dialog.setAttribute('aria-hidden', 'false');
+
+ this.rebuild(id);
+
+ _activeDialog = id;
+
+ if (typeof options.onShow === 'function') {
+ options.onShow(id);
+ }
+
+ WCF.DOMNodeInsertedHandler.execute();
+ },
+
+ /**
+ * Updates the dialog's content element.
+ *
+ * @param {string} id element id
+ * @param {?string} html content html, prevent changes by passing null
+ */
+ _updateDialog: function(id, html) {
+ var data = _dialogs.get(id);
+ if (typeof data === 'undefined') {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (typeof html === 'string') {
+ data.content.innerHTML = '';
+
+ var content = document.createElement('div');
+ content.innerHTML = html;
+
+ data.content.appendChild(content);
+ }
+
+ if (data.dialog.getAttribute('aria-hidden') === 'true') {
+ data.dialog.setAttribute('aria-hidden', 'false');
+ _container.setAttribute('aria-hidden', 'false');
+ _container.setAttribute('data-close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ _activeDialog = id;
+
+ window.addEventListener('keyup', _keyupListener);
+
+ this.rebuild(id);
+
+ if (typeof data.onShow === 'function') {
+ data.onShow(id);
+ }
+ }
+
+ WCF.DOMNodeInsertedHandler.execute();
+ },
+
+ rebuild: function(id) {
+ var data = _dialogs.get(id);
+ if (typeof data === 'undefined') {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ // ignore non-active dialogs
+ if (data.dialog.getAttribute('aria-hidden') === 'true') {
+ return;
+ }
+
+ // fix for a calculation bug in Chrome causing the scrollbar to overlap the border
+ if ($.browser.chrome) {
+ data.content.style.setProperty('margin-right', '-1px');
+ }
+
+ var contentContainer = data.content.parentNode;
+
+ var formSubmit = data.content.querySelector('.formSubmit');
+ var unavailableHeight = 0;
+ if (formSubmit !== null) {
+ contentContainer.classList.add('dialogForm');
+ formSubmit.classList.add('dialogFormSubmit');
+
+ unavailableHeight += DOMUtil.outerHeight(formSubmit);
+ contentContainer.style.setProperty('margin-bottom', unavailableHeight + 'px');
+ }
+ else {
+ contentContainer.classList.remove('dialogForm');
+ }
+
+ unavailableHeight += DOMUtil.outerHeight(data.header);
+
+ var maximumHeight = (window.innerHeight * 0.8) - unavailableHeight;
+ contentContainer.style.setProperty('max-height', ~~maximumHeight + 'px');
+ },
+
+ /**
+ * Handles clicks on the close button or the backdrop if enabled.
+ *
+ * @param {object} event click event
+ * @return {boolean} false if the event should be cancelled
+ */
+ _close: function(event) {
+ event.preventDefault();
+
+ var data = _dialogs.get(_activeDialog);
+ if (typeof data.onBeforeClose === 'function') {
+ data.onBeforeClose(_activeDialog);
+
+ return false;
+ }
+
+ this.close(_activeDialog);
+ },
+
+ /**
+ * Closes the current active dialog by clicks on the backdrop.
+ *
+ * @param {object} event event object
+ */
+ _closeOnBackdrop: function(event) {
+ if (event.target !== _container) {
+ return true;
+ }
+
+ if (_container.getAttribute('data-close-on-click') === 'true') {
+ this._close(event);
+ }
+ else {
+ event.preventDefault();
+ }
+ },
+
+ /**
+ * Closes a dialog identified by given id.
+ *
+ * @param {string} id element id
+ */
+ close: function(id) {
+ var data = _dialogs.get(id);
+ if (typeof data === 'undefined') {
+ throw new Error("Expected a valid dialog id, '" + id + "' does not match any active dialog.");
+ }
+
+ if (typeof data.onClose === 'function') {
+ data.onClose(id);
+ }
+
+ if (data.dialog.getAttribute('data-dispose-on-close')) {
+ setTimeout(function() {
+ if (data.dialog.getAttribute('aria-hidden') === 'true') {
+ _container.removeChild(data.dialog);
+ _dialogs.remove(id);
+ }
+ }, 5000);
+ }
+ else {
+ data.dialog.setAttribute('aria-hidden', 'true');
+ }
+
+ // get next active dialog
+ _activeDialog = null;
+ for (var i = 0; i < _container.childElementCount; i++) {
+ var child = _container.children[i];
+ if (child.getAttribute('aria-hidden') === 'false') {
+ _activeDialog = child.getAttribute('data-id');
+ break;
+ }
+ }
+
+ if (_activeDialog === null) {
+ _container.setAttribute('aria-hidden', 'true');
+ _container.setAttribute('data-close-on-click', 'false');
+
+ window.removeEventListener('keyup', _keyupListener);
+ }
+ else {
+ data = _dialogs.get(_activeDialog);
+ _container.setAttribute('data-close-on-click', (data.backdropCloseOnClick ? 'true' : 'false'));
+ }
+ },
+
+ /**
+ * Returns the dialog data for given element id.
+ *
+ * @param {string} id element id
+ * @return {(object|undefined)} dialog data or undefined if element id is unknown
+ */
+ getDialog: function(id) {
+ return _dialogs.get(id);
+ }
+ };
+
+ return new UIDialog();
+});
--- /dev/null
+/**
+ * Provides enhanced tooltips.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @module WoltLab/WCF/UI/Tooltip
+ */
+define(['jquery', 'UI/Alignment'], function($, UIAlignment) {
+ "use strict";
+
+ var _elements = null;
+ var _pointer = null;
+ var _text = null;
+ var _tooltip = null;
+
+ /**
+ * @constructor
+ */
+ function UITooltip() {};
+ UITooltip.prototype = {
+ /**
+ * Initializes the tooltip element and binds event listener.
+ */
+ setup: function() {
+ if ($.browser.mobile) return;
+
+ _tooltip = document.createElement('div');
+ _tooltip.setAttribute('id', 'balloonTooltip');
+ _tooltip.classList.add('balloonTooltip');
+
+ _text = document.createElement('span');
+ _text.setAttribute('id', 'balloonTooltipText');
+ _tooltip.appendChild(_text);
+
+ _pointer = document.createElement('span');
+ _pointer.classList.add('elementPointer');
+ _pointer.appendChild(document.createElement('span'));
+ _tooltip.appendChild(_pointer);
+
+ document.body.appendChild(_tooltip);
+
+ _elements = document.getElementsByClassName('jsTooltip');
+
+ this.init();
+
+ WCF.DOMNodeInsertedHandler.addCallback('WoltLab/WCF/UI/Tooltip', this.init.bind(this));
+ },
+
+ /**
+ * Initializes tooltip elements.
+ */
+ init: function() {
+ while (_elements.length) {
+ var element = _elements[0];
+ element.classList.remove('jsTooltip');
+
+ var title = element.getAttribute('title');
+ if (title.length) {
+ element.setAttribute('data-tooltip', title);
+ element.removeAttribute('title');
+
+ element.addEventListener('mouseenter', this._mouseEnter.bind(this));
+ element.addEventListener('mouseleave', this._mouseLeave.bind(this));
+ element.addEventListener('click', this._mouseLeave.bind(this));
+ }
+ }
+ },
+
+ /**
+ * Displays the tooltip on mouse enter.
+ *
+ * @param {object} event event object
+ */
+ _mouseEnter: function(event) {
+ var element = event.currentTarget;
+ var title = element.getAttribute('title');
+ if (typeof title === 'string' && title !== '') {
+ element.setAttribute('data-tooltip', title);
+ element.removeAttribute('title');
+ }
+
+ title = element.getAttribute('data-tooltip');
+
+ // reset tooltip position
+ _tooltip.style.removeProperty('top');
+ _tooltip.style.removeProperty('left');
+
+ // ignore empty tooltip
+ if (!title.length) {
+ _tooltip.classList.remove('active');
+ return;
+ }
+ else {
+ _tooltip.classList.add('active');
+ }
+
+ _text.textContent = title;
+
+ UIAlignment.set(_tooltip, element, {
+ horizontal: 'center',
+ pointer: true,
+ pointerClassNames: ['inverse']
+ });
+ },
+
+ /**
+ * Hides the tooltip once the mouse leaves the element.
+ *
+ * @param {object} event event object
+ */
+ _mouseLeave: function(event) {
+ _tooltip.classList.remove('active');
+ }
+ };
+
+ return new UITooltip();
+});
\ No newline at end of file
'DOM/Util': 'WoltLab/WCF/DOM/Util',
'EventHandler': 'WoltLab/WCF/Event/Handler',
'UI/Alignment': 'WoltLab/WCF/UI/Alignment',
+ 'UI/Dialog': 'WoltLab/WCF/UI/Dialog',
'UI/SimpleDropdown': 'WoltLab/WCF/UI/Dropdown/Simple'
}
}
border-width: 1px;
cursor: pointer;
display: inline-block;
+ line-height: @wcfBaseLineHeight;
margin: 0 4px;
padding: 5px 13px;
position: relative;
-.dialogContainer {
- background: rgba(0, 0, 0, .4);
- border: 14px solid transparent;
- border-radius: 15px;
- margin-left: auto;
- margin-right: auto;
- max-width: 90%;
- min-width: 500px;
+.dialogOverlay {
+ background-color: transparent;
+ bottom: 0;
+ left: 0;
position: fixed;
+ right: 0;
+ top: 0;
+ visibility: hidden;
+ z-index: 399;
- .boxShadow(0, 1px, rgba(0, 0, 0, .3), 23px);
+ transition: visibility 0s linear .3s;
+
+ &[aria-hidden=false] {
+ /* do not animate opacity or background-color, the transition is anything but smooth due to the large area covered */
+ background-color: rgba(255, 255, 255, .4);
+ visibility: visible;
+
+ transition-delay: 0s;
+ }
}
-@media only screen and (max-width: 800px) {
- .dialogContainer {
- border: 0;
- border-radius: 0;
- left: 0 !important;
- max-width: none;
- min-width: 0;
- position: absolute;
- top: 0 !important;
- width: 100%;
- }
+@-webkit-keyframes wcfDialog {
+ 0% { visibility: visible; opacity: 0; top: 8%; }
+ 100% { visibility: visible; opacity: 1; top: 10%; }
+}
+
+@-webkit-keyframes wcfDialogOut {
+ 0% { visibility: visible; opacity: 1; top: 10%; }
+ 100% { visibility: hidden; opacity: 0; top: 12%; }
}
-.dialogTitlebar {
- background-color: @wcfTabularBoxBackgroundColor;
- border-bottom: 1px solid rgba(0, 0, 0, .1);
- border-top-left-radius: 7px;
- border-top-right-radius: 7px;
- display: block;
- padding: 10px 20px;
- min-height: 27px;
- position: relative;
+.dialogContainer {
+ background-color: rgba(0, 0, 0, .4);
+ border: 3px solid transparent;
+ border-radius: 3px;
+ box-shadow: 0 1px 15px 0 rgba(0, 0, 0, .3);
+ box-sizing: border-box;
+ left: 50%;
+ max-height: 80%;
+ max-width: 80%;
+ min-width: 400px;
+ position: absolute;
+ top: 10%;
+ transform: translateX(-50%);
+
+ -webkit-animation: wcfDialogOut .3s;
+ -webkit-animation-fill-mode: forwards;
+
+ &[aria-hidden=false] {
+ -webkit-animation: wcfDialog .3s;
+ -webkit-animation-fill-mode: forwards;
+ }
- .dialogTitle {
+ > header {
+ background: linear-gradient(to right, @wcfTabularBoxBackgroundColor, lighten(@wcfTabularBoxBackgroundColor, 10%));
+ border-top-left-radius: 3px;
+ border-top-right-radius: 3px;
color: @wcfTabularBoxColor;
- display: block;
- font-size: @wcfHeadlineFontSize;
- font-weight: bold;
- margin-right: 28px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
+ display: flex;
+ padding: 7px 10px;
.textShadow(@wcfTabularBoxBackgroundColor);
+
+ > span {
+ flex: 1;
+ font-size: 1.2rem;
+ }
+
+ > a {
+ color: @wcfTabularBoxColor;
+ flex: 0 0 20px;
+ font-family: FontAwesome;
+ font-size: 18px;
+ text-align: right;
+ text-decoration: none;
+
+ &:before {
+ content: @fa-var-times-circle;
+ }
+
+ > span {
+ display: none;
+ }
+ }
}
- .dialogCloseButton {
- color: @wcfTabularBoxColor;
- cursor: pointer;
- display: inline-block;
- font-family: FontAwesome;
- font-size: 28px;
- height: 32px;
- position: absolute;
- right: 10px;
- text-align: center;
- text-decoration: none;
- top: 7px;
- width: 32px;
+ > .dialogContent {
+ background-color: @wcfContainerBackgroundColor;
+ box-sizing: border-box;
+ color: @wcfColor;
+ overflow: auto;
+ padding: 10px;
+ padding-bottom: 0;
- .textShadow(@wcfTabularBoxBackgroundColor);
+ &:after {
+ content: "";
+ display: block;
+ height: 10px;
+ }
+
+ &.dialogForm:after {
+ height: 17px;
+ }
+
+ &:not(.dialogForm) {
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+ }
- &::before {
- content: "\f057";
+ dl:not(.plain) {
+ > dt {
+ width: 170px;
+ }
+
+ > dd {
+ margin-left: 190px;
+ }
}
- span {
- display: none;
+ .dialogFormSubmit {
+ background-color: @wcfContainerAccentBackgroundColor;
+ border-bottom-left-radius: 3px;
+ border-bottom-right-radius: 3px;
+ bottom: 0;
+ left: 0;
+ padding: 7px 10px;
+ position: absolute;
+ right: 0;
}
}
}
@media only screen and (max-width: 800px) {
- .dialogTitlebar {
- border-radius: 0;
- }
}
-.dialogContent {
- background-color: @wcfContainerBackgroundColor;
- color: @wcfColor;
- padding: 10px 20px 20px;
-
- &:not(.dialogForm) {
- border-bottom-left-radius: 7px;
- border-bottom-right-radius: 7px;
- }
+
+.dialogContentX {
> .icon-spinner {
left: 50%;
top: 50%;
}
- dl:not(.plain) {
- > dt {
- width: 170px;
- }
-
- > dd {
- margin-left: 190px;
- }
- }
-
.formSubmit {
background-color: @wcfContainerAccentBackgroundColor;
border-bottom-left-radius: 7px;
}
}
-@media only screen and (max-width: 800px) {
- .dialogContent {
- max-height: none !important;
- max-width: none !important;
-
- &:not(.dialogForm) {
- border-radius: 0;
- }
-
- .formSubmit {
- border-radius: 0;
- }
- }
-}
-
-.dialogOverlay {
- background-color: rgba(0, 0, 0, .5);
- bottom: 0;
- left: 0;
- position: fixed;
- right: 0;
- top: 0;
-}
-
/* package (un-)installation */
#packageInstallationDialogContainer > .boxHeadline {
margin-top: 0;
color: @wcfColor;
font-family: @wcfBaseFontFamily;
line-height: @wcfBaseLineHeight;
+ position: relative;
word-wrap: break-word;
}
}
}
+.elementPointer {
+ position: absolute;
+ top: 0;
+ transform: translateY(-100%);
+
+ &.center {
+ left: 50%;
+ transform: translateX(-50%) translateY(-100%);
+ }
+
+ &.left {
+ left: 4px;
+ }
+
+ &.right {
+ right: 4px;
+ }
+
+ &.flipVertical {
+ bottom: 0;
+ top: auto;
+ transform: translateY(100%);
+
+ &.center {
+ transform: translateX(-50%) translateY(100%);
+ }
+ }
+}
+
/* balloon tooltips */
.balloonTooltip {
background-color: @wcfTooltipBackgroundColor;
color: @wcfTooltipColor;
font-size: @wcfSmallFontSize;
max-width: 300px;
+ opacity: 0;
padding: 5px 10px 7px;
position: absolute;
+ visibility: hidden;
z-index: 800;
+ transition: visibility 0s linear .2s, opacity .2s linear .2s;
+
+ > .elementPointer {
+ border-color: @wcfTooltipBackgroundColor transparent;
+ border-style: solid;
+ border-width: 0 5px 5px;
+
+ &.flipVertical {
+ border-width: 5px 5px 0;
+ }
+ }
+
.pointer {
border-color: @wcfTooltipBackgroundColor transparent;
border-style: solid;
.boxShadow(0, 3px, rgba(0, 0, 0, .3), 7px);
- &.inverse {
- .pointer {
- border-width: 5px 5px 0;
- }
+ &.inverse > .pointer {
+ border-width: 5px 5px 0;
+ }
+
+ &.active {
+ opacity: 1;
+ visibility: visible;
+
+ transition-delay: 0s;
}
}
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfUserPanelHoverColor', 'rgba(255, 255, 255, 1)');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonBackgroundColor', 'rgba(249, 249, 249, 1)');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonBorderColor', 'rgba(221, 221, 221, 1)');
-INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonBorderRadius', '15px');
+INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonBorderRadius', '3px');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfSmallButtonBorderRadius', '3px');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonColor', 'rgba(102, 102, 102, 1)');
INSERT INTO wcf1_style_variable (variableName, defaultValue) VALUES ('wcfButtonPrimaryBackgroundColor', 'rgba(211, 232, 254, 1)');