')
.sortable({
// general settings...
forcePlaceholderSize: true,
forceHelperSize: true,
opacity: 0.7,
connectWith: '.panel-content',
helper: _prepareHelper,
start: _startSortHandler,
// - create a new panel when dropping outside of curent panel...
// - remove empty panels...
beforeStop: function(e, ui){
// do this only when dropping outside the panel...
if(ui.item.data('isoutside')
// prevent draggingout the last panel...
// NOTE: 2 because we are taking into account
// the placeholders...
&& panel.find('.sub-panel').length > 2){
wrapWithPanel(ui.item, panel.parent(), ui.offset)
}
panel.trigger('subPanelsUpdated')
_resetSidePanels()
_resetSortedElem(ui.item)
.data('isoutside', false)
},
over: function(e, ui){
ui.item.data('isoutside', false)
ui.placeholder
//.height(ui.helper.outerHeight())
// NOTE: for some reason width does not allways get
// set by jquery-ui...
.width(ui.helper.outerWidth())
.show()
},
out: function(e, ui){
ui.item.data('isoutside', true)
ui.placeholder.hide()
},
})
.appendTo(panel)
if(parent != false){
panel.appendTo(parent)
}
// NOTE: no need to call the panelOpening event here as at this point
// no one had the chance to bind a handler...
return panel
}
// side can be:
// - left
// - right
//
// XXX in part this is exactly the same as makePanel
// XXX need to trigger open/close sub panel events...
function makeSidePanel(side, parent, autohide){
autohide = autohide == null ? 'on' : 'off'
parent = parent == null ? $(PANEL_ROOT) : parent
var panel = $('.side-panel.'+side)
// only one panel from each side can exist...
if(panel.length != 0){
return panel
}
panel = $('')
.addClass('side-panel panel-content ' + side)
.attr('autohide', autohide)
// XXX trigger open/close events on hide/show..
// XXX
// toggle auto-hide...
.dblclick(function(e){
var elem = $(this)
if(elem.attr('autohide') == 'off'){
elem.attr('autohide', 'on')
} else {
elem.attr('autohide', 'off')
}
return false
})
// hide temporarily opened side-panels...
.mouseout(function(){
// XXX jQuery bug: this does not work...
//panel.prop('open', false)
panel.attr('open', null)
})
.sortable({
forcePlaceholderSize: true,
opacity: 0.7,
connectWith: '.panel-content',
helper: _prepareHelper,
start: _startSortHandler,
// - create a new panel when dropping outside of curent panel...
// - remove empty panels...
beforeStop: function(e, ui){
// do this only when dropping outside the panel...
if(ui.item.data('isoutside')){
wrapWithPanel(ui.item, panel.parent(), ui.offset)
}
_resetSidePanels()
_resetSortedElem(ui.item)
.data('isoutside', false)
},
over: function(e, ui){
ui.item.data('isoutside', false)
ui.placeholder
//.height(ui.helper.outerHeight())
// NOTE: for some reason width does not allways get
// set by jquery-ui...
.width(ui.helper.outerWidth())
.show()
},
out: function(e, ui){
ui.item.data('isoutside', true)
ui.placeholder.hide()
},
})
if(parent != false){
panel.appendTo(parent)
}
// NOTE: no need to call the panelOpening event here as at this point
// no one had the chance to bind a handler...
return panel
}
// NOTE: if parent is not given this will create a new panel...
// NOTE: title must be unique...
function makeSubPanel(title, content, parent, open, content_resizable, close_button){
title = title == null || title.trim() == '' ? ' ' : title
parent = parent == null ? makePanel() : parent
close_button = close_button == null ? true : close_button
open = open == null ? true : open
content_resizable = content_resizable == null
? false
: content_resizable
var content_elem = $('')
if(content != null){
content_elem
.append(content)
}
var sub_panel = $(' ')
.attr('id', title)
.addClass('sub-panel noScroll')
.prop('open', open)
.append((close_button
? $(''+title+'')
.append($('')
.addClass('close-button')
.click(function(){
var parent = sub_panel.parents('.panel').first()
removePanel(sub_panel)
// notify the parent context update...
parent.trigger('subPanelsUpdated')
return false
})
.html('×'))
: $(''+title+''))
// XXX also do this on enter...
// XXX
.click(function(){
if(!sub_panel.prop('open')){
sub_panel.trigger('panelOpening', sub_panel)
} else {
sub_panel.trigger('panelClosing', sub_panel)
}
}))
.append(content_elem)
if(parent != null && parent != false){
if(parent.hasClass('panel-content')){
sub_panel.appendTo(parent)
} else {
sub_panel.appendTo(parent.find('.panel-content'))
}
}
if(content_resizable){
// NOTE: we are wrapping the content into a div so as to make
// the fact that the panel is resizable completely
// transparent for the user -- no need to be aware of the
// sizing elements, etc.
content_elem.wrap($('')).parent()
.resizable({
handles: 's',
})
.css({
overflow: 'hidden',
})
}
// NOTE: no need to call the panelOpening event here as at this point
// no one had the chance to bind a handler...
return sub_panel
}
/**********************************************************************
* Actions...
*/
// XXX this should take the state into consideration while opening panels
// and open panels in specific parents and locations, maybe even with
// other neighbor panels...
// XXX currently parent is ignored if panel is already created, is this
// correct???
// XXX update panel state...
function openPanel(panel, parent, no_blink){
var title = typeof(panel) == typeof('str') ? panel : null
panel = typeof(panel) == typeof('str')
? getPanel(panel)
: panel
title = title == null ? panel.attr('id') : title
var open = false
// create a new panel...
if(panel.length == 0){
if(title in PANELS){
var builder = PANELS[title]
panel = builder({
open: true,
parent: parent,
})
}
// show/open the panel and all it's parents...
} else {
open = isPanelVisible(panel)
// show panels...
panel
.css('display', '')
.prop('open', true)
.parents('.panel')
.css('display', '')
.prop('open', true)
// show side panels...
panel
.parents('.side-panel').first()
// XXX jQuery bug: this does not work...
//.prop('open', true)
.attr('open', 'yes')
}
// if the panel was not open trigger the event...
if(!open){
panel.trigger('panelOpening', panel)
}
return no_blink ? panel : blinkPanel(panel)
}
// Open a set of sub-panels in one parent panel...
//
// returns the parent panel.
//
// NOTE: if parent is given and already exists then this will append the
// new panels to it...
// NOTE: this will not re-group already opened panels...
function openGroupedPanels(panels, parent){
panels = typeof(panels) == typeof('str') ? [panels] : panels
parent = parent == null ? makePanel() : parent
panels.forEach(function(title){
openPanel(title, parent, true)
})
return parent
}
// XXX
// XXX update panel state...
function openPanels(){
// XXX
}
// Close the panel...
//
// NOTE: this does not care if it's a panel or sub-panel...
// XXX do we need a panelRemoved event???
// ...and a symmetrical panelCreated??
// XXX update panel state...
function closePanel(panel){
panel = typeof(panel) == typeof('str')
? getPanel(panel)
: panel
panel.find('.sub-panel:visible').each(function(){
var p = $(this)
if(p.prop('open')){
p.trigger('panelClosing', p)
}
})
return panel
.prop('open', false)
.trigger('panelClosing', panel)
}
// Remove the panel after firing close events on it and all sub-panels...
//
// XXX update panel state...
function removePanel(panel){
panel = typeof(panel) == typeof('str')
? getPanel(panel)
: panel
/*
if(PANEL_CLOSE_METHOD == 'hide'){
return closePanel(panel)
.hide()
} else {
return closePanel(panel)
.remove()
}
*/
return closePanel(panel)
.hide()
}
/**********************************************************************
* High level interface...
*/
//
// content_builder() - should build and setup panel content
// panel_setup(panel) - should register panel open/close event
// handlers
//
// NOTE: this will search an element by title, so if it is not unique
// an existing element will be returned...
function Panel(title, content_builder, panel_setup, content_resizable){
var controller = function(state){
state = state == null ? {} : state
var parent = state.parent
var open = state.open
// 1) search for panel and return it if it exists...
var panel = getPanel(title)
// 2) if no panel exists, create it
// - content_builder() must return panel content
if(panel.length == 0){
panel = makeSubPanel(title, content_builder(), parent, open, content_resizable)
.attr('id', title)
panel_setup(panel)
// trigger the open event...
if(isPanelVisible(panel)){
panel.trigger('panelOpening', panel)
}
} else {
var v = isPanelVisible(panel)
if(open && !v){
openPanel(panel)
} else if(!open && v){
closePanel(panel)
}
}
// XXX set panel position, size, ...
return panel
}
PANELS[title] = controller
return controller
}
// XXX also need:
// - togglePanels()
// show/hide all the panels (a-la Photoshop's Tab action)
/*********************************************************************/
function getPanelState(){
var res = []
var _getPanel = function(){
var panel = $(this)
var offset = panel.offset()
var sub_panels = panel.find('.sub-panel')
res.push({
type: (panel.hasClass('panel') ? 'panel'
: panel.hasClass('side-panel')
&& panel.hasClass('left') ? 'side-panel-left'
: panel.hasClass('side-panel')
&& panel.hasClass('right') ? 'side-panel-right'
: null),
top: offset.top,
left: offset.left,
open: panel.prop('open') ? true : false,
autohide: panel.attr('autohide'),
content: sub_panels.map(function(){
var p = $(this)
return {
title: p.find('summary').text(),
}
}).toArray(),
})
}
$('.panel, .side-panel').each(_getPanel)
return res
}
function setPanelState(data){
// XXX
}
/**********************************************************************
* vim:set ts=4 sw=4 : */