mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-11-25 18:20:20 -06:00
Add a context menu to the treeview, with options to delete and rename server nodes.
TODO: Make the menu items hook in.
This commit is contained in:
parent
89cc11fb80
commit
b43580a19e
@ -9,7 +9,7 @@
|
||||
|
||||
"""Integration hooks for server groups."""
|
||||
|
||||
from flask import url_for
|
||||
from flask import render_template, url_for
|
||||
from flask.ext.security import current_user
|
||||
|
||||
from pgadmin.settings.settings_model import db, ServerGroup
|
||||
@ -33,37 +33,7 @@ def get_file_menu_items():
|
||||
|
||||
def get_script_snippets():
|
||||
"""Return the script snippets needed to handle treeview node operations."""
|
||||
script = """function add_server_group() {
|
||||
var alert = alertify.prompt(
|
||||
'Add a server group',
|
||||
'Enter a name for the new server group',
|
||||
'',
|
||||
function(evt, value) { $.post("%s", { name: value })
|
||||
.done(function(data) {
|
||||
if (data.success == 0) {
|
||||
report_error(data.errormsg, data.info);
|
||||
} else {
|
||||
var item = {
|
||||
id: data.data.id,
|
||||
label: data.data.name,
|
||||
inode: true,
|
||||
open: false,
|
||||
icon: 'icon-server-group'
|
||||
}
|
||||
|
||||
treeApi.append(null, {
|
||||
itemData: item
|
||||
});
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
function(evt, value) { }
|
||||
);
|
||||
alert.show();
|
||||
}
|
||||
""" % url_for('NODE-server-group.add')
|
||||
return script
|
||||
return render_template('server_groups/server_groups.js')
|
||||
|
||||
def get_css_snippets():
|
||||
"""Return the CSS needed to display the treeview node image."""
|
||||
|
@ -0,0 +1,82 @@
|
||||
// Add a server group
|
||||
function add_server_group() {
|
||||
var alert = alertify.prompt(
|
||||
'Add a server group',
|
||||
'Enter a name for the new server group',
|
||||
'',
|
||||
function(evt, value) {
|
||||
$.post("{{ url_for('NODE-server-group.add') }}", { name: value })
|
||||
.done(function(data) {
|
||||
if (data.success == 0) {
|
||||
report_error(data.errormsg, data.info);
|
||||
} else {
|
||||
var item = {
|
||||
id: data.data.id,
|
||||
label: data.data.name,
|
||||
inode: true,
|
||||
open: false,
|
||||
icon: 'icon-server-group'
|
||||
}
|
||||
|
||||
treeApi.append(null, {
|
||||
itemData: item
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
null
|
||||
);
|
||||
alert.show();
|
||||
}
|
||||
|
||||
// Delete a server group
|
||||
function delete_server_group(item) {
|
||||
alertify.confirm(
|
||||
'Delete server group?',
|
||||
'Are you sure you wish to delete the server group "{0}"?'.replace('{0}', treeApi.getLabel(item)),
|
||||
function() {
|
||||
var id = treeApi.getId(item)
|
||||
$.post("{{ url_for('NODE-server-group.delete') }}", { id: id })
|
||||
.done(function(data) {
|
||||
if (data.success == 0) {
|
||||
report_error(data.errormsg, data.info);
|
||||
} else {
|
||||
var next = treeApi.next(item);
|
||||
var prev = treeApi.prev(item);
|
||||
treeApi.remove(item);
|
||||
if (next.length) {
|
||||
treeApi.select(next);
|
||||
} else if (prev.length) {
|
||||
treeApi.select(prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
// Rename a server group
|
||||
function rename_server_group(item) {
|
||||
alertify.prompt(
|
||||
'Rename server group',
|
||||
'Enter a new name for the server group',
|
||||
treeApi.getLabel(item),
|
||||
function(evt, value) {
|
||||
var id = treeApi.getId(item)
|
||||
$.post("{{ url_for('NODE-server-group.rename') }}", { id: id, name: value })
|
||||
.done(function(data) {
|
||||
if (data.success == 0) {
|
||||
report_error(data.errormsg, data.info);
|
||||
} else {
|
||||
treeApi.setLabel(item, { label: value });
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
@ -17,7 +17,7 @@ import traceback
|
||||
from flask import Blueprint, Response, current_app, request
|
||||
from flask.ext.security import current_user, login_required
|
||||
|
||||
from utils.ajax import make_json_result
|
||||
from utils.ajax import make_json_response
|
||||
from pgadmin.settings.settings_model import db, ServerGroup
|
||||
import config
|
||||
|
||||
@ -50,15 +50,70 @@ def add():
|
||||
data['id'] = servergroup.id
|
||||
data['name'] = servergroup.name
|
||||
|
||||
value = make_json_result(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form,
|
||||
data=data)
|
||||
|
||||
resp = Response(response=value,
|
||||
status=200,
|
||||
mimetype="text/json")
|
||||
|
||||
return resp
|
||||
|
||||
return make_json_response(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form,
|
||||
data=data)
|
||||
|
||||
@blueprint.route('/delete/', methods=['POST'])
|
||||
@login_required
|
||||
def delete():
|
||||
"""Delete a server group node in the settings database"""
|
||||
success = 1
|
||||
errormsg = ''
|
||||
|
||||
if request.form['id'] != '':
|
||||
# There can be only one record at most
|
||||
servergroup = ServerGroup.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
|
||||
|
||||
if servergroup is None:
|
||||
success = 0
|
||||
errormsg = 'The specified server group could not be found.'
|
||||
else:
|
||||
try:
|
||||
db.session.delete(servergroup)
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
success = 0
|
||||
errormsg = e.message
|
||||
|
||||
else:
|
||||
success = 0
|
||||
errormsg = "No server group was specified."
|
||||
|
||||
return make_json_response(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form)
|
||||
|
||||
@blueprint.route('/rename/', methods=['POST'])
|
||||
@login_required
|
||||
def rename():
|
||||
"""Rename a server group node in the settings database"""
|
||||
success = 1
|
||||
errormsg = ''
|
||||
|
||||
if request.form['id'] != '':
|
||||
# There can be only one record at most
|
||||
servergroup = ServerGroup.query.filter_by(user_id=current_user.id, id=int(request.form['id'])).first()
|
||||
|
||||
if servergroup is None:
|
||||
success = 0
|
||||
errormsg = 'The specified server group could not be found.'
|
||||
else:
|
||||
try:
|
||||
servergroup.name = request.form['name']
|
||||
db.session.commit()
|
||||
except Exception as e:
|
||||
success = 0
|
||||
errormsg = e.message
|
||||
|
||||
else:
|
||||
success = 0
|
||||
errormsg = "No server group was specified."
|
||||
|
||||
return make_json_response(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form)
|
142
web/pgadmin/browser/static/css/jQuery-contextMenu/jquery.contextMenu.css
Executable file
142
web/pgadmin/browser/static/css/jQuery-contextMenu/jquery.contextMenu.css
Executable file
@ -0,0 +1,142 @@
|
||||
/*!
|
||||
* jQuery contextMenu - Plugin for simple contextMenu handling
|
||||
*
|
||||
* Version: git-master
|
||||
*
|
||||
* Authors: Rodney Rehm, Addy Osmani (patches for FF)
|
||||
* Web: http://medialize.github.com/jQuery-contextMenu/
|
||||
*
|
||||
* Licensed under
|
||||
* MIT License http://www.opensource.org/licenses/mit-license
|
||||
* GPL v3 http://opensource.org/licenses/GPL-3.0
|
||||
*
|
||||
*/
|
||||
|
||||
.context-menu-list {
|
||||
margin:0;
|
||||
padding:0;
|
||||
|
||||
min-width: 120px;
|
||||
max-width: 250px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
|
||||
border: 1px solid #DDD;
|
||||
background: #EEE;
|
||||
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
-moz-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
-ms-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
-o-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
|
||||
|
||||
font-family: Verdana, Arial, Helvetica, sans-serif;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
padding: 2px 2px 2px 24px;
|
||||
background-color: #EEE;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: -moz-none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.context-menu-separator {
|
||||
padding-bottom:0;
|
||||
border-bottom: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.context-menu-item > label > input,
|
||||
.context-menu-item > label > textarea {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-ms-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.context-menu-item.hover {
|
||||
cursor: pointer;
|
||||
background-color: #39F;
|
||||
}
|
||||
|
||||
.context-menu-item.disabled {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.context-menu-input.hover,
|
||||
.context-menu-item.disabled.hover {
|
||||
cursor: default;
|
||||
background-color: #EEE;
|
||||
}
|
||||
|
||||
.context-menu-submenu:after {
|
||||
content: ">";
|
||||
color: #666;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 3px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* icons
|
||||
#protip:
|
||||
In case you want to use sprites for icons (which I would suggest you do) have a look at
|
||||
http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement
|
||||
.context-menu-item.icon:before {}
|
||||
*/
|
||||
.context-menu-item.icon { min-height: 18px; background-repeat: no-repeat; background-position: 4px 2px; }
|
||||
.context-menu-item.icon-edit { background-image: url(images/page_white_edit.png); }
|
||||
.context-menu-item.icon-cut { background-image: url(images/cut.png); }
|
||||
.context-menu-item.icon-copy { background-image: url(images/page_white_copy.png); }
|
||||
.context-menu-item.icon-paste { background-image: url(images/page_white_paste.png); }
|
||||
.context-menu-item.icon-delete { background-image: url(images/page_white_delete.png); }
|
||||
.context-menu-item.icon-add { background-image: url(images/page_white_add.png); }
|
||||
.context-menu-item.icon-quit { background-image: url(images/door.png); }
|
||||
|
||||
/* vertically align inside labels */
|
||||
.context-menu-input > label > * { vertical-align: top; }
|
||||
|
||||
/* position checkboxes and radios as icons */
|
||||
.context-menu-input > label > input[type="checkbox"],
|
||||
.context-menu-input > label > input[type="radio"] {
|
||||
margin-left: -17px;
|
||||
}
|
||||
.context-menu-input > label > span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.context-menu-input > label,
|
||||
.context-menu-input > label > input[type="text"],
|
||||
.context-menu-input > label > textarea,
|
||||
.context-menu-input > label > select {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
-o-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.context-menu-input > label > textarea {
|
||||
height: 100px;
|
||||
}
|
||||
.context-menu-item > .context-menu-list {
|
||||
display: none;
|
||||
/* re-positioned by js */
|
||||
right: -5px;
|
||||
top: 5px;
|
||||
}
|
||||
|
||||
.context-menu-item.hover > .context-menu-list {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.context-menu-accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
1686
web/pgadmin/browser/static/js/jQuery-contextMenu/jquery.contextMenu.js
Executable file
1686
web/pgadmin/browser/static/js/jQuery-contextMenu/jquery.contextMenu.js
Executable file
File diff suppressed because it is too large
Load Diff
497
web/pgadmin/browser/static/js/jQuery-contextMenu/jquery.ui.position.js
vendored
Executable file
497
web/pgadmin/browser/static/js/jQuery-contextMenu/jquery.ui.position.js
vendored
Executable file
@ -0,0 +1,497 @@
|
||||
/*!
|
||||
* jQuery UI Position v1.10.0
|
||||
* http://jqueryui.com
|
||||
*
|
||||
* Copyright 2013 jQuery Foundation and other contributors
|
||||
* Released under the MIT license.
|
||||
* http://jquery.org/license
|
||||
*
|
||||
* http://api.jqueryui.com/position/
|
||||
*/
|
||||
(function( $, undefined ) {
|
||||
|
||||
$.ui = $.ui || {};
|
||||
|
||||
var cachedScrollbarWidth,
|
||||
max = Math.max,
|
||||
abs = Math.abs,
|
||||
round = Math.round,
|
||||
rhorizontal = /left|center|right/,
|
||||
rvertical = /top|center|bottom/,
|
||||
roffset = /[\+\-]\d+%?/,
|
||||
rposition = /^\w+/,
|
||||
rpercent = /%$/,
|
||||
_position = $.fn.position;
|
||||
|
||||
function getOffsets( offsets, width, height ) {
|
||||
return [
|
||||
parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
|
||||
parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
|
||||
];
|
||||
}
|
||||
|
||||
function parseCss( element, property ) {
|
||||
return parseInt( $.css( element, property ), 10 ) || 0;
|
||||
}
|
||||
|
||||
function getDimensions( elem ) {
|
||||
var raw = elem[0];
|
||||
if ( raw.nodeType === 9 ) {
|
||||
return {
|
||||
width: elem.width(),
|
||||
height: elem.height(),
|
||||
offset: { top: 0, left: 0 }
|
||||
};
|
||||
}
|
||||
if ( $.isWindow( raw ) ) {
|
||||
return {
|
||||
width: elem.width(),
|
||||
height: elem.height(),
|
||||
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
|
||||
};
|
||||
}
|
||||
if ( raw.preventDefault ) {
|
||||
return {
|
||||
width: 0,
|
||||
height: 0,
|
||||
offset: { top: raw.pageY, left: raw.pageX }
|
||||
};
|
||||
}
|
||||
return {
|
||||
width: elem.outerWidth(),
|
||||
height: elem.outerHeight(),
|
||||
offset: elem.offset()
|
||||
};
|
||||
}
|
||||
|
||||
$.position = {
|
||||
scrollbarWidth: function() {
|
||||
if ( cachedScrollbarWidth !== undefined ) {
|
||||
return cachedScrollbarWidth;
|
||||
}
|
||||
var w1, w2,
|
||||
div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
|
||||
innerDiv = div.children()[0];
|
||||
|
||||
$( "body" ).append( div );
|
||||
w1 = innerDiv.offsetWidth;
|
||||
div.css( "overflow", "scroll" );
|
||||
|
||||
w2 = innerDiv.offsetWidth;
|
||||
|
||||
if ( w1 === w2 ) {
|
||||
w2 = div[0].clientWidth;
|
||||
}
|
||||
|
||||
div.remove();
|
||||
|
||||
return (cachedScrollbarWidth = w1 - w2);
|
||||
},
|
||||
getScrollInfo: function( within ) {
|
||||
var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
|
||||
overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
|
||||
hasOverflowX = overflowX === "scroll" ||
|
||||
( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
|
||||
hasOverflowY = overflowY === "scroll" ||
|
||||
( overflowY === "auto" && within.height < within.element[0].scrollHeight );
|
||||
return {
|
||||
width: hasOverflowX ? $.position.scrollbarWidth() : 0,
|
||||
height: hasOverflowY ? $.position.scrollbarWidth() : 0
|
||||
};
|
||||
},
|
||||
getWithinInfo: function( element ) {
|
||||
var withinElement = $( element || window ),
|
||||
isWindow = $.isWindow( withinElement[0] );
|
||||
return {
|
||||
element: withinElement,
|
||||
isWindow: isWindow,
|
||||
offset: withinElement.offset() || { left: 0, top: 0 },
|
||||
scrollLeft: withinElement.scrollLeft(),
|
||||
scrollTop: withinElement.scrollTop(),
|
||||
width: isWindow ? withinElement.width() : withinElement.outerWidth(),
|
||||
height: isWindow ? withinElement.height() : withinElement.outerHeight()
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.position = function( options ) {
|
||||
if ( !options || !options.of ) {
|
||||
return _position.apply( this, arguments );
|
||||
}
|
||||
|
||||
// make a copy, we don't want to modify arguments
|
||||
options = $.extend( {}, options );
|
||||
|
||||
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
|
||||
target = $( options.of ),
|
||||
within = $.position.getWithinInfo( options.within ),
|
||||
scrollInfo = $.position.getScrollInfo( within ),
|
||||
collision = ( options.collision || "flip" ).split( " " ),
|
||||
offsets = {};
|
||||
|
||||
dimensions = getDimensions( target );
|
||||
if ( target[0].preventDefault ) {
|
||||
// force left top to allow flipping
|
||||
options.at = "left top";
|
||||
}
|
||||
targetWidth = dimensions.width;
|
||||
targetHeight = dimensions.height;
|
||||
targetOffset = dimensions.offset;
|
||||
// clone to reuse original targetOffset later
|
||||
basePosition = $.extend( {}, targetOffset );
|
||||
|
||||
// force my and at to have valid horizontal and vertical positions
|
||||
// if a value is missing or invalid, it will be converted to center
|
||||
$.each( [ "my", "at" ], function() {
|
||||
var pos = ( options[ this ] || "" ).split( " " ),
|
||||
horizontalOffset,
|
||||
verticalOffset;
|
||||
|
||||
if ( pos.length === 1) {
|
||||
pos = rhorizontal.test( pos[ 0 ] ) ?
|
||||
pos.concat( [ "center" ] ) :
|
||||
rvertical.test( pos[ 0 ] ) ?
|
||||
[ "center" ].concat( pos ) :
|
||||
[ "center", "center" ];
|
||||
}
|
||||
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
|
||||
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
|
||||
|
||||
// calculate offsets
|
||||
horizontalOffset = roffset.exec( pos[ 0 ] );
|
||||
verticalOffset = roffset.exec( pos[ 1 ] );
|
||||
offsets[ this ] = [
|
||||
horizontalOffset ? horizontalOffset[ 0 ] : 0,
|
||||
verticalOffset ? verticalOffset[ 0 ] : 0
|
||||
];
|
||||
|
||||
// reduce to just the positions without the offsets
|
||||
options[ this ] = [
|
||||
rposition.exec( pos[ 0 ] )[ 0 ],
|
||||
rposition.exec( pos[ 1 ] )[ 0 ]
|
||||
];
|
||||
});
|
||||
|
||||
// normalize collision option
|
||||
if ( collision.length === 1 ) {
|
||||
collision[ 1 ] = collision[ 0 ];
|
||||
}
|
||||
|
||||
if ( options.at[ 0 ] === "right" ) {
|
||||
basePosition.left += targetWidth;
|
||||
} else if ( options.at[ 0 ] === "center" ) {
|
||||
basePosition.left += targetWidth / 2;
|
||||
}
|
||||
|
||||
if ( options.at[ 1 ] === "bottom" ) {
|
||||
basePosition.top += targetHeight;
|
||||
} else if ( options.at[ 1 ] === "center" ) {
|
||||
basePosition.top += targetHeight / 2;
|
||||
}
|
||||
|
||||
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
|
||||
basePosition.left += atOffset[ 0 ];
|
||||
basePosition.top += atOffset[ 1 ];
|
||||
|
||||
return this.each(function() {
|
||||
var collisionPosition, using,
|
||||
elem = $( this ),
|
||||
elemWidth = elem.outerWidth(),
|
||||
elemHeight = elem.outerHeight(),
|
||||
marginLeft = parseCss( this, "marginLeft" ),
|
||||
marginTop = parseCss( this, "marginTop" ),
|
||||
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
|
||||
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
|
||||
position = $.extend( {}, basePosition ),
|
||||
myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
|
||||
|
||||
if ( options.my[ 0 ] === "right" ) {
|
||||
position.left -= elemWidth;
|
||||
} else if ( options.my[ 0 ] === "center" ) {
|
||||
position.left -= elemWidth / 2;
|
||||
}
|
||||
|
||||
if ( options.my[ 1 ] === "bottom" ) {
|
||||
position.top -= elemHeight;
|
||||
} else if ( options.my[ 1 ] === "center" ) {
|
||||
position.top -= elemHeight / 2;
|
||||
}
|
||||
|
||||
position.left += myOffset[ 0 ];
|
||||
position.top += myOffset[ 1 ];
|
||||
|
||||
// if the browser doesn't support fractions, then round for consistent results
|
||||
if ( !$.support.offsetFractions ) {
|
||||
position.left = round( position.left );
|
||||
position.top = round( position.top );
|
||||
}
|
||||
|
||||
collisionPosition = {
|
||||
marginLeft: marginLeft,
|
||||
marginTop: marginTop
|
||||
};
|
||||
|
||||
$.each( [ "left", "top" ], function( i, dir ) {
|
||||
if ( $.ui.position[ collision[ i ] ] ) {
|
||||
$.ui.position[ collision[ i ] ][ dir ]( position, {
|
||||
targetWidth: targetWidth,
|
||||
targetHeight: targetHeight,
|
||||
elemWidth: elemWidth,
|
||||
elemHeight: elemHeight,
|
||||
collisionPosition: collisionPosition,
|
||||
collisionWidth: collisionWidth,
|
||||
collisionHeight: collisionHeight,
|
||||
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
|
||||
my: options.my,
|
||||
at: options.at,
|
||||
within: within,
|
||||
elem : elem
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if ( options.using ) {
|
||||
// adds feedback as second argument to using callback, if present
|
||||
using = function( props ) {
|
||||
var left = targetOffset.left - position.left,
|
||||
right = left + targetWidth - elemWidth,
|
||||
top = targetOffset.top - position.top,
|
||||
bottom = top + targetHeight - elemHeight,
|
||||
feedback = {
|
||||
target: {
|
||||
element: target,
|
||||
left: targetOffset.left,
|
||||
top: targetOffset.top,
|
||||
width: targetWidth,
|
||||
height: targetHeight
|
||||
},
|
||||
element: {
|
||||
element: elem,
|
||||
left: position.left,
|
||||
top: position.top,
|
||||
width: elemWidth,
|
||||
height: elemHeight
|
||||
},
|
||||
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
|
||||
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
|
||||
};
|
||||
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
|
||||
feedback.horizontal = "center";
|
||||
}
|
||||
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
|
||||
feedback.vertical = "middle";
|
||||
}
|
||||
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
|
||||
feedback.important = "horizontal";
|
||||
} else {
|
||||
feedback.important = "vertical";
|
||||
}
|
||||
options.using.call( this, props, feedback );
|
||||
};
|
||||
}
|
||||
|
||||
elem.offset( $.extend( position, { using: using } ) );
|
||||
});
|
||||
};
|
||||
|
||||
$.ui.position = {
|
||||
fit: {
|
||||
left: function( position, data ) {
|
||||
var within = data.within,
|
||||
withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
|
||||
outerWidth = within.width,
|
||||
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
|
||||
overLeft = withinOffset - collisionPosLeft,
|
||||
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
|
||||
newOverRight;
|
||||
|
||||
// element is wider than within
|
||||
if ( data.collisionWidth > outerWidth ) {
|
||||
// element is initially over the left side of within
|
||||
if ( overLeft > 0 && overRight <= 0 ) {
|
||||
newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
|
||||
position.left += overLeft - newOverRight;
|
||||
// element is initially over right side of within
|
||||
} else if ( overRight > 0 && overLeft <= 0 ) {
|
||||
position.left = withinOffset;
|
||||
// element is initially over both left and right sides of within
|
||||
} else {
|
||||
if ( overLeft > overRight ) {
|
||||
position.left = withinOffset + outerWidth - data.collisionWidth;
|
||||
} else {
|
||||
position.left = withinOffset;
|
||||
}
|
||||
}
|
||||
// too far left -> align with left edge
|
||||
} else if ( overLeft > 0 ) {
|
||||
position.left += overLeft;
|
||||
// too far right -> align with right edge
|
||||
} else if ( overRight > 0 ) {
|
||||
position.left -= overRight;
|
||||
// adjust based on position and margin
|
||||
} else {
|
||||
position.left = max( position.left - collisionPosLeft, position.left );
|
||||
}
|
||||
},
|
||||
top: function( position, data ) {
|
||||
var within = data.within,
|
||||
withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
|
||||
outerHeight = data.within.height,
|
||||
collisionPosTop = position.top - data.collisionPosition.marginTop,
|
||||
overTop = withinOffset - collisionPosTop,
|
||||
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
|
||||
newOverBottom;
|
||||
|
||||
// element is taller than within
|
||||
if ( data.collisionHeight > outerHeight ) {
|
||||
// element is initially over the top of within
|
||||
if ( overTop > 0 && overBottom <= 0 ) {
|
||||
newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
|
||||
position.top += overTop - newOverBottom;
|
||||
// element is initially over bottom of within
|
||||
} else if ( overBottom > 0 && overTop <= 0 ) {
|
||||
position.top = withinOffset;
|
||||
// element is initially over both top and bottom of within
|
||||
} else {
|
||||
if ( overTop > overBottom ) {
|
||||
position.top = withinOffset + outerHeight - data.collisionHeight;
|
||||
} else {
|
||||
position.top = withinOffset;
|
||||
}
|
||||
}
|
||||
// too far up -> align with top
|
||||
} else if ( overTop > 0 ) {
|
||||
position.top += overTop;
|
||||
// too far down -> align with bottom edge
|
||||
} else if ( overBottom > 0 ) {
|
||||
position.top -= overBottom;
|
||||
// adjust based on position and margin
|
||||
} else {
|
||||
position.top = max( position.top - collisionPosTop, position.top );
|
||||
}
|
||||
}
|
||||
},
|
||||
flip: {
|
||||
left: function( position, data ) {
|
||||
var within = data.within,
|
||||
withinOffset = within.offset.left + within.scrollLeft,
|
||||
outerWidth = within.width,
|
||||
offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
|
||||
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
|
||||
overLeft = collisionPosLeft - offsetLeft,
|
||||
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
|
||||
myOffset = data.my[ 0 ] === "left" ?
|
||||
-data.elemWidth :
|
||||
data.my[ 0 ] === "right" ?
|
||||
data.elemWidth :
|
||||
0,
|
||||
atOffset = data.at[ 0 ] === "left" ?
|
||||
data.targetWidth :
|
||||
data.at[ 0 ] === "right" ?
|
||||
-data.targetWidth :
|
||||
0,
|
||||
offset = -2 * data.offset[ 0 ],
|
||||
newOverRight,
|
||||
newOverLeft;
|
||||
|
||||
if ( overLeft < 0 ) {
|
||||
newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
|
||||
if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
|
||||
position.left += myOffset + atOffset + offset;
|
||||
}
|
||||
}
|
||||
else if ( overRight > 0 ) {
|
||||
newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
|
||||
if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
|
||||
position.left += myOffset + atOffset + offset;
|
||||
}
|
||||
}
|
||||
},
|
||||
top: function( position, data ) {
|
||||
var within = data.within,
|
||||
withinOffset = within.offset.top + within.scrollTop,
|
||||
outerHeight = within.height,
|
||||
offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
|
||||
collisionPosTop = position.top - data.collisionPosition.marginTop,
|
||||
overTop = collisionPosTop - offsetTop,
|
||||
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
|
||||
top = data.my[ 1 ] === "top",
|
||||
myOffset = top ?
|
||||
-data.elemHeight :
|
||||
data.my[ 1 ] === "bottom" ?
|
||||
data.elemHeight :
|
||||
0,
|
||||
atOffset = data.at[ 1 ] === "top" ?
|
||||
data.targetHeight :
|
||||
data.at[ 1 ] === "bottom" ?
|
||||
-data.targetHeight :
|
||||
0,
|
||||
offset = -2 * data.offset[ 1 ],
|
||||
newOverTop,
|
||||
newOverBottom;
|
||||
if ( overTop < 0 ) {
|
||||
newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
|
||||
if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
|
||||
position.top += myOffset + atOffset + offset;
|
||||
}
|
||||
}
|
||||
else if ( overBottom > 0 ) {
|
||||
newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
|
||||
if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
|
||||
position.top += myOffset + atOffset + offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
flipfit: {
|
||||
left: function() {
|
||||
$.ui.position.flip.left.apply( this, arguments );
|
||||
$.ui.position.fit.left.apply( this, arguments );
|
||||
},
|
||||
top: function() {
|
||||
$.ui.position.flip.top.apply( this, arguments );
|
||||
$.ui.position.fit.top.apply( this, arguments );
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// fraction support test
|
||||
(function () {
|
||||
var testElement, testElementParent, testElementStyle, offsetLeft, i,
|
||||
body = document.getElementsByTagName( "body" )[ 0 ],
|
||||
div = document.createElement( "div" );
|
||||
|
||||
//Create a "fake body" for testing based on method used in jQuery.support
|
||||
testElement = document.createElement( body ? "div" : "body" );
|
||||
testElementStyle = {
|
||||
visibility: "hidden",
|
||||
width: 0,
|
||||
height: 0,
|
||||
border: 0,
|
||||
margin: 0,
|
||||
background: "none"
|
||||
};
|
||||
if ( body ) {
|
||||
$.extend( testElementStyle, {
|
||||
position: "absolute",
|
||||
left: "-1000px",
|
||||
top: "-1000px"
|
||||
});
|
||||
}
|
||||
for ( i in testElementStyle ) {
|
||||
testElement.style[ i ] = testElementStyle[ i ];
|
||||
}
|
||||
testElement.appendChild( div );
|
||||
testElementParent = body || document.documentElement;
|
||||
testElementParent.insertBefore( testElement, testElementParent.firstChild );
|
||||
|
||||
div.style.cssText = "position: absolute; left: 10.7432222px;";
|
||||
|
||||
offsetLeft = $( div ).offset().left;
|
||||
$.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
|
||||
|
||||
testElement.innerHTML = "";
|
||||
testElementParent.removeChild( testElement );
|
||||
})();
|
||||
|
||||
}( jQuery ) );
|
@ -166,5 +166,30 @@ $('#tree').aciTree({
|
||||
});
|
||||
var treeApi = $('#tree').aciTree('api');
|
||||
|
||||
$('#tree').contextMenu({
|
||||
selector: '.aciTreeLine',
|
||||
build: function(element) {
|
||||
var item = treeApi.itemFrom(element);
|
||||
var menu = {
|
||||
};
|
||||
|
||||
menu['rename'] = {
|
||||
name: 'Rename server group',
|
||||
callback: function() { rename_server_group(item); }
|
||||
};
|
||||
|
||||
menu['delete'] = {
|
||||
name: 'Delete server group',
|
||||
callback: function() { delete_server_group(item); }
|
||||
};
|
||||
|
||||
return {
|
||||
autoHide: true,
|
||||
items: menu,
|
||||
callback: null
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -52,6 +52,7 @@ def index():
|
||||
stylesheets.append(url_for('static', filename='css/codemirror/codemirror.css'))
|
||||
stylesheets.append(url_for('browser.static', filename='css/browser.css'))
|
||||
stylesheets.append(url_for('browser.static', filename='css/aciTree/css/aciTree.css'))
|
||||
stylesheets.append(url_for('browser.static', filename='css/jQuery-contextMenu/jQuery.contextMenu.css'))
|
||||
stylesheets.append(url_for('browser.browser_css'))
|
||||
|
||||
# Add browser scripts
|
||||
@ -61,6 +62,8 @@ def index():
|
||||
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciPlugin.min.js'))
|
||||
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.dom.js'))
|
||||
scripts.append(url_for('browser.static', filename='js/aciTree/jquery.aciTree.min.js'))
|
||||
scripts.append(url_for('browser.static', filename='js/jQuery-contextMenu/jquery.ui.position.js'))
|
||||
scripts.append(url_for('browser.static', filename='js/jQuery-contextMenu/jQuery.contextMenu.js'))
|
||||
scripts.append(url_for('browser.browser_js'))
|
||||
|
||||
for module in modules_and_nodes:
|
||||
|
@ -15,7 +15,7 @@ from flask import Blueprint, Response, abort, request, render_template
|
||||
from flask.ext.security import login_required
|
||||
|
||||
import config
|
||||
from utils.ajax import make_json_result
|
||||
from utils.ajax import make_json_response
|
||||
from . import get_setting, store_setting
|
||||
|
||||
# Initialise the module
|
||||
@ -52,13 +52,10 @@ def store(setting=None, value=None):
|
||||
success = 0
|
||||
errormsg = e.message
|
||||
|
||||
value = make_json_result(success=success, errormsg=errormsg, info=traceback.format_exc(), result=request.form)
|
||||
|
||||
resp = Response(response=value,
|
||||
status=200,
|
||||
mimetype="text/json")
|
||||
|
||||
return resp
|
||||
return make_json_response(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form)
|
||||
|
||||
@blueprint.route("/get", methods=['POST'])
|
||||
@blueprint.route("/get/<setting>", methods=['GET'])
|
||||
@ -80,10 +77,7 @@ def get(setting=None, default=None):
|
||||
success = 0
|
||||
errormsg = e.message
|
||||
|
||||
value = make_json_result(success=success, errormsg=errormsg, info=traceback.format_exc(), result=request.form)
|
||||
|
||||
resp = Response(response=value,
|
||||
status=200,
|
||||
mimetype="text/json")
|
||||
|
||||
return resp
|
||||
return make_json_response(success=success,
|
||||
errormsg=errormsg,
|
||||
info=traceback.format_exc(),
|
||||
result=request.form)
|
||||
|
@ -9,10 +9,11 @@
|
||||
|
||||
"""Utility functions for dealing with AJAX."""
|
||||
|
||||
from flask import Response
|
||||
import json
|
||||
|
||||
def make_json_result(success=1, errormsg='', info='', result={}, data={}):
|
||||
"""Create a JSON response document describing the results of a request and
|
||||
def make_json_response(success=1, errormsg='', info='', result={}, data={}):
|
||||
"""Create a HTML response document describing the results of a request and
|
||||
containing the data."""
|
||||
doc = { }
|
||||
doc['success'] = success
|
||||
@ -21,4 +22,7 @@ def make_json_result(success=1, errormsg='', info='', result={}, data={}):
|
||||
doc['result'] = result
|
||||
doc['data'] = data
|
||||
|
||||
return json.dumps(doc)
|
||||
response = Response(response=json.dumps(doc),
|
||||
status=200,
|
||||
mimetype="text/json")
|
||||
return response
|
Loading…
Reference in New Issue
Block a user