silverstripe-framework/admin/javascript/LeftAndMain.ActionTabSet.js

240 lines
7.9 KiB
JavaScript

/**
* File: LeftAndMain.ActionTabset.js
*
* Contains rules for .ss-ui-action-tabset, used for:
* * Site tree action tabs (to perform actions on the site tree)
* * Actions menu (Edit page actions)
*
*/
(function($){
$.entwine('ss', function($) {
/**
* Generic rules for all ss-ui-action-tabsets
* * ActionMenus
* * SiteTree ActionTabs
*/
$('.ss-tabset.ss-ui-action-tabset').entwine({
// Ignore tab state so it will not be reopened on form submission.
IgnoreTabState: true,
onadd: function() {
// Make sure the .ss-tabset is already initialised to apply our modifications on top.
this._super();
//Set actionTabs to allow closing and be closed by default
this.tabs({'collapsible': true, 'active': false});
},
onremove: function() {
// Remove all bound events.
// This guards against an edge case where the click handlers are not unbound
// because the panel is still open when the ajax edit form reloads.
var frame = $('.cms-container').find('iframe');
frame.each(function(index, iframe){
$(iframe).contents().off('click.ss-ui-action-tabset');
});
$(document).off('click.ss-ui-action-tabset');
this._super();
},
/**
* Deal with available vertical space
*/
'ontabsbeforeactivate': function(event, ui) {
this.riseUp(event, ui);
},
/**
* Handle opening and closing tabs
*/
onclick: function(event, ui) {
this.attachCloseHandler(event, ui);
},
/**
* Generic function to close open tabs. Stores event in a handler,
* and removes the bound event once activated.
*
* Note: Should be called by a click event attached to 'this'
*/
attachCloseHandler: function(event, ui) {
var that = this, frame = $('.cms-container').find('iframe'), closeHandler;
// Create a handler for the click event so we can close tabs
// and easily remove the event once done
closeHandler = function(event) {
var panel, frame;
panel = $(event.target).closest('.ss-ui-action-tabset .ui-tabs-panel');
// If anything except the ui-nav button or panel is clicked,
// close panel and remove handler. We can't close if click was
// within panel, as it might've caused a button action,
// and we need to show its loading indicator.
if (!$(event.target).closest(that).length && !panel.length) {
that.tabs('option', 'active', false); // close tabs
// remove click event from objects it is bound to (iframe's and document)
frame = $('.cms-container').find('iframe');
frame.each(function(index, iframe){
$(iframe).contents().off('click.ss-ui-action-tabset', closeHandler);
});
$(document).off('click.ss-ui-action-tabset', closeHandler);
}
};
// Bind click event to document, and use closeHandler to handle the event
$(document).on('click.ss-ui-action-tabset', closeHandler);
// Make sure iframe click also closes tab
// iframe needs a special case, else the click event will not register here
if(frame.length > 0){
frame.each(function(index, iframe) {
$(iframe).contents().on('click.ss-ui-action-tabset', closeHandler);
});
}
},
/**
* Function riseUp checks to see if a tab should be opened upwards
* (based on space concerns). If true, the rise-up class is applied
* and a new position is calculated and applied to the element.
*
* Note: Should be called by a tabsbeforeactivate event
*/
riseUp: function(event, ui) {
var elHeight, trigger, endOfWindow, elPos, activePanel, activeTab, topPosition, containerSouth, padding;
// Get the numbers needed to calculate positions
elHeight = $(this).find('.ui-tabs-panel').outerHeight();
trigger = $(this).find('.ui-tabs-nav').outerHeight();
endOfWindow = ($(window).height() + $(document).scrollTop()) - trigger;
elPos = $(this).find('.ui-tabs-nav').offset().top;
activePanel = ui.newPanel;
activeTab = ui.newTab;
if (elPos + elHeight >= endOfWindow && elPos - elHeight > 0){
this.addClass('rise-up');
if (activeTab.position() !== null){
topPosition = -activePanel.outerHeight();
containerSouth = activePanel.parents('.south');
if (containerSouth){
// If container is the southern panel, make tab appear from the top of the container
padding = activeTab.offset().top - containerSouth.offset().top;
topPosition = topPosition-padding;
}
$(activePanel).css('top',topPosition+"px");
}
} else {
// else remove the rise-up class and set top to 0
this.removeClass('rise-up');
if (activeTab.position() !== null){
$(activePanel).css('top','0px');
}
}
return false;
}
});
/**
* ActionMenus
* * Specific rules for ActionMenus, used for edit page actions
*/
$('.cms-content-actions .ss-tabset.ss-ui-action-tabset').entwine({
/**
* Make necessary adjustments before tab is activated
*/
'ontabsbeforeactivate': function(event, ui) {
this._super(event, ui);
//Set the position of the opening tab (if it exists)
if($(ui.newPanel).length > 0){
$(ui.newPanel).css('left', ui.newTab.position().left+"px");
}
}
});
/**
* SiteTree ActionTabs
* Specific rules for site tree action tabs. Applies to tabs
* within the expanded content area, and within the sidebar
*/
$('.cms-actions-row.ss-tabset.ss-ui-action-tabset').entwine({
/**
* Make necessary adjustments before tab is activated
*/
'ontabsbeforeactivate': function(event, ui) {
this._super(event, ui);
// Remove tabset open classes (Last gets a unique class
// in the bigger sitetree. Remove this if we have it)
$(this).closest('.ss-ui-action-tabset')
.removeClass('tabset-open tabset-open-last');
}
});
/**
* SiteTree ActionTabs: expanded
* * Specific rules for siteTree actions within the expanded content area.
*/
$('.cms-content-fields .ss-tabset.ss-ui-action-tabset').entwine({
/**
* Make necessary adjustments before tab is activated
*/
'ontabsbeforeactivate': function(event, ui) {
this._super(event, ui);
if($( ui.newPanel).length > 0){
if($(ui.newTab).hasClass("last")){
// Align open tab to the right (because opened tab is last)
$(ui.newPanel).css({'left': 'auto', 'right': '0px'});
// Last needs to be styled differently when open, so apply a unique class
$(ui.newPanel).parent().addClass('tabset-open-last');
}else{
// Assign position to tabpanel based on position of relivent active tab item
$(ui.newPanel).css('left', ui.newTab.position().left+"px");
// If this is the first tab, make sure the position doesn't include border
// (hard set position to 0 ), and add the tab-set open class
if($(ui.newTab).hasClass("first")){
$(ui.newPanel).css('left',"0px");
$(ui.newPanel).parent().addClass('tabset-open');
}
}
}
}
});
/**
* SiteTree ActionTabs: sidebar
* * Specific rules for when the site tree actions panel appears in
* * the side-bar
*/
$('.cms-tree-view-sidebar .cms-actions-row.ss-tabset.ss-ui-action-tabset').entwine({
// If actions panel is within the sidebar, apply active class
// to help animate open/close on hover
'from .ui-tabs-nav li': {
onhover: function(e) {
$(e.target).parent().find('li .active').removeClass('active');
$(e.target).find('a').addClass('active');
}
},
/**
* Make necessary adjustments before tab is activated
*/
'ontabsbeforeactivate': function(event, ui) {
this._super(event, ui);
// Reset position of tabs, else anyone going between the large
// and the small sitetree will see broken tabs
// Apply styles with .css, to avoid overriding currently applied styles
$(ui.newPanel).css({'left': 'auto', 'right': 'auto'});
if($(ui.newPanel).length > 0){
$(ui.newPanel).parent().addClass('tabset-open');
}
}
});
});
}(jQuery));