1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-18 15:08:22 +00:00
seahub/media/aloha-0.22.7/plugins/common/ui/lib/floating.js

404 lines
11 KiB
JavaScript
Raw Normal View History

/* floating.js is part of Aloha Editor project http://aloha-editor.org
*
* Aloha Editor is a WYSIWYG HTML5 inline editing library and editor.
* Copyright (c) 2010-2012 Gentics Software GmbH, Vienna, Austria.
* Contributors http://aloha-editor.org/contribution.php
*
* Aloha Editor is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* Aloha Editor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* As an additional permission to the GNU GPL version 2, you may distribute
* non-source (e.g., minimized or compacted) forms of the Aloha-Editor
* source code without the copy of the GNU GPL normally required,
* provided you include this license notice and a URL through which
* recipients can access the Corresponding Source.
*
* @overview
* Implements position and floating animation effect for UI surfaces.
*/
define([
'jquery',
'aloha/core',
'ui/surface',
'ui/subguarded',
'PubSub',
'vendor/amplify.store'
], function (
$,
Aloha,
Surface,
subguarded,
PubSub,
amplifyStore
) {
'use strict';
/**
* The distance that the floating surface should maintain from the editable
* it is floating to.
*
* @type {string}
* @const
*/
var DISTANCE = 10;
/**
* The duration of the floating animation in milliseconds.
*
* @type {number}
* @const
*/
var DURATION = 500;
/**
* jQuery unit set containing a feference to the global window.
*
* @type {jQuery.<window>}
* @const
*/
var $WINDOW = $(window);
/**
* The "position" style value.
*
* IE 7 does not support "fixed" position styling. Since "fixed" position
* results in smoother animation the use of "absolute" is made as a special
* accomodation for IE 7.
*
* @type {string}
* @const
*/
var POSITION_STYLE = ($.browser.msie && /^7\.\d+/.test($.browser.version))
? 'absolute'
: 'fixed';
/**
* The position of the floating menu.
*
* Used to float dialoges (eg special character-picker) with the floating
* menu.
*
* @type {object<string,*>}
*/
var POSITION = {
style: POSITION_STYLE,
offset: {
top: 0,
left: 0
}
};
/**
* Animates a surface element to the given position.
*
* @param {jQuery.<HTMLElement>} $element jQuery unit set of the DOM
* element to move.
* @param {object} position The x and y position to which the element
* should end up.
* @param {number} duration The length of time (in milliseconds) that the
* animation should run for.
* @param {function} callback Function to be invoked when animation
* completes.
*/
function floatTo($element, position, duration, callback) {
if ('absolute' === POSITION_STYLE) {
position.top += $WINDOW.scrollTop();
position.left += $WINDOW.scrollLeft();
}
POSITION.offset = position;
$element.stop().animate(position, duration, function () {
callback(position);
PubSub.pub('aloha.floating.changed', {position: POSITION});
});
}
/**
* Moves an element above the given coordinates.
*
* @param {jQuery.<HTMLElement>} $element jQuery unit set of the DOM
* element to move.
* @param {object} position The x and y position to which the element
* should end up.
* @param {number} duration The length of time (in milliseconds) that the
* animation should run for.
* @param {function} callback Function to be invoked when animation
* completes.
*/
function floatAbove($element, position, duration, callback) {
position.top -= $element.height() + DISTANCE;
floatTo($element, position, duration, callback);
}
/**
* Moves the element below the given coordinates.
*
* @param {jQuery.<HTMLElement>} $element jQuery unit set of the DOM
* element to move.
* @param {object} position The x and y position to which the element
* should end up.
* @param {number} duration The length of time (in milliseconds) that the
* animation should run for.
* @param {function} callback Function to be invoked when animation
* completes.
*/
function floatBelow($element, position, duration, callback) {
position.top += DISTANCE;
floatTo($element, position, duration, callback);
}
/**
* Persist the "top" and "left" positions of the FloatingMenu surface.
*/
function storePinPosition(offset) {
amplifyStore.store('Aloha.FloatingMenu.pinned', 'true');
amplifyStore.store('Aloha.FloatingMenu.top', offset.top);
amplifyStore.store('Aloha.FloatingMenu.left', offset.left);
}
/**
* Clears persisted state of the FloatingMenu surface.
*/
function unstorePinPosition() {
amplifyStore.store('Aloha.FloatingMenu.pinned', null);
amplifyStore.store('Aloha.FloatingMenu.top', null);
amplifyStore.store('Aloha.FloatingMenu.left', null);
}
/**
* Retreive the persisted pinned position of the FloatingMenu surface.
*
* @return {object}
*/
function getPinState() {
if (amplifyStore.store('Aloha.FloatingMenu.pinned') === 'true') {
return {
top: parseInt(amplifyStore.store('Aloha.FloatingMenu.top'), 10),
left: parseInt(amplifyStore.store('Aloha.FloatingMenu.left'), 10),
isPinned: true
};
}
return {
top: null,
left: null,
isPinned: false
};
}
/**
* Constrains the given position coordinates to be within the viewport.
*
* @param {object} position "Top" and "left" coordinates.
* @param {object} Constrained "top" and "left" coordinates.
*/
function forcePositionIntoWindow(position) {
var left = position.left;
var top = position.top;
if (top < 0) {
top = 0;
} else if (top > $WINDOW.height()) {
top = $WINDOW.height() / 2;
}
if (left < 0) {
left = 0;
} else if (left > $WINDOW.width()) {
left = $WINDOW.width() / 2;
}
return {
top: top,
left: left
};
}
/**
* Floats a surface to the appropriate position around the given editable.
*
* @param {Surface} surface The surface to be positioned.
* @param {Aloha.Editable} editable The editable around which the surface
* should be positioned.
* @param {number} duration The length of time (in milliseconds) for the
* animation should run.
* @param {function} callback Function to be invoked after the animation
* is completed.
*/
function floatSurface(surface, editable, duration, callback) {
if (typeof duration !== 'number') {
duration = DURATION;
}
var topGutter = (parseInt($('body').css('marginTop'), 10) || 0)
+ (parseInt($('body').css('paddingTop'), 10) || 0);
var $element = surface.$element;
var offset = editable.obj.offset();
var top = offset.top;
var left = offset.left;
var scrollTop = $WINDOW.scrollTop();
var availableSpace = top - scrollTop - topGutter;
var horizontalOverflow = left + $element.width() - $WINDOW.width();
if (horizontalOverflow > 0) {
left = Math.max(0, left - horizontalOverflow);
}
if (availableSpace >= $element.height()) {
floatAbove($element, {
top: top - scrollTop,
left: left
}, duration, callback);
} else if (availableSpace + $element.height() >
top + editable.obj.height()) {
floatBelow($element, {
top: top + editable.obj.height(),
left: left
}, duration, callback);
} else {
floatBelow($element, {
top: topGutter,
left: left
}, duration, callback);
}
}
/**
* Pins a surface at the speficied position on the viewport.
*
* @param {Surface} surfaces The surfaces that are to be pinned.
* @param {object} position The "top" and "left" position of where the
* surface is to be pinned.
* @param {boolean} isFloating Whether or not the surface type is in
* "floating" mode or not.
*/
function togglePinSurface(surface, position, isFloating) {
var $surface = surface.$element;
if (isFloating) {
unstorePinPosition();
$surface.find('.aloha-ui-pin').removeClass('aloha-ui-pin-down');
} else {
storePinPosition(position);
$surface.find('.aloha-ui-pin').addClass('aloha-ui-pin-down');
}
$surface.css({
position: 'fixed',
top: position.top
});
}
/**
* Filters surface activation events.
*/
function onActivatedSurface(tuples, eventName, $event, range, nativeEvent) {
var i;
for (i = 0; i < tuples.length; i++) {
if (tuples[i][0].isActive()) {
tuples[i][1]($event, range, nativeEvent);
}
}
}
/**
* Sets the surface's DOM element's "position" property to "fixed."
*
* IE7 will not properly set the position property to "fixed" if our
* element is not rendered. We therefore have to do a rigmarole to
* temorarily render the element in order to set the position correctly.
*
* @param {Surface} surface
*/
function setPositionStyleToFixed(surface) {
if ($.browser.msie) {
var $parent = surface.$element.parent();
surface.$element.appendTo('body');
surface.$element.css('position', POSITION_STYLE);
if ($parent.length) {
surface.$element.appendTo($parent);
} else {
surface.$element.detach();
}
} else {
surface.$element.css('position', POSITION_STYLE);
}
}
/**
* Binds floating facilities on a surface.
*
* @TODO:
* Resizable toolbars are possible, and would be a nice feature:
* surface.$element.resizable();
*
* @param {Surface} surface A UI Surface instance.
* @param {object} SurfaceTypeManager
*/
function makeFloating(surface, SurfaceTypeManager) {
subguarded([
'aloha-selection-changed',
'aloha.ui.container.selected'
], onActivatedSurface, surface, function () {
surface._move();
});
var updateSurfacePosition = function () {
var position = forcePositionIntoWindow({
top: SurfaceTypeManager.pinTop,
left: SurfaceTypeManager.pinLeft
});
SurfaceTypeManager.setFloatingPosition(position);
surface.$element.css({
top: position.top,
left: position.left
});
};
$WINDOW.scroll(function () {
// TODO: only do this for active surfaces.
surface._move(0);
});
$WINDOW.resize(function () {
if (!SurfaceTypeManager.isFloatingMode) {
updateSurfacePosition();
}
});
surface.addPin();
setPositionStyleToFixed(surface);
if (!SurfaceTypeManager.isFloatingMode) {
updateSurfacePosition();
}
surface.$element.css('z-index', 10100).draggable({
distance: 20,
stop: function (event, ui) {
SurfaceTypeManager.setFloatingPosition(ui.position);
if (!SurfaceTypeManager.isFloatingMode) {
storePinPosition(ui.position);
}
}
});
}
return {
getPinState: getPinState,
makeFloating: makeFloating,
floatSurface: floatSurface,
togglePinSurface: togglePinSurface,
POSITION_STYLE: POSITION_STYLE
};
});