mirror of
https://github.com/haiwen/seahub.git
synced 2025-08-18 15:08:22 +00:00
404 lines
11 KiB
JavaScript
404 lines
11 KiB
JavaScript
|
/* 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
|
||
|
};
|
||
|
});
|