mirror of https://github.com/apache/cloudstack.git
442 lines
15 KiB
JavaScript
442 lines
15 KiB
JavaScript
// Licensed to the Apache Software Foundation (ASF) under one
|
|
// or more contributor license agreements. See the NOTICE file
|
|
// distributed with this work for additional information
|
|
// regarding copyright ownership. The ASF licenses this file
|
|
// to you under the Apache License, Version 2.0 (the
|
|
// "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
(function($, cloudStack) {
|
|
cloudStack.ui.widgets.browser = {};
|
|
|
|
/**
|
|
* Breadcrumb-related functions
|
|
*/
|
|
var _breadcrumb = cloudStack.ui.widgets.browser.breadcrumb = {
|
|
/**
|
|
* Generate new breadcrumb
|
|
*/
|
|
create: function($panel, title) {
|
|
// Attach panel as ref for breadcrumb
|
|
return cloudStack.ui.event.elem(
|
|
'cloudBrowser', 'breadcrumb',
|
|
$('<div>')
|
|
.append(
|
|
$('<li>')
|
|
.attr({
|
|
title: title
|
|
})
|
|
.append(
|
|
$('<span>').html(title)
|
|
)
|
|
)
|
|
.append($('<div>').addClass('end'))
|
|
.children(), {
|
|
panel: $panel
|
|
}
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Get breadcrumbs matching specified panels
|
|
*/
|
|
filter: function($panels) {
|
|
var $breadcrumbs = $('#breadcrumbs ul li');
|
|
var $result = $([]);
|
|
|
|
$panels.each(function() {
|
|
var $panel = $(this);
|
|
|
|
$.merge(
|
|
$result,
|
|
$.merge(
|
|
$breadcrumbs.filter(function() {
|
|
return $(this).index('#breadcrumbs ul li') == $panel.index();
|
|
}),
|
|
|
|
// Also include ends
|
|
$breadcrumbs.siblings('div.end').filter(function() {
|
|
return $(this).index('div.end') == $panel.index() + 1;
|
|
})
|
|
)
|
|
);
|
|
});
|
|
|
|
return $result;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Container-related functions
|
|
*/
|
|
var _container = cloudStack.ui.widgets.browser.container = {
|
|
/**
|
|
* Get all panels from container
|
|
*/
|
|
panels: function($container) {
|
|
return $container.find('div.panel');
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Panel-related functions
|
|
*/
|
|
var _panel = cloudStack.ui.widgets.browser.panel = {
|
|
/**
|
|
* Compute width of panel, relative to container
|
|
*/
|
|
width: function($container, options) {
|
|
options = options ? options : {};
|
|
var width = $container.find('div.panel').size() < 1 || options.maximized == true ?
|
|
$container.width() : $container.width() - $container.width() / 4;
|
|
|
|
return width;
|
|
},
|
|
|
|
/**
|
|
* Get left position
|
|
*/
|
|
position: function($container, options) {
|
|
return $container.find('div.panel').size() <= 1 || options.maximized == true ?
|
|
0 : _panel.width($container, options) - _panel.width($container, options) / 1.5;
|
|
},
|
|
|
|
/**
|
|
* Get the top panel z-index, for proper stacking
|
|
*/
|
|
topIndex: function($container) {
|
|
var base = 50; // Minimum z-index
|
|
|
|
return Math.max.apply(
|
|
null,
|
|
$.map(
|
|
$container.find('div.panel'),
|
|
function(elem) {
|
|
return parseInt($(elem).css('z-index')) || base;
|
|
}
|
|
)
|
|
) + 1;
|
|
},
|
|
|
|
/**
|
|
* State when panel is outside container
|
|
*/
|
|
initialState: function($container) {
|
|
return {
|
|
left: $container.width()
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Get panel and breadcrumb behind specific panel
|
|
*/
|
|
lower: function($container, $panel) {
|
|
return _container.panels($container).filter(function() {
|
|
return $(this).index() < $panel.index();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Get panel and breadcrumb stacked above specific panel
|
|
*/
|
|
higher: function($container, $panel) {
|
|
return _container.panels($container).filter(function() {
|
|
return $(this).index() > $panel.index();
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Generate new panel
|
|
*/
|
|
create: function($container, options) {
|
|
var $panel = $('<div>').addClass('panel').css({
|
|
position: 'absolute',
|
|
width: _panel.width($container, {
|
|
maximized: options.maximized
|
|
}),
|
|
zIndex: _panel.topIndex($container)
|
|
}).append(
|
|
// Shadow
|
|
$('<div>').addClass('shadow')
|
|
).append(options.data);
|
|
|
|
if (options.maximized) $panel.addClass('always-maximized');
|
|
|
|
return $panel;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Browser -- jQuery widget
|
|
*/
|
|
$.widget('cloudStack.cloudBrowser', {
|
|
_init: function() {
|
|
this.element.addClass('cloudStack-widget cloudBrowser');
|
|
$('#breadcrumbs').append(
|
|
$('<ul>')
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Make target panel the top-most
|
|
*/
|
|
selectPanel: function(args) {
|
|
var $panel = args.panel;
|
|
var $container = this.element;
|
|
var $toShow = _panel.lower($container, $panel);
|
|
var $toRemove = _panel.higher($container, $panel);
|
|
var complete = args.complete;
|
|
|
|
if ($panel.hasClass('maximized')) return false;
|
|
|
|
_breadcrumb.filter($toRemove).remove();
|
|
_breadcrumb.filter($panel.siblings()).removeClass('active');
|
|
_breadcrumb.filter($panel).addClass('active');
|
|
_breadcrumb.filter($('div.panel')).find('span').animate({
|
|
opacity: 1
|
|
});
|
|
_breadcrumb.filter(
|
|
$('div.panel.maximized')
|
|
.removeClass('maximized')
|
|
.addClass('reduced')
|
|
).removeClass('active maximized');
|
|
|
|
$toRemove.animate(
|
|
_panel.initialState($container), {
|
|
duration: 500,
|
|
complete: function() {
|
|
$(this).remove();
|
|
|
|
if (complete) complete($toShow);
|
|
}
|
|
}
|
|
);
|
|
$toShow.show();
|
|
$panel.animate({
|
|
left: _panel.position($container, {
|
|
maximized: $panel.hasClass('always-maximized')
|
|
})
|
|
});
|
|
$panel.show().removeClass('reduced');
|
|
},
|
|
|
|
/**
|
|
* Toggle selected panel as fully expanded, hiding/showing other panels
|
|
*/
|
|
toggleMaximizePanel: function(args) {
|
|
var $panel = args.panel;
|
|
var $container = this.element;
|
|
var $toHide = $panel.siblings(':not(.always-maximized)');
|
|
var $shadow = $toHide.find('div.shadow');
|
|
|
|
if (args.panel.hasClass('maximized')) {
|
|
_breadcrumb.filter($panel).removeClass('maximized');
|
|
$panel.removeClass('maximized');
|
|
$panel.addClass('reduced');
|
|
_breadcrumb.filter($panel.siblings()).find('span').animate({
|
|
opacity: 1
|
|
});
|
|
$toHide.animate({
|
|
left: _panel.position($container, {})
|
|
}, {
|
|
duration: 500
|
|
});
|
|
$shadow.show();
|
|
} else {
|
|
_breadcrumb.filter($panel).addClass('maximized');
|
|
$panel.removeClass('reduced');
|
|
$panel.addClass('maximized');
|
|
_breadcrumb.filter($panel.siblings()).find('span').animate({
|
|
opacity: 0.5
|
|
});
|
|
$toHide.animate(_panel.initialState($container), {
|
|
duration: 500
|
|
});
|
|
$shadow.hide();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Append new panel to end of container
|
|
*/
|
|
addPanel: function(args) {
|
|
var duration = args.duration ? args.duration : 500;
|
|
var $container = this.element;
|
|
var $parent = args.parent;
|
|
var $panel, $reduced, targetPosition;
|
|
|
|
// Create panel
|
|
$panel = _panel.create(this.element, {
|
|
maximized: args.maximizeIfSelected,
|
|
data: args.data
|
|
});
|
|
|
|
// Remove existing panels from parent
|
|
if ($parent) {
|
|
// Cleanup transitioning panels -- prevent old complete actions from running
|
|
$parent.siblings().stop();
|
|
|
|
_breadcrumb.filter(
|
|
$('div.panel.maximized')
|
|
.removeClass('maximized')
|
|
.addClass('reduced')
|
|
).removeClass('active maximized');
|
|
|
|
$parent.removeClass('maximized');
|
|
_breadcrumb.filter($parent.next()).remove();
|
|
$container.find($parent.next()).remove();
|
|
}
|
|
|
|
// Append panel
|
|
$panel.appendTo($container);
|
|
_breadcrumb.filter($panel.siblings()).removeClass('active');
|
|
_breadcrumb.create($panel, args.title)
|
|
.addClass('active')
|
|
.appendTo('#breadcrumbs ul');
|
|
|
|
// Reduced appearance for previous panels
|
|
$panel.siblings().filter(function() {
|
|
return $(this).index() < $panel.index();
|
|
}).addClass('reduced');
|
|
|
|
// Panel initial state
|
|
if ($panel.index() == 0) $panel.addClass('always-maximized');
|
|
$panel.css(
|
|
_panel.initialState($container, $panel)
|
|
);
|
|
|
|
// Panel slide-in
|
|
targetPosition = _panel.position($container, {
|
|
maximized: args.maximizeIfSelected
|
|
});
|
|
if (!$panel.index()) {
|
|
// Just show immediately if this is the first panel
|
|
$panel.css({
|
|
left: targetPosition
|
|
});
|
|
if (args.complete) args.complete($panel, _breadcrumb.filter($panel));
|
|
} else {
|
|
// Animate slide-in
|
|
$panel.animate({
|
|
left: targetPosition
|
|
}, {
|
|
duration: duration,
|
|
easing: 'easeOutCirc',
|
|
complete: function() {
|
|
// Hide panels
|
|
$panel.siblings().filter(function() {
|
|
return $(this).width() == $panel.width();
|
|
});
|
|
|
|
if ($panel.is(':visible') && args.complete) args.complete($panel);
|
|
}
|
|
});
|
|
};
|
|
|
|
return $panel;
|
|
},
|
|
|
|
/**
|
|
* Clear all panels
|
|
*/
|
|
removeAllPanels: function(args) {
|
|
$('div.panel').stop(); // Prevent destroyed panels from animating
|
|
this.element.find('div.panel').remove();
|
|
$('#breadcrumbs').find('ul li').remove();
|
|
$('#breadcrumbs').find('ul div.end').remove();
|
|
}
|
|
});
|
|
|
|
$('#breadcrumbs li').live('click', cloudStack.ui.event.bind(
|
|
'cloudBrowser', {
|
|
'breadcrumb': function($target, $browser, data) {
|
|
|
|
if ($('#browser').hasClass('panel-highlight')) {
|
|
return false;
|
|
}
|
|
|
|
$browser.cloudBrowser('selectPanel', {
|
|
panel: data.panel
|
|
});
|
|
}
|
|
}
|
|
));
|
|
|
|
// Breadcrumb hovering
|
|
$('#breadcrumbs li').live('mouseover', cloudStack.ui.event.bind(
|
|
'cloudBrowser', {
|
|
'breadcrumb': function($target, $browser, data) {
|
|
var $hiddenPanels = data.panel.siblings().filter(function() {
|
|
return $(this).index() > data.panel.index();
|
|
});
|
|
var $targetPanel = data.panel.filter(':first');
|
|
var $targetBreadcrumb = _breadcrumb.filter($targetPanel);
|
|
var $panelWrapper = $('<div>').addClass('panel panel-highlight-wrapper');
|
|
|
|
$hiddenPanels.addClass('mouseover-hidden');
|
|
|
|
$browser.data('browser-panel-highlight-timer', setTimeout(function() {
|
|
$('#browser').addClass('panel-highlight');
|
|
$('.overlay').remove();
|
|
|
|
// Setup panel and wrapper positioning
|
|
$panelWrapper
|
|
.css({
|
|
left: $targetPanel.position().left
|
|
})
|
|
.width($targetPanel.width());
|
|
$targetPanel
|
|
.wrap($panelWrapper);
|
|
$panelWrapper
|
|
.zIndex(10000)
|
|
.overlay();
|
|
$targetPanel.filter(':last').addClass('highlighted');
|
|
|
|
// Setup breadcrumbs
|
|
$targetBreadcrumb.each(function() {
|
|
$(this).data('breadcrumb-original-zindex', $(this).zIndex());
|
|
});
|
|
$targetBreadcrumb.zIndex(10001);
|
|
|
|
$hiddenPanels.hide();
|
|
}, 1000));
|
|
}
|
|
}
|
|
));
|
|
|
|
$('#breadcrumbs li').live('mouseout', cloudStack.ui.event.bind(
|
|
'cloudBrowser', {
|
|
'breadcrumb': function($target, $browser, data) {
|
|
var $getHiddenPanels = $browser.find('.panel.mouseover-hidden');
|
|
var $visiblePanels = $getHiddenPanels.siblings();
|
|
var $visibleBreadcrumbs = _breadcrumb.filter($visiblePanels);
|
|
|
|
clearTimeout($browser.data('browser-panel-highlight-timer'));
|
|
$('#browser').removeClass('panel-highlight');
|
|
$('#browser .panel').removeClass('highlighted');
|
|
$('#browser .panel.panel-highlight-wrapper').each(function() {
|
|
var $wrapper = $(this);
|
|
var $panel = $wrapper.find('.panel');
|
|
|
|
$wrapper.after($panel);
|
|
$wrapper.remove();
|
|
});
|
|
$getHiddenPanels.removeClass('mouseover-hidden').show();
|
|
$visibleBreadcrumbs.each(function() {
|
|
$(this).zIndex($(this).data('breadcrumb-original-zindex'));
|
|
});
|
|
$('.overlay').remove();
|
|
$('#browser .panel > .highlight-arrow').remove();
|
|
}
|
|
}
|
|
));
|
|
})(jQuery, cloudStack);
|